TooN: SVD.h Source File

TooN 2.0.0-beta8

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List

SVD.h

    00001 // -*- c++ -*-
    00002
    00003 // Copyright (C) 2005,2009 Tom Drummond (twd20@cam.ac.uk)
    00004 //
    00005 // This file is part of the TooN Library.  This library is free
    00006 // software; you can redistribute it and/or modify it under the
    00007 // terms of the GNU General Public License as published by the
    00008 // Free Software Foundation; either version 2, or (at your option)
    00009 // any later version.
    00010
    00011 // This library is distributed in the hope that it will be useful,
    00012 // but WITHOUT ANY WARRANTY; without even the implied warranty of
    00013 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    00014 // GNU General Public License for more details.
    00015
    00016 // You should have received a copy of the GNU General Public License along
    00017 // with this library; see the file COPYING.  If not, write to the Free
    00018 // Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
    00019 // USA.
    00020
    00021 // As a special exception, you may use this file as part of a free software
    00022 // library without restriction.  Specifically, if other files instantiate
    00023 // templates or use macros or inline functions from this file, or you compile
    00024 // this file and link it with other files to produce an executable, this
    00025 // file does not by itself cause the resulting executable to be covered by
    00026 // the GNU General Public License.  This exception does not however
    00027 // invalidate any other reasons why the executable file might be covered by
    00028 // the GNU General Public License.
    00029
    00030 #ifndef __SVD_H
    00031 #define __SVD_H
    00032
    00033 #include <TooN/TooN.h>
    00034 #include <TooN/lapack.h>
    00035
    00036 namespace TooN {
    00037
    00038     // TODO - should this depend on precision?
    00039 static const double condition_no=1e9; // GK HACK TO GLOBAL
    00040
    00041
    00042
    00043
    00044
    00045
    00046
    00047 /**
    00048 @class SVD TooN/SVD.h
    00049 Performs %SVD and back substitute to solve equations.
    00050 Singular value decompositions are more robust than LU decompositions in the face of
    00051 singular or nearly singular matrices. They decompose a matrix (of any shape) \f$M\f$ into:
    00052 \f[M = U \times D \times V^T\f]
    00053 where \f$D\f$ is a diagonal matrix of positive numbers whose dimension is the minimum
    00054 of the dimensions of \f$M\f$. If \f$M\f$ is tall and thin (more rows than columns)
    00055 then \f$U\f$ has the same shape as \f$M\f$ and \f$V\f$ is square (vice-versa if \f$M\f$
    00056 is short and fat). The columns of \f$U\f$ and the rows of \f$V\f$ are orthogonal
    00057 and of unit norm (so one of them lies in SO(N)). The inverse of \f$M\f$ (or pseudo-inverse
    00058 if \f$M\f$ is not square) is then given by
    00059 \f[M^{\dagger} = V \times D^{-1} \times U^T\f]
    00060
    00061 If \f$M\f$ is nearly singular then the diagonal matrix \f$D\f$ has some small values
    00062 (relative to its largest value) and these terms dominate \f$D^{-1}\f$. To deal with
    00063 this problem, the inverse is conditioned by setting a maximum ratio
    00064 between the largest and smallest values in \f$D\f$ (passed as the <code>condition</code>
    00065 parameter to the various functions). Any values which are too small
    00066 are set to zero in the inverse (rather than a large number)
    00067
    00068 It can be used as follows to solve the \f$M\underline{x} = \underline{c}\f$ problem as follows:
    00069 @code
    00070 // construct M
    00071 Matrix<3> M;
    00072 M[0] = makeVector(1,2,3);
    00073 M[1] = makeVector(4,5,6);
    00074 M[2] = makeVector(7,8.10);
    00075 // construct c
    00076  Vector<3> c;
    00077 c = 2,3,4;
    00078 // create the SVD decomposition of M
    00079 SVD<3> svdM(M);
    00080 // compute x = M^-1 * c
    00081 Vector<3> x = svdM.backsub(c);
    00082  @endcode
    00083
    00084 SVD<> (= SVD<-1>) can be used to create an SVD whose size is determined at run-time.
    00085 @ingroup gDecomps
    00086 **/
    00087 template<int Rows=Dynamic, int Cols=Rows, typename Precision=DefaultPrecision>
    00088 class SVD {
    00089     // this is the size of the diagonal
    00090     // NB works for semi-dynamic sizes because -1 < +ve ints
    00091     static const int Min_Dim = Rows<Cols?Rows:Cols;
    00092
    00093 public:
    00094
    00095     /// default constructor for Rows>0 and Cols>0
    00096     SVD() {}
    00097
    00098     /// constructor for Rows=-1 or Cols=-1 (or both)
    00099     SVD(int rows, int cols)
    00100         : my_copy(rows,cols),
    00101           my_diagonal(std::min(rows,cols)),
    00102           my_square(std::min(rows,cols), std::min(rows,cols))
    00103     {}
    00104
    00105     /// Construct the %SVD decomposition of a matrix. This initialises the class, and
    00106     /// performs the decomposition immediately.
    00107     template <int R2, int C2, typename P2, typename B2>
    00108     SVD(const Matrix<R2,C2,P2,B2>& m)
    00109         : my_copy(m),
    00110           my_diagonal(std::min(m.num_rows(),m.num_cols())),
    00111           my_square(std::min(m.num_rows(),m.num_cols()),std::min(m.num_rows(),m.num_cols()))
    00112     {
    00113         do_compute();
    00114     }
    00115
    00116     /// Compute the %SVD decomposition of M, typically used after the default constructor
    00117     template <int R2, int C2, typename P2, typename B2>
    00118     void compute(const Matrix<R2,C2,P2,B2>& m){
    00119         my_copy=m;
    00120         do_compute();
    00121     }
    00122
    00123     private:
    00124     void do_compute(){
    00125         Precision* const a = my_copy.my_data;
    00126         int lda = my_copy.num_cols();
    00127         int m = my_copy.num_cols();
    00128         int n = my_copy.num_rows();
    00129         Precision* const uorvt = my_square.my_data;
    00130         Precision* const s = my_diagonal.my_data;
    00131         int ldu;
    00132         int ldvt = lda;
    00133         int LWORK;
    00134         int INFO;
    00135         char JOBU;
    00136         char JOBVT;
    00137
    00138         if(is_vertical()){ // u is a
    00139             JOBU='O';
    00140             JOBVT='S';
    00141             ldu = lda;
    00142         } else { // vt is a
    00143             JOBU='S';
    00144             JOBVT='O';
    00145             ldu = my_square.num_cols();
    00146         }
    00147
    00148         Precision* wk;
    00149
    00150         Precision size;
    00151         LWORK = -1;
    00152
    00153         // arguments are scrambled because we use rowmajor and lapack uses colmajor
    00154         // thus u and vt play each other's roles.
    00155         gesvd_( &JOBVT, &JOBU, &m, &n, a, &lda, s, uorvt,
    00156                  &ldvt, uorvt, &ldu, &size, &LWORK, &INFO);
    00157
    00158         LWORK = (long int)(size);
    00159         wk = new Precision[LWORK];
    00160
    00161         gesvd_( &JOBVT, &JOBU, &m, &n, a, &lda, s, uorvt,
    00162                  &ldvt, uorvt, &ldu, wk, &LWORK, &INFO);
    00163
    00164         delete[] wk;
    00165     }
    00166
    00167     bool is_vertical(){
    00168         return (my_copy.num_rows() >= my_copy.num_cols());
    00169     }
    00170
    00171     int min_dim(){ return std::min(my_copy.num_rows(), my_copy.num_cols()); }
    00172
    00173     public:
    00174
    00175     /// Calculate result of multiplying the (pseudo-)inverse of M by another matrix.
    00176     /// For a matrix \f$A\f$, this calculates \f$M^{\dagger}A\f$ by back substitution
    00177     /// (i.e. without explictly calculating the (pseudo-)inverse).
    00178     /// See the detailed description for a description of condition variables.
    00179     template <int Rows2, int Cols2, typename P2, typename B2>
    00180     Matrix<Cols,Cols2, typename Internal::MultiplyType<Precision,P2>::type >
    00181     backsub(const Matrix<Rows2,Cols2,P2,B2>& rhs, const Precision condition=condition_no)
    00182     {
    00183         Vector<Min_Dim> inv_diag(min_dim());
    00184         get_inv_diag(inv_diag,condition);
    00185         return (get_VT().T() * diagmult(inv_diag, (get_U().T() * rhs)));
    00186     }
    00187
    00188     /// Calculate result of multiplying the (pseudo-)inverse of M by a vector.
    00189     /// For a vector \f$b\f$, this calculates \f$M^{\dagger}b\f$ by back substitution
    00190     /// (i.e. without explictly calculating the (pseudo-)inverse).
    00191     /// See the detailed description for a description of condition variables.
    00192     template <int Size, typename P2, typename B2>
    00193     Vector<Cols, typename Internal::MultiplyType<Precision,P2>::type >
    00194     backsub(const Vector<Size,P2,B2>& rhs, const Precision condition=condition_no)
    00195     {
    00196         Vector<Min_Dim> inv_diag(min_dim());
    00197         get_inv_diag(inv_diag,condition);
    00198         return (get_VT().T() * diagmult(inv_diag, (get_U().T() * rhs)));
    00199     }
    00200
    00201     /// Calculate (pseudo-)inverse of the matrix. This is not usually needed:
    00202     /// if you need the inverse just to multiply it by a matrix or a vector, use
    00203     /// one of the backsub() functions, which will be faster.
    00204     /// See the detailed description of the pseudo-inverse and condition variables.
    00205     Matrix<Cols,Rows> get_pinv(const Precision condition = condition_no){
    00206         Vector<Min_Dim> inv_diag(min_dim());
    00207         get_inv_diag(inv_diag,condition);
    00208         return diagmult(get_VT().T(),inv_diag) * get_U().T();
    00209     }
    00210
    00211     /// Calculate the product of the singular values
    00212     /// for square matrices this is the determinant
    00213     Precision determinant() {
    00214         Precision result = my_diagonal[0];
    00215         for(int i=1; i<my_diagonal.size(); i++){
    00216             result *= my_diagonal[i];
    00217         }
    00218         return result;
    00219     }
    00220
    00221     /// Calculate the rank of the matrix.
    00222     /// See the detailed description of the pseudo-inverse and condition variables.
    00223     int rank(const Precision condition = condition_no) {
    00224         if (my_diagonal[0] == 0) return 0;
    00225         int result=1;
    00226         for(int i=0; i<min_dim(); i++){
    00227             if(my_diagonal[i] * condition <= my_diagonal[0]){
    00228                 result++;
    00229             }
    00230         }
    00231         return result;
    00232     }
    00233
    00234     /// Return the U matrix from the decomposition
    00235     /// The size of this depends on the shape of the original matrix
    00236     /// it is square if the original matrix is wide or tall if the original matrix is tall
    00237     Matrix<Rows,Min_Dim,Precision,Reference::RowMajor> get_U(){
    00238         if(is_vertical()){
    00239             return Matrix<Rows,Min_Dim,Precision,Reference::RowMajor>
    00240                 (my_copy.my_data,my_copy.num_rows(),my_copy.num_cols());
    00241         } else {
    00242             return Matrix<Rows,Min_Dim,Precision,Reference::RowMajor>
    00243                 (my_square.my_data, my_square.num_rows(), my_square.num_cols());
    00244         }
    00245     }
    00246
    00247     /// Return the singular values as a vector
    00248     Vector<Min_Dim,Precision>& get_diagonal(){ return my_diagonal; }
    00249
    00250     /// Return the VT matrix from the decomposition
    00251     /// The size of this depends on the shape of the original matrix
    00252     /// it is square if the original matrix is tall or wide if the original matrix is wide
    00253     Matrix<Min_Dim,Cols,Precision,Reference::RowMajor> get_VT(){
    00254         if(is_vertical()){
    00255             return Matrix<Min_Dim,Cols,Precision,Reference::RowMajor>
    00256                 (my_square.my_data, my_square.num_rows(), my_square.num_cols());
    00257         } else {
    00258             return Matrix<Min_Dim,Cols,Precision,Reference::RowMajor>
    00259                 (my_copy.my_data,my_copy.num_rows(),my_copy.num_cols());
    00260         }
    00261     }
    00262
    00263     ///Return the pesudo-inverse diagonal. The reciprocal of the diagonal elements
    00264     ///is returned if the elements are well scaled with respect to the largest element,
    00265     ///otherwise 0 is returned.
    00266     ///@param inv_diag Vector in which to return the inverse diagonal.
    00267     ///@param condition Elements must be larger than this factor times the largest diagonal element to be considered well scaled.
    00268     void get_inv_diag(Vector<Min_Dim>& inv_diag, const Precision condition){
    00269         for(int i=0; i<min_dim(); i++){
    00270             if(my_diagonal[i] * condition <= my_diagonal[0]){
    00271                 inv_diag[i]=0;
    00272             } else {
    00273                 inv_diag[i]=static_cast<Precision>(1)/my_diagonal[i];
    00274             }
    00275         }
    00276     }
    00277
    00278 private:
    00279     Matrix<Rows,Cols,Precision,RowMajor> my_copy;
    00280     Vector<Min_Dim,Precision> my_diagonal;
    00281     Matrix<Min_Dim,Min_Dim,Precision,RowMajor> my_square; // square matrix (U or V' depending on the shape of my_copy)
    00282 };
    00283
    00284
    00285
    00286
    00287
    00288
    00289 /// version of SVD forced to be square
    00290 /// princiapally here to allow use in WLS
    00291 /// @ingroup gDecomps
    00292 template<int Size, typename Precision>
    00293 struct SQSVD : public SVD<Size, Size, Precision> {
    00294     ///All constructors are forwarded to SVD in a straightforward manner.
    00295     ///@name Constructors
    00296     ///@{
    00297     SQSVD() {}
    00298     SQSVD(int size) : SVD<Size,Size,Precision>(size, size) {}
    00299
    00300     template <int R2, int C2, typename P2, typename B2>
    00301     SQSVD(const Matrix<R2,C2,P2,B2>& m) : SVD<Size,Size,Precision>(m) {}
    00302     ///@}
    00303 };
    00304
    00305
    00306 }
    00307
    00308
    00309 #endif