3 * $Date: 2005-11-10 09:52:44f -0600 (Thu, 10 Nov 2005) $
6 * Some portions of this file have been modified by Robert Hanson hansonr.at.stolaf.edu 2012-2017
7 * for use in SwingJS via transpilation into JavaScript using Java2Script.
9 * Copyright (C) 2003-2005 Miguel, Jmol Development, www.jmol.org
11 * Contact: jmol-developers@lists.sf.net
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
30 import javajs.api.EigenInterface;
34 * Eigenvalues and eigenvectors of a real matrix.
35 * See javajs.api.EigenInterface() as well.
37 * adapted by Bob Hanson from http://math.nist.gov/javanumerics/jama/ (public
38 * domain); adding quaternion superimposition capability; removing
39 * nonsymmetric reduction to Hessenberg form, which we do not need in Jmol.
41 * Output is as a set of double[n] columns, but for the EigenInterface
42 * we return them as V3[3] and float[3] (or double[3]) values.
44 * Eigenvalues and eigenvectors are sorted from smallest to largest eigenvalue.
47 * If A is symmetric, then A = V*D*V' where the eigenvalue matrix D is diagonal
48 * and the eigenvector matrix V is orthogonal. I.e. A =
49 * V.times(D.times(V.transpose())) and V.times(V.transpose()) equals the
52 * If A is not symmetric, then the eigenvalue matrix D is block diagonal with
53 * the real eigenvalues in 1-by-1 blocks and any complex eigenvalues, lambda +
54 * i*mu, in 2-by-2 blocks, [lambda, mu; -mu, lambda]. The columns of V represent
55 * the eigenvectors in the sense that A*V = V*D, i.e. A.times(V) equals
56 * V.times(D). The matrix V may be badly conditioned, or even singular, so the
57 * validity of the equation A = V*D*inverse(V) depends upon V.cond().
60 public class Eigen implements EigenInterface {
62 /* ------------------------
64 * ------------------------ */
68 public Eigen set(int n) {
77 public Eigen setM(double[][] m) {
84 * return values sorted from smallest to largest value.
87 public double[] getEigenvalues() {
92 * Specifically for 3x3 systems, returns eigenVectors as V3[3]
93 * and values as float[3]; sorted from smallest to largest value.
95 * @param eigenVectors returned vectors
96 * @param eigenValues returned values
100 public void fillFloatArrays(V3[] eigenVectors, float[] eigenValues) {
101 for (int i = 0; i < 3; i++) {
102 if (eigenVectors != null) {
103 if (eigenVectors[i] == null)
104 eigenVectors[i] = new V3();
105 eigenVectors[i].set((float) V[0][i], (float) V[1][i], (float) V[2][i]);
107 if (eigenValues != null)
108 eigenValues[i] = (float) d[i];
113 * Transpose V and turn into floats; sorted from smallest to largest value.
115 * @return ROWS of eigenvectors f[0], f[1], f[2], etc.
118 public float[][] getEigenvectorsFloatTransposed() {
119 float[][] f = new float[n][n];
120 for (int i = n; --i >= 0;)
121 for (int j = n; --j >= 0;)
122 f[j][i] = (float) V[i][j];
128 * Check for symmetry, then construct the eigenvalue decomposition
134 public void calc(double[][] A) {
136 /* Jmol only has need of symmetric solutions
140 for (int j = 0; (j < n) & issymmetric; j++) {
141 for (int i = 0; (i < n) & issymmetric; i++) {
142 issymmetric = (A[i][j] == A[j][i]);
148 for (int i = 0; i < n; i++) {
149 for (int j = 0; j < n; j++) {
161 H = new double[n][n];
164 for (int j = 0; j < n; j++) {
165 for (int i = 0; i < n; i++) {
170 // Reduce to Hessenberg form.
173 // Reduce Hessenberg to real Schur form.
181 * Return the real parts of the eigenvalues
183 * @return real(diag(D))
186 public double[] getRealEigenvalues() {
191 * Return the imaginary parts of the eigenvalues
193 * @return imag(diag(D))
196 public double[] getImagEigenvalues() {
200 /* ------------------------
202 * ------------------------ */
205 * Row and column dimension (square matrix).
207 * @serial matrix dimension.
214 * @serial internal symmetry flag.
216 //private boolean issymmetric = true;
219 * Arrays for internal storage of eigenvalues.
221 * @serial internal storage of eigenvalues.
223 private double[] d, e;
226 * Array for internal storage of eigenvectors.
228 * @serial internal storage of eigenvectors.
230 private double[][] V;
233 * Array for internal storage of nonsymmetric Hessenberg form.
235 * @serial internal storage of nonsymmetric Hessenberg form.
237 //private double[][] H;
240 * Working storage for nonsymmetric algorithm.
242 * @serial working storage for nonsymmetric algorithm.
244 //private double[] ort;
246 /* ------------------------
248 * ------------------------ */
250 // Symmetric Householder reduction to tridiagonal form.
252 private void tred2() {
254 // This is derived from the Algol procedures tred2 by
255 // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
256 // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
257 // Fortran subroutine in EISPACK.
259 for (int j = 0; j < n; j++) {
263 // Householder reduction to tridiagonal form.
265 for (int i = n - 1; i > 0; i--) {
267 // Scale to avoid under/overflow.
271 for (int k = 0; k < i; k++) {
272 scale = scale + Math.abs(d[k]);
276 for (int j = 0; j < i; j++) {
283 // Generate Householder vector.
285 for (int k = 0; k < i; k++) {
290 double g = Math.sqrt(h);
297 for (int j = 0; j < i; j++) {
301 // Apply similarity transformation to remaining columns.
303 for (int j = 0; j < i; j++) {
306 g = e[j] + V[j][j] * f;
307 for (int k = j + 1; k <= i - 1; k++) {
314 for (int j = 0; j < i; j++) {
318 double hh = f / (h + h);
319 for (int j = 0; j < i; j++) {
322 for (int j = 0; j < i; j++) {
325 for (int k = j; k <= i - 1; k++) {
326 V[k][j] -= (f * e[k] + g * d[k]);
335 // Accumulate transformations.
337 for (int i = 0; i < n - 1; i++) {
338 V[n - 1][i] = V[i][i];
342 for (int k = 0; k <= i; k++) {
343 d[k] = V[k][i + 1] / h;
345 for (int j = 0; j <= i; j++) {
347 for (int k = 0; k <= i; k++) {
348 g += V[k][i + 1] * V[k][j];
350 for (int k = 0; k <= i; k++) {
355 for (int k = 0; k <= i; k++) {
359 for (int j = 0; j < n; j++) {
363 V[n - 1][n - 1] = 1.0;
367 // Symmetric tridiagonal QL algorithm.
369 private void tql2() {
371 // This is derived from the Algol procedures tql2, by
372 // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
373 // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
374 // Fortran subroutine in EISPACK.
376 for (int i = 1; i < n; i++) {
383 double eps = Math.pow(2.0, -52.0);
384 for (int l = 0; l < n; l++) {
386 // Find small subdiagonal element
388 tst1 = Math.max(tst1, Math.abs(d[l]) + Math.abs(e[l]));
391 if (Math.abs(e[m]) <= eps * tst1) {
397 // If m == l, d[l] is an eigenvalue,
398 // otherwise, iterate.
403 iter = iter + 1; // (Could check iteration count here.)
405 // Compute implicit shift
408 double p = (d[l + 1] - g) / (2.0 * e[l]);
409 double r = hypot(p, 1.0);
413 d[l] = e[l] / (p + r);
414 d[l + 1] = e[l] * (p + r);
415 double dl1 = d[l + 1];
417 for (int i = l + 2; i < n; i++) {
422 // Implicit QL transformation.
428 double el1 = e[l + 1];
431 for (int i = m - 1; i >= l; i--) {
441 p = c * d[i] - s * g;
442 d[i + 1] = h + s * (c * g + s * d[i]);
444 // Accumulate transformation.
446 for (int k = 0; k < n; k++) {
448 V[k][i + 1] = s * V[k][i] + c * h;
449 V[k][i] = c * V[k][i] - s * h;
452 p = -s * s2 * c3 * el1 * e[l] / dl1;
456 // Check for convergence.
458 } while (Math.abs(e[l]) > eps * tst1);
464 // Sort eigenvalues and corresponding vectors.
466 for (int i = 0; i < n - 1; i++) {
469 for (int j = i + 1; j < n; j++) {
478 for (int j = 0; j < n; j++) {
487 private static double hypot(double a, double b) {
489 // sqrt(a^2 + b^2) without under/overflow.
492 if (Math.abs(a) > Math.abs(b)) {
494 r = Math.abs(a) * Math.sqrt(1 + r * r);
497 r = Math.abs(b) * Math.sqrt(1 + r * r);
504 // Nonsymmetric reduction to Hessenberg form.
507 private void orthes() {
509 // This is derived from the Algol procedures orthes and ortran,
510 // by Martin and Wilkinson, Handbook for Auto. Comp.,
511 // Vol.ii-Linear Algebra, and the corresponding
512 // Fortran subroutines in EISPACK.
517 for (int m = low + 1; m <= high - 1; m++) {
522 for (int i = m; i <= high; i++) {
523 scale = scale + Math.abs(H[i][m - 1]);
527 // Compute Householder transformation.
530 for (int i = high; i >= m; i--) {
531 ort[i] = H[i][m - 1] / scale;
532 h += ort[i] * ort[i];
534 double g = Math.sqrt(h);
541 // Apply Householder similarity transformation
542 // H = (I-u*u'/h)*H*(I-u*u')/h)
544 for (int j = m; j < n; j++) {
546 for (int i = high; i >= m; i--) {
547 f += ort[i] * H[i][j];
550 for (int i = m; i <= high; i++) {
551 H[i][j] -= f * ort[i];
555 for (int i = 0; i <= high; i++) {
557 for (int j = high; j >= m; j--) {
558 f += ort[j] * H[i][j];
561 for (int j = m; j <= high; j++) {
562 H[i][j] -= f * ort[j];
565 ort[m] = scale * ort[m];
566 H[m][m - 1] = scale * g;
570 // Accumulate transformations (Algol's ortran).
572 for (int i = 0; i < n; i++) {
573 for (int j = 0; j < n; j++) {
574 V[i][j] = (i == j ? 1.0 : 0.0);
578 for (int m = high - 1; m >= low + 1; m--) {
579 if (H[m][m - 1] != 0.0) {
580 for (int i = m + 1; i <= high; i++) {
581 ort[i] = H[i][m - 1];
583 for (int j = m; j <= high; j++) {
585 for (int i = m; i <= high; i++) {
586 g += ort[i] * V[i][j];
588 // Double division avoids possible underflow
589 g = (g / ort[m]) / H[m][m - 1];
590 for (int i = m; i <= high; i++) {
591 V[i][j] += g * ort[i];
598 // Complex scalar division.
600 private transient double cdivr, cdivi;
602 private void cdiv(double xr, double xi, double yr, double yi) {
604 if (Math.abs(yr) > Math.abs(yi)) {
607 cdivr = (xr + r * xi) / d;
608 cdivi = (xi - r * xr) / d;
612 cdivr = (r * xr + xi) / d;
613 cdivi = (r * xi - xr) / d;
617 // Nonsymmetric reduction from Hessenberg to real Schur form.
619 private void hqr2() {
621 // This is derived from the Algol procedure hqr2,
622 // by Martin and Wilkinson, Handbook for Auto. Comp.,
623 // Vol.ii-Linear Algebra, and the corresponding
624 // Fortran subroutine in EISPACK.
632 double eps = Math.pow(2.0, -52.0);
633 double exshift = 0.0;
634 double p = 0, q = 0, r = 0, s = 0, z = 0, t, w, x, y;
636 // Store roots isolated by balanc and compute matrix norm
639 for (int i = 0; i < nn; i++) {
640 if (i < low || i > high) {
644 for (int j = Math.max(i - 1, 0); j < nn; j++) {
645 norm = norm + Math.abs(H[i][j]);
649 // Outer loop over eigenvalue index
654 // Look for single small sub-diagonal element
658 s = Math.abs(H[l - 1][l - 1]) + Math.abs(H[l][l]);
662 if (Math.abs(H[l][l - 1]) < eps * s) {
668 // Check for convergence
672 H[n][n] = H[n][n] + exshift;
680 } else if (l == n - 1) {
681 w = H[n][n - 1] * H[n - 1][n];
682 p = (H[n - 1][n - 1] - H[n][n]) / 2.0;
684 z = Math.sqrt(Math.abs(q));
685 H[n][n] = H[n][n] + exshift;
686 H[n - 1][n - 1] = H[n - 1][n - 1] + exshift;
705 s = Math.abs(x) + Math.abs(z);
708 r = Math.sqrt(p * p + q * q);
714 for (int j = n - 1; j < nn; j++) {
716 H[n - 1][j] = q * z + p * H[n][j];
717 H[n][j] = q * H[n][j] - p * z;
720 // Column modification
722 for (int i = 0; i <= n; i++) {
724 H[i][n - 1] = q * z + p * H[i][n];
725 H[i][n] = q * H[i][n] - p * z;
728 // Accumulate transformations
730 for (int i = low; i <= high; i++) {
732 V[i][n - 1] = q * z + p * V[i][n];
733 V[i][n] = q * V[i][n] - p * z;
747 // No convergence yet
758 w = H[n][n - 1] * H[n - 1][n];
761 // Wilkinson's original ad hoc shift
765 for (int i = low; i <= n; i++) {
768 s = Math.abs(H[n][n - 1]) + Math.abs(H[n - 1][n - 2]);
773 // MATLAB's new ad hoc shift
783 s = x - w / ((y - x) / 2.0 + s);
784 for (int i = low; i <= n; i++) {
792 iter = iter + 1; // (Could check iteration count here.)
794 // Look for two consecutive small sub-diagonal elements
801 p = (r * s - w) / H[m + 1][m] + H[m][m + 1];
802 q = H[m + 1][m + 1] - z - r - s;
804 s = Math.abs(p) + Math.abs(q) + Math.abs(r);
811 if (Math.abs(H[m][m - 1]) * (Math.abs(q) + Math.abs(r)) < eps
812 * (Math.abs(p) * (Math.abs(H[m - 1][m - 1]) + Math.abs(z) + Math
813 .abs(H[m + 1][m + 1])))) {
819 for (int i = m + 2; i <= n; i++) {
826 // Double QR step involving rows l:n and columns m:n
828 for (int k = m; k <= n - 1; k++) {
829 boolean notlast = (k != n - 1);
833 r = (notlast ? H[k + 2][k - 1] : 0.0);
834 x = Math.abs(p) + Math.abs(q) + Math.abs(r);
844 s = Math.sqrt(p * p + q * q + r * r);
850 H[k][k - 1] = -s * x;
852 H[k][k - 1] = -H[k][k - 1];
863 for (int j = k; j < nn; j++) {
864 p = H[k][j] + q * H[k + 1][j];
866 p = p + r * H[k + 2][j];
867 H[k + 2][j] = H[k + 2][j] - p * z;
869 H[k][j] = H[k][j] - p * x;
870 H[k + 1][j] = H[k + 1][j] - p * y;
873 // Column modification
875 for (int i = 0; i <= Math.min(n, k + 3); i++) {
876 p = x * H[i][k] + y * H[i][k + 1];
878 p = p + z * H[i][k + 2];
879 H[i][k + 2] = H[i][k + 2] - p * r;
881 H[i][k] = H[i][k] - p;
882 H[i][k + 1] = H[i][k + 1] - p * q;
885 // Accumulate transformations
887 for (int i = low; i <= high; i++) {
888 p = x * V[i][k] + y * V[i][k + 1];
890 p = p + z * V[i][k + 2];
891 V[i][k + 2] = V[i][k + 2] - p * r;
893 V[i][k] = V[i][k] - p;
894 V[i][k + 1] = V[i][k + 1] - p * q;
898 } // check convergence
899 } // while (n >= low)
901 // Backsubstitute to find vectors of upper triangular form
907 for (n = nn - 1; n >= 0; n--) {
916 for (int i = n - 1; i >= 0; i--) {
919 for (int j = l; j <= n; j++) {
920 r = r + H[i][j] * H[j][n];
931 H[i][n] = -r / (eps * norm);
934 // Solve real equations
939 q = (d[i] - p) * (d[i] - p) + e[i] * e[i];
940 t = (x * s - z * r) / q;
942 if (Math.abs(x) > Math.abs(z)) {
943 H[i + 1][n] = (-r - w * t) / x;
945 H[i + 1][n] = (-s - y * t) / z;
951 t = Math.abs(H[i][n]);
952 if ((eps * t) * t > 1) {
953 for (int j = i; j <= n; j++) {
954 H[j][n] = H[j][n] / t;
965 // Last vector component imaginary so matrix is triangular
967 if (Math.abs(H[n][n - 1]) > Math.abs(H[n - 1][n])) {
968 H[n - 1][n - 1] = q / H[n][n - 1];
969 H[n - 1][n] = -(H[n][n] - p) / H[n][n - 1];
971 cdiv(0.0, -H[n - 1][n], H[n - 1][n - 1] - p, q);
972 H[n - 1][n - 1] = cdivr;
977 for (int i = n - 2; i >= 0; i--) {
978 double ra, sa, vr, vi;
981 for (int j = l; j <= n; j++) {
982 ra = ra + H[i][j] * H[j][n - 1];
983 sa = sa + H[i][j] * H[j][n];
994 cdiv(-ra, -sa, w, q);
999 // Solve complex equations
1003 vr = (d[i] - p) * (d[i] - p) + e[i] * e[i] - q * q;
1004 vi = (d[i] - p) * 2.0 * q;
1005 if (vr == 0.0 & vi == 0.0) {
1008 * (Math.abs(w) + Math.abs(q) + Math.abs(x) + Math.abs(y) + Math
1011 cdiv(x * r - z * ra + q * sa, x * s - z * sa - q * ra, vr, vi);
1012 H[i][n - 1] = cdivr;
1014 if (Math.abs(x) > (Math.abs(z) + Math.abs(q))) {
1015 H[i + 1][n - 1] = (-ra - w * H[i][n - 1] + q * H[i][n]) / x;
1016 H[i + 1][n] = (-sa - w * H[i][n] - q * H[i][n - 1]) / x;
1018 cdiv(-r - y * H[i][n - 1], -s - y * H[i][n], z, q);
1019 H[i + 1][n - 1] = cdivr;
1020 H[i + 1][n] = cdivi;
1026 t = Math.max(Math.abs(H[i][n - 1]), Math.abs(H[i][n]));
1027 if ((eps * t) * t > 1) {
1028 for (int j = i; j <= n; j++) {
1029 H[j][n - 1] = H[j][n - 1] / t;
1030 H[j][n] = H[j][n] / t;
1038 // Vectors of isolated roots
1040 for (int i = 0; i < nn; i++) {
1041 if (i < low || i > high) {
1042 for (int j = i; j < nn; j++) {
1048 // Back transformation to get eigenvectors of original matrix
1050 for (int j = nn - 1; j >= low; j--) {
1051 for (int i = low; i <= high; i++) {
1053 for (int k = low; k <= Math.min(j, high); k++) {
1054 z = z + V[i][k] * H[k][j];