bpp-core3  3.0.0
EigenValue.h
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: The Bio++ Development Group
2 //
3 // SPDX-License-Identifier: CECILL-2.1
4 
5 #ifndef BPP_NUMERIC_MATRIX_EIGENVALUE_H
6 #define BPP_NUMERIC_MATRIX_EIGENVALUE_H
7 
8 
9 #define TOST(i) static_cast<size_t>(i)
10 
11 #include <algorithm>
12 // for min(), max() below
13 
14 #include <cmath>
15 // for abs() below
16 #include <climits>
17 
18 #include "Matrix.h"
19 #include "../NumTools.h"
20 
21 namespace bpp
22 {
68 template<class Real>
70 {
71 private:
75  size_t n_;
76 
81 
87  std::vector<Real> d_; /* real part */
88  std::vector<Real> e_; /* img part */
95 
102 
103 
110 
116  std::vector<Real> ort_;
117 
126  void tred2()
127  {
128  for (size_t j = 0; j < n_; j++)
129  {
130  d_[j] = V_(n_ - 1, j);
131  }
132 
133  // Householder reduction to tridiagonal form.
134 
135  for (size_t i = n_ - 1; i > 0; i--)
136  {
137  // Scale to avoid under/overflow.
138 
139  Real scale = 0.0;
140  Real h = 0.0;
141  for (size_t k = 0; k < i; ++k)
142  {
143  scale = scale + NumTools::abs<Real>(d_[k]);
144  }
145  if (scale == 0.0)
146  {
147  e_[i] = d_[i - 1];
148  for (size_t j = 0; j < i; ++j)
149  {
150  d_[j] = V_(i - 1, j);
151  V_(i, j) = 0.0;
152  V_(j, i) = 0.0;
153  }
154  }
155  else
156  {
157  // Generate Householder vector.
158 
159  for (size_t k = 0; k < i; ++k)
160  {
161  d_[k] /= scale;
162  h += d_[k] * d_[k];
163  }
164  Real f = d_[i - 1];
165  Real g = sqrt(h);
166  if (f > 0)
167  {
168  g = -g;
169  }
170  e_[i] = scale * g;
171  h = h - f * g;
172  d_[i - 1] = f - g;
173  for (size_t j = 0; j < i; ++j)
174  {
175  e_[j] = 0.0;
176  }
177 
178  // Apply similarity transformation to remaining columns.
179 
180  for (size_t j = 0; j < i; ++j)
181  {
182  f = d_[j];
183  V_(j, i) = f;
184  g = e_[j] + V_(j, j) * f;
185  for (size_t k = j + 1; k <= i - 1; k++)
186  {
187  g += V_(k, j) * d_[k];
188  e_[k] += V_(k, j) * f;
189  }
190  e_[j] = g;
191  }
192  f = 0.0;
193  for (size_t j = 0; j < i; ++j)
194  {
195  e_[j] /= h;
196  f += e_[j] * d_[j];
197  }
198  Real hh = f / (h + h);
199  for (size_t j = 0; j < i; ++j)
200  {
201  e_[j] -= hh * d_[j];
202  }
203  for (size_t j = 0; j < i; ++j)
204  {
205  f = d_[j];
206  g = e_[j];
207  for (size_t k = j; k <= i - 1; ++k)
208  {
209  V_(k, j) -= (f * e_[k] + g * d_[k]);
210  }
211  d_[j] = V_(i - 1, j);
212  V_(i, j) = 0.0;
213  }
214  }
215  d_[i] = h;
216  }
217 
218  // Accumulate transformations.
219 
220  for (size_t i = 0; i < n_ - 1; i++)
221  {
222  V_(n_ - 1, i) = V_(i, i);
223  V_(i, i) = 1.0;
224  Real h = d_[i + 1];
225  if (h != 0.0)
226  {
227  for (size_t k = 0; k <= i; k++)
228  {
229  d_[k] = V_(k, i + 1) / h;
230  }
231  for (size_t j = 0; j <= i; j++)
232  {
233  Real g = 0.0;
234  for (size_t k = 0; k <= i; k++)
235  {
236  g += V_(k, i + 1) * V_(k, j);
237  }
238  for (size_t k = 0; k <= i; k++)
239  {
240  V_(k, j) -= g * d_[k];
241  }
242  }
243  }
244  for (size_t k = 0; k <= i; k++)
245  {
246  V_(k, i + 1) = 0.0;
247  }
248  }
249  for (size_t j = 0; j < n_; j++)
250  {
251  d_[j] = V_(n_ - 1, j);
252  V_(n_ - 1, j) = 0.0;
253  }
254  V_(n_ - 1, n_ - 1) = 1.0;
255  e_[0] = 0.0;
256  }
257 
266  void tql2 ()
267  {
268  for (size_t i = 1; i < n_; i++)
269  {
270  e_[i - 1] = e_[i];
271  }
272  e_[n_ - 1] = 0.0;
273 
274  Real f = 0.0;
275  Real tst1 = 0.0;
276  Real eps = std::pow(2.0, -52.0);
277  for (size_t l = 0; l < n_; ++l)
278  {
279  // Find small subdiagonal element
280 
281  tst1 = std::max(tst1, NumTools::abs<Real>(d_[l]) + NumTools::abs<Real>(e_[l]));
282  size_t m = l;
283 
284  // Original while-loop from Java code
285  while (m < n_)
286  {
287  if (NumTools::abs<Real>(e_[m]) <= eps * tst1)
288  {
289  break;
290  }
291  m++;
292  }
293 
294  // If m == l, d_[l] is an eigenvalue,
295  // otherwise, iterate.
296 
297  if (m > l)
298  {
299  int iter = 0;
300  do
301  {
302  iter = iter + 1; // (Could check iteration count here.)
303 
304  // Compute implicit shift
305 
306  Real g = d_[l];
307  Real p = (d_[l + 1] - g) / (2.0 * e_[l]);
308  Real r = hypot(p, 1.0);
309  if (p < 0)
310  {
311  r = -r;
312  }
313  d_[l] = e_[l] / (p + r);
314  d_[l + 1] = e_[l] * (p + r);
315  Real dl1 = d_[l + 1];
316  Real h = g - d_[l];
317  for (size_t i = l + 2; i < n_; ++i)
318  {
319  d_[i] -= h;
320  }
321  f = f + h;
322 
323  // Implicit QL transformation.
324 
325  p = d_[m];
326  Real c = 1.0;
327  Real c2 = c;
328  Real c3 = c;
329  Real el1 = e_[l + 1];
330  Real s = 0.0;
331  Real s2 = 0.0;
332  // for (size_t i = m - 1; i >= l; --i)
333  for (size_t ii = m; ii > l; --ii)
334  {
335  size_t i = ii - 1; // to avoid infinite loop!
336  c3 = c2;
337  c2 = c;
338  s2 = s;
339  g = c * e_[i];
340  h = c * p;
341  r = hypot(p, e_[i]);
342  e_[i + 1] = s * r;
343  s = e_[i] / r;
344  c = p / r;
345  p = c * d_[i] - s * g;
346  d_[i + 1] = h + s * (c * g + s * d_[i]);
347 
348  // Accumulate transformation.
349 
350  for (size_t k = 0; k < n_; k++)
351  {
352  h = V_(k, i + 1);
353  V_(k, i + 1) = s * V_(k, i) + c * h;
354  V_(k, i) = c * V_(k, i) - s * h;
355  }
356  }
357  p = -s * s2 * c3 * el1 * e_[l] / dl1;
358  e_[l] = s * p;
359  d_[l] = c * p;
360 
361  // Check for convergence.
362  }
363  while (NumTools::abs<Real>(e_[l]) > eps * tst1);
364  }
365  d_[l] = d_[l] + f;
366  e_[l] = 0.0;
367  }
368 
369  // Sort eigenvalues and corresponding vectors.
370 
371  for (size_t i = 0; n_ > 0 && i < n_ - 1; i++)
372  {
373  size_t k = i;
374  Real p = d_[i];
375  for (size_t j = i + 1; j < n_; j++)
376  {
377  if (d_[j] < p)
378  {
379  k = j;
380  p = d_[j];
381  }
382  }
383  if (k != i)
384  {
385  d_[k] = d_[i];
386  d_[i] = p;
387  for (size_t j = 0; j < n_; j++)
388  {
389  p = V_(j, i);
390  V_(j, i) = V_(j, k);
391  V_(j, k) = p;
392  }
393  }
394  }
395  }
396 
405  void orthes()
406  {
407  if (n_ == 0) return;
408  size_t low = 0;
409  size_t high = n_ - 1;
410 
411  for (size_t m = low + 1; m <= high - 1; ++m)
412  {
413  // Scale column.
414 
415  Real scale = 0.0;
416  for (size_t i = m; i <= high; ++i)
417  {
418  scale = scale + NumTools::abs<Real>(H_(i, m - 1));
419  }
420  if (scale != 0.0)
421  {
422  // Compute Householder transformation.
423 
424  Real h = 0.0;
425  for (size_t i = high; i >= m; --i)
426  {
427  ort_[i] = H_(i, m - 1) / scale;
428  h += ort_[i] * ort_[i];
429  }
430  Real g = sqrt(h);
431  if (ort_[m] > 0)
432  {
433  g = -g;
434  }
435  h = h - ort_[m] * g;
436  ort_[m] = ort_[m] - g;
437 
438  // Apply Householder similarity transformation
439  // H = (I-u*u'/h)*H*(I-u*u')/h)
440 
441  for (size_t j = m; j < n_; ++j)
442  {
443  Real f = 0.0;
444  for (size_t i = high; i >= m; --i)
445  {
446  f += ort_[i] * H_(i, j);
447  }
448  f = f / h;
449  for (size_t i = m; i <= high; ++i)
450  {
451  H_(i, j) -= f * ort_[i];
452  }
453  }
454 
455  for (size_t i = 0; i <= high; ++i)
456  {
457  Real f = 0.0;
458  for (size_t j = high; j >= m; --j)
459  {
460  f += ort_[j] * H_(i, j);
461  }
462  f = f / h;
463  for (size_t j = m; j <= high; ++j)
464  {
465  H_(i, j) -= f * ort_[j];
466  }
467  }
468  ort_[m] = scale * ort_[m];
469  H_(m, m - 1) = scale * g;
470  }
471  }
472 
473  // Accumulate transformations (Algol's ortran).
474 
475  for (size_t i = 0; i < n_; i++)
476  {
477  for (size_t j = 0; j < n_; j++)
478  {
479  V_(i, j) = (i == j ? 1.0 : 0.0);
480  }
481  }
482 
483  for (size_t m = high - 1; m >= low + 1; --m)
484  {
485  if (H_(m, m - 1) != 0.0)
486  {
487  for (size_t i = m + 1; i <= high; ++i)
488  {
489  ort_[i] = H_(i, m - 1);
490  }
491  for (size_t j = m; j <= high; ++j)
492  {
493  Real g = 0.0;
494  for (size_t i = m; i <= high; i++)
495  {
496  g += ort_[i] * V_(i, j);
497  }
498  // Double division avoids possible underflow
499  g = (g / ort_[m]) / H_(m, m - 1);
500  for (size_t i = m; i <= high; ++i)
501  {
502  V_(i, j) += g * ort_[i];
503  }
504  }
505  }
506  }
507  }
508 
509 
510  // Complex scalar division.
511 
512  Real cdivr, cdivi;
513  void cdiv(Real xr, Real xi, Real yr, Real yi)
514  {
515  Real r, d;
516  if (NumTools::abs<Real>(yr) > NumTools::abs<Real>(yi))
517  {
518  r = yi / yr;
519  d = yr + r * yi;
520  cdivr = (xr + r * xi) / d;
521  cdivi = (xi - r * xr) / d;
522  }
523  else
524  {
525  r = yr / yi;
526  d = yi + r * yr;
527  cdivr = (r * xr + xi) / d;
528  cdivi = (r * xi - xr) / d;
529  }
530  }
531 
532 
533  // Nonsymmetric reduction from Hessenberg to real Schur form.
534 
535  void hqr2 ()
536  {
537  // This is derived from the Algol procedure hqr2,
538  // by Martin and Wilkinson, Handbook for Auto. Comp.,
539  // Vol.ii-Linear Algebra, and the corresponding
540  // Fortran subroutine in EISPACK.
541 
542  // Initialize
543 
544  int nn = static_cast<int>(this->n_);
545  int n = nn - 1;
546  int low = 0;
547  int high = nn - 1;
548  Real eps = std::pow(2.0, -52.0);
549  Real exshift = 0.0;
550  Real p = 0, q = 0, r = 0, s = 0, z = 0, t, w, x, y;
551 
552  // Store roots isolated by balanc and compute matrix norm
553 
554  Real norm = 0.0;
555  for (int i = 0; i < nn; i++)
556  {
557  if ((i < low) || (i > high))
558  {
559  d_[TOST(i)] = H_(TOST(i), TOST(i));
560  e_[TOST(i)] = 0.0;
561  }
562  for (int j = std::max(i - 1, 0); j < nn; j++)
563  {
564  norm = norm + NumTools::abs<Real>(H_(TOST(i), TOST(j)));
565  }
566  }
567 
568  // Outer loop over eigenvalue index
569 
570  int iter = 0;
571  while (n >= low)
572  {
573  // Look for single small sub-diagonal element
574 
575  int l = n;
576  while (l > low)
577  {
578  s = NumTools::abs<Real>(H_(TOST(l - 1), TOST(l - 1))) + NumTools::abs<Real>(H_(TOST(l), TOST(l)));
579  if (s == 0.0)
580  {
581  s = norm;
582  }
583  if (NumTools::abs<Real>(H_(TOST(l), TOST(l - 1))) < eps * s)
584  {
585  break;
586  }
587  l--;
588  }
589 
590  // Check for convergence
591  // One root found
592 
593  if (l == n)
594  {
595  H_(TOST(n), TOST(n)) = H_(TOST(n), TOST(n)) + exshift;
596  d_[TOST(n)] = H_(TOST(n), TOST(n));
597  e_[TOST(n)] = 0.0;
598  n--;
599  iter = 0;
600 
601  // Two roots found
602  }
603  else if (l == n - 1)
604  {
605  w = H_(TOST(n), TOST(n - 1)) * H_(TOST(n - 1), TOST(n));
606  p = (H_(TOST(n - 1), TOST(n - 1)) - H_(TOST(n), TOST(n))) / 2.0;
607  q = p * p + w;
608  z = sqrt(NumTools::abs<Real>(q));
609  H_(TOST(n), TOST(n)) = H_(TOST(n), TOST(n)) + exshift;
610  H_(TOST(n - 1), TOST(n - 1)) = H_(TOST(n - 1), TOST(n - 1)) + exshift;
611  x = H_(TOST(n), TOST(n));
612 
613  // Real pair
614 
615  if (q >= 0)
616  {
617  if (p >= 0)
618  {
619  z = p + z;
620  }
621  else
622  {
623  z = p - z;
624  }
625  d_[TOST(n - 1)] = x + z;
626  d_[TOST(n)] = d_[TOST(n - 1)];
627  if (z != 0.0)
628  {
629  d_[TOST(n)] = x - w / z;
630  }
631  e_[TOST(n - 1)] = 0.0;
632  e_[TOST(n)] = 0.0;
633  x = H_(TOST(n), TOST(n - 1));
634  s = NumTools::abs<Real>(x) + NumTools::abs<Real>(z);
635  p = x / s;
636  q = z / s;
637  r = sqrt(p * p + q * q);
638  p = p / r;
639  q = q / r;
640 
641  // Row modification
642 
643  for (int j = n - 1; j < nn; j++)
644  {
645  z = H_(TOST(n - 1), TOST(j));
646  H_(TOST(n - 1), TOST(j)) = q * z + p * H_(TOST(n), TOST(j));
647  H_(TOST(n), TOST(j)) = q * H_(TOST(n), TOST(j)) - p * z;
648  }
649 
650  // Column modification
651 
652  for (int i = 0; i <= n; i++)
653  {
654  z = H_(TOST(i), TOST(n - 1));
655  H_(TOST(i), TOST(n - 1)) = q * z + p * H_(TOST(i), TOST(n));
656  H_(TOST(i), TOST(n)) = q * H_(TOST(i), TOST(n)) - p * z;
657  }
658 
659  // Accumulate transformations
660 
661  for (int i = low; i <= high; i++)
662  {
663  z = V_(TOST(i), TOST(n - 1));
664  V_(TOST(i), TOST(n - 1)) = q * z + p * V_(TOST(i), TOST(n));
665  V_(TOST(i), TOST(n)) = q * V_(TOST(i), TOST(n)) - p * z;
666  }
667 
668  // Complex pair
669  }
670  else
671  {
672  d_[TOST(n - 1)] = x + p;
673  d_[TOST(n)] = x + p;
674  e_[TOST(n - 1)] = z;
675  e_[TOST(n)] = -z;
676  }
677  n = n - 2;
678  iter = 0;
679 
680  // No convergence yet
681  }
682  else
683  {
684  // Form shift
685 
686  x = H_(TOST(n), TOST(n));
687  y = 0.0;
688  w = 0.0;
689  if (l < n)
690  {
691  y = H_(TOST(n - 1), TOST(n - 1));
692  w = H_(TOST(n), TOST(n - 1)) * H_(TOST(n - 1), TOST(n));
693  }
694 
695  // Wilkinson's original ad hoc shift
696 
697  if (iter == 10)
698  {
699  exshift += x;
700  for (int i = low; i <= n; i++)
701  {
702  H_(TOST(i), TOST(i)) -= x;
703  }
704  s = NumTools::abs<Real>(H_(TOST(n), TOST(n - 1))) + NumTools::abs<Real>(H_(TOST(n - 1), TOST(n - 2)));
705  x = y = 0.75 * s;
706  w = -0.4375 * s * s;
707  }
708 
709  // MATLAB's new ad hoc shift
710  if (iter == 30)
711  {
712  s = (y - x) / 2.0;
713  s = s * s + w;
714  if (s > 0)
715  {
716  s = sqrt(s);
717  if (y < x)
718  {
719  s = -s;
720  }
721  s = x - w / ((y - x) / 2.0 + s);
722  for (int i = low; i <= n; i++)
723  {
724  H_(TOST(i), TOST(i)) -= s;
725  }
726  exshift += s;
727  x = y = w = 0.964;
728  }
729  }
730 
731  iter = iter + 1; // (Could check iteration count here.)
732 
733  // Look for two consecutive small sub-diagonal elements
734 
735  int m = n - 2;
736  while (m >= l)
737  {
738  z = H_(TOST(m), TOST(m));
739  r = x - z;
740  s = y - z;
741  p = (r * s - w) / H_(TOST(m + 1), TOST(m)) + H_(TOST(m), TOST(m + 1));
742  q = H_(TOST(m + 1), TOST(m + 1)) - z - r - s;
743  r = H_(TOST(m + 2), TOST(m + 1));
744  s = NumTools::abs<Real>(p) + NumTools::abs<Real>(q) + NumTools::abs<Real>(r);
745  p = p / s;
746  q = q / s;
747  r = r / s;
748  if (m == l)
749  {
750  break;
751  }
752  if (NumTools::abs<Real>(H_(TOST(m), TOST(m - 1))) * (NumTools::abs<Real>(q) + NumTools::abs<Real>(r)) <
753  eps * (NumTools::abs<Real>(p) * (NumTools::abs<Real>(H_(TOST(m - 1), TOST(m - 1))) + NumTools::abs<Real>(z) +
754  NumTools::abs<Real>(H_(TOST(m + 1), TOST(m + 1))))))
755  {
756  break;
757  }
758  m--;
759  }
760 
761  for (int i = m + 2; i <= n; i++)
762  {
763  H_(TOST(i), TOST(i - 2)) = 0.0;
764  if (i > m + 2)
765  {
766  H_(TOST(i), TOST(i - 3)) = 0.0;
767  }
768  }
769 
770  // Double QR step involving rows l:n and columns m:n
771 
772  for (int k = m; k <= n - 1; k++)
773  {
774  int notlast = (k != n - 1);
775  if (k != m)
776  {
777  p = H_(TOST(k), TOST(k - 1));
778  q = H_(TOST(k + 1), TOST(k - 1));
779  r = (notlast ? H_(TOST(k + 2), TOST(k - 1)) : 0.0);
780  x = NumTools::abs<Real>(p) + NumTools::abs<Real>(q) + NumTools::abs<Real>(r);
781  if (x != 0.0)
782  {
783  p = p / x;
784  q = q / x;
785  r = r / x;
786  }
787  }
788  if (x == 0.0)
789  {
790  break;
791  }
792  s = sqrt(p * p + q * q + r * r);
793  if (p < 0)
794  {
795  s = -s;
796  }
797  if (s != 0)
798  {
799  if (k != m)
800  {
801  H_(TOST(k), TOST(k - 1)) = -s * x;
802  }
803  else if (l != m)
804  {
805  H_(TOST(k), TOST(k - 1)) = -H_(TOST(k), TOST(k - 1));
806  }
807  p = p + s;
808  x = p / s;
809  y = q / s;
810  z = r / s;
811  q = q / p;
812  r = r / p;
813 
814  // Row modification
815 
816  for (int j = k; j < nn; j++)
817  {
818  p = H_(TOST(k), TOST(j)) + q * H_(TOST(k + 1), TOST(j));
819  if (notlast)
820  {
821  p = p + r * H_(TOST(k + 2), TOST(j));
822  H_(TOST(k + 2), TOST(j)) = H_(TOST(k + 2), TOST(j)) - p * z;
823  }
824  H_(TOST(k), TOST(j)) = H_(TOST(k), TOST(j)) - p * x;
825  H_(TOST(k + 1), TOST(j)) = H_(TOST(k + 1), TOST(j)) - p * y;
826  }
827 
828  // Column modification
829 
830  for (int i = 0; i <= std::min(n, k + 3); i++)
831  {
832  p = x * H_(TOST(i), TOST(k)) + y * H_(TOST(i), TOST(k + 1));
833  if (notlast)
834  {
835  p = p + z * H_(TOST(i), TOST(k + 2));
836  H_(TOST(i), TOST(k + 2)) = H_(TOST(i), TOST(k + 2)) - p * r;
837  }
838  H_(TOST(i), TOST(k)) = H_(TOST(i), TOST(k)) - p;
839  H_(TOST(i), TOST(k + 1)) = H_(TOST(i), TOST(k + 1)) - p * q;
840  }
841 
842  // Accumulate transformations
843 
844  for (int i = low; i <= high; i++)
845  {
846  p = x * V_(TOST(i), TOST(k)) + y * V_(TOST(i), TOST(k + 1));
847  if (notlast)
848  {
849  p = p + z * V_(TOST(i), TOST(k + 2));
850  V_(TOST(i), TOST(k + 2)) = V_(TOST(i), TOST(k + 2)) - p * r;
851  }
852  V_(TOST(i), TOST(k)) = V_(TOST(i), TOST(k)) - p;
853  V_(TOST(i), TOST(k + 1)) = V_(TOST(i), TOST(k + 1)) - p * q;
854  }
855  } // (s != 0)
856  } // k loop
857  } // check convergence
858  } // while (n >= low)
859 
860  // Backsubstitute to find vectors of upper triangular form
861 
862  if (norm == 0.0)
863  {
864  return;
865  }
866 
867  for (n = nn - 1; n >= 0; n--)
868  {
869  p = d_[TOST(n)];
870  q = e_[TOST(n)];
871 
872  // Real vector
873 
874  if (q == 0)
875  {
876  int l = n;
877  H_(TOST(n), TOST(n)) = 1.0;
878  for (int i = n - 1; i >= 0; i--)
879  {
880  w = H_(TOST(i), TOST(i)) - p;
881  r = 0.0;
882  for (int j = l; j <= n; j++)
883  {
884  r = r + H_(TOST(i), TOST(j)) * H_(TOST(j), TOST(n));
885  }
886  if (e_[TOST(i)] < 0.0)
887  {
888  z = w;
889  s = r;
890  }
891  else
892  {
893  l = i;
894  if (e_[TOST(i)] == 0.0)
895  {
896  if (w != 0.0)
897  {
898  H_(TOST(i), TOST(n)) = -r / w;
899  }
900  else
901  {
902  H_(TOST(i), TOST(n)) = -r / (eps * norm);
903  }
904 
905  // Solve real equations
906  }
907  else
908  {
909  x = H_(TOST(i), TOST(i + 1));
910  y = H_(TOST(i + 1), TOST(i));
911  q = (d_[TOST(i)] - p) * (d_[TOST(i)] - p) + e_[TOST(i)] * e_[TOST(i)];
912  t = (x * s - z * r) / q;
913  H_(TOST(i), TOST(n)) = t;
914  if (NumTools::abs<Real>(x) > NumTools::abs<Real>(z))
915  {
916  H_(TOST(i + 1), TOST(n)) = (-r - w * t) / x;
917  }
918  else
919  {
920  H_(TOST(i + 1), TOST(n)) = (-s - y * t) / z;
921  }
922  }
923 
924  // Overflow control
925 
926  t = NumTools::abs<Real>(H_(TOST(i), TOST(n)));
927  if ((eps * t) * t > 1)
928  {
929  for (int j = i; j <= n; j++)
930  {
931  H_(TOST(j), TOST(n)) = H_(TOST(j), TOST(n)) / t;
932  }
933  }
934  }
935  }
936 
937  // Complex vector
938  }
939  else if (q < 0)
940  {
941  int l = n - 1;
942 
943  // Last vector component imaginary so matrix is triangular
944 
945  if (NumTools::abs<Real>(H_(TOST(n), TOST(n - 1))) > NumTools::abs<Real>(H_(TOST(n - 1), TOST(n))))
946  {
947  H_(TOST(n - 1), TOST(n - 1)) = q / H_(TOST(n), TOST(n - 1));
948  H_(TOST(n - 1), TOST(n)) = -(H_(TOST(n), TOST(n)) - p) / H_(TOST(n), TOST(n - 1));
949  }
950  else
951  {
952  cdiv(0.0, -H_(TOST(n - 1), TOST(n)), H_(TOST(n - 1), TOST(n - 1)) - p, q);
953  H_(TOST(n - 1), TOST(n - 1)) = cdivr;
954  H_(TOST(n - 1), TOST(n)) = cdivi;
955  }
956  H_(TOST(n), TOST(n - 1)) = 0.0;
957  H_(TOST(n), TOST(n)) = 1.0;
958  for (int i = n - 2; i >= 0; i--)
959  {
960  Real ra, sa, vr, vi;
961  ra = 0.0;
962  sa = 0.0;
963  for (int j = l; j <= n; j++)
964  {
965  ra = ra + H_(TOST(i), TOST(j)) * H_(TOST(j), TOST(n - 1));
966  sa = sa + H_(TOST(i), TOST(j)) * H_(TOST(j), TOST(n));
967  }
968  w = H_(TOST(i), TOST(i)) - p;
969 
970  if (e_[TOST(i)] < 0.0)
971  {
972  z = w;
973  r = ra;
974  s = sa;
975  }
976  else
977  {
978  l = i;
979  if (e_[TOST(i)] == 0)
980  {
981  cdiv(-ra, -sa, w, q);
982  H_(TOST(i), TOST(n - 1)) = cdivr;
983  H_(TOST(i), TOST(n)) = cdivi;
984  }
985  else
986  {
987  // Solve complex equations
988 
989  x = H_(TOST(i), TOST(i + 1));
990  y = H_(TOST(i + 1), TOST(i));
991  vr = (d_[TOST(i)] - p) * (d_[TOST(i)] - p) + e_[TOST(i)] * e_[TOST(i)] - q * q;
992  vi = (d_[TOST(i)] - p) * 2.0 * q;
993  if ((vr == 0.0) && (vi == 0.0))
994  {
995  vr = eps * norm * (NumTools::abs<Real>(w) + NumTools::abs<Real>(q) +
996  NumTools::abs<Real>(x) + NumTools::abs<Real>(y) + NumTools::abs<Real>(z));
997  }
998  cdiv(x * r - z * ra + q * sa, x * s - z * sa - q * ra, vr, vi);
999  H_(TOST(i), TOST(n - 1)) = cdivr;
1000  H_(TOST(i), TOST(n)) = cdivi;
1001  if (NumTools::abs<Real>(x) > (NumTools::abs<Real>(z) + NumTools::abs<Real>(q)))
1002  {
1003  H_(TOST(i + 1), TOST(n - 1)) = (-ra - w * H_(TOST(i), TOST(n - 1)) + q * H_(TOST(i), TOST(n))) / x;
1004  H_(TOST(i + 1), TOST(n)) = (-sa - w * H_(TOST(i), TOST(n)) - q * H_(TOST(i), TOST(n - 1))) / x;
1005  }
1006  else
1007  {
1008  cdiv(-r - y * H_(TOST(i), TOST(n - 1)), -s - y * H_(TOST(i), TOST(n)), z, q);
1009  H_(TOST(i + 1), TOST(n - 1)) = cdivr;
1010  H_(TOST(i + 1), TOST(n)) = cdivi;
1011  }
1012  }
1013 
1014  // Overflow control
1015  t = std::max(NumTools::abs<Real>(H_(TOST(i), TOST(n - 1))), NumTools::abs<Real>(H_(TOST(i), TOST(n))));
1016  if ((eps * t) * t > 1)
1017  {
1018  for (int j = i; j <= n; j++)
1019  {
1020  H_(TOST(j), TOST(n - 1)) = H_(TOST(j), TOST(n - 1)) / t;
1021  H_(TOST(j), TOST(n)) = H_(TOST(j), TOST(n)) / t;
1022  }
1023  }
1024  }
1025  }
1026  }
1027  }
1028 
1029  // Vectors of isolated roots
1030 
1031  for (int i = 0; i < nn; i++)
1032  {
1033  if (i < low || i > high)
1034  {
1035  for (int j = i; j < nn; j++)
1036  {
1037  V_(TOST(i), TOST(j)) = H_(TOST(i), TOST(j));
1038  }
1039  }
1040  }
1041 
1042  // Back transformation to get eigenvectors of original matrix
1043 
1044  for (int j = nn - 1; j >= low; j--)
1045  {
1046  for (int i = low; i <= high; i++)
1047  {
1048  z = 0.0;
1049  for (int k = low; k <= std::min(j, high); k++)
1050  {
1051  z = z + V_(TOST(i), TOST(k)) * H_(TOST(k), TOST(j));
1052  }
1053  V_(TOST(i), TOST(j)) = z;
1054  }
1055  }
1056  }
1057 
1058 public:
1059  bool isSymmetric() const { return issymmetric_; }
1060 
1061 
1068  n_(A.getNumberOfColumns()),
1069  issymmetric_(true),
1070  d_(n_),
1071  e_(n_),
1072  V_(n_, n_),
1073  H_(),
1074  D_(n_, n_),
1075  ort_(),
1076  cdivr(), cdivi()
1077  {
1078  if (n_ > INT_MAX)
1079  throw Exception("EigenValue: can only be computed for matrices <= " + TextTools::toString(INT_MAX));
1080  for (size_t j = 0; (j < n_) && issymmetric_; j++)
1081  {
1082  for (size_t i = 0; (i < n_) && issymmetric_; i++)
1083  {
1084  issymmetric_ = (A(i, j) == A(j, i));
1085  }
1086  }
1087 
1088  if (issymmetric_)
1089  {
1090  for (size_t i = 0; i < n_; i++)
1091  {
1092  for (size_t j = 0; j < n_; j++)
1093  {
1094  V_(i, j) = A(i, j);
1095  }
1096  }
1097 
1098  // Tridiagonalize.
1099  tred2();
1100 
1101  // Diagonalize.
1102  tql2();
1103  }
1104  else
1105  {
1106  H_.resize(n_, n_);
1107  ort_.resize(n_);
1108 
1109  for (size_t j = 0; j < n_; j++)
1110  {
1111  for (size_t i = 0; i < n_; i++)
1112  {
1113  H_(i, j) = A(i, j);
1114  }
1115  }
1116 
1117  // Reduce to Hessenberg form.
1118  orthes();
1119 
1120  // Reduce Hessenberg to real Schur form.
1121  hqr2();
1122  }
1123  }
1124 
1125 
1131  const RowMatrix<Real>& getV() const { return V_; }
1132 
1138  const std::vector<Real>& getRealEigenValues() const { return d_; }
1139 
1145  const std::vector<Real>& getImagEigenValues() const { return e_; }
1146 
1180  const RowMatrix<Real>& getD() const
1181  {
1182  for (size_t i = 0; i < n_; i++)
1183  {
1184  for (size_t j = 0; j < n_; j++)
1185  {
1186  D_(i, j) = 0.0;
1187  }
1188  D_(i, i) = d_[i];
1189  if (e_[i] > 0)
1190  {
1191  D_(i, i + 1) = e_[i];
1192  }
1193  else if (e_[i] < 0)
1194  {
1195  D_(i, i - 1) = e_[i];
1196  }
1197  }
1198  return D_;
1199  }
1200 };
1201 } // end of namespace bpp.
1202 #endif // BPP_NUMERIC_MATRIX_EIGENVALUE_H
The matrix template interface.
Definition: Matrix.h:22
bool isSymmetric() const
Definition: EigenValue.h:1059
std::vector< Real > ort_
Working storage for nonsymmetric algorithm.
Definition: EigenValue.h:116
RowMatrix< Real > V_
Array for internal storage of eigenvectors.
Definition: EigenValue.h:94
#define TOST(i)
Definition: EigenValue.h:9
std::vector< Real > d_
Definition: EigenValue.h:87
const RowMatrix< Real > & getD() const
Computes the block diagonal eigenvalue matrix.
Definition: EigenValue.h:1180
const RowMatrix< Real > & getV() const
Return the eigenvector matrix.
Definition: EigenValue.h:1131
void orthes()
Nonsymmetric reduction to Hessenberg form.
Definition: EigenValue.h:405
void resize(size_t nRows, size_t nCols)
Resize the matrix.
Definition: Matrix.h:176
Computes eigenvalues and eigenvectors of a real (non-complex) matrix.
Definition: EigenValue.h:69
const std::vector< Real > & getImagEigenValues() const
Return the imaginary parts of the eigenvalues in parameter e.
Definition: EigenValue.h:1145
Exception base class. Overload exception constructor (to control the exceptions mechanism). Destructor is already virtual (from std::exception)
Definition: Exceptions.h:20
void tql2()
Symmetric tridiagonal QL algorithm.
Definition: EigenValue.h:266
const std::vector< Real > & getRealEigenValues() const
Return the real parts of the eigenvalues.
Definition: EigenValue.h:1138
EigenValue(const Matrix< Real > &A)
Check for symmetry, then construct the eigenvalue decomposition.
Definition: EigenValue.h:1067
bool issymmetric_
Tell if the matrix is symmetric.
Definition: EigenValue.h:80
RowMatrix< Real > D_
Matrix for internal storage of eigen values in a matrix form.
Definition: EigenValue.h:109
std::string toString(T t)
General template method to convert to a string.
Definition: TextTools.h:115
std::vector< Real > e_
Definition: EigenValue.h:88
void tred2()
Symmetric Householder reduction to tridiagonal form.
Definition: EigenValue.h:126
size_t n_
Row and column dimension (square matrix).
Definition: EigenValue.h:75
void cdiv(Real xr, Real xi, Real yr, Real yi)
Definition: EigenValue.h:513
RowMatrix< Real > H_
Matrix for internal storage of nonsymmetric Hessenberg form.
Definition: EigenValue.h:101