SVD.hh

Go to the documentation of this file.
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 

Generated on Tue Jan 6 01:01:45 2009 for Basic Image AlgorithmS Library by  doxygen 1.5.7.1