core/vnl/algo/vnl_symmetric_eigensystem.cxx

Go to the documentation of this file.
00001 // This is core/vnl/algo/vnl_symmetric_eigensystem.cxx
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005 //:
00006 // \file
00007 // \author Andrew W. Fitzgibbon, Oxford RRG
00008 // Created: 29 Aug 96
00009 //
00010 //-----------------------------------------------------------------------------
00011 
00012 #include "vnl_symmetric_eigensystem.h"
00013 #include <vcl_cassert.h>
00014 #include <vcl_algorithm.h> // for swap
00015 #include <vcl_cmath.h> // for sqrt(double), acos, etc.
00016 #include <vcl_iostream.h>
00017 #include <vnl/vnl_copy.h>
00018 #include <vnl/vnl_math.h>
00019 #include <vnl/algo/vnl_netlib.h> // rs_()
00020 
00021 //: Find eigenvalues of a symmetric 3x3 matrix
00022 // \verbatim
00023 // Matrix is   M11  M12  M13
00024 //             M12  M22  M23
00025 //             M13  M23  M33
00026 // \endverbatim
00027 void vnl_symmetric_eigensystem_compute_eigenvals(
00028   double M11, double M12, double M13,
00029               double M22, double M23,
00030                           double M33,
00031   double &l1, double &l2, double &l3)
00032 {
00033   // Characteristic eqtn |M - xI| = 0
00034   // x^3 + b x^2 + c x + d = 0
00035   const double b = -M11-M22-M33;
00036   const double c =  M11*M22 +M11*M33 +M22*M33  -M12*M12 -M13*M13 -M23*M23;
00037   const double d = M11*M23*M23 +M12*M12*M33 +M13*M13*M22 -2.0*M12*M13*M23 -M11*M22*M33;
00038 
00039  
00040   // Using a numerically tweaked version of the real cubic solver http://www.1728.com/cubic2.htm
00041   const double b_3 = b/3.0;
00042   const double f = b_3*b_3 -  c/3.0 ;
00043   const double g = b*c/6.0 - b_3*b_3*b_3 - 0.5*d;
00044 
00045 
00046   if (f == 0.0 && g == 0.0)
00047   {
00048     l1 = l2 = l3 = - b_3 ;
00049     return;
00050   }
00051 
00052   
00053   const double f3 = f*f*f;
00054   const double g2 = g*g;
00055   const double sqrt_f = -vcl_sqrt(f);
00056       
00057   // deal explicitly with repeated root and treat
00058   // complex conjugate roots as numerically inaccurate repeated roots.
00059   
00060   // first check we are not too numerically innacurate
00061   assert((g2 - f3) / vnl_math_sqr(vnl_math_cube(b)) < 1e-8);  
00062   
00063   if (g2 >= f3)
00064   {
00065     if (g < 0.0)
00066       {
00067         l1 = 2.0 * sqrt_f  - b_3;
00068         l2 = l3 = - sqrt_f - b_3;
00069       }
00070     else
00071       {
00072         l1 = l2 = sqrt_f  - b_3;
00073         l3 = -2.0 * sqrt_f - b_3;
00074       }
00075     return;
00076   }
00077   
00078 
00079   const double sqrt_f3 = sqrt_f * sqrt_f * sqrt_f;
00080   const double k = vcl_acos(g / sqrt_f3) / 3.0;
00081   const double j = 2.0 * sqrt_f;
00082   l1 = j * vcl_cos(k) - b_3;
00083   l2 = j * vcl_cos(k + vnl_math::pi * 2.0 / 3.0) - b_3;
00084   l3 = j * vcl_cos(k - vnl_math::pi * 2.0 / 3.0) - b_3;
00085 
00086   if (l2 < l1) vcl_swap(l2, l1);
00087   if (l3 < l2)
00088   {
00089     vcl_swap(l2, l3);
00090     if (l2 < l1) vcl_swap(l2, l1);
00091   }
00092 
00093 
00094 
00095 }
00096 
00097 bool vnl_symmetric_eigensystem_compute(vnl_matrix<float> const & A,
00098                                        vnl_matrix<float>       & V,
00099                                        vnl_vector<float>       & D)
00100 {
00101   vnl_matrix<double> Ad(A.rows(), A.cols());
00102   vnl_matrix<double> Vd(V.rows(), V.cols());
00103   vnl_vector<double> Dd(D.size());
00104   vnl_copy(A, Ad);
00105   bool f = vnl_symmetric_eigensystem_compute(Ad, Vd, Dd);
00106   vnl_copy(Vd, V);
00107   vnl_copy(Dd, D);
00108   return f;
00109 }
00110 
00111 bool vnl_symmetric_eigensystem_compute(vnl_matrix<double> const & A,
00112                                        vnl_matrix<double>       & V,
00113                                        vnl_vector<double>       & D)
00114 {
00115   A.assert_finite();
00116   const long n = A.rows();
00117 
00118   // Set the size of the eigenvalue vector D (output) if it does not match the size of A:
00119   if (D.size() != A.rows())
00120     D.set_size(n);
00121 
00122   vnl_vector<double> work1(n);
00123   vnl_vector<double> work2(n);
00124   vnl_vector<double> Vvec(n*n);
00125 
00126   long want_eigenvectors = 1;
00127   long ierr = 0;
00128 
00129   // No need to transpose A, cos it's symmetric...
00130   vnl_matrix<double> B = A; // since A is read-only and rs_ might change its third argument...
00131   v3p_netlib_rs_(&n, &n, B.data_block(), &D[0], &want_eigenvectors, &Vvec[0], &work1[0], &work2[0], &ierr);
00132 
00133   if (ierr) {
00134     vcl_cerr << "vnl_symmetric_eigensystem: ierr = " << ierr << vcl_endl;
00135     return false;
00136   }
00137 
00138   // Transpose-copy into V, which is first resized if necessary
00139   if (V.rows() != A.rows() || V.cols() != A.rows())
00140     V.set_size(n,n);
00141   double *vptr = &Vvec[0];
00142   for (int c = 0; c < n; ++c)
00143     for (int r = 0; r < n; ++r)
00144       V(r,c) = *vptr++;
00145 
00146   return true;
00147 }
00148 
00149 //----------------------------------------------------------------------
00150 
00151 // - @{ Solve real symmetric eigensystem $A x = \lambda x$ @}
00152 template <class T>
00153 vnl_symmetric_eigensystem<T>::vnl_symmetric_eigensystem(vnl_matrix<T> const& A)
00154   : n_(A.rows()), V(n_, n_), D(n_)
00155 {
00156   vnl_vector<T> Dvec(n_);
00157 
00158   vnl_symmetric_eigensystem_compute(A, V, Dvec);
00159 
00160   // Copy Dvec into diagonal of D
00161   for (int i = 0; i < n_; ++i)
00162     D(i,i) = Dvec[i];
00163 }
00164 
00165 template <class T>
00166 vnl_vector<T> vnl_symmetric_eigensystem<T>::get_eigenvector(int i) const
00167 {
00168   return vnl_vector<T>(V.extract(n_,1,0,i).data_block(), n_);
00169 }
00170 
00171 template <class T>
00172 T vnl_symmetric_eigensystem<T>::get_eigenvalue(int i) const
00173 {
00174   return D(i, i);
00175 }
00176 
00177 template <class T>
00178 vnl_vector<T> vnl_symmetric_eigensystem<T>::solve(vnl_vector<T> const& b)
00179 {
00180   //vnl_vector<T> ret(b.length());
00181   //FastOps::AtB(V, b, &ret);
00182   vnl_vector<T> ret(b*V); // same as V.transpose()*b
00183 
00184   vnl_vector<T> tmp(b.size());
00185   D.solve(ret, &tmp);
00186 
00187   return V * tmp;
00188 }
00189 
00190 template <class T>
00191 T vnl_symmetric_eigensystem<T>::determinant() const
00192 {
00193   int const n = D.size();
00194   T det(1);
00195   for (int i=0; i<n; ++i)
00196     det *= D[i];
00197   return det;
00198 }
00199 
00200 template <class T>
00201 vnl_matrix<T> vnl_symmetric_eigensystem<T>::pinverse() const
00202 {
00203   unsigned n = D.rows();
00204   vnl_diag_matrix<T> invD(n);
00205   for (unsigned i=0; i<n; ++i)
00206     if (D(i, i) == 0) {
00207       vcl_cerr << __FILE__ ": pinverse(): eigenvalue " << i << " is zero.\n";
00208       invD(i, i) = 0;
00209     }
00210     else
00211       invD(i, i) = 1 / D(i, i);
00212   return V * invD * V.transpose();
00213 }
00214 
00215 template <class T>
00216 vnl_matrix<T> vnl_symmetric_eigensystem<T>::square_root() const
00217 {
00218   unsigned n = D.rows();
00219   vnl_diag_matrix<T> sqrtD(n);
00220   for (unsigned i=0; i<n; ++i)
00221     if (D(i, i) < 0) {
00222       vcl_cerr << __FILE__ ": square_root(): eigenvalue " << i << " is negative (" << D(i, i) << ").\n";
00223       sqrtD(i, i) = (T)vcl_sqrt((typename vnl_numeric_traits<T>::real_t)(-D(i, i)));
00224                     // gives square root of the absolute value of T.
00225     }
00226     else
00227       sqrtD(i, i) = (T)vcl_sqrt((typename vnl_numeric_traits<T>::real_t)(D(i, i)));
00228   return V * sqrtD * V.transpose();
00229 }
00230 
00231 template <class T>
00232 vnl_matrix<T> vnl_symmetric_eigensystem<T>::inverse_square_root() const
00233 {
00234   unsigned n = D.rows();
00235   vnl_diag_matrix<T> inv_sqrtD(n);
00236   for (unsigned i=0; i<n; ++i)
00237     if (D(i, i) <= 0) {
00238       vcl_cerr << __FILE__ ": square_root(): eigenvalue " << i << " is non-positive (" << D(i, i) << ").\n";
00239       inv_sqrtD(i, i) = (T)vcl_sqrt(-1.0/(typename vnl_numeric_traits<T>::real_t)(D(i, i))); // ??
00240     }
00241     else
00242       inv_sqrtD(i, i) = (T)vcl_sqrt(1.0/(typename vnl_numeric_traits<T>::real_t)(D(i, i)));
00243   return V * inv_sqrtD * V.transpose();
00244 }
00245 
00246 //--------------------------------------------------------------------------------
00247 
00248 template class vnl_symmetric_eigensystem<float>;
00249 template class vnl_symmetric_eigensystem<double>;

Generated on Mon Mar 8 05:06:43 2010 for core/vnl by  doxygen 1.5.1