00001 // This is core/vnl/algo/vnl_svd.h 00002 #ifndef vnl_svd_h_ 00003 #define vnl_svd_h_ 00004 #ifdef VCL_NEEDS_PRAGMA_INTERFACE 00005 #pragma interface 00006 #endif 00007 //: 00008 // \file 00009 // \brief Holds the singular value decomposition of a vnl_matrix. 00010 // \author Andrew W. Fitzgibbon, Oxford IERG 00011 // \date 15 Jul 96 00012 // 00013 // \verbatim 00014 // Modifications 00015 // fsm, Oxford IESRG, 26 Mar 1999 00016 // 1. The singular values are now stored as reals (not complexes) when T is complex. 00017 // 2. Fixed bug : for complex T, matrices have to be conjugated as well as transposed. 00018 // Feb.2002 - Peter Vanroose - brief doxygen comment placed on single line 00019 // \endverbatim 00020 00021 #include <vnl/vnl_numeric_traits.h> 00022 #include <vnl/vnl_vector.h> 00023 #include <vnl/vnl_matrix.h> 00024 #include <vnl/vnl_diag_matrix.h> 00025 #include <vcl_iosfwd.h> 00026 00027 //: Holds the singular value decomposition of a vnl_matrix. 00028 // 00029 // The class holds three matrices U, W, V such that the original matrix 00030 // $M = U W V^\top$. The DiagMatrix W stores the singular values in decreasing 00031 // order. The columns of U which correspond to the nonzero singular values 00032 // form a basis for range of M, while the columns of V corresponding to the 00033 // zero singular values are the nullspace. 00034 // 00035 // The SVD is computed at construction time, and inquiries may then be made 00036 // of the SVD. In particular, this allows easy access to multiple 00037 // right-hand-side solves without the bother of putting all the RHS's into a 00038 // Matrix. 00039 // 00040 // This class is supplied even though there is an existing vnl_matrix method 00041 // for several reasons: 00042 // 00043 // It is more convenient to use as it manages all the storage for 00044 // the U,S,V matrices, allowing repeated queries of the same SVD 00045 // results. 00046 // 00047 // It avoids namespace clutter in the Matrix class. While svd() 00048 // is a perfectly reasonable method for a Matrix, there are many other 00049 // decompositions that might be of interest, and adding them all would 00050 // make for a very large Matrix class. 00051 // 00052 // It demonstrates the holder model of compute class, implementing an 00053 // algorithm on an object without adding a member that may not be of 00054 // general interest. A similar pattern can be used for other 00055 // decompositions which are not defined as members of the library Matrix 00056 // class. 00057 // 00058 // It extends readily to n-ary operations, such as generalized 00059 // eigensystems, which cannot be members of just one matrix. 00060 00061 export template <class T> 00062 class vnl_svd 00063 { 00064 public: 00065 //: The singular values of a matrix of complex<T> are of type T, not complex<T> 00066 typedef typename vnl_numeric_traits<T>::abs_t singval_t; 00067 00068 //: 00069 // Construct a vnl_svd<T> object from $m \times n$ matrix $M$. The 00070 // vnl_svd<T> object contains matrices $U$, $W$, $V$ such that 00071 // $U W V^\top = M$. 00072 // 00073 // Uses linpack routine DSVDC to calculate an ``economy-size'' SVD 00074 // where the returned $U$ is the same size as $M$, while $W$ 00075 // and $V$ are both $n \times n$. This is efficient for 00076 // large rectangular solves where $m > n$, typical in least squares. 00077 // 00078 // The optional argument zero_out_tol is used to mark the zero singular 00079 // values: If nonnegative, any s.v. smaller than zero_out_tol in 00080 // absolute value is set to zero. If zero_out_tol is negative, the 00081 // zeroing is relative to |zero_out_tol| * sigma_max(); 00082 00083 vnl_svd(vnl_matrix<T> const &M, double zero_out_tol = 0.0); 00084 ~vnl_svd() {} 00085 00086 // Data Access--------------------------------------------------------------- 00087 00088 //: find weights below threshold tol, zero them out, and update W_ and Winverse_ 00089 void zero_out_absolute(double tol = 1e-8); //sqrt(machine epsilon) 00090 00091 //: find weights below tol*max(w) and zero them out 00092 void zero_out_relative(double tol = 1e-8); //sqrt(machine epsilon) 00093 int singularities () const { return W_.rows() - rank(); } 00094 unsigned int rank () const { return rank_; } 00095 singval_t well_condition () const { return sigma_min()/sigma_max(); } 00096 00097 //: Calculate determinant as product of diagonals in W. 00098 singval_t determinant_magnitude () const; 00099 singval_t norm() const; 00100 00101 //: Return the matrix U. 00102 vnl_matrix<T> & U() { return U_; } 00103 00104 //: Return the matrix U. 00105 vnl_matrix<T> const& U() const { return U_; } 00106 00107 //: Return the matrix U's (i,j)th entry (to avoid svd.U()(i,j); ). 00108 T U(int i, int j) const { return U_(i,j); } 00109 00110 //: Get at DiagMatrix (q.v.) of singular values, sorted from largest to smallest 00111 vnl_diag_matrix<singval_t> & W() { return W_; } 00112 00113 //: Get at DiagMatrix (q.v.) of singular values, sorted from largest to smallest 00114 vnl_diag_matrix<singval_t> const & W() const { return W_; } 00115 vnl_diag_matrix<singval_t> & Winverse() { return Winverse_; } 00116 vnl_diag_matrix<singval_t> const & Winverse() const { return Winverse_; } 00117 singval_t & W(int i, int j) { return W_(i,j); } 00118 singval_t & W(int i) { return W_(i,i); } 00119 singval_t sigma_max() const { return W_(0,0); } // largest 00120 singval_t sigma_min() const { return W_(n_-1,n_-1); } // smallest 00121 00122 //: Return the matrix V. 00123 vnl_matrix<T> & V() { return V_; } 00124 00125 //: Return the matrix V. 00126 vnl_matrix<T> const& V() const { return V_; } 00127 00128 //: Return the matrix V's (i,j)th entry (to avoid svd.V()(i,j); ). 00129 T V(int i, int j) const { return V_(i,j); } 00130 00131 //: 00132 inline vnl_matrix<T> inverse () const { return pinverse(); } 00133 00134 //: pseudo-inverse (for non-square matrix) of desired rank. 00135 vnl_matrix<T> pinverse (unsigned int rank = ~0u) const; // ~0u == (unsigned int)-1 00136 00137 //: Calculate inverse of transpose, using desired rank. 00138 vnl_matrix<T> tinverse (unsigned int rank = ~0u) const; // ~0u == (unsigned int)-1 00139 00140 //: Recompose SVD to U*W*V', using desired rank. 00141 vnl_matrix<T> recompose (unsigned int rank = ~0u) const; // ~0u == (unsigned int)-1 00142 00143 //: Solve the matrix equation M X = B, returning X 00144 vnl_matrix<T> solve (vnl_matrix<T> const& B) const; 00145 00146 //: Solve the matrix-vector system M x = y, returning x. 00147 vnl_vector<T> solve (vnl_vector<T> const& y) const; 00148 void solve (T const *rhs, T *lhs) const; // min ||A*lhs - rhs|| 00149 00150 //: Solve the matrix-vector system M x = y. 00151 // Assuming that the singular values W have been preinverted by the caller. 00152 void solve_preinverted(vnl_vector<T> const& rhs, vnl_vector<T>* out) const; 00153 00154 //: Return N such that M * N = 0 00155 vnl_matrix<T> nullspace() const; 00156 00157 //: Return N such that M' * N = 0 00158 vnl_matrix<T> left_nullspace() const; 00159 00160 //: Return N such that M * N = 0 00161 vnl_matrix<T> nullspace(int required_nullspace_dimension) const; 00162 00163 //: Implementation to be done yet; currently returns left_nullspace(). - PVR. 00164 vnl_matrix<T> left_nullspace(int required_nullspace_dimension) const; 00165 00166 //: Return the rightmost column of V. 00167 // Does not check to see whether or not the matrix actually was rank-deficient - 00168 // the caller is assumed to have examined W and decided that to his or her satisfaction. 00169 vnl_vector<T> nullvector() const; 00170 00171 //: Return the rightmost column of U. 00172 // Does not check to see whether or not the matrix actually was rank-deficient. 00173 vnl_vector<T> left_nullvector() const; 00174 00175 bool valid() const { return valid_; } 00176 00177 private: 00178 00179 int m_, n_; // Size of M, local cache. 00180 vnl_matrix<T> U_; // Columns Ui are basis for range of M for Wi != 0 00181 vnl_diag_matrix<singval_t> W_;// Singular values, sorted in decreasing order 00182 vnl_diag_matrix<singval_t> Winverse_; 00183 vnl_matrix<T> V_; // Columns Vi are basis for nullspace of M for Wi = 0 00184 unsigned rank_; 00185 bool have_max_; 00186 singval_t max_; 00187 bool have_min_; 00188 singval_t min_; 00189 double last_tol_; 00190 bool valid_; // false if the NETLIB call failed. 00191 00192 // Disallow assignment. 00193 vnl_svd(vnl_svd<T> const &) { } 00194 vnl_svd<T>& operator=(vnl_svd<T> const &) { return *this; } 00195 }; 00196 00197 template <class T> 00198 inline 00199 vnl_matrix<T> vnl_svd_inverse(vnl_matrix<T> const& m) 00200 { 00201 return vnl_svd<T>(m).inverse(); 00202 } 00203 00204 export template <class T> 00205 vcl_ostream& operator<<(vcl_ostream&, vnl_svd<T> const& svd); 00206 00207 #endif // vnl_svd_h_