TooN: SymEigen.h Source File

TooN 2.0.0-beta8

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

- File List

SymEigen.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 __SYMEIGEN_H
    00031 #define __SYMEIGEN_H
    00032
    00033 #include <iostream>
    00034 #include <cassert>
    00035 #include <cmath>
    00036 #include <complex>
    00037 #include <TooN/lapack.h>
    00038
    00039 #include <TooN/TooN.h>
    00040
    00041 namespace TooN {
    00042 static const double root3=1.73205080756887729352744634150587236694280525381038062805580;
    00043
    00044 namespace Internal{
    00045
    00046         using std::swap;
    00047
    00048     ///Default condition number for SymEigen::backsub, SymEigen::get_pinv and SymEigen::get_inv_diag
    00049     static const double symeigen_condition_no=1e9;
    00050
    00051     ///@internal
    00052     ///@brief Compute eigensystems for sizes > 2
    00053     ///Helper struct for computing eigensystems, to allow for specialization on
    00054     ///2x2 matrices.
    00055     ///@ingroup gInternal
    00056     template <int Size> struct ComputeSymEigen {
    00057
    00058         ///@internal
    00059         ///Compute an eigensystem.
    00060         ///@param m Input matrix (assumed to be symmetric)
    00061         ///@param evectors Eigen vector output
    00062         ///@param evalues Eigen values output
    00063         template<int Rows, int Cols, typename P, typename B>
    00064         static inline void compute(const Matrix<Rows,Cols,P, B>& m, Matrix<Size,Size,P> & evectors, Vector<Size, P>& evalues) {
    00065
    00066             SizeMismatch<Rows, Cols>::test(m.num_rows(), m.num_cols());  //m must be square
    00067             SizeMismatch<Size, Rows>::test(m.num_rows(), evalues.size()); //m must be the size of the system
    00068
    00069
    00070             evectors = m;
    00071             FortranInteger N = evalues.size();
    00072             FortranInteger lda = evalues.size();
    00073             FortranInteger info;
    00074             FortranInteger lwork=-1;
    00075             P size;
    00076
    00077             // find out how much space fortran needs
    00078             syev_((char*)"V",(char*)"U",&N,&evectors[0][0],&lda,&evalues[0], &size,&lwork,&info);
    00079             lwork = int(size);
    00080             Vector<Dynamic, P> WORK(lwork);
    00081
    00082             // now compute the decomposition
    00083             syev_((char*)"V",(char*)"U",&N,&evectors[0][0],&lda,&evalues[0], &WORK[0],&lwork,&info);
    00084
    00085             if(info!=0){
    00086                 std::cerr << "In SymEigen<"<<Size<<">: " << info
    00087                         << " off-diagonal elements of an intermediate tridiagonal form did not converge to zero." << std::endl
    00088                         << "M = " << m << std::endl;
    00089             }
    00090         }
    00091     };
    00092
    00093     ///@internal
    00094     ///@brief Compute 2x2 eigensystems
    00095     ///Helper struct for computing eigensystems, specialized on 2x2 matrices.
    00096     ///@ingroup gInternal
    00097     template <> struct ComputeSymEigen<2> {
    00098
    00099         ///@internal
    00100         ///Compute an eigensystem.
    00101         ///@param m Input matrix (assumed to be symmetric)
    00102         ///@param eig Eigen vector output
    00103         ///@param ev Eigen values output
    00104         template<typename P, typename B>
    00105         static inline void compute(const Matrix<2,2,P,B>& m, Matrix<2,2,P>& eig, Vector<2, P>& ev) {
    00106             double trace = m[0][0] + m[1][1];
    00107             double det = m[0][0]*m[1][1] - m[0][1]*m[1][0];
    00108             double disc = trace*trace - 4 * det;
    00109             assert(disc>=0);
    00110             using std::sqrt;
    00111             double root_disc = sqrt(disc);
    00112             ev[0] = 0.5 * (trace - root_disc);
    00113             ev[1] = 0.5 * (trace + root_disc);
    00114             double a = m[0][0] - ev[0];
    00115             double b = m[0][1];
    00116             double magsq = a*a + b*b;
    00117             if (magsq == 0) {
    00118                 eig[0][0] = 1.0;
    00119                 eig[0][1] = 0;
    00120             } else {
    00121                 eig[0][0] = -b;
    00122                 eig[0][1] = a;
    00123                 eig[0] *= 1.0/sqrt(magsq);
    00124             }
    00125             eig[1][0] = -eig[0][1];
    00126             eig[1][1] = eig[0][0];
    00127         }
    00128     };
    00129
    00130     ///@internal
    00131     ///@brief Compute 3x3 eigensystems
    00132     ///Helper struct for computing eigensystems, specialized on 3x3 matrices.
    00133     ///@ingroup gInternal
    00134     template <> struct ComputeSymEigen<3> {
    00135
    00136         ///@internal
    00137         ///Compute an eigensystem.
    00138         ///@param m Input matrix (assumed to be symmetric)
    00139         ///@param eig Eigen vector output
    00140         ///@param ev Eigen values output
    00141         template<typename P, typename B>
    00142         static inline void compute(const Matrix<3,3,P,B>& m, Matrix<3,3,P>& eig, Vector<3, P>& ev) {
    00143             //method uses closed form solution of cubic equation to obtain roots of characteristic equation.
    00144             using std::sqrt;
    00145
    00146             //Polynomial terms of |a - l * Identity|
    00147             //l^3 + a*l^2 + b*l + c
    00148
    00149             const double& a11 = m[0][0];
    00150             const double& a12 = m[0][1];
    00151             const double& a13 = m[0][2];
    00152
    00153             const double& a22 = m[1][1];
    00154             const double& a23 = m[1][2];
    00155
    00156             const double& a33 = m[2][2];
    00157
    00158             //From matlab:
    00159             double a = -a11-a22-a33;
    00160             double b = a11*a22+a11*a33+a22*a33-a12*a12-a13*a13-a23*a23;
    00161             double c = a11*(a23*a23)+(a13*a13)*a22+(a12*a12)*a33-a12*a13*a23*2.0-a11*a22*a33;
    00162
    00163             //Using Cardano's method:
    00164             double p = b - a*a/3;
    00165             double q = c + (2*a*a*a - 9*a*b)/27;
    00166
    00167             double alpha = -q/2;
    00168             double beta_descriminant = q*q/4 + p*p*p/27;
    00169
    00170             //beta_descriminant <= 0 for real roots!
    00171             double beta = sqrt(-beta_descriminant);
    00172             double r2 = alpha*alpha  - beta_descriminant;
    00173
    00174             ///Need A,B = cubert(alpha +- beta)
    00175             ///Turn in to r, theta
    00176             /// r^(1/3) * e^(i * theta/3)
    00177
    00178             double cuberoot_r = pow(r2, 1./6);
    00179             double theta3 = atan2(beta, alpha)/3;
    00180
    00181             double A_plus_B = 2*cuberoot_r*cos(theta3);
    00182             double A_minus_B = -2*cuberoot_r*sin(theta3);
    00183
    00184             //calculate eigenvalues
    00185             ev =  makeVector(A_plus_B, -A_plus_B/2 + A_minus_B * sqrt(3)/2, -A_plus_B/2 - A_minus_B * sqrt(3)/2) - Ones * a/3;
    00186
    00187             if(ev[0] > ev[1])
    00188                 swap(ev[0], ev[1]);
    00189             if(ev[1] > ev[2])
    00190                 swap(ev[1], ev[2]);
    00191             if(ev[0] > ev[1])
    00192                 swap(ev[0], ev[1]);
    00193
    00194             //calculate the eigenvectors
    00195             eig[0][0]=a12 * a23 - a13 * (a22 - ev[0]);
    00196             eig[0][1]=a12 * a13 - a23 * (a11 - ev[0]);
    00197             eig[0][2]=(a11-ev[0])*(a22-ev[0]) - a12*a12;
    00198             normalize(eig[0]);
    00199             eig[1][0]=a12 * a23 - a13 * (a22 - ev[1]);
    00200             eig[1][1]=a12 * a13 - a23 * (a11 - ev[1]);
    00201             eig[1][2]=(a11-ev[1])*(a22-ev[1]) - a12*a12;
    00202             normalize(eig[1]);
    00203             eig[2][0]=a12 * a23 - a13 * (a22 - ev[2]);
    00204             eig[2][1]=a12 * a13 - a23 * (a11 - ev[2]);
    00205             eig[2][2]=(a11-ev[2])*(a22-ev[2]) - a12*a12;
    00206             normalize(eig[2]);
    00207         }
    00208     };
    00209
    00210 };
    00211
    00212 /**
    00213 Performs eigen decomposition of a matrix.
    00214 Real symmetric (and hence square matrices) can be decomposed into
    00215 \f[M = U \times \Lambda \times U^T\f]
    00216 where \f$U\f$ is an orthogonal matrix (and hence \f$U^T = U^{-1}\f$) whose columns
    00217 are the eigenvectors of \f$M\f$ and \f$\Lambda\f$ is a diagonal matrix whose entries
    00218 are the eigenvalues of \f$M\f$. These quantities are often of use directly, and can
    00219 be obtained as follows:
    00220 @code
    00221 // construct M
    00222 Matrix<3> M(3,3);
    00223 M[0]=makeVector(4,0,2);
    00224 M[1]=makeVector(0,5,3);
    00225 M[2]=makeVector(2,3,6);
    00226
    00227 // create the eigen decomposition of M
    00228 SymEigen<3> eigM(M);
    00229 cout << "A=" << M << endl;
    00230 cout << "(E,v)=eig(A)" << endl;
    00231 // print the smallest eigenvalue
    00232 cout << "v[0]=" << eigM.get_evalues()[0] << endl;
    00233 // print the associated eigenvector
    00234 cout << "E[0]=" << eigM.get_evectors()[0] << endl;
    00235 @endcode
    00236
    00237 Further, provided the eigenvalues are nonnegative, the square root of
    00238 a matrix and its inverse can also be obtained,
    00239 @code
    00240 // print the square root of the matrix.
    00241 cout << "R=sqrtm(A)=" << eigM.get_sqrtm() << endl;
    00242 // print the square root of the matrix squared.
    00243 cout << "(should equal A), R^T*R="
    00244      << eigM.get_sqrtm().T() * eigM.get_sqrtm() << endl;
    00245 // print the inverse of the matrix.
    00246 cout << "A^-1=" << eigM.get_pinv() << endl;
    00247 // print the inverse square root of the matrix.
    00248 cout << "C=isqrtm(A)=" << eigM.get_isqrtm() << endl;
    00249 // print the inverse square root of the matrix squared.
    00250 cout << "(should equal A^-1), C^T*C="
    00251      << eigM.get_isqrtm().T() * eigM.get_isqrtm() << endl;
    00252 @endcode
    00253
    00254 This decomposition is very similar to the SVD (q.v.), and can be used to solve
    00255 equations using backsub() or get_pinv(), with the same treatment of condition numbers.
    00256
    00257 SymEigen<> (= SymEigen<-1>) can be used to create an eigen decomposition whose size is determined at run-time.
    00258 @ingroup gDecomps
    00259 **/
    00260 template <int Size=Dynamic, typename Precision = double>
    00261 class SymEigen {
    00262 public:
    00263     inline SymEigen(){}
    00264
    00265         /// Initialise this eigen decomposition but do no immediately
    00266         /// perform a decomposition.
    00267         ///
    00268         /// @param m The size of the matrix to perform the eigen decomposition on.
    00269         inline SymEigen(int m) : my_evectors(m,m), my_evalues(m) {}
    00270
    00271     /// Construct the eigen decomposition of a matrix. This initialises the class, and
    00272     /// performs the decomposition immediately.
    00273     template<int R, int C, typename B>
    00274     inline SymEigen(const Matrix<R, C, Precision, B>& m) : my_evectors(m.num_rows(), m.num_cols()), my_evalues(m.num_rows()) {
    00275         compute(m);
    00276     }
    00277
    00278     /// Perform the eigen decomposition of a matrix.
    00279     template<int R, int C, typename B>
    00280     inline void compute(const Matrix<R,C,Precision,B>& m){
    00281         SizeMismatch<R, C>::test(m.num_rows(), m.num_cols());
    00282         SizeMismatch<R, Size>::test(m.num_rows(), my_evectors.num_rows());
    00283         Internal::ComputeSymEigen<Size>::compute(m, my_evectors, my_evalues);
    00284     }
    00285
    00286     /// Calculate result of multiplying the (pseudo-)inverse of M by a vector.
    00287     /// For a vector \f$b\f$, this calculates \f$M^{\dagger}b\f$ by back substitution
    00288     /// (i.e. without explictly calculating the (pseudo-)inverse).
    00289     /// See the SVD detailed description for a description of condition variables.
    00290     template <int S, typename P, typename B>
    00291     Vector<Size, Precision> backsub(const Vector<S,P,B>& rhs) const {
    00292         return (my_evectors.T() * diagmult(get_inv_diag(Internal::symeigen_condition_no),(my_evectors * rhs)));
    00293     }
    00294
    00295     /// Calculate result of multiplying the (pseudo-)inverse of M by another matrix.
    00296     /// For a matrix \f$A\f$, this calculates \f$M^{\dagger}A\f$ by back substitution
    00297     /// (i.e. without explictly calculating the (pseudo-)inverse).
    00298     /// See the SVD detailed description for a description of condition variables.
    00299     template <int R, int C, typename P, typename B>
    00300     Matrix<Size,C, Precision> backsub(const Matrix<R,C,P,B>& rhs) const {
    00301         return (my_evectors.T() * diagmult(get_inv_diag(Internal::symeigen_condition_no),(my_evectors * rhs)));
    00302     }
    00303
    00304     /// Calculate (pseudo-)inverse of the matrix. This is not usually needed:
    00305     /// if you need the inverse just to multiply it by a matrix or a vector, use
    00306     /// one of the backsub() functions, which will be faster.
    00307     /// See the SVD detailed description for a description of the pseudo-inverse
    00308     /// and condition variables.
    00309     Matrix<Size, Size, Precision> get_pinv(const double condition=Internal::symeigen_condition_no) const {
    00310         return my_evectors.T() * diagmult(get_inv_diag(condition),my_evectors);
    00311     }
    00312
    00313     /// Calculates the reciprocals of the eigenvalues of the matrix.
    00314     /// The vector <code>invdiag</code> lists the eigenvalues in order, from
    00315     /// the largest (i.e. smallest reciprocal) to the smallest.
    00316     /// These are also the diagonal values of the matrix \f$Lambda^{-1}\f$.
    00317     /// Any eigenvalues which are too small are set to zero (see the SVD
    00318     /// detailed description for a description of the and condition variables).
    00319     Vector<Size, Precision> get_inv_diag(const double condition) const {
    00320         Precision max_diag = -my_evalues[0] > my_evalues[my_evalues.size()-1] ? -my_evalues[0]:my_evalues[my_evalues.size()-1];
    00321         Vector<Size, Precision> invdiag(my_evalues.size());
    00322         for(int i=0; i<my_evalues.size(); i++){
    00323             if(fabs(my_evalues[i]) * condition > max_diag) {
    00324                 invdiag[i] = 1/my_evalues[i];
    00325             } else {
    00326                 invdiag[i]=0;
    00327             }
    00328         }
    00329         return invdiag;
    00330     }
    00331
    00332     /// Returns the eigenvectors of the matrix.
    00333     /// This returns \f$U^T\f$, so that the rows of the matrix are the eigenvectors,
    00334     /// which can be extracted using usual Matrix::operator[]() subscript operator.
    00335     /// They are returned in order of the size of the corresponding eigenvalue, i.e.
    00336     /// the vector with the largest eigenvalue is first.
    00337     Matrix<Size,Size,Precision>& get_evectors() {return my_evectors;}
    00338
    00339     /**\overload
    00340     */
    00341     const Matrix<Size,Size,Precision>& get_evectors() const {return my_evectors;}
    00342
    00343
    00344     /// Returns the eigenvalues of the matrix.
    00345     /// The eigenvalues are listed in order, from the smallest to the largest.
    00346     /// These are also the diagonal values of the matrix \f$\Lambda\f$.
    00347     Vector<Size, Precision>& get_evalues() {return my_evalues;}
    00348     /**\overload
    00349     */
    00350     const Vector<Size, Precision>& get_evalues() const {return my_evalues;}
    00351
    00352     /// Is the matrix positive definite?
    00353     bool is_posdef() const {
    00354         for (int i = 0; i < my_evalues.size(); ++i) {
    00355             if (my_evalues[i] <= 0.0)
    00356                 return false;
    00357         }
    00358         return true;
    00359     }
    00360
    00361     /// Is the matrix negative definite?
    00362     bool is_negdef() const {
    00363         for (int i = 0; i < my_evalues.size(); ++i) {
    00364             if (my_evalues[i] >= 0.0)
    00365                 return false;
    00366         }
    00367         return true;
    00368     }
    00369
    00370     /// Get the determinant of the matrix
    00371     Precision get_determinant () const {
    00372         Precision det = 1.0;
    00373         for (int i = 0; i < my_evalues.size(); ++i) {
    00374             det *= my_evalues[i];
    00375         }
    00376         return det;
    00377     }
    00378
    00379     /// Calculate the square root of a matrix which is a matrix M
    00380     /// such that M.T*M=A.
    00381     Matrix<Size, Size, Precision> get_sqrtm () const {
    00382         Vector<Size, Precision> diag_sqrt(my_evalues.size());
    00383         // In the future, maybe throw an exception if an
    00384         // eigenvalue is negative?
    00385         for (int i = 0; i < my_evalues.size(); ++i) {
    00386             diag_sqrt[i] = std::sqrt(my_evalues[i]);
    00387         }
    00388         return my_evectors.T() * diagmult(diag_sqrt, my_evectors);
    00389     }
    00390
    00391     /// Calculate the inverse square root of a matrix which is a
    00392     /// matrix M such that M.T*M=A^-1.
    00393         ///
    00394         /// Any square-rooted eigenvalues which are too small are set
    00395         /// to zero (see the SVD detailed description for a
    00396         /// description of the condition variables).
    00397     Matrix<Size, Size, Precision> get_isqrtm (const double condition=Internal::symeigen_condition_no) const {
    00398         Vector<Size, Precision> diag_isqrt(my_evalues.size());
    00399
    00400         // Because sqrt is a monotonic-preserving transformation,
    00401         Precision max_diag = -my_evalues[0] > my_evalues[my_evalues.size()-1] ? (-std::sqrt(my_evalues[0])):(std::sqrt(my_evalues[my_evalues.size()-1]));
    00402         // In the future, maybe throw an exception if an
    00403         // eigenvalue is negative?
    00404         for (int i = 0; i < my_evalues.size(); ++i) {
    00405             diag_isqrt[i] = std::sqrt(my_evalues[i]);
    00406             if(fabs(diag_isqrt[i]) * condition > max_diag) {
    00407                 diag_isqrt[i] = 1/diag_isqrt[i];
    00408             } else {
    00409                 diag_isqrt[i] = 0;
    00410             }
    00411         }
    00412         return my_evectors.T() * diagmult(diag_isqrt, my_evectors);
    00413     }
    00414
    00415 private:
    00416     // eigen vectors laid out row-wise so evectors[i] is the ith evector
    00417     Matrix<Size,Size,Precision> my_evectors;
    00418
    00419     Vector<Size, Precision> my_evalues;
    00420 };
    00421
    00422 }
    00423
    00424 #endif
    00425