头文件:
/* * 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.