📄 jama_eig.h
字号:
#ifndef JAMA_EIG_H#define JAMA_EIG_H#include "tnt_array1d.h"#include "tnt_array2d.h"#include "tnt_math_utils.h"#include <algorithm>// for min(), max() below#include <cmath>// for abs() belowusing namespace TNT;using namespace std;namespace JAMA{/** Computes eigenvalues and eigenvectors of a real (non-complex) matrix. <P> If A is symmetric, then A = V*D*V' where the eigenvalue matrix D is diagonal and the eigenvector matrix V is orthogonal. That is, the diagonal values of D are the eigenvalues, and V*V' = I, where I is the identity matrix. The columns of V represent the eigenvectors in the sense that A*V = V*D. <P> If A is not symmetric, then the eigenvalue matrix D is block diagonal with the real eigenvalues in 1-by-1 blocks and any complex eigenvalues, a + i*b, in 2-by-2 blocks, [a, b; -b, a]. That is, if the complex eigenvalues look like<pre> u + iv . . . . . . u - iv . . . . . . a + ib . . . . . . a - ib . . . . . . x . . . . . . y</pre> then D looks like<pre> u v . . . . -v u . . . . . . a b . . . . -b a . . . . . . x . . . . . . y</pre> This keeps V a real matrix in both symmetric and non-symmetric cases, and A*V = V*D. <p> The matrix V may be badly conditioned, or even singular, so the validity of the equation A = V*D*inverse(V) depends upon the condition number of V. <p> (Adapted from JAMA, a Java Matrix Library, developed by jointly by the Mathworks and NIST; see http://math.nist.gov/javanumerics/jama).**/template <class Real>class Eigenvalue{ /** Row and column dimension (square matrix). */ int n; int issymmetric; /* boolean*/ /** Arrays for internal storage of eigenvalues. */ TNT::Array1D<Real> d; /* real part */ TNT::Array1D<Real> e; /* img part */ /** Array for internal storage of eigenvectors. */ TNT::Array2D<Real> V; /** Array for internal storage of nonsymmetric Hessenberg form. @serial internal storage of nonsymmetric Hessenberg form. */ TNT::Array2D<Real> H; /** Working storage for nonsymmetric algorithm. @serial working storage for nonsymmetric algorithm. */ TNT::Array1D<Real> ort; // Symmetric Householder reduction to tridiagonal form. void tred2() { // This is derived from the Algol procedures tred2 by // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding // Fortran subroutine in EISPACK. for (int j = 0; j < n; j++) { d[j] = V[n-1][j]; } // Householder reduction to tridiagonal form. for (int i = n-1; i > 0; i--) { // Scale to avoid under/overflow. Real scale = 0.0; Real h = 0.0; for (int k = 0; k < i; k++) { scale = scale + abs(d[k]); } if (scale == 0.0) { e[i] = d[i-1]; for (int j = 0; j < i; j++) { d[j] = V[i-1][j]; V[i][j] = 0.0; V[j][i] = 0.0; } } else { // Generate Householder vector. for (int k = 0; k < i; k++) { d[k] /= scale; h += d[k] * d[k]; } Real f = d[i-1]; Real g = sqrt(h); if (f > 0) { g = -g; } e[i] = scale * g; h = h - f * g; d[i-1] = f - g; for (int j = 0; j < i; j++) { e[j] = 0.0; } // Apply similarity transformation to remaining columns. for (int j = 0; j < i; j++) { f = d[j]; V[j][i] = f; g = e[j] + V[j][j] * f; for (int k = j+1; k <= i-1; k++) { g += V[k][j] * d[k]; e[k] += V[k][j] * f; } e[j] = g; } f = 0.0; for (int j = 0; j < i; j++) { e[j] /= h; f += e[j] * d[j]; } Real hh = f / (h + h); for (int j = 0; j < i; j++) { e[j] -= hh * d[j]; } for (int j = 0; j < i; j++) { f = d[j]; g = e[j]; for (int k = j; k <= i-1; k++) { V[k][j] -= (f * e[k] + g * d[k]); } d[j] = V[i-1][j]; V[i][j] = 0.0; } } d[i] = h; } // Accumulate transformations. for (int i = 0; i < n-1; i++) { V[n-1][i] = V[i][i]; V[i][i] = 1.0; Real h = d[i+1]; if (h != 0.0) { for (int k = 0; k <= i; k++) { d[k] = V[k][i+1] / h; } for (int j = 0; j <= i; j++) { Real g = 0.0; for (int k = 0; k <= i; k++) { g += V[k][i+1] * V[k][j]; } for (int k = 0; k <= i; k++) { V[k][j] -= g * d[k]; } } } for (int k = 0; k <= i; k++) { V[k][i+1] = 0.0; } } for (int j = 0; j < n; j++) { d[j] = V[n-1][j]; V[n-1][j] = 0.0; } V[n-1][n-1] = 1.0; e[0] = 0.0; } // Symmetric tridiagonal QL algorithm. void tql2 () { // This is derived from the Algol procedures tql2, by // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding // Fortran subroutine in EISPACK. for (int i = 1; i < n; i++) { e[i-1] = e[i]; } e[n-1] = 0.0; Real f = 0.0; Real tst1 = 0.0; Real eps = pow(2.0,-52.0); for (int l = 0; l < n; l++) { // Find small subdiagonal element tst1 = max(tst1,abs(d[l]) + abs(e[l])); int m = l; // Original while-loop from Java code while (m < n) { if (abs(e[m]) <= eps*tst1) { break; } m++; } // If m == l, d[l] is an eigenvalue, // otherwise, iterate. if (m > l) { int iter = 0; do { iter = iter + 1; // (Could check iteration count here.) // Compute implicit shift Real g = d[l]; Real p = (d[l+1] - g) / (2.0 * e[l]); Real r = hypot(p,1.0); if (p < 0) { r = -r; } d[l] = e[l] / (p + r); d[l+1] = e[l] * (p + r); Real dl1 = d[l+1]; Real h = g - d[l]; for (int i = l+2; i < n; i++) { d[i] -= h; } f = f + h; // Implicit QL transformation. p = d[m]; Real c = 1.0; Real c2 = c; Real c3 = c; Real el1 = e[l+1]; Real s = 0.0; Real s2 = 0.0; for (int i = m-1; i >= l; i--) { c3 = c2; c2 = c; s2 = s; g = c * e[i]; h = c * p; r = hypot(p,e[i]); e[i+1] = s * r; s = e[i] / r; c = p / r; p = c * d[i] - s * g; d[i+1] = h + s * (c * g + s * d[i]); // Accumulate transformation. for (int k = 0; k < n; k++) { h = V[k][i+1]; V[k][i+1] = s * V[k][i] + c * h; V[k][i] = c * V[k][i] - s * h; } } p = -s * s2 * c3 * el1 * e[l] / dl1; e[l] = s * p; d[l] = c * p; // Check for convergence. } while (abs(e[l]) > eps*tst1); } d[l] = d[l] + f; e[l] = 0.0; } // Sort eigenvalues and corresponding vectors. for (int i = 0; i < n-1; i++) { int k = i; Real p = d[i]; for (int j = i+1; j < n; j++) { if (d[j] < p) { k = j; p = d[j]; } } if (k != i) { d[k] = d[i]; d[i] = p; for (int j = 0; j < n; j++) { p = V[j][i]; V[j][i] = V[j][k]; V[j][k] = p; } } } } // Nonsymmetric reduction to Hessenberg form. void orthes () { // This is derived from the Algol procedures orthes and ortran, // by Martin and Wilkinson, Handbook for Auto. Comp., // Vol.ii-Linear Algebra, and the corresponding // Fortran subroutines in EISPACK. int low = 0; int high = n-1; for (int m = low+1; m <= high-1; m++) { // Scale column. Real scale = 0.0; for (int i = m; i <= high; i++) { scale = scale + abs(H[i][m-1]); } if (scale != 0.0) { // Compute Householder transformation. Real h = 0.0; for (int i = high; i >= m; i--) { ort[i] = H[i][m-1]/scale; h += ort[i] * ort[i]; } Real g = sqrt(h); if (ort[m] > 0) { g = -g; } h = h - ort[m] * g; ort[m] = ort[m] - g; // Apply Householder similarity transformation // H = (I-u*u'/h)*H*(I-u*u')/h) for (int j = m; j < n; j++) { Real f = 0.0; for (int i = high; i >= m; i--) { f += ort[i]*H[i][j]; } f = f/h; for (int i = m; i <= high; i++) { H[i][j] -= f*ort[i]; } } for (int i = 0; i <= high; i++) { Real f = 0.0; for (int j = high; j >= m; j--) { f += ort[j]*H[i][j]; } f = f/h; for (int j = m; j <= high; j++) { H[i][j] -= f*ort[j]; } } ort[m] = scale*ort[m]; H[m][m-1] = scale*g; } } // Accumulate transformations (Algol's ortran). for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { V[i][j] = (i == j ? 1.0 : 0.0); } } for (int m = high-1; m >= low+1; m--) { if (H[m][m-1] != 0.0) { for (int i = m+1; i <= high; i++) { ort[i] = H[i][m-1]; } for (int j = m; j <= high; j++) { Real g = 0.0; for (int i = m; i <= high; i++) { g += ort[i] * V[i][j]; } // Double division avoids possible underflow g = (g / ort[m]) / H[m][m-1]; for (int i = m; i <= high; i++) { V[i][j] += g * ort[i]; } } } } } // Complex scalar division. Real cdivr, cdivi; void cdiv(Real xr, Real xi, Real yr, Real yi) { Real r,d; if (abs(yr) > abs(yi)) { r = yi/yr; d = yr + r*yi; cdivr = (xr + r*xi)/d; cdivi = (xi - r*xr)/d; } else { r = yr/yi; d = yi + r*yr; cdivr = (r*xr + xi)/d; cdivi = (r*xi - xr)/d; } } // Nonsymmetric reduction from Hessenberg to real Schur form. void hqr2 () { // This is derived from the Algol procedure hqr2, // by Martin and Wilkinson, Handbook for Auto. Comp., // Vol.ii-Linear Algebra, and the corresponding // Fortran subroutine in EISPACK. // Initialize int nn = this->n; int n = nn-1; int low = 0; int high = nn-1; Real eps = pow(2.0,-52.0); Real exshift = 0.0; Real p=0,q=0,r=0,s=0,z=0,t,w,x,y; // Store roots isolated by balanc and compute matrix norm Real norm = 0.0; for (int i = 0; i < nn; i++) { if ((i < low) || (i > high)) { d[i] = H[i][i]; e[i] = 0.0; } for (int j = max(i-1,0); j < nn; j++) { norm = norm + abs(H[i][j]); } } // Outer loop over eigenvalue index int iter = 0; while (n >= low) { // Look for single small sub-diagonal element int l = n; while (l > low) { s = abs(H[l-1][l-1]) + abs(H[l][l]); if (s == 0.0) { s = norm; } if (abs(H[l][l-1]) < eps * s) { break; } l--; } // Check for convergence // One root found if (l == n) { H[n][n] = H[n][n] + exshift; d[n] = H[n][n]; e[n] = 0.0; n--; iter = 0; // Two roots found
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -