TooN 2.0.0-beta8
|
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