📄 eigenvaluedecomposition.java
字号:
/* * This software is a cooperative product of The MathWorks and the National * Institute of Standards and Technology (NIST) which has been released to the * public domain. Neither The MathWorks nor NIST assumes any responsibility * whatsoever for its use by other parties, and makes no guarantees, expressed * or implied, about its quality, reliability, or any other characteristic. *//* * EigenvalueDecomposition.java * Copyright (C) 1999 The Mathworks and NIST * */package weka.core.matrix;import java.io.Serializable;/** * Eigenvalues and eigenvectors of a real 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. I.e. A = * V.times(D.times(V.transpose())) and V.times(V.transpose()) equals the * identity matrix. * <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, lambda + * i*mu, in 2-by-2 blocks, [lambda, mu; -mu, lambda]. The columns of V * represent the eigenvectors in the sense that A*V = V*D, i.e. A.times(V) * equals V.times(D). The matrix V may be badly conditioned, or even singular, * so the validity of the equation A = V*D*inverse(V) depends upon V.cond(). * <p/> * Adapted from the <a href="http://math.nist.gov/javanumerics/jama/" target="_blank">JAMA</a> package. * * @author The Mathworks and NIST * @author Fracpete (fracpete at waikato dot ac dot nz) * @version $Revision: 1.2 $ */public class EigenvalueDecomposition implements Serializable { /** * Row and column dimension (square matrix). * @serial matrix dimension. */ private int n; /** * Symmetry flag. * @serial internal symmetry flag. */ private boolean issymmetric; /** * Arrays for internal storage of eigenvalues. * @serial internal storage of eigenvalues. */ private double[] d, e; /** * Array for internal storage of eigenvectors. * @serial internal storage of eigenvectors. */ private double[][] V; /** * Array for internal storage of nonsymmetric Hessenberg form. * @serial internal storage of nonsymmetric Hessenberg form. */ private double[][] H; /** * Working storage for nonsymmetric algorithm. * @serial working storage for nonsymmetric algorithm. */ private double[] ort; /** * helper variables for the comples scalar division * @see #cdiv(double,double,double,double) */ private transient double cdivr, cdivi; /** * Symmetric Householder reduction to tridiagonal form. * <p/> * 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. */ private void tred2() { 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. double scale = 0.0; double h = 0.0; for (int k = 0; k < i; k++) { scale = scale + Math.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]; } double f = d[i-1]; double g = Math.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]; } double 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; double 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++) { double 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. * <p/> * 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. */ private void tql2() { for (int i = 1; i < n; i++) { e[i-1] = e[i]; } e[n-1] = 0.0; double f = 0.0; double tst1 = 0.0; double eps = Math.pow(2.0,-52.0); for (int l = 0; l < n; l++) { // Find small subdiagonal element tst1 = Math.max(tst1,Math.abs(d[l]) + Math.abs(e[l])); int m = l; while (m < n) { if (Math.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 double g = d[l]; double p = (d[l+1] - g) / (2.0 * e[l]); double r = Maths.hypot(p,1.0); if (p < 0) { r = -r; } d[l] = e[l] / (p + r); d[l+1] = e[l] * (p + r); double dl1 = d[l+1]; double h = g - d[l]; for (int i = l+2; i < n; i++) { d[i] -= h; } f = f + h; // Implicit QL transformation. p = d[m]; double c = 1.0; double c2 = c; double c3 = c; double el1 = e[l+1]; double s = 0.0; double 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 = Maths.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 (Math.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; double 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. * <p/> * 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. */ private void orthes() { int low = 0; int high = n-1; for (int m = low+1; m <= high-1; m++) { // Scale column. double scale = 0.0; for (int i = m; i <= high; i++) { scale = scale + Math.abs(H[i][m-1]); } if (scale != 0.0) { // Compute Householder transformation. double h = 0.0; for (int i = high; i >= m; i--) { ort[i] = H[i][m-1]/scale; h += ort[i] * ort[i]; } double g = Math.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++) { double 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++) { double 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++) { double 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. */ private void cdiv(double xr, double xi, double yr, double yi) { double r,d; if (Math.abs(yr) > Math.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. * <p/> * 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. */ private void hqr2() { // Initialize int nn = this.n; int n = nn-1; int low = 0; int high = nn-1; double eps = Math.pow(2.0,-52.0); double exshift = 0.0; double p=0,q=0,r=0,s=0,z=0,t,w,x,y; // Store roots isolated by balanc and compute matrix norm double 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 = Math.max(i-1,0); j < nn; j++) { norm = norm + Math.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 = Math.abs(H[l-1][l-1]) + Math.abs(H[l][l]); if (s == 0.0) { s = norm; } if (Math.abs(H[l][l-1]) < eps * s) { break; } l--; } // Check for convergence
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -