复数矩阵QR分解算法的C++实现

头文件:

/*
 * Copyright (c) 2008-2011 Zhang Ming (M. Zhang), [email protected]
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 2 or any later version.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details. A copy of the GNU General Public License is available at:
 * http://www.fsf.org/licensing/licenses
 */


/*****************************************************************************
 *                                    cqrd.h
 *
 * Class template of QR decomposition for complex matrix.
 *
 * For an m-by-n complex matrix A, the QR decomposition is an m-by-m unitary
 * matrix Q and an m-by-n upper triangular matrix R so that A = Q*R.
 *
 * For economy size, denotes p = min(m,n), then Q is m-by-p, and R is n-by-p,
 * this file provides the economic decomposition format.
 *
 * The QR decompostion always exists, even if the matrix does not have full
 * rank, so the constructor will never fail. The Q and R factors can be
 * retrived via the getQ() and getR() methods. Furthermore, a solve() method
 * is provided to find the least squares solution of Ax=b or AX=B using the
 * QR factors.
 *
 * Zhang Ming, 2010-12, Xi'an Jiaotong University.
 *****************************************************************************/


#ifndef CQRD_H
#define CQRD_H


#include <matrix.h>


namespace splab
{

	template <typename Type>
	class CQRD
	{

	public:

		CQRD();
		~CQRD();

		void dec( const Matrix<complex<Type> > &A );
		bool isFullRank() const;

		Matrix<complex<Type> > getQ();
		Matrix<complex<Type> > getR();

		Vector<complex<Type> > solve( const Vector<complex<Type> > &b );
		Matrix<complex<Type> > solve( const Matrix<complex<Type> > &B );

    private:

		// internal storage of QR
		Matrix<complex<Type> > QR;

		// diagonal of R
		Vector<complex<Type> > diagR;

		// constants for generating Householder vector
		Vector<Type> betaR;


	};
	// class CQRD


    #include <cqrd-impl.h>

}
// namespace splab


#endif
// CQRD_H

实现文件:

/*
 * Copyright (c) 2008-2011 Zhang Ming (M. Zhang), [email protected]
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 2 or any later version.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details. A copy of the GNU General Public License is available at:
 * http://www.fsf.org/licensing/licenses
 */


/*****************************************************************************
 *                               qrd-impl.h
 *
 * Implementation for CQRD class.
 *
 * Zhang Ming, 2010-12, Xi'an Jiaotong University.
 *****************************************************************************/


/**
 * constructor and destructor
 */
template<typename Type>
CQRD<Type>::CQRD()
{
}

template<typename Type>
CQRD<Type>::~CQRD()
{
}


/**
 * Create a QR factorization for a complex matrix A.
 */
template <typename Type>
void CQRD<Type>::dec( const Matrix<complex<Type> > &A )
{
    int m = A.rows(),
        n = A.cols(),
        p = min(m,n);

    Type absV0, normV, beta;
    complex<Type> alpha;

    diagR = Vector<complex<Type> >(p);
    betaR = Vector<Type>(p);
	QR = A;

    // main loop.
    for( int k=0; k<p; ++k )
    {
		// Form k-th Householder vector.
		normV = 0;
		for( int i=k; i<m; ++i )
			normV += norm(QR[i][k]);
        normV = sqrt(normV);

		absV0 = abs(QR[k][k]);
		alpha = -normV * QR[k][k]/absV0;
		beta  = 1 / ( normV * (normV+absV0) );
		QR[k][k] -= alpha;

		// Apply transformation to remaining columns.
		for( int j=k+1; j<n; ++j )
		{
			complex<Type> s = 0;
			for( int i=k; i<m; ++i )
				s += conj(QR[i][k]) * QR[i][j];
            s *= beta;
			for( int i=k; i<m; ++i )
				QR[i][j] -= s*QR[i][k];
		}

		diagR[k] = alpha;
		betaR[k] = beta;
	}

//    int m = A.rows(),
//        n = A.cols(),
//        p = min(m,n);
//
//    Type absV0, normV, beta;
//    complex<Type> alpha;
//
//    diagR = Vector<complex<Type> >(p);
//    betaR = Vector<Type>(p);
//	QR = A;
//
//    // main loop.
//    for( int k=0; k<p; ++k )
//    {
//		// Form k-th Householder vector.
//		Vector<complex<Type> > v(m-k);
//		for( int i=k; i<m; ++i )
//			v[i-k] = QR[i][k];
//
//		absV0 = abs(v[0]);
//		normV = norm(v);
//		alpha = -normV * v[0]/absV0;
//		beta  = 1 / ( normV * (normV+absV0) );
//		v[0] -= alpha;
//
//		for( int i=k; i<m; ++i )
//			QR[i][k] = v[i-k];
//
//		// Apply transformation to remaining columns.
//		for( int j=k+1; j<n; ++j )
//		{
//			complex<Type> s = 0;
//			for( int i=k; i<m; ++i )
//				s += conj(v[i-k]) * QR[i][j];
//			for( int i=k; i<m; ++i )
//				QR[i][j] -= beta*s*v[i-k];
//		}
//
//		diagR[k] = alpha;
//		betaR[k] = beta;
//	}
}


/**
 * Flag to denote the matrix is of full rank.
 */
template <typename Type>
inline bool CQRD<Type>::isFullRank() const
{
    for( int j=0; j<diagR.dim(); ++j )
        if( abs(diagR[j]) == Type(0) )
            return false;

    return true;
}


/**
 * Return the upper triangular factorof the QR factorization.
 */
template <typename Type>
Matrix<complex<Type> > CQRD<Type>::getQ()
{
    int m = QR.rows(),
        p = betaR.dim();

    Matrix<complex<Type> >Q( m, p );
//	for( int i=0; i<p; ++i )
//		Q[i][i] = 1;

    for( int k=p-1; k>=0; --k )
    {
		Q[k][k] = 1 - betaR[k]*norm(QR[k][k]);
		for( int i=k+1; i<m; ++i )
			Q[i][k] = -betaR[k]*QR[i][k]*conj(QR[k][k]);

		for( int j=k+1; j<p; ++j )
		{
			complex<Type> s = 0;
			for( int i=k; i<m; ++i )
				s += conj(QR[i][k])*Q[i][j];
            s *= betaR[k];

			for( int i=k; i<m; ++i )
				Q[i][j] -= s * QR[i][k];

		}
	}

	return Q;
}


/**
 * Return the orthogonal factorof the QR factorization.
 */
template <typename Type>
Matrix<complex<Type> > CQRD<Type>::getR()
{
    int n = QR.cols(),
        p = diagR.dim();
    Matrix<complex<Type> > R( p, n );

    for( int i=0; i<p; ++i )
	{
		R[i][i] = diagR[i];

        for( int j=i+1; j<n; ++j )
            R[i][j] = QR[i][j];
	}

    return R;
}


/**
 * Least squares solution of A*x = b, where A and b are complex.
 * Return x: a n-length vector that minimizes the two norm
 * of Q*R*X-B. If B is non-conformant, or if QR.isFullRank()
 * is false, the routinereturns a null (0-length) vector.
 */
template <typename Type>
Vector<complex<Type> > CQRD<Type>::solve( const Vector<complex<Type> > &b )
{
    int m = QR.rows(),
        n = QR.cols();

    assert( b.dim() == m );

    // matrix is rank deficient
    if( !isFullRank() )
        return Vector<complex<Type> >();

    Vector<complex<Type> > x = b;

    // compute y = Q^H * b
    for( int k=0; k<n; ++k )
    {
        complex<Type> s = 0;
        for( int i=k; i<m; ++i )
            s += conj(QR[i][k])*x[i];

        s *= betaR[k];
        for( int i=k; i<m; ++i )
            x[i] -= s * QR[i][k];
    }

    // solve R*x = y;
    for( int k=n-1; k>=0; --k )
    {
        x[k] /= diagR[k];
        for( int i=0; i<k; ++i )
            x[i] -= x[k]*QR[i][k];
    }

    // return n portion of x
    Vector<complex<Type> > x_(n);
    for( int i=0; i<n; ++i )
        x_[i] = x[i];

    return x_;
}


/**
 * Least squares solution of A*X = B, where A and B are complex.
 * return X: a matrix that minimizes the two norm of Q*R*X-B.
 * If B is non-conformant, or if QR.isFullRank() is false, the
 * routinereturns a null (0) matrix.
 */
template <typename Type>
Matrix<complex<Type> > CQRD<Type>::solve( const Matrix<complex<Type> > &B )
{
    int m = QR.rows();
    int n = QR.cols();

    assert( B.rows() == m );

    // matrix is rank deficient
    if( !isFullRank() )
        return Matrix<complex<Type> >(0,0);

    int nx = B.cols();
    Matrix<complex<Type> > X = B;

    // compute Y = Q^H*B
    for( int k=0; k<n; ++k )
        for( int j=0; j<nx; ++j )
        {
            complex<Type> s = 0;
            for( int i=k; i<m; ++i )
                s += conj(QR[i][k])*X[i][j];

            s *= betaR[k];
            for( int i=k; i<m; ++i )
                X[i][j] -= s*QR[i][k];
        }

    // solve R*X = Y;
    for( int k=n-1; k>=0; --k )
    {
        for( int j=0; j<nx; ++j )
            X[k][j] /= diagR[k];

        for( int i=0; i<k; ++i )
            for( int j=0; j<nx; ++j )
                X[i][j] -= X[k][j]*QR[i][k];
    }

    // return n x nx portion of X
    Matrix<complex<Type> > X_( n, nx );
    for( int i=0; i<n; ++i )
        for( int j=0; j<nx; ++j )
            X_[i][j] = X[i][j];

     return X_;
}

测试代码:

/*****************************************************************************
 *                               cqrd_test.cpp
 *
 * CQRD class testing.
 *
 * Zhang Ming, 2010-12, Xi'an Jiaotong University.
 *****************************************************************************/


#define BOUNDS_CHECK

#include <iostream>
#include <iomanip>
#include <cqrd.h>


using namespace std;
using namespace splab;


typedef double  Type;
const   int     M = 4;
const   int     N = 3;


int main()
{
	Matrix<Type> A(M,N);
	A[0][0] = 1;	A[0][1] = 2;	A[0][2] = 3;
	A[1][0] = 1;	A[1][1] = 2;	A[1][2] = 4;
	A[2][0] = 1;	A[2][1] = 9;	A[2][2] = 7;
	A[3][0] = 5;	A[3][1] = 6;	A[3][2] = 8;
//	A = trT(A);

	Matrix<complex<Type> > cA = complexMatrix( A, elemMult(A,A) );
	CQRD<Type> qr;
	qr.dec(cA);
	Matrix<complex<Type> > Q = qr.getQ();
	Matrix<complex<Type> > R = qr.getR();

    cout << setiosflags(ios::fixed) << setprecision(3);
	cout << "The original matrix cA : " << cA << endl;
	cout << "The orthogonal matrix Q  : " << Q << endl;
	cout << "The upper triangular matrix R : " << R << endl;
	cout << "Q^H * Q : " << trMult(Q,Q) << endl;
	cout << "cA - Q*R : " << cA - Q*R << endl;

    Vector<Type> b(M);
	b[0]= 1;	b[1] = 0;	b[2] = 1, b[3] = 2;
	Vector<complex<Type> > cb = complexVector(b);

	if( qr.isFullRank() )
	{
	    Vector<complex<Type> > x = qr.solve(cb);
        cout << "The constant vector cb : " << cb << endl;
        cout << "The least squares solution of cA * x = cb : " << x << endl;

        Matrix<complex<Type> > X = qr.solve( eye( M, complex<Type>(1) ) );
        cout << "The least squares solution of cA * X = I : " << X << endl;
        cout << "The cA * X: " << cA*X << endl;
	}
	else
        cout << " The matrix is rank deficient! " << endl;

	return 0;
}

运行结果:

The original matrix cA : size: 4 by 3
(1.000,1.000)   (2.000,4.000)   (3.000,9.000)
(1.000,1.000)   (2.000,4.000)   (4.000,16.000)
(1.000,1.000)   (9.000,81.000)  (7.000,49.000)
(5.000,25.000)  (6.000,36.000)  (8.000,64.000)

The orthogonal matrix Q  : size: 4 by 3
(-0.055,0.000)  (-0.029,-0.000) (0.370,-0.040)
(-0.055,0.000)  (-0.029,-0.000) (0.913,-0.143)
(-0.055,0.000)  (-0.985,-0.158) (-0.042,-0.000)
(-0.828,-0.552) (0.043,0.039)   (-0.063,-0.030)

The upper triangular matrix R : size: 3 by 3
(-18.111,-18.111)       (-25.565,-31.418)       (-42.737,-52.676)
(0.000,0.000)   (-20.071,-77.269)       (-11.956,-45.432)
(0.000,0.000)   (0.000,0.000)   (-0.593,12.784)

Q^H * Q : size: 3 by 3
(1.000,0.000)   (0.000,0.000)   (-0.000,-0.000)
(0.000,-0.000)  (1.000,0.000)   (-0.000,0.000)
(-0.000,0.000)  (-0.000,-0.000) (1.000,0.000)

cA - Q*R : size: 4 by 3
(-0.000,-0.000) (-0.000,-0.000) (-0.000,-0.000)
(0.000,0.000)   (-0.000,-0.000) (-0.000,-0.000)
(0.000,0.000)   (0.000,0.000)   (0.000,-0.000)
(0.000,0.000)   (0.000,0.000)   (0.000,-0.000)

The constant vector cb : size: 4 by 1
(1.000,0.000)
(0.000,0.000)
(1.000,0.000)
(2.000,0.000)

The least squares solution of cA * x = cb : size: 3 by 1
(-0.002,-0.035)
(-0.002,-0.002)
(0.007,-0.016)

The least squares solution of cA * X = I : size: 3 by 4
(-0.007,0.048)  (-0.025,0.120)  (-0.002,0.012)  (0.004,-0.048)
(-0.001,0.017)  (-0.004,0.042)  (0.001,-0.014)  (-0.001,-0.002)
(0.002,-0.029)  (0.008,-0.072)  (0.000,0.003)   (0.003,0.005)

The cA * X: size: 4 by 4
(0.143,-0.000)  (0.348,0.017)   (0.016,-0.003)  (0.022,-0.016)
(0.348,-0.017)  (0.858,0.000)   (-0.007,0.001)  (-0.009,0.007)
(0.016,0.003)   (-0.007,-0.001) (1.000,-0.000)  (-0.000,0.000)
(0.022,0.016)   (-0.009,-0.007) (-0.000,-0.000) (0.999,0.000)


Process returned 0 (0x0)   execution time : 0.109 s
Press any key to continue.

你可能感兴趣的:(复数矩阵QR分解算法的C++实现)