core/vpdl/vpdt/vpdt_eigen_sym_matrix.h
Go to the documentation of this file.
00001 // This is core/vpdl/vpdt/vpdt_eigen_sym_matrix.h
00002 #ifndef vpdt_eigen_sym_matrix_h_
00003 #define vpdt_eigen_sym_matrix_h_
00004 //:
00005 // \file
00006 // \author Matthew Leotta
00007 // \date March 5, 2009
00008 // \brief A symmetric matrix represented in eigenvalue decomposition
00009 //
00010 // \verbatim
00011 // Modifications
00012 //   <None yet>
00013 // \endverbatim
00014 
00015 #include <vnl/algo/vnl_symmetric_eigensystem.h>
00016 #include <vpdl/vpdt/vpdt_field_traits.h>
00017 #include <vpdl/vpdt/vpdt_field_default.h>
00018 #include <vpdl/vpdt/vpdt_access.h>
00019 #include <vcl_limits.h>
00020 #include <vcl_cassert.h>
00021 
00022 
00023 //: wrapper for the vnl eigensystem function for fixed size data
00024 template<class T, unsigned int n>
00025 inline void vnl_symmetric_eigensystem_compute(const vnl_matrix_fixed<T,n,n>& A,
00026                                               vnl_matrix_fixed<T,n,n>& V,
00027                                               vnl_vector_fixed<T,n>& D)
00028 {
00029   vnl_matrix_ref<T> Vr(n,n,V.data_block());
00030   vnl_vector_ref<T> Dr(n,D.data_block());
00031   vnl_symmetric_eigensystem_compute(A.as_ref(),Vr,Dr);
00032 }
00033 
00034 
00035 //: A symmetric matrix represented in eigenvalue decomposition
00036 template<class T, unsigned int n=0>
00037 class vpdt_eigen_sym_matrix
00038 {
00039  public:
00040   //: the data type used for vectors
00041   typedef typename vpdt_field_default<T,n>::type vector;
00042   //: the data type used for matrices
00043   typedef typename vpdt_field_traits<vector>::matrix_type matrix;
00044 
00045   //: Constructor
00046   // Optionally initialize the dimension for when n==0.
00047   // Otherwise var_dim is ignored
00048   vpdt_eigen_sym_matrix(unsigned int var_dim = n)
00049   {
00050     vpdt_set_size(eigen_vec_,var_dim);
00051     vpdt_set_size(eigen_val_,var_dim);
00052     eigen_vec_.set_identity();
00053     eigen_val_.fill(T(0));
00054   }
00055 
00056   //: Constructor - from eigenvectors and eigenvalues
00057   vpdt_eigen_sym_matrix(const matrix& evec, const vector& eval)
00058   : eigen_vec_(evec), eigen_val_(eval)
00059   {
00060     assert(are_evec_orthonormal());
00061   }
00062 
00063   //: Constructor - from symmetric matrix
00064   vpdt_eigen_sym_matrix(const matrix& m)
00065   {
00066     set_matrix(m);
00067   }
00068 
00069   //: Return the dimension
00070   unsigned int dimension() const { return eigen_val_.size(); }
00071 
00072   //: Access to the eigenvectors
00073   const matrix& eigenvectors() const { return eigen_vec_; }
00074 
00075   //: Access to the eigenvalues
00076   const vector& eigenvalues() const { return eigen_val_; }
00077 
00078   //: Set the eigenvectors
00079   void set_eigenvectors(const matrix& m)
00080   {
00081     eigen_vec_ = m;
00082     assert(are_evec_orthonormal());
00083   }
00084 
00085   //: set the eigenvalues
00086   void set_eigenvalues(const vector& v) { eigen_val_ = v; }
00087 
00088   //: Set the size (if variable) and reset to default
00089   void set_size(unsigned int dim)
00090   {
00091     vpdt_set_size(eigen_vec_,dim);
00092     vpdt_set_size(eigen_val_,dim);
00093     eigen_vec_.set_identity();
00094     eigen_val_.fill(T(0));
00095   }
00096 
00097   //: set the eigenvectors and eigen values by decomposing m
00098   void set_matrix(const matrix& m)
00099   {
00100     const unsigned int dim = vpdt_size(m);
00101     vpdt_set_size(eigen_vec_,dim);
00102     vpdt_set_size(eigen_val_,dim);
00103     vnl_symmetric_eigensystem_compute(m, eigen_vec_, eigen_val_);
00104   }
00105 
00106   //: multiply the matrix by a scalar
00107   vpdt_eigen_sym_matrix<T,n>& operator*=(const T& val)
00108   {
00109     const unsigned int dim = eigen_val_.size();
00110     for (unsigned int i=0; i<dim; ++i)
00111       eigen_val_[i] *= val;
00112     return *this;
00113   }
00114 
00115   //: Reform the matrix
00116   // m = eigen_vec_ * diag(eigen_val_) * eigen_vec_.transpose()
00117   void form_matrix(matrix& m) const
00118   {
00119     const unsigned int dim = eigen_val_.size();
00120     vpdt_set_size(m,dim);
00121     m.fill(T(0));
00122 
00123     for (unsigned int i = 0; i < dim; ++i)
00124       for (unsigned int k = 0; k < dim; ++k){
00125         T tmp =  eigen_vec_[i][k] * eigen_val_[k];
00126         for (unsigned int j = i; j < dim; ++j)
00127           m[i][j] += tmp * eigen_vec_[j][k];
00128       }
00129 
00130     // fill in other side of diagonal
00131     for (unsigned int i = 0; i < dim; ++i)
00132       for (unsigned int j = i+1; j < dim; ++j)
00133         m[j][i] = m[i][j];
00134   }
00135 
00136   //: compute the matrix inverse
00137   // m = eigen_vec_ * inverse(diag(eigen_val_)) * eigen_vec_.transpose()
00138   void form_inverse(matrix& m) const
00139   {
00140     const unsigned int dim = eigen_val_.size();
00141     vpdt_set_size(m,dim);
00142     m.fill(T(0));
00143 
00144     for (unsigned int k = 0; k < dim; ++k){
00145       if (eigen_val_[k] <= T(0))
00146         continue;
00147       for (unsigned int i = 0; i < dim; ++i){
00148         T tmp =  eigen_vec_[i][k] / eigen_val_[k];
00149         for (unsigned int j = i; j < dim; ++j)
00150           m[i][j] += tmp * eigen_vec_[j][k];
00151       }
00152     }
00153 
00154     // fill in other side of diagonal
00155     for (unsigned int i = 0; i < dim; ++i)
00156       for (unsigned int j = i+1; j < dim; ++j)
00157         m[j][i] = m[i][j];
00158   }
00159 
00160   //: evaluate y = M * x
00161   void product(const vector& x, vector& y) const
00162   {
00163     const unsigned int dim = eigen_val_.size();
00164     vpdt_set_size(y,dim);
00165     vpdt_fill(y,T(0));
00166     for (unsigned int i = 0; i < dim; ++i){
00167       T t_i = T(0);
00168       for (unsigned int j = 0; j < dim; ++j){
00169         t_i += eigen_vec_[j][i] * x[j];
00170       }
00171       t_i *= eigen_val_[i];
00172       for (unsigned int j = 0; j < dim; ++j){
00173         y[j] += eigen_vec_[j][i] * t_i;
00174       }
00175     }
00176   }
00177 
00178   //: evaluate y = M^-1 * x
00179   void inverse_product(const vector& x, vector& y) const
00180   {
00181     const unsigned int dim = eigen_val_.size();
00182     vpdt_set_size(y,dim);
00183     vpdt_fill(y,T(0));
00184     for (unsigned int i = 0; i < dim; ++i){
00185       T t_i = T(0);
00186       for (unsigned int j = 0; j < dim; ++j){
00187         t_i += eigen_vec_[j][i] * x[j];
00188       }
00189       t_i /= eigen_val_[i];
00190       for (unsigned int j = 0; j < dim; ++j){
00191         y[j] += eigen_vec_[j][i] * t_i;
00192       }
00193     }
00194   }
00195 
00196   //: evaluate the Quadratic form x^t * M * x
00197   T quad_form(const vector& x) const
00198   {
00199     const unsigned int dim = eigen_val_.size();
00200     T val = T(0);
00201     for (unsigned int i = 0; i < dim; ++i){
00202       T tmp = T(0);
00203       for (unsigned int j = 0; j < dim; ++j){
00204         tmp += eigen_vec_[j][i] * x[j];
00205       }
00206       val += tmp*tmp*eigen_val_[i];
00207     }
00208     return val;
00209   }
00210 
00211   //: evaluate the inverse Quadratic form x^t * M^-1 * x
00212   T inverse_quad_form(const vector& x) const
00213   {
00214     const unsigned int dim = eigen_val_.size();
00215     T val = T(0);
00216     for (unsigned int i = 0; i < dim; ++i){
00217       if (eigen_val_[i] <= T(0))
00218         return vcl_numeric_limits<T>::infinity();
00219       T tmp = T(0);
00220       for (unsigned int j = 0; j < dim; ++j){
00221         tmp += eigen_vec_[j][i] * x[j];
00222       }
00223       val += tmp*tmp/eigen_val_[i];
00224     }
00225     return val;
00226   }
00227 
00228   //: compute the determinant
00229   T determinant() const
00230   {
00231     const unsigned int dim = eigen_val_.size();
00232     T det = T(1);
00233     for (unsigned int i=0; i<dim; ++i)
00234       det *= eigen_val_[i];
00235     return det;
00236   }
00237 
00238  private:
00239   //: the matrix of eigenvectors
00240   matrix eigen_vec_;
00241   //: the vector of eigenvalues
00242   vector eigen_val_;
00243 
00244   //: return true if the eigenvectors are (approximately) orthonormal
00245   bool are_evec_orthonormal() const
00246   {
00247     // FIXME: implement this
00248     return false;
00249   }
00250 };
00251 
00252 
00253 //: A symmetric matrix represented in eigenvalue decomposition
00254 // This partial specialization for the scalar case is no longer needed
00255 // If you get an error related to this, you should be using a scalar instead.
00256 template<class T>
00257 class vpdt_eigen_sym_matrix<T,1> {};
00258 
00259 
00260 //==============================================================================
00261 // These type generators produce a vpdt_eigen_sym_matrix for a field type
00262 
00263 //: generate the vpdt_eigen_sys_matrix type from a field type
00264 template <class F, class Disambiguate= void>
00265 struct vpdt_eigen_sym_matrix_gen;
00266 
00267 //: generate the vpdt_eigen_sys_matrix type from a field type
00268 template <class F>
00269 struct vpdt_eigen_sym_matrix_gen<F,typename vpdt_field_traits<F>::type_is_vector>
00270 {
00271   typedef vpdt_eigen_sym_matrix<typename vpdt_field_traits<F>::scalar_type,
00272                                 vpdt_field_traits<F>::dimension> type;
00273 };
00274 
00275 //: generate the vpdt_eigen_sys_matrix type from a field type
00276 template <class F>
00277 struct vpdt_eigen_sym_matrix_gen<F,typename vpdt_field_traits<F>::type_is_scalar>
00278 {
00279   typedef typename vpdt_field_traits<F>::matrix_type type;
00280 };
00281 
00282 
00283 //==============================================================================
00284 // universal access functions (See vpdt_access.h)
00285 
00286 //: Set the size of a vpdt_eigen_sym_matrix
00287 template <class T>
00288 inline void vpdt_set_size(vpdt_eigen_sym_matrix<T,0>& m, unsigned int s)
00289 {
00290   m.set_size(s);
00291 }
00292 
00293 
00294 //: Fill a vpdt_eigen_sym_matrix
00295 template <class T, unsigned int n>
00296 inline void vpdt_fill(vpdt_eigen_sym_matrix<T,n>& m, const T& val)
00297 {
00298   typename vpdt_eigen_sym_matrix<T,n>::vector v;
00299   vpdt_set_size(v,m.dimension());
00300   vpdt_fill(v,val);
00301   m.set_eigenvalues(v);
00302 }
00303 
00304 
00305 #endif // vpdt_eigen_sym_matrix_h_