core/vnl/algo/vnl_svd.h
Go to the documentation of this file.
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_