vnl_symmetric_eigensystem.hxx
Go to the documentation of this file.
1 // This is core/vnl/algo/vnl_symmetric_eigensystem.hxx
2 #ifndef vnl_symmetric_eigensystem_hxx_
3 #define vnl_symmetric_eigensystem_hxx_
4 //:
5 // \file
6 // \author Andrew W. Fitzgibbon, Oxford RRG
7 // \date Created: 29 Aug 96
8 // \endverbatim
9 //
10 //-----------------------------------------------------------------------------
11 
12 #include <algorithm>
13 #include <cmath>
14 #include <iostream>
16 #include <cassert>
17 #ifdef _MSC_VER
18 # include <vcl_msvc_warnings.h>
19 #endif
20 #include <vnl/vnl_copy.h>
21 #include <vnl/vnl_math.h>
22 #include <vnl/algo/vnl_netlib.h> // rs_()
23 
24 //: Find eigenvalues of a symmetric 3x3 matrix
25 // \verbatim
26 // Matrix is M11 M12 M13
27 // M12 M22 M23
28 // M13 M23 M33
29 // \endverbatim
30 template <class T>
32  T M11, T M12, T M13,
33  T M22, T M23,
34  T M33,
35  T &l1, T &l2, T &l3)
36 {
37  // Characteristic eqtn |M - xI| = 0
38  // x^3 + b x^2 + c x + d = 0
39  const T b = -M11-M22-M33;
40  const T c = M11*M22 +M11*M33 +M22*M33 -M12*M12 -M13*M13 -M23*M23;
41  const T d = M11*M23*M23 +M12*M12*M33 +M13*M13*M22 -2*M12*M13*M23 -M11*M22*M33;
42 
43  // Using a numerically tweaked version of the real cubic solver http://www.1728.com/cubic2.htm
44  const T b_3 = b/3;
45  const T f = b_3*b_3 - c/3 ;
46  const T g = b*c/6 - b_3*b_3*b_3 - d/2;
47 
48  if (f == 0 && g == 0)
49  {
50  l1 = l2 = l3 = - b_3 ;
51  return;
52  }
53 
54  const T f3 = f*f*f;
55  const T g2 = g*g;
56  const T sqrt_f = -std::sqrt(f);
57 
58  // deal explicitly with repeated root and treat
59  // complex conjugate roots as numerically inaccurate repeated roots.
60 
61  // first check we are not too numerically inaccurate
62  assert((g2 - f3) / vnl_math::sqr(vnl_math::cube(b)) < 1e-8);
63 
64  if (g2 >= f3)
65  {
66  if (g < 0)
67  {
68  l1 = 2 * sqrt_f - b_3;
69  l2 = l3 = - sqrt_f - b_3;
70  }
71  else
72  {
73  l1 = l2 = sqrt_f - b_3;
74  l3 = -2 * sqrt_f - b_3;
75  }
76  return;
77  }
78 
79 
80  const T sqrt_f3 = sqrt_f * sqrt_f * sqrt_f;
81  const T k = std::acos(g / sqrt_f3) / 3;
82  const T j = 2 * sqrt_f;
83  l1 = j * std::cos(k) - b_3;
84  l2 = j * std::cos(k + T(vnl_math::twopi / 3.0)) - b_3;
85  l3 = j * std::cos(k - T(vnl_math::twopi / 3.0)) - b_3;
86 
87  if (l2 < l1) std::swap(l2, l1);
88  if (l3 < l2)
89  {
90  std::swap(l2, l3);
91  if (l2 < l1) std::swap(l2, l1);
92  }
93 }
94 
95 template <class T>
97  vnl_matrix<T> & V,
98  vnl_vector<T> & D)
99 {
100  A.assert_finite();
101  const long n = A.rows();
102 
103  // Set the size of the eigenvalue vector D (output) if it does not match the size of A:
104  if (D.size() != A.rows())
105  D.set_size(n);
106 
107  // convert to double
108  vnl_matrix<double> Ad(A.rows(), A.cols()); vnl_copy(A, Ad);
109  vnl_vector<double> Dd(D.size());
110  vnl_vector<double> work1(n);
111  vnl_vector<double> work2(n);
112  vnl_vector<double> Vvec(n*n);
113 
114  long want_eigenvectors = 1;
115  long ierr = 0;
116 
117  // No need to transpose A, 'cos it's symmetric...
118  v3p_netlib_rs_(&n, &n, Ad.data_block(), &Dd[0], &want_eigenvectors, &Vvec[0], &work1[0], &work2[0], &ierr);
119  vnl_copy(Dd, D);
120 
121  if (ierr) {
122  std::cerr << "vnl_symmetric_eigensystem: ierr = " << ierr << '\n';
123  return false;
124  }
125 
126  // Transpose-copy into V, which is first resized if necessary
127  if (V.rows() != A.rows() || V.cols() != A.rows())
128  V.set_size(n,n);
129  double *vptr = &Vvec[0];
130  for (int c = 0; c < n; ++c)
131  for (int r = 0; r < n; ++r)
132  V(r,c) = T(*vptr++);
133 
134  return true;
135 }
136 
137 //----------------------------------------------------------------------
138 
139 // - @{ Solve real symmetric eigensystem $A x = \lambda x$ @}
140 template <class T>
142  : n_(A.rows()), V(n_, n_), D(n_)
143 {
144  vnl_vector<T> Dvec(n_);
145 
147 
148  // Copy Dvec into diagonal of D
149  for (int i = 0; i < n_; ++i)
150  D(i,i) = Dvec[i];
151 }
152 
153 template <class T>
155 {
156  return vnl_vector<T>(V.extract(n_,1,0,i).data_block(), n_);
157 }
158 
159 template <class T>
161 {
162  return D(i, i);
163 }
164 
165 template <class T>
167 {
168  //vnl_vector<T> ret(b.length());
169  //FastOps::AtB(V, b, &ret);
170  vnl_vector<T> ret(b*V); // same as V.transpose()*b
171 
172  vnl_vector<T> tmp(b.size());
173  D.solve(ret, &tmp);
174 
175  return V * tmp;
176 }
177 
178 template <class T>
180 {
181  int const n = D.size();
182  T det(1);
183  for (int i=0; i<n; ++i)
184  det *= D[i];
185  return det;
186 }
187 
188 template <class T>
190 {
191  unsigned n = D.rows();
192  vnl_diag_matrix<T> invD(n);
193  for (unsigned i=0; i<n; ++i)
194  if (D(i, i) == 0) {
195  std::cerr << __FILE__ ": pinverse(): eigenvalue " << i << " is zero.\n";
196  invD(i, i) = 0;
197  }
198  else
199  invD(i, i) = 1 / D(i, i);
200  return V * invD * V.transpose();
201 }
202 
203 template <class T>
205 {
206  unsigned n = D.rows();
207  vnl_diag_matrix<T> sqrtD(n);
208  for (unsigned i=0; i<n; ++i)
209  if (D(i, i) < 0) {
210  std::cerr << __FILE__ ": square_root(): eigenvalue " << i << " is negative (" << D(i, i) << ").\n";
211  sqrtD(i, i) = (T)std::sqrt((typename vnl_numeric_traits<T>::real_t)(-D(i, i)));
212  // gives square root of the absolute value of T.
213  }
214  else
215  sqrtD(i, i) = (T)std::sqrt((typename vnl_numeric_traits<T>::real_t)(D(i, i)));
216  return V * sqrtD * V.transpose();
217 }
218 
219 template <class T>
221 {
222  unsigned n = D.rows();
223  vnl_diag_matrix<T> inv_sqrtD(n);
224  for (unsigned i=0; i<n; ++i)
225  if (D(i, i) <= 0) {
226  std::cerr << __FILE__ ": square_root(): eigenvalue " << i << " is non-positive (" << D(i, i) << ").\n";
227  inv_sqrtD(i, i) = (T)std::sqrt(-1.0/(typename vnl_numeric_traits<T>::real_t)(D(i, i))); // ??
228  }
229  else
230  inv_sqrtD(i, i) = (T)std::sqrt(1.0/(typename vnl_numeric_traits<T>::real_t)(D(i, i)));
231  return V * inv_sqrtD * V.transpose();
232 }
233 
234 //--------------------------------------------------------------------------------
235 
236 #undef VNL_SYMMETRIC_EIGENSYSTEM_INSTANTIATE
237 #define VNL_SYMMETRIC_EIGENSYSTEM_INSTANTIATE(T) \
238 template class VNL_ALGO_EXPORT vnl_symmetric_eigensystem<T >; \
239 template VNL_ALGO_EXPORT void vnl_symmetric_eigensystem_compute_eigenvals(T,T,T,T,T,T,T&,T&,T&); \
240 template VNL_ALGO_EXPORT bool vnl_symmetric_eigensystem_compute(vnl_matrix<T > const&, vnl_matrix<T > &, vnl_vector<T >&)
241 
242 #endif // vnl_symmetric_eigensystem_hxx_
unsigned int cols() const
Return the number of columns.
Definition: vnl_matrix.h:183
void assert_finite() const
abort if matrix contains any INFs or NANs.
Definition: vnl_matrix.h:582
void swap(double &a, double &b)
VNL_EXPORT void vnl_copy(S const *const src, T *const dst, const unsigned n)
Easy conversion between vectors and matrices templated over different types.
Definition: vnl_copy.cxx:15
vnl_matrix< T > V
Public eigenvectors.
bool set_size(size_t n)
Resize to n elements.
Definition: vnl_vector.hxx:250
vnl_diag_matrix< T > D
Public eigenvalues.
size_t size() const
Return the length, number of elements, dimension of this vector.
Definition: vnl_vector.h:126
vnl_matrix< T > square_root() const
return the square root, if positive semi-definite.
Namespace with standard math functions.
vnl_symmetric_eigensystem(vnl_matrix< T > const &M)
Solve real symmetric eigensystem $A x = \lambda x$.
T get_eigenvalue(int i) const
Recover specified eigenvalue after computation.
bool vnl_symmetric_eigensystem_compute(vnl_matrix< T > const &A, vnl_matrix< T > &V, vnl_vector< T > &D)
Find eigenvalues of a symmetric matrix.
An ordinary mathematical matrix.
Definition: vnl_adjugate.h:22
Declare in a central place the list of symbols from netlib.
Easy conversion between vectors and matrices templated over different types.
vnl_vector< T > get_eigenvector(int i) const
Recover specified eigenvector after computation.
vnl_decnum cube(vnl_decnum const &x)
Definition: vnl_decnum.h:408
Mathematical vector class, templated by type of element.
Definition: vnl_fwd.h:16
vnl_matrix< T > pinverse() const
return the pseudoinverse.
Find eigenvalues of a symmetric matrix.
T determinant() const
return product of eigenvalues.
stores a diagonal matrix as a single vector.
unsigned int rows() const
Return the number of rows.
Definition: vnl_matrix.h:179
vnl_matrix< T > inverse_square_root() const
return the inverse of the square root, if positive semi-definite.
vnl_vector< T > solve(vnl_vector< T > const &b)
Solve LS problem M x = b.
bool set_size(unsigned r, unsigned c)
Resize to r rows by c columns. Old data lost.
Definition: vnl_matrix.hxx:390
vnl_bignum sqr(vnl_bignum const &x)
Definition: vnl_bignum.h:434
void vnl_symmetric_eigensystem_compute_eigenvals(T M11, T M12, T M13, T M22, T M23, T M33, T &l1, T &l2, T &l3)
Find eigenvalues of a symmetric 3x3 matrix.