MorphoGraphX  2.0-1-227
Eigenvalues.hpp
Go to the documentation of this file.
1 //
2 // This file is part of MorphoGraphX - http://www.MorphoGraphX.org
3 // Copyright (C) 2012-2016 Richard S. Smith and collaborators.
4 //
5 // If you use MorphoGraphX in your work, please cite:
6 // http://dx.doi.org/10.7554/eLife.05864
7 //
8 // MorphoGraphX is free software, and is licensed under under the terms of the
9 // GNU General (GPL) Public License version 2.0, http://www.gnu.org/licenses.
10 //
11 
12 //
13 // Eigen-decomposition for symmetric 3x3 real matrices.
14 // Public domain, copied from the public domain Java library JAMA.
15 //
16 // Returns sorted list of eigenvectors and eigenvalues of a matrix
17 //
18 // Usage:
19 // void EigenDecompSym3x3(double matrix[3][3], double eigenvecs[3][3], double eigenvals[3]);
20 //
21 #ifndef EIGENVALUES_H
22 #define EIGENVALUES_H
23 
24 #include <cmath>
25 
26 #include <cuda/CudaGlobal.hpp>
27 
28 namespace
29 {
30  #ifdef MAX
31  # undef MAX
32  #endif
33 
34  #define MAX(a, b) ((a) > (b) ? (a) : (b))
35 
36  #define n 3
37 
39  static double hypot2(double x, double y) {
40  return std::sqrt(x * x + y * y);
41  }
42 
43  // Symmetric Householder reduction to tridiagonal form.
44 
46  static void tred2(double V[n][n], double d[n], double e[n])
47  {
48 
49  // This is derived from the Algol procedures tred2 by
50  // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
51  // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
52  // Fortran subroutine in EISPACK.
53 
54  for(int j = 0; j < n; j++) {
55  d[j] = V[n - 1][j];
56  }
57 
58  // Householder reduction to tridiagonal form.
59 
60  for(int i = n - 1; i > 0; i--) {
61 
62  // Scale to avoid under/overflow.
63 
64  double scale = 0.0;
65  double h = 0.0;
66  for(int k = 0; k < i; k++) {
67  scale = scale + std::fabs(d[k]);
68  }
69  if(scale == 0.0) {
70  e[i] = d[i - 1];
71  for(int j = 0; j < i; j++) {
72  d[j] = V[i - 1][j];
73  V[i][j] = 0.0;
74  V[j][i] = 0.0;
75  }
76  } else {
77 
78  // Generate Householder vector.
79 
80  for(int k = 0; k < i; k++) {
81  d[k] /= scale;
82  h += d[k] * d[k];
83  }
84  double f = d[i - 1];
85  double g = std::sqrt(h);
86  if(f > 0) {
87  g = -g;
88  }
89  e[i] = scale * g;
90  h = h - f * g;
91  d[i - 1] = f - g;
92  for(int j = 0; j < i; j++) {
93  e[j] = 0.0;
94  }
95 
96  // Apply similarity transformation to remaining columns.
97 
98  for(int j = 0; j < i; j++) {
99  f = d[j];
100  V[j][i] = f;
101  g = e[j] + V[j][j] * f;
102  for(int k = j + 1; k <= i - 1; k++) {
103  g += V[k][j] * d[k];
104  e[k] += V[k][j] * f;
105  }
106  e[j] = g;
107  }
108  f = 0.0;
109  for(int j = 0; j < i; j++) {
110  e[j] /= h;
111  f += e[j] * d[j];
112  }
113  double hh = f / (h + h);
114  for(int j = 0; j < i; j++) {
115  e[j] -= hh * d[j];
116  }
117  for(int j = 0; j < i; j++) {
118  f = d[j];
119  g = e[j];
120  for(int k = j; k <= i - 1; k++) {
121  V[k][j] -= (f * e[k] + g * d[k]);
122  }
123  d[j] = V[i - 1][j];
124  V[i][j] = 0.0;
125  }
126  }
127  d[i] = h;
128  }
129 
130  // Accumulate transformations.
131 
132  for(int i = 0; i < n - 1; i++) {
133  V[n - 1][i] = V[i][i];
134  V[i][i] = 1.0;
135  double h = d[i + 1];
136  if(h != 0.0) {
137  for(int k = 0; k <= i; k++) {
138  d[k] = V[k][i + 1] / h;
139  }
140  for(int j = 0; j <= i; j++) {
141  double g = 0.0;
142  for(int k = 0; k <= i; k++) {
143  g += V[k][i + 1] * V[k][j];
144  }
145  for(int k = 0; k <= i; k++) {
146  V[k][j] -= g * d[k];
147  }
148  }
149  }
150  for(int k = 0; k <= i; k++) {
151  V[k][i + 1] = 0.0;
152  }
153  }
154  for(int j = 0; j < n; j++) {
155  d[j] = V[n - 1][j];
156  V[n - 1][j] = 0.0;
157  }
158  V[n - 1][n - 1] = 1.0;
159  e[0] = 0.0;
160  }
161 
162  // Symmetric tridiagonal QL algorithm.
164  static void tql2(double V[n][n], double d[n], double e[n])
165  {
166 
167  // This is derived from the Algol procedures tql2, by
168  // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
169  // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
170  // Fortran subroutine in EISPACK.
171 
172  for(int i = 1; i < n; i++) {
173  e[i - 1] = e[i];
174  }
175  e[n - 1] = 0.0;
176 
177  double f = 0.0;
178  double tst1 = 0.0;
179  double eps = std::pow(2.0, -52.0);
180  for(int l = 0; l < n; l++) {
181 
182  // Find small subdiagonal element
183 
184  tst1 = MAX(tst1, std::fabs(d[l]) + std::fabs(e[l]));
185  int m = l;
186  while(m < n) {
187  if(std::fabs(e[m]) <= eps * tst1) {
188  break;
189  }
190  m++;
191  }
192 
193  // If m == l, d[l] is an eigenvalue,
194  // otherwise, iterate.
195 
196  if(m > l) {
197  int iter = 0;
198  do {
199  iter = iter + 1; // (Could check iteration count here.)
200 
201  // Compute implicit shift
202 
203  double g = d[l];
204  double p = (d[l + 1] - g) / (2.0 * e[l]);
205  double r = hypot2(p, 1.0);
206  if(p < 0) {
207  r = -r;
208  }
209  d[l] = e[l] / (p + r);
210  d[l + 1] = e[l] * (p + r);
211  double dl1 = d[l + 1];
212  double h = g - d[l];
213  for(int i = l + 2; i < n; i++) {
214  d[i] -= h;
215  }
216  f = f + h;
217 
218  // Implicit QL transformation.
219 
220  p = d[m];
221  double c = 1.0;
222  double c2 = c;
223  double c3 = c;
224  double el1 = e[l + 1];
225  double s = 0.0;
226  double s2 = 0.0;
227  for(int i = m - 1; i >= l; i--) {
228  c3 = c2;
229  c2 = c;
230  s2 = s;
231  g = c * e[i];
232  h = c * p;
233  r = hypot2(p, e[i]);
234  e[i + 1] = s * r;
235  s = e[i] / r;
236  c = p / r;
237  p = c * d[i] - s * g;
238  d[i + 1] = h + s * (c * g + s * d[i]);
239 
240  // Accumulate transformation.
241 
242  for(int k = 0; k < n; k++) {
243  h = V[k][i + 1];
244  V[k][i + 1] = s * V[k][i] + c * h;
245  V[k][i] = c * V[k][i] - s * h;
246  }
247  }
248  p = -s * s2 * c3 * el1 * e[l] / dl1;
249  e[l] = s * p;
250  d[l] = c * p;
251 
252  // Check for convergence.
253 
254  } while(std::fabs(e[l]) > eps * tst1);
255  }
256  d[l] = d[l] + f;
257  e[l] = 0.0;
258  }
259 
260  // Sort eigenvalues and corresponding vectors.
261 
262  for(int i = 0; i < n - 1; i++) {
263  int k = i;
264  double p = d[i];
265  for(int j = i + 1; j < n; j++) {
266  if(d[j] < p) {
267  k = j;
268  p = d[j];
269  }
270  }
271  if(k != i) {
272  d[k] = d[i];
273  d[i] = p;
274  for(int j = 0; j < n; j++) {
275  p = V[j][i];
276  V[j][i] = V[j][k];
277  V[j][k] = p;
278  }
279  }
280  }
281  }
282  #undef n
283 }
284 
286 void EigenDecompSym3x3(double A[3][3], double V[3][3], double d[3])
287 {
288  double e[3];
289  for(int i = 0; i < 3; i++) {
290  for(int j = 0; j < 3; j++) {
291  V[i][j] = A[i][j];
292  }
293  }
294  tred2(V, d, e);
295  tql2(V, d, e);
296 }
297 
298 #endif
n
#define n
Definition: Eigenvalues.hpp:36
mgx::c2
CU_HOST_DEVICE const const T T c2
Definition: Geometry.hpp:234
MAX
#define MAX(a, b)
Definition: Eigenvalues.hpp:34
CudaGlobal.hpp
CU_HOST_DEVICE
#define CU_HOST_DEVICE
Definition: CudaGlobal.hpp:22
mgx::c3
CU_HOST_DEVICE const const T T T c3
Definition: Geometry.hpp:235
EigenDecompSym3x3
CU_HOST_DEVICE void EigenDecompSym3x3(double A[3][3], double V[3][3], double d[3])
Definition: Eigenvalues.hpp:286