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