00001 /* 00002 This file is part of the BIAS library (Basic ImageAlgorithmS). 00003 00004 Copyright (C) 2003, 2004 (see file CONTACTS for details) 00005 Multimediale Systeme der Informationsverarbeitung 00006 Institut fuer Informatik 00007 Christian-Albrechts-Universitaet Kiel 00008 00009 00010 BIAS is free software; you can redistribute it and/or modify 00011 it under the terms of the GNU Lesser General Public License as published by 00012 the Free Software Foundation; either version 2.1 of the License, or 00013 (at your option) any later version. 00014 00015 BIAS is distributed in the hope that it will be useful, 00016 but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00018 GNU Lesser General Public License for more details. 00019 00020 You should have received a copy of the GNU Lesser General Public License 00021 along with BIAS; if not, write to the Free Software 00022 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00023 */ 00024 00025 00026 #ifndef _SVD_hh_ 00027 #define _SVD_hh_ 00028 00029 #include "bias_config.h" 00030 00031 #include <Base/Debug/Error.hh> 00032 #include <Base/Debug/Debug.hh> 00033 00034 #include <Base/Math/Matrix.hh> 00035 #include <Base/Math/Vector.hh> 00036 00037 #include <float.h> 00038 00039 // for netlib lapck svd routine tests: 00040 //#include "etlib/dggsvd.c" 00041 00042 // double values below this threshold are treated as zero 00043 // (for nullspace computation) 00044 //#define DEFAULT_DOUBLE_ZERO_THRESHOLD DBL_EPSILON 00045 #define DEFAULT_DOUBLE_ZERO_THRESHOLD 1E-8 00046 00047 #define SVDSOLVE 0x00000001 00048 #define D_SVD_RANK 0x00000002 00049 #define D_SVD_SOLVE 0x00000004 00050 00051 namespace BIAS { 00052 /** 00053 @class SVD 00054 @ingroup g_mathalgo 00055 @brief computes and holds the singular value decomposition 00056 of a rectangular (not necessarily quadratic) Matrix A 00057 00058 M = U S V't. 00059 00060 The singular vectors vi and (right) singular values sigma_i satisfy the 00061 equation: A vi = sigma_i vi 00062 00063 The Vector S_ stores the singular values of A in decreasing 00064 order (smallest is last) 00065 00066 The columns of U are the left singular vectors of A and form 00067 an orthonormal basis of A 00068 which corresponds to the nonzero singular values. 00069 while the columns of U corresponding to (nearly) zero values 00070 are the nullspace. 00071 00072 This function is a base class and not a member in Matrix because: 00073 - holding the U,S,V matrices allows repeated queries of the 00074 same SVD results. 00075 -avoid namespace clutter in the Matrix class. 00076 There are many other 00077 decompositions that might be of interest, 00078 and adding them all would make for a very large Matrix class with 00079 extra members which are irrelevant for the Matrix itself. 00080 00081 You can create the svd object either in relative or in absolute mode: 00082 if AbsoluteZeroThreshold is true, a singular value is compared 00083 to the threshold "absolutely", otherwise the ratio of the largest and 00084 the current singular value is compared against the threshold. 00085 Usually you want AbsoluteZeroThreshold = false, but for the sake of 00086 interface stability/downwards compatibility, the default is true 00087 00088 @author Jan Woetzel 00089 **/ 00090 class BIASMathAlgo_EXPORT SVD : public Debug { 00091 public: 00092 /** @brief solve the general eigenproblem of the rectangular matrix M 00093 or with other words, make the U*S*V^T decomposition 00094 @param M matrix to decompose 00095 @param ZeroThreshold for nullspace determination, if a singular value 00096 is lower than ZeroThreshold, define the corresponding vector 00097 in V^T to be a null-vector (also sing.vals are treated this way) 00098 @param AbsoluteZeroThreshold if true, a singular value is compared 00099 to the threshold "absoultely", otherwise the ratio of the largest and 00100 the current singular value is compared against the threshold. 00101 Usually you want AbsoluteZeroThreshold = false, but for the sake of 00102 interface stability/downwards compatibility, the default is true 00103 @author Jan Woetzel **/ 00104 SVD(const Matrix <double> & M, 00105 double ZeroThreshold = DEFAULT_DOUBLE_ZERO_THRESHOLD, 00106 bool AbsoluteZeroThreshold = true); 00107 00108 /** @brief creates an empty svd without automatically decomposing any 00109 matrix 00110 @param AbsoluteZeroThreshold if true, a singular value is compared 00111 to the threshold "absolutely", otherwise the ratio of the largest and 00112 the current singular value is compared against the threshold. 00113 Usually you want AbsoluteZeroThreshold = false, but for the sake of 00114 interface stability/downwards compatibility, the default is true */ 00115 inline SVD(bool AbsoluteZeroThreshold = true) { 00116 Decomposed_ = false; 00117 ZeroThreshold_ = DEFAULT_DOUBLE_ZERO_THRESHOLD; 00118 AbsoluteZeroThreshold_ = AbsoluteZeroThreshold; 00119 }; 00120 00121 inline virtual ~SVD(){}; 00122 00123 /** set a new matrix and compute its decomposition. 00124 solves the general eigenproblem of the rectangular matrix M 00125 @author Jan Woetzel 05/142002)*/ 00126 int Compute(const Matrix<double>& M, 00127 double ZeroThreshold=DEFAULT_DOUBLE_ZERO_THRESHOLD); 00128 /** use our naming convention */ 00129 inline int compute(const Matrix<double>& M, 00130 double ZeroThreshold=DEFAULT_DOUBLE_ZERO_THRESHOLD) 00131 { return Compute(M, ZeroThreshold); }; 00132 00133 /** solve the general (non-special) eigenvalue/eigenvector problem of 00134 a general (non-symmetric) matrix M 00135 calls the extern liblapack routine dgesvd . 00136 computes the genral singular value decomposition with 00137 M = U * Sigma(S) * VT 00138 with 00139 Sigma(S) is the qudratic, symmetric matrix which contains 00140 the singular vales in the diagonal and zero and zero else. 00141 U, VT as described in the data mambers. 00142 @author Jan Woetzel (04/04/2002) */ 00143 int General_Eigenproblem_GeneralMatrix_Lapack(const Matrix<double> &M); 00144 00145 /** @brief return zerothresh currently used 00146 @author koeser */ 00147 inline double GetZeroThreshold() const 00148 { return ZeroThreshold_; } 00149 00150 /** return S 00151 which is a vector of the singular values of A in descending order. 00152 The diagonalelements of Sigma are the singular values of A. 00153 @author Jan Woetzel 04/04/2002 */ 00154 inline const Vector<double>& GetS() const 00155 { return S_; } 00156 00157 /** return one singular value (which may be zero). 00158 the index runs from 0.. (size-1) 00159 @author Jan Woetzel 04/04/2002 **/ 00160 inline const double GetSingularValue(int index ) const; 00161 00162 /** return VT (=transposed(V)) 00163 @author Jan Woetzel (04/04/2002) **/ 00164 inline const Matrix<double>& GetVT() const 00165 { return VT_; }; 00166 00167 /** return V 00168 @author Jan Woetzel (04/04/2002) **/ 00169 inline Matrix<double> GetV() const; 00170 00171 /** return U 00172 U is a m x m orthogonal matrix 00173 @author Jan Woetzel (04/04/2002) **/ 00174 inline const Matrix<double>& GetU() const 00175 { return U_; }; 00176 00177 /** return the length of the singular value vector 00178 inline const int size() const */ 00179 inline int size() const { 00180 if (U_.num_cols()>VT_.num_cols()) 00181 return U_.num_cols(); 00182 else 00183 return VT_.num_cols(); 00184 }; 00185 00186 /** @brief return the dim of the nullspace. 00187 00188 For rectangular matrices this is the number of singular values which 00189 are (about) zero. The nullspace is spaned by the singular vectors 00190 corresponding to the zero singular values. 00191 woelk 4 2003: changed API, diffentiate between Left and Right Nullspace 00192 koeser 01/2005: rel/abs handled by flag: 00193 AbsoluteZeroThreshold_ determines whther we compare absolute or 00194 relative singular values against the threshold 00195 @author Jan Woetzel 04/04/2002 **/ 00196 inline const int NullspaceDim() const { 00197 if (AbsoluteZeroThreshold_) return AbsNullspaceDim(); 00198 else return RelNullspaceDim(); 00199 } 00200 00201 /** @brief returns the dim of the matrix's right nullspace 00202 @author woelk 04 2003 */ 00203 inline const int RightNullspaceDim() const { 00204 if (AbsoluteZeroThreshold_) return AbsRightNullspaceDim(); 00205 else return RelRightNullspaceDim(); 00206 } 00207 00208 /** @brief returns the dim of the matrix's left nullspace 00209 @author woelk 04 2003 */ 00210 inline const int LeftNullspaceDim() const { 00211 if (AbsoluteZeroThreshold_) return AbsLeftNullspaceDim(); 00212 else return RelLeftNullspaceDim(); 00213 } 00214 00215 /** @brief returns dim of nullspace using the absolute threshold criterion 00216 00217 For rectangular matrices this is the number of singular values which 00218 are (about) zero. The nullspace is spaned by the singular vectors 00219 corresponding to the zero singular values. 00220 woelk 4 2003: changed API, diffentiate between Left and Right Nullspace 00221 @author Jan Woetzel 04/04/2002 **/ 00222 const int AbsNullspaceDim() const; 00223 00224 /** @brief returns the dim of the matrix's right nullspace, using absolute 00225 threshold criterion 00226 @author woelk 04 2003 */ 00227 const int AbsRightNullspaceDim() const; 00228 00229 /** @brief returns the dim of the matrix's left nullspace, using absolute 00230 threshold criterion 00231 @author woelk 04 2003 */ 00232 const int AbsLeftNullspaceDim() const; 00233 00234 /** @brief compare singular values against greatest, not absolute 00235 @author Christian Buck 00236 changed criterion for insignificant singular values according to 00237 hartley/zisserman from s_i<ZeroThreshold to s_i/s_0<ZeroThreshold. 00238 fallback to old criterion iff s_0=0. */ 00239 const int RelNullspaceDim() const; 00240 const int RelRightNullspaceDim() const; 00241 const int RelLeftNullspaceDim() const; 00242 00243 /** return one of the nullvectors. 00244 if last_offset=0 then the last nullvector 00245 (corresponding to the smallest singular value) is returned, 00246 if last_offset=1 then the last but one nullvector is returned, 00247 and so on. 00248 The last_offset must be in [0..NullspaceDim-1]. 00249 @author Jan Woetzel(08/08/2002), JMF */ 00250 inline Vector<double> GetNullvector(const int last_offset = 0); 00251 00252 /** Returns one of the nullvectors in argument and true if 00253 Nullvector exists. 00254 if last_offset=0 then the last nullvector (corresponding to the 00255 smallest singular value) is returned, 00256 if last_offset=1 then the last but one nullvector is returned, 00257 and so on. 00258 The last_offset must be in [0..NullspaceDim-1]. 00259 @author Jan Woetzel (08/08/2002), JMF */ 00260 inline bool GetNullvector(Vector<double>& NullVec, 00261 const int last_offset = 0 ); 00262 00263 /** Return one of the left nullvectors. 00264 If last_offset=0 then the last nullvector 00265 (corresponding to the smallest 00266 singular value) is returned, 00267 if last_offset=1 then the last but one nullvector is returned, 00268 and so on. 00269 The last_offset must be in [0..NullspaceDim-1] otherwise 0 is returned 00270 @author JMF/koeser **/ 00271 inline bool GetLeftNullvector(Vector<double>&nv, const int last_offset=0); 00272 00273 /** @brief same as above but returning vector */ 00274 inline Vector<double> GetLeftNullvector( const int last_offset = 0 ); 00275 00276 00277 // -- Solve the matrix-vector system M x = y, returning x. 00278 Vector<double> Solve(const Vector<double>& y) const; 00279 /** use our naming convention */ 00280 inline Vector<double> solve(const Vector<double>& y)const 00281 { return Solve(y); }; 00282 00283 /** returns pseudoinverse of A = U * S * V^T 00284 A^+ = V * S^+ * U^T 00285 @author F Woelk */ 00286 Matrix<double> Invert(); 00287 00288 /** returns pseudoinverse of A = U * S * V^T 00289 A^+ = V * S^+ * U^T 00290 the first "rank" elements of S are inverted the others are set to zero 00291 @author Christian Beder */ 00292 Matrix<double> Invert(int rank); 00293 00294 /** as above, but compute new svd for a */ 00295 Matrix<double> Invert(Matrix<double> A); 00296 00297 /** returns the square root of a symmetric positive definite matrix M 00298 A = sqrt(M) = U * sqrt(S) * V_T, 00299 such that A*A = M. 00300 The result is only valid when the M *is* symmetric positive definite. 00301 @author woelk 01/2006 */ 00302 Matrix<double> Sqrt(); 00303 00304 /** as above, but compute new svd for a */ 00305 Matrix<double> Sqrt(const Matrix<double>& A); 00306 00307 /** returns the square root of a symmetric positive definite matrix M 00308 A = sqrt(M) = U * sqrt(S), 00309 such that A*A^T = M. 00310 The result is only valid when the M *is* symmetric positive definite. 00311 @author woelk 01/2006 */ 00312 Matrix<double> SqrtT(); 00313 00314 /** as above, but compute new svd for a */ 00315 Matrix<double> SqrtT(const Matrix<double>& A); 00316 00317 /// returns the rank of A_ 00318 unsigned int Rank(); 00319 00320 00321 /** solves the overdetermined linear system $AX=B$ with the unknown $X$, 00322 where $A$ is an $m x n$ matrix ($m>n$)and B is a vector of 00323 size $m$. 00324 @author woelk 07/2004 */ 00325 00326 int Solve(Matrix<double>& A, Vector<double>& B, Vector<double>& X); 00327 00328 /** Call this after Compute(), 00329 returns the eigenvalues of the matrix in ascending 00330 order (smallest first), which are NOT the singular values! 00331 Works only, if the original Matrix was symmetric. 00332 @author grest, July 2006 00333 */ 00334 BIAS::Vector<double> GetEigenValues(); 00335 00336 00337 protected: 00338 /// data members: 00339 /// original matrix (to be decomposed) 00340 Matrix<double> A_; 00341 00342 /// contains the singular values of A_ corresponding to the 00343 /// i'th column in descending order. 00344 Vector<double> S_; 00345 00346 /// contains columnwise the left singular vectors 00347 /// of A_ corresponding to the i'th singular value 00348 Matrix<double> U_; 00349 00350 /// contains the right singular vectors of A- in rows 00351 /// [beause VT is transpose(V) ] 00352 Matrix<double> VT_; 00353 00354 /// determines whether we compare singular-values directly (absolute) 00355 /// to the zero threshold or whether we compare the ratio of the sing-value 00356 /// under inspection and the largest one to the threshold (relative) 00357 bool AbsoluteZeroThreshold_; 00358 00359 /// values below this threshold are treated as zero 00360 double ZeroThreshold_; 00361 00362 /// flag for holding decomposed matrix 00363 bool Decomposed_; 00364 00365 }; // class SVD 00366 00367 ///////////////////////////////////////////////////////////////// 00368 /// inline implementations 00369 ///////////////////////////////////////////////////////////////// 00370 00371 inline const double SVD::GetSingularValue(int index) const 00372 { 00373 BIASASSERT( index >=0 ); 00374 BIASASSERT( index < size() ); 00375 if (index < S_.size()) 00376 return S_[index]; 00377 else 00378 return 0.0; 00379 } 00380 00381 inline Matrix<double> SVD::GetV() const 00382 { 00383 Matrix<double> matV; 00384 //matV = transpose( VT_ ); //uncomment if error under UNIX 00385 matV = VT_.Transpose(); 00386 return matV; 00387 } 00388 00389 inline Vector<double> SVD::GetNullvector(const int last_offset) 00390 { 00391 Vector<double> vec(VT_.num_cols(), 0.0); 00392 if (!GetNullvector(vec, last_offset)) { 00393 //BIASERR("No Nullvector for "<<last_offset); 00394 } 00395 return vec; 00396 } 00397 00398 inline bool SVD::GetNullvector(Vector<double>& NullVec, 00399 const int last_offset ) 00400 { 00401 // create a new vector 00402 NullVec.newsize(VT_.num_cols()); 00403 // fill the (null-)vector 00404 00405 for (int col=0; col < VT_.num_cols(); col++) { 00406 NullVec[col] = VT_[ VT_.num_cols() -1 - last_offset ][col]; 00407 } 00408 00409 // check if there is a nullvector with this last_offset_index 00410 if (last_offset > (NullspaceDim() -1)) { 00411 // index not OK 00412 return false; 00413 } else { 00414 // index OK 00415 return true; 00416 } 00417 } 00418 00419 inline Vector<double> SVD::GetLeftNullvector(const int last_offset) 00420 { 00421 Vector<double> vec(VT_.num_rows(), 0.0); 00422 if (!GetLeftNullvector(vec, last_offset)) { 00423 BIASERR("No left nullvector for "<<last_offset); 00424 } 00425 return vec; 00426 } 00427 00428 inline bool SVD::GetLeftNullvector(Vector<double>& vec, 00429 const int last_offset) { 00430 // check if there is a nullvector with this last_offset_index 00431 if (last_offset > (NullspaceDim() -1)) { 00432 // index not OK 00433 BIASERR("for last_offset=" <<last_offset 00434 <<" with actual NullspaceDim= "<<NullspaceDim() 00435 <<" can no left nullvector be retrieved. aborting. "); 00436 BIASERR(S_ << U_ << VT_); 00437 return false; 00438 } 00439 // index OK 00440 vec.newsize( U_.num_rows()); 00441 // fill the (null-)vector 00442 for (int row=0; row < VT_.num_rows(); row++) { 00443 vec[row] = U_[row][ U_.num_rows() -1 - last_offset ]; 00444 } 00445 return true; 00446 } 00447 00448 00449 } // namespace BIAS 00450 #endif // _SVD_hh_ 00451
1.5.7.1