复数矩阵SVD分解算法的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
 */


/*****************************************************************************
 *                                    csvd.h
 *
 * Class template for complex matrix Singular Value Decomposition.
 *
 * For an m-by-n complex matrix A, the singular value decomposition is an
 * m-by-m unitary matrix U, an m-by-n diagonal matrix S, and an n-by-n
 * unitary matrix V so that A = U*S*V^H.
 *
 * The singular values, sigma[k] = S[k][k], are ordered so that sigma[0] >=
 * sigma[1] >= ... >= sigma[n-1].
 *
 * For economy size, denotes p = min(m,n), then U is m-by-p, S is p-by-p,
 * and V is n-by-p, this file provides the economic decomposition format.
 *
 * The singular value decompostion always exists, so the constructor will
 * never fail. The matrix condition number and the effective numerical rank
 * can be computed from this decomposition.
 *
 * This code is adapted from Collected Algorithms from ACM, Algorithm 358,
 * Peter Businger, Gene Golub.
 *
 * Zhang Ming, 2010-12, Xi'an Jiaotong University.
 *****************************************************************************/


#ifndef CSVD_H
#define CSVD_H


#include <matrix.h>


namespace splab
{

	template <typename Type>
	class CSVD
	{

	public:

        CSVD();
		~CSVD();

        void dec( const Matrix< complex<Type> >& );
		Matrix< complex<Type> > getU() const;
		Matrix< complex<Type> > getV() const;
		Matrix<Type> getSM();
		Vector<Type> getSV() const;

		Type norm2() const;
		Type cond() const;
		int  rank();

    private:

		// the orthogonal matrix and singular value vector
		Matrix< complex<Type> > U;
		Matrix< complex<Type> > V;
		Vector<Type> S;

		// docomposition for matrix with rows >= columns
		void decomposition( Matrix< complex<Type> >&,
                            Matrix< complex<Type> >&,
                            Vector<Type>&,
                            Matrix< complex<Type> >& );

	};
	// class CSVD


    #include <csvd-impl.h>

}
// namespace splab


#endif
// CSVD_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
 */


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


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

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


/**
 * Making singular decomposition.
 */
template <typename Type>
void CSVD<Type>::dec( const Matrix< complex<Type> > &A )
{
    int m = A.rows(),
        n = A.cols(),
        p = min( m, n );

    U.resize( m, p );
    V.resize( n, p );
    S.resize( p );
    if( m >= n )
    {
        Matrix< complex<Type> > B(A);
        decomposition( B, U, S, V );
    }
    else
    {
        Matrix< complex<Type> > B( trH( A ) );
        decomposition( B, V, S, U );
    }
}


/**
 * The singular value decomposition of a complex M by N (N<=M)
 * matrix A has the form A = U S V*, where:
 *     U is an M by N unitary matrix (for economy size),
 *     S is an M by N diagonal matrix,
 *     V is an N by N unitary matrix.
 *
 * Input:
 *     a ---> the complex matrix to be decomposition.
 *     m ---> the number of rows in a.
 *     n ---> the number of columns in a.
 *
 * Output:
 *     u ---> the left singular vectors.
 *     s ---> the singular values.
 *     v ---> the right singular vectors.
 */
template <typename Type>
void CSVD<Type>::decomposition( Matrix< complex<Type> > &B,
                                Matrix< complex<Type> > &U,
                                Vector<Type> &S,
                                Matrix< complex<Type> > &V )
{
    int k, k1,
        L, L1,
        nM1,
        m = B.rows(),
        n = B.cols();

    Type    tol, eta;
	Type    w, x, y, z;
	Type    cs, sn, f, g, h;
    complex<Type>   q;

    Vector<Type>    b(m), c(m), t(m);

    L = 0;
    nM1 = n - 1;
	eta = 2.8E-16;      // the relative machine precision

	// Householder Reduction
	c[0] = 0;
	k = 0;
	while( 1 )
	{
		k1 = k + 1;

		// elimination of a[i][k], i = k, ..., m-1
		z = 0;
		for( int i=k; i<m; ++i )
			z += norm(B[i][k]);
		b[k] = 0;

		if( z > EPS )
		{
			z = sqrt(z);
			b[k] = z;
			w = abs(B[k][k]);
			q = 1;
			if( w != Type(0) )
                q = B[k][k] / w;

            B[k][k] = q * ( z + w );

			if( k != n - 1 )
                for( int j=k1; j<n; ++j )
				{
                    q = 0;
					for( int i=k; i<m; ++i )
                        q += conj(B[i][k]) * B[i][j];
                    q /= z * ( z + w );

					for( int i=k; i<m; ++i )
                        B[i][j] -= q * B[i][k];
				}

			// phase transformation
			q = -conj(B[k][k]) / abs(B[k][k]);
			for( int j=k1; j<n; ++j )
				B[k][j] *= q;
		}

		// elimination of a[k][j], j = k+2, ..., n-1
		if( k == nM1 )
            break;
		z = 0;
		for( int j=k1; j<n; ++j )
            z += norm( B[k][j] );
		c[k1] = 0;

		if( z > EPS )
		{
			z = sqrt(z);
			c[k1] = z;
			w = abs( B[k][k1] );
			q = 1;
			if( w != Type(0) )
				q = B[k][k1] / w;

			B[k][k1] = q * ( z + w );
			for( int i=k1; i<m; ++i )
			{
				q = 0;
				for( int j=k1; j<n; ++j )
					q += conj(B[k][j]) * B[i][j];

				q /= z * ( z + w );
				for( int j=k1; j<n; ++j )
					B[i][j] -= q * B[k][j];
			}

			// phase transformation
			q = -conj(B[k][k1]) / abs(B[k][k1]);
			for( int i=k1; i<m; ++i )
                B[i][k1] *= q;
		}
		k = k1;
	}

	// tolerance for negligible elements
	tol = 0;
	for( k=0; k<n; ++k )
	{
		S[k] = b[k];
		t[k] = c[k];
		if( S[k]+t[k] > tol )
            tol = S[k] + t[k];
	}
	tol *= eta;

	// initialization fo U and V
    for( int j=0; j<n; ++j )
    {
        for( int i=0; i<m; ++i )
            U[i][j] = 0;
        U[j][j] = 1;

        for( int i=0; i<n; ++i )
            V[i][j] = 0;
        V[j][j] = 1;
    }

	// QR diagonallization
	for( k = nM1; k >= 0; --k )
	{
		// test for split
		while( 1 )
		{
			for( L=k; L>=0; --L )
            {
				if( abs(t[L]) <= tol )
                    goto Test;
				if( abs(S[L - 1]) <= tol )
                    break;
			}

			// cancellation of E(L)
			cs = 0;
			sn = 1;
			L1 = L - 1;
			for( int i=L; i<=k; ++i )
			{
				f = sn * t[i];
				t[i] *= cs;
				if( abs(f) <= tol )
                    goto Test;

				h = S[i];
				w = sqrt( f*f + h*h );
				S[i] = w;
				cs = h / w;
				sn = -f / w;

                for( int j=0; j<n; ++j )
                {
                    x = real( U[j][L1] );
                    y = real( U[j][i] );
                    U[j][L1] = x*cs + y*sn;
                    U[j][i]  = y*cs - x*sn;
                }
			}

			// test for convergence
	Test:	w = S[k];
			if( L == k )
                break;

			// origin shift
			x = S[L];
			y = S[k-1];
			g = t[k-1];
			h = t[k];
			f = ( (y-w)*(y+w) + (g-h)*(g+h) ) / ( 2*h*y);
			g = sqrt( f*f + 1 );
			if( f < Type(0) )
                g = -g;
			f = ( (x-w)*(x+w) + h*(y/(f+g)-h) ) / x;

			// QR step
			cs = 1;
			sn = 1;
			L1 = L + 1;
			for( int i=L1; i<=k; ++i )
			{
				g = t[i];
				y = S[i];
				h = sn * g;
				g = cs * g;
				w = sqrt( h*h + f*f );
				t[i-1] = w;
				cs = f / w;
				sn = h / w;
				f = x * cs + g * sn;
				g = g * cs - x * sn;
				h = y * sn;
				y = y * cs;

                for( int j=0; j<n; ++j )
                {
                    x = real( V[j][i-1] );
                    w = real( V[j][i] );
                    V[j][i-1] = x*cs + w*sn;
                    V[j][i]   = w*cs - x*sn;
                }

				w = sqrt( h*h + f*f );
				S[i-1] = w;
				cs = f / w;
				sn = h / w;
				f = cs * g + sn * y;
				x = cs * y - sn * g;

                for( int j=0; j<n; ++j )
                {
                    y = real(U[j][i-1]);
                    w = real(U[j][i]);
                    U[j][i-1] = y*cs + w*sn;
                    U[j][i]   = w*cs - y*sn;
                }
			}
			t[L] = 0;
			t[k] = f;
			S[k] = x;
		}

		// convergence
		if( w >= Type(0) )
            continue;
		S[k] = -w;

		for( int j=0; j<n; ++j )
			V[j][k] = -V[j][k];
	}

	// sort dingular values
	for( k=0; k<n; ++k )
	{
		g = -1.0;
		int j = k;
		for( int i=k; i<n; ++i )
		{
			if( S[i] <= g )
                continue;
			g = S[i];
			j = i;
		}

		if( j == k )
            continue;
		S[j] = S[k];
		S[k] = g;

        for( int i=0; i<n; ++i )
        {
            q = V[i][j];
            V[i][j] = V[i][k];
            V[i][k] = q;
        }

        for( int i=0; i<n; ++i )
        {
            q = U[i][j];
            U[i][j] = U[i][k];
            U[i][k] = q;
        }
	}

	// back transformation
    for( k=nM1; k>=0; --k )
    {
        if( b[k] == Type(0) )
            continue;
        q = -B[k][k] / abs(B[k][k]);

        for( int j=0; j<n; ++j )
            U[k][j] *= q;

        for( int j=0; j<n; ++j )
        {
            q = 0;
            for( int i=k; i<m; ++i )
                q += conj(B[i][k]) * U[i][j];
            q /= abs(B[k][k]) * b[k];

            for( int i=k; i<m; ++i )
                U[i][j] -= q * B[i][k];
        }
    }

	if( n > 1 )
	{
		for( k = n-2; k>=0; --k )
		{
			k1 = k + 1;
			if( c[k1] == Type(0) )
                continue;
			q = -conj(B[k][k1]) / abs(B[k][k1]);

			for( int j=0; j<n; ++j )
				V[k1][j] *= q;

			for( int j=0; j<n; ++j )
			{
				q = 0;
				for( int i=k1; i<n; ++i )
					q += B[k][i] * V[i][j];
				q /= abs(B[k][k1]) * c[k1];

				for( int i=k1; i<n; ++i )
					V[i][j] -= q * conj(B[k][i]);
			}
		}
	}
}


/**
 * Get the left singular vectors.
 */
template<typename Type>
inline Matrix< complex<Type> > CSVD<Type>::getU() const
{
    return U;
}


/**
 * Get the singular values matrix.
 */
template<typename Type>
inline Matrix<Type> CSVD<Type>::getSM()
{
    int N = S.size();
    Matrix<Type> tmp( N, N );
    for( int i=0; i<N; ++i )
        tmp[i][i] = S[i];

    return tmp;
}


/**
 * Get the singular values vector.
 */
template<typename Type>
inline Vector<Type> CSVD<Type>::getSV() const
{
    return S;
}


/**
 * Get the right singular vectors.
 */
template<typename Type>
inline Matrix< complex<Type> > CSVD<Type>::getV() const
{
    return V;
}


/**
 * Two norm (max(S)).
 */
template <typename Type>
inline Type CSVD<Type>::norm2() const
{
    return S[0];
}


/**
 * Two norm of condition number (max(S)/min(S)).
 */
template <typename Type>
inline Type CSVD<Type>::cond() const
{
    return ( S[0] / S(S.size()) );
}


/**
 * Effective numerical matrix rank.
 */
template <typename Type>
int CSVD<Type>::rank()
{
    int N = S.size();
    double tol = N * S[0] * EPS;
    int r = 0;

    for( int i=0; i<N; ++i )
        if( S[i] > tol )
            r++;

    return r;
}

测试代码:

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


#define BOUNDS_CHECK

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


using namespace std;
using namespace splab;


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


int main()
{
    Matrix< complex<Type> > A( M, N ), B(M,N);
    for( int i=0; i<M; i++ )
		for( int j=0; j<N; j++ )
			A[i][j] = complex<Type>( Type(0.3*i+0.7*j), sin(Type(i+j)) );

	CSVD<Type> svd;
	svd.dec(A);

	Matrix< complex<Type> > U = svd.getU();
	Matrix< complex<Type> > V = svd.getV();
	Matrix<Type> S = svd.getSM();
    Matrix< complex<Type> > CS = complexMatrix( S );
//	Vector<Type> S = svd.getSV();

    cout << setiosflags(ios::fixed) << setprecision(3);
	cout << "Matrix--------A: " << A << endl;
	cout << "Matrix--------U: " << U << endl;
	cout << "Vector--------S: " << S << endl;
	cout << "Matrix--------V: " << V << endl;
    cout << "Matrix--------A - U * S * V^H:  "
         << A - U*CS*trH(V) << endl;
//         << A - U*multTr(CS,V) << endl;

	cout << "The rank of A : " << svd.rank() << endl << endl;
	cout << "The condition number of A : " << svd.cond() << endl << endl;

	return 0;
}

运行结果:

Matrix--------A: size: 4 by 5
(0.000,0.000)   (0.700,0.841)   (1.400,0.909)   (2.100,0.141)   (2.800,-0.757)

(0.300,0.841)   (1.000,0.909)   (1.700,0.141)   (2.400,-0.757)  (3.100,-0.959)

(0.600,0.909)   (1.300,0.141)   (2.000,-0.757)  (2.700,-0.959)  (3.400,-0.279)

(0.900,0.141)   (1.600,-0.757)  (2.300,-0.959)  (3.000,-0.279)  (3.700,0.657)


Matrix--------U: size: 4 by 4
(0.394,0.000)   (-0.579,0.000)  (0.632,0.000)   (-0.331,0.000)
(0.466,-0.095)  (-0.431,0.137)  (-0.349,0.187)  (0.642,0.005)
(0.524,-0.121)  (0.130,0.031)   (-0.537,0.070)  (-0.629,-0.064)
(0.571,-0.060)  (0.644,-0.164)  (0.378,-0.085)  (0.275,0.053)

Vector--------S: size: 4 by 4
9.642   0.000   0.000   0.000
0.000   2.428   0.000   0.000
0.000   0.000   1.021   0.000
0.000   0.000   0.000   0.028

Matrix--------V: size: 5 by 4
(0.080,-0.114)  (0.267,0.027)   (0.120,0.734)   (0.561,0.118)
(0.236,-0.077)  (0.254,0.521)   (0.241,0.283)   (-0.451,-0.114)
(0.398,-0.002)  (0.145,0.504)   (0.141,-0.301)  (0.031,-0.058)
(0.547,0.024)   (-0.022,-0.007) (-0.008,-0.372) (0.555,0.074)
(0.677,-0.042)  (-0.156,-0.541) (0.009,0.243)   (-0.371,-0.001)

Matrix--------A - U * S * V^H:  size: 4 by 5
(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)  (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 rank of A : 4

The condition number of A : 350.515


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

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