/*************************************************************************** * SVD.C * * * * Singular Value Decomposition. * * * * HISTORY * * Name Date Description * * * * arvo 08/22/2000 Copied to CIT library. * * arvo 06/28/1993 Rewritten from "Numerical Recipes" C-code. * * * *--------------------------------------------------------------------------* * Copyright (C) 2000, James Arvo * * * * 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. See http://www.fsf.org/copyleft/gpl.html * * * * This program is distributed in the hope that it will be useful, but * * WITHOUT EXPRESS OR IMPLIED WARRANTY of merchantability or fitness for * * any particular purpose. See the GNU General Public License for more * * details. * * * ***************************************************************************/ #include #include #include "ArvoMath.h" #include "Vector.h" #include "Matrix.h" #include "SVD.h" namespace ArvoMath { static const int MaxIterations = 30; static double svd_pythag( double a, double b ) { double at = Abs(a); double bt = Abs(b); if( at > bt ) return at * sqrt( 1.0 + Sqr( bt / at ) ); else if( bt > 0.0 ) return bt * sqrt( 1.0 + Sqr( at / bt ) ); else return 0.0; } static inline double SameSign( double a, double b ) { double t; if( b >= 0.0 ) t = Abs( a ); else t = -Abs( a ); return t; } static int ComputeRank( const Matrix &D, double epsilon ) { int rank = 0; for( int i = 0; i < D.Rows(); i++ ) if( Abs(D(i,i)) > epsilon ) rank++; return rank; } SVD::SVD( ) : Q_(0), D_(0), R_(0) { } SVD::SVD( const Matrix &M ) : Q_(0), D_(0), R_(0) { (*this) = M; } void SVD::operator=( const Matrix &A ) { if( A.Rows() >= A.Cols() ) Q_ = A; else { Q_ = Matrix( A.Cols() ); for( int i = 0; i < A.Rows(); i++ ) for( int j = 0; j < A.Cols(); j++ ) Q_(i,j) = A(i,j); } R_ = Matrix( A.Cols() ); Decompose( Q_, D_, R_ ); } const Matrix &SVD::Q( double epsilon ) const { int rank = 0; if( epsilon != 0.0 ) rank = ComputeRank( D_, epsilon ); return Q_; } const Matrix &SVD::D( double epsilon ) const { int rank = 0; if( epsilon != 0.0 ) rank = ComputeRank( D_, epsilon ); return D_; } const Matrix &SVD::R( double epsilon ) const { int rank = 0; if( epsilon != 0.0 ) rank = ComputeRank( D_, epsilon ); return R_; } int SVD::Rank( double epsilon ) const { return ComputeRank( D_, epsilon ); } int SVD::Decompose( Matrix &Q, Matrix &D, Matrix &R ) { int i, j, k, l, m, n, p, q, iter; double c, f, h, s, x, y, z; double norm = 0.0; double g = 0.0; double scale = 0.0; m = Q.Rows(); n = Q.Cols(); Vector Temp( n ); Vector diag( n ); for( i = 0; i < n; i++ ) { Temp(i) = scale * g; scale = 0.0; g = 0.0; s = 0.0; l = i + 1; if( i < m ) { for( k = i; k < m; k++ ) scale += Abs( Q(k,i) ); if( scale != 0.0 ) { for( k = i; k < m; k++ ) { Q(k,i) /= scale; s += Sqr( Q(k,i) ); } f = Q(i,i); g = -SameSign( sqrt(s), f ); h = f * g - s; Q(i,i) = f - g; if( i != n - 1 ) { for( j = l; j < n; j++ ) { s = 0.0; for( k = i; k < m; k++ ) s += Q(k,i) * Q(k,j); f = s / h; for( k = i; k < m; k++ ) Q(k,j) += f * Q(k,i); } } for( k = i; k < m; k++ ) Q(k,i) *= scale; } } diag(i) = scale * g; g = 0.0; s = 0.0; scale = 0.0; if( i < m && i != n - 1 ) { for( k = l; k < n; k++ ) scale += Abs( Q(i,k) ); if( scale != 0.0 ) { for( k = l; k < n; k++ ) { Q(i,k) /= scale; s += Sqr( Q(i,k) ); } f = Q(i,l); g = -SameSign( sqrt(s), f ); h = f * g - s; Q(i,l) = f - g; for( k = l; k < n; k++ ) Temp(k) = Q(i,k) / h; if( i != m - 1 ) { for( j = l; j < m; j++ ) { s = 0.0; for( k = l; k < n; k++ ) s += Q(j,k) * Q(i,k); for( k = l; k < n; k++ ) Q(j,k) += s * Temp(k); } } for( k = l; k < n; k++ ) Q(i,k) *= scale; } } norm = Max( norm, Abs( diag(i) ) + Abs( Temp(i) ) ); } for( i = n - 1; i >= 0; i-- ) { if( i < n - 1 ) { if( g != 0.0 ) { for( j = l; j < n; j++ ) R(i,j) = ( Q(i,j) / Q(i,l) ) / g; for( j = l; j < n; j++ ) { s = 0.0; for( k = l; k < n; k++ ) s += Q(i,k) * R(j,k); for( k = l; k < n; k++ ) R(j,k) += s * R(i,k); } } for( j = l; j < n; j++ ) { R(i,j) = 0.0; R(j,i) = 0.0; } } R(i,i) = 1.0; g = Temp(i); l = i; } for( i = n - 1; i >= 0; i-- ) { l = i + 1; g = diag(i); if( i < n - 1 ) for( j = l; j < n; j++ ) Q(i,j) = 0.0; if( g != 0.0 ) { g = 1.0 / g; if( i != n - 1 ) { for( j = l; j < n; j++ ) { s = 0.0; for( k = l; k < m; k++ ) s += Q(k,i) * Q(k,j); f = ( s / Q(i,i) ) * g; for( k = i; k < m; k++ ) Q(k,j) += f * Q(k,i); } } for( j = i; j < m; j++ ) Q(j,i) *= g; } else { for( j = i; j < m; j++ ) Q(j,i) = 0.0; } Q(i,i) += 1.0; } for( k = n - 1; k >= 0; k-- ) { for( iter = 1; iter <= MaxIterations; iter++ ) { int jump; for( l = k; l >= 0; l-- ) { q = l - 1; if( Abs( Temp(l) ) + norm == norm ) { jump = 1; break; } if( Abs( diag(q) ) + norm == norm ) { jump = 0; break; } } if( !jump ) { c = 0.0; s = 1.0; for( i = l; i <= k; i++ ) { f = s * Temp(i); Temp(i) *= c; if( Abs( f ) + norm == norm ) break; g = diag(i); h = svd_pythag( f, g ); diag(i) = h; h = 1.0 / h; c = g * h; s = -f * h; for( j = 0; j < m; j++ ) { y = Q(j,q); z = Q(j,i); Q(j,q) = y * c + z * s; Q(j,i) = z * c - y * s; } } } z = diag(k); if( l == k ) { if( z < 0.0 ) { diag(k) = -z; for( j = 0; j < n; j++ ) R(k,j) *= -1.0; } break; } if( iter >= MaxIterations ) return 0; x = diag(l); q = k - 1; y = diag(q); g = Temp(q); h = Temp(k); f = ( ( y - z ) * ( y + z ) + ( g - h ) * ( g + h ) ) / ( 2.0 * h * y ); g = svd_pythag( f, 1.0 ); f = ( ( x - z ) * ( x + z ) + h * ( ( y / ( f + SameSign( g, f ) ) ) - h ) ) / x; c = 1.0; s = 1.0; for( j = l; j <= q; j++ ) { i = j + 1; g = Temp(i); y = diag(i); h = s * g; g = c * g; z = svd_pythag( f, h ); Temp(j) = z; c = f / z; s = h / z; f = x * c + g * s; g = g * c - x * s; h = y * s; y = y * c; for( p = 0; p < n; p++ ) { x = R(j,p); z = R(i,p); R(j,p) = x * c + z * s; R(i,p) = z * c - x * s; } z = svd_pythag( f, h ); diag(j) = z; if( z != 0.0 ) { z = 1.0 / z; c = f * z; s = h * z; } f = c * g + s * y; x = c * y - s * g; for( p = 0; p < m; p++ ) { y = Q(p,j); z = Q(p,i); Q(p,j) = y * c + z * s; Q(p,i) = z * c - y * s; } } Temp(l) = 0.0; Temp(k) = f; diag(k) = x; } } // Sort the singular values into descending order. for( i = 0; i < n - 1; i++ ) { double biggest = diag(i); // Biggest singular value so far. int bindex = i; // The row/col it occurred in. for( j = i + 1; j < n; j++ ) { if( diag(j) > biggest ) { biggest = diag(j); bindex = j; } } if( bindex != i ) // Need to swap rows and columns. { Q.SwapCols( i, bindex ); // Swap columns in Q. R.SwapRows( i, bindex ); // Swap rows in R. diag.Swap ( i, bindex ); // Swap elements in diag. } } D = Diag( diag ); return 1; } const Matrix &SVD::PseudoInverse( double epsilon ) { if( Null(P_) ) { Matrix D_Inverse( D_ ); for( int i = 0; i < D_Inverse.Rows(); i++ ) { if( Abs( D_Inverse(i,i) ) > epsilon ) D_Inverse(i,i) = 1.0 / D_Inverse(i,i); else D_Inverse(i,i) = 0.0; } P_ = Q_ * D_Inverse * R_; } return P_; } };