1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.math3.linear;
19
20 import org.apache.commons.math3.complex.Complex;
21 import org.apache.commons.math3.exception.MathArithmeticException;
22 import org.apache.commons.math3.exception.MathUnsupportedOperationException;
23 import org.apache.commons.math3.exception.MaxCountExceededException;
24 import org.apache.commons.math3.exception.DimensionMismatchException;
25 import org.apache.commons.math3.exception.util.LocalizedFormats;
26 import org.apache.commons.math3.util.Precision;
27 import org.apache.commons.math3.util.FastMath;
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76 public class EigenDecomposition {
77
78 private static final double EPSILON = 1e-12;
79
80 private byte maxIter = 30;
81
82 private double[] main;
83
84 private double[] secondary;
85
86
87
88
89 private TriDiagonalTransformer transformer;
90
91 private double[] realEigenvalues;
92
93 private double[] imagEigenvalues;
94
95 private ArrayRealVector[] eigenvectors;
96
97 private RealMatrix cachedV;
98
99 private RealMatrix cachedD;
100
101 private RealMatrix cachedVt;
102
103 private final boolean isSymmetric;
104
105
106
107
108
109
110
111
112
113
114
115
116 public EigenDecomposition(final RealMatrix matrix)
117 throws MathArithmeticException {
118 final double symTol = 10 * matrix.getRowDimension() * matrix.getColumnDimension() * Precision.EPSILON;
119 isSymmetric = MatrixUtils.isSymmetric(matrix, symTol);
120 if (isSymmetric) {
121 transformToTridiagonal(matrix);
122 findEigenVectors(transformer.getQ().getData());
123 } else {
124 final SchurTransformer t = transformToSchur(matrix);
125 findEigenVectorsFromSchur(t);
126 }
127 }
128
129
130
131
132
133
134
135
136
137
138
139
140 @Deprecated
141 public EigenDecomposition(final RealMatrix matrix,
142 final double splitTolerance)
143 throws MathArithmeticException {
144 this(matrix);
145 }
146
147
148
149
150
151
152
153
154
155
156 public EigenDecomposition(final double[] main, final double[] secondary) {
157 isSymmetric = true;
158 this.main = main.clone();
159 this.secondary = secondary.clone();
160 transformer = null;
161 final int size = main.length;
162 final double[][] z = new double[size][size];
163 for (int i = 0; i < size; i++) {
164 z[i][i] = 1.0;
165 }
166 findEigenVectors(z);
167 }
168
169
170
171
172
173
174
175
176
177
178
179
180 @Deprecated
181 public EigenDecomposition(final double[] main, final double[] secondary,
182 final double splitTolerance) {
183 this(main, secondary);
184 }
185
186
187
188
189
190
191
192
193
194
195
196 public RealMatrix getV() {
197
198 if (cachedV == null) {
199 final int m = eigenvectors.length;
200 cachedV = MatrixUtils.createRealMatrix(m, m);
201 for (int k = 0; k < m; ++k) {
202 cachedV.setColumnVector(k, eigenvectors[k]);
203 }
204 }
205
206 return cachedV;
207 }
208
209
210
211
212
213
214
215
216
217
218
219
220 public RealMatrix getD() {
221
222 if (cachedD == null) {
223
224 cachedD = MatrixUtils.createRealDiagonalMatrix(realEigenvalues);
225
226 for (int i = 0; i < imagEigenvalues.length; i++) {
227 if (Precision.compareTo(imagEigenvalues[i], 0.0, EPSILON) > 0) {
228 cachedD.setEntry(i, i+1, imagEigenvalues[i]);
229 } else if (Precision.compareTo(imagEigenvalues[i], 0.0, EPSILON) < 0) {
230 cachedD.setEntry(i, i-1, imagEigenvalues[i]);
231 }
232 }
233 }
234 return cachedD;
235 }
236
237
238
239
240
241
242
243
244
245
246
247 public RealMatrix getVT() {
248
249 if (cachedVt == null) {
250 final int m = eigenvectors.length;
251 cachedVt = MatrixUtils.createRealMatrix(m, m);
252 for (int k = 0; k < m; ++k) {
253 cachedVt.setRowVector(k, eigenvectors[k]);
254 }
255 }
256
257
258 return cachedVt;
259 }
260
261
262
263
264
265
266
267
268
269
270 public boolean hasComplexEigenvalues() {
271 for (int i = 0; i < imagEigenvalues.length; i++) {
272 if (!Precision.equals(imagEigenvalues[i], 0.0, EPSILON)) {
273 return true;
274 }
275 }
276 return false;
277 }
278
279
280
281
282
283
284
285
286
287
288 public double[] getRealEigenvalues() {
289 return realEigenvalues.clone();
290 }
291
292
293
294
295
296
297
298
299
300
301
302
303
304 public double getRealEigenvalue(final int i) {
305 return realEigenvalues[i];
306 }
307
308
309
310
311
312
313
314
315
316
317
318
319 public double[] getImagEigenvalues() {
320 return imagEigenvalues.clone();
321 }
322
323
324
325
326
327
328
329
330
331
332
333
334
335 public double getImagEigenvalue(final int i) {
336 return imagEigenvalues[i];
337 }
338
339
340
341
342
343
344
345
346 public RealVector getEigenvector(final int i) {
347 return eigenvectors[i].copy();
348 }
349
350
351
352
353
354
355 public double getDeterminant() {
356 double determinant = 1;
357 for (double lambda : realEigenvalues) {
358 determinant *= lambda;
359 }
360 return determinant;
361 }
362
363
364
365
366
367
368
369
370
371
372
373 public RealMatrix getSquareRoot() {
374 if (!isSymmetric) {
375 throw new MathUnsupportedOperationException();
376 }
377
378 final double[] sqrtEigenValues = new double[realEigenvalues.length];
379 for (int i = 0; i < realEigenvalues.length; i++) {
380 final double eigen = realEigenvalues[i];
381 if (eigen <= 0) {
382 throw new MathUnsupportedOperationException();
383 }
384 sqrtEigenValues[i] = FastMath.sqrt(eigen);
385 }
386 final RealMatrix sqrtEigen = MatrixUtils.createRealDiagonalMatrix(sqrtEigenValues);
387 final RealMatrix v = getV();
388 final RealMatrix vT = getVT();
389
390 return v.multiply(sqrtEigen).multiply(vT);
391 }
392
393
394
395
396
397
398
399
400
401
402
403
404 public DecompositionSolver getSolver() {
405 if (hasComplexEigenvalues()) {
406 throw new MathUnsupportedOperationException();
407 }
408 return new Solver(realEigenvalues, imagEigenvalues, eigenvectors);
409 }
410
411
412 private static class Solver implements DecompositionSolver {
413
414 private double[] realEigenvalues;
415
416 private double[] imagEigenvalues;
417
418 private final ArrayRealVector[] eigenvectors;
419
420
421
422
423
424
425
426
427 private Solver(final double[] realEigenvalues,
428 final double[] imagEigenvalues,
429 final ArrayRealVector[] eigenvectors) {
430 this.realEigenvalues = realEigenvalues;
431 this.imagEigenvalues = imagEigenvalues;
432 this.eigenvectors = eigenvectors;
433 }
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448 public RealVector solve(final RealVector b) {
449 if (!isNonSingular()) {
450 throw new SingularMatrixException();
451 }
452
453 final int m = realEigenvalues.length;
454 if (b.getDimension() != m) {
455 throw new DimensionMismatchException(b.getDimension(), m);
456 }
457
458 final double[] bp = new double[m];
459 for (int i = 0; i < m; ++i) {
460 final ArrayRealVector v = eigenvectors[i];
461 final double[] vData = v.getDataRef();
462 final double s = v.dotProduct(b) / realEigenvalues[i];
463 for (int j = 0; j < m; ++j) {
464 bp[j] += s * vData[j];
465 }
466 }
467
468 return new ArrayRealVector(bp, false);
469 }
470
471
472 public RealMatrix solve(RealMatrix b) {
473
474 if (!isNonSingular()) {
475 throw new SingularMatrixException();
476 }
477
478 final int m = realEigenvalues.length;
479 if (b.getRowDimension() != m) {
480 throw new DimensionMismatchException(b.getRowDimension(), m);
481 }
482
483 final int nColB = b.getColumnDimension();
484 final double[][] bp = new double[m][nColB];
485 final double[] tmpCol = new double[m];
486 for (int k = 0; k < nColB; ++k) {
487 for (int i = 0; i < m; ++i) {
488 tmpCol[i] = b.getEntry(i, k);
489 bp[i][k] = 0;
490 }
491 for (int i = 0; i < m; ++i) {
492 final ArrayRealVector v = eigenvectors[i];
493 final double[] vData = v.getDataRef();
494 double s = 0;
495 for (int j = 0; j < m; ++j) {
496 s += v.getEntry(j) * tmpCol[j];
497 }
498 s /= realEigenvalues[i];
499 for (int j = 0; j < m; ++j) {
500 bp[j][k] += s * vData[j];
501 }
502 }
503 }
504
505 return new Array2DRowRealMatrix(bp, false);
506
507 }
508
509
510
511
512
513
514 public boolean isNonSingular() {
515 double largestEigenvalueNorm = 0.0;
516
517
518 for (int i = 0; i < realEigenvalues.length; ++i) {
519 largestEigenvalueNorm = FastMath.max(largestEigenvalueNorm, eigenvalueNorm(i));
520 }
521
522 if (largestEigenvalueNorm == 0.0) {
523 return false;
524 }
525 for (int i = 0; i < realEigenvalues.length; ++i) {
526
527
528 if (Precision.equals(eigenvalueNorm(i) / largestEigenvalueNorm, 0, EPSILON)) {
529 return false;
530 }
531 }
532 return true;
533 }
534
535
536
537
538
539 private double eigenvalueNorm(int i) {
540 final double re = realEigenvalues[i];
541 final double im = imagEigenvalues[i];
542 return FastMath.sqrt(re * re + im * im);
543 }
544
545
546
547
548
549
550
551 public RealMatrix getInverse() {
552 if (!isNonSingular()) {
553 throw new SingularMatrixException();
554 }
555
556 final int m = realEigenvalues.length;
557 final double[][] invData = new double[m][m];
558
559 for (int i = 0; i < m; ++i) {
560 final double[] invI = invData[i];
561 for (int j = 0; j < m; ++j) {
562 double invIJ = 0;
563 for (int k = 0; k < m; ++k) {
564 final double[] vK = eigenvectors[k].getDataRef();
565 invIJ += vK[i] * vK[j] / realEigenvalues[k];
566 }
567 invI[j] = invIJ;
568 }
569 }
570 return MatrixUtils.createRealMatrix(invData);
571 }
572 }
573
574
575
576
577
578
579 private void transformToTridiagonal(final RealMatrix matrix) {
580
581 transformer = new TriDiagonalTransformer(matrix);
582 main = transformer.getMainDiagonalRef();
583 secondary = transformer.getSecondaryDiagonalRef();
584 }
585
586
587
588
589
590
591
592 private void findEigenVectors(final double[][] householderMatrix) {
593 final double[][]z = householderMatrix.clone();
594 final int n = main.length;
595 realEigenvalues = new double[n];
596 imagEigenvalues = new double[n];
597 final double[] e = new double[n];
598 for (int i = 0; i < n - 1; i++) {
599 realEigenvalues[i] = main[i];
600 e[i] = secondary[i];
601 }
602 realEigenvalues[n - 1] = main[n - 1];
603 e[n - 1] = 0;
604
605
606 double maxAbsoluteValue = 0;
607 for (int i = 0; i < n; i++) {
608 if (FastMath.abs(realEigenvalues[i]) > maxAbsoluteValue) {
609 maxAbsoluteValue = FastMath.abs(realEigenvalues[i]);
610 }
611 if (FastMath.abs(e[i]) > maxAbsoluteValue) {
612 maxAbsoluteValue = FastMath.abs(e[i]);
613 }
614 }
615
616 if (maxAbsoluteValue != 0) {
617 for (int i=0; i < n; i++) {
618 if (FastMath.abs(realEigenvalues[i]) <= Precision.EPSILON * maxAbsoluteValue) {
619 realEigenvalues[i] = 0;
620 }
621 if (FastMath.abs(e[i]) <= Precision.EPSILON * maxAbsoluteValue) {
622 e[i]=0;
623 }
624 }
625 }
626
627 for (int j = 0; j < n; j++) {
628 int its = 0;
629 int m;
630 do {
631 for (m = j; m < n - 1; m++) {
632 double delta = FastMath.abs(realEigenvalues[m]) +
633 FastMath.abs(realEigenvalues[m + 1]);
634 if (FastMath.abs(e[m]) + delta == delta) {
635 break;
636 }
637 }
638 if (m != j) {
639 if (its == maxIter) {
640 throw new MaxCountExceededException(LocalizedFormats.CONVERGENCE_FAILED,
641 maxIter);
642 }
643 its++;
644 double q = (realEigenvalues[j + 1] - realEigenvalues[j]) / (2 * e[j]);
645 double t = FastMath.sqrt(1 + q * q);
646 if (q < 0.0) {
647 q = realEigenvalues[m] - realEigenvalues[j] + e[j] / (q - t);
648 } else {
649 q = realEigenvalues[m] - realEigenvalues[j] + e[j] / (q + t);
650 }
651 double u = 0.0;
652 double s = 1.0;
653 double c = 1.0;
654 int i;
655 for (i = m - 1; i >= j; i--) {
656 double p = s * e[i];
657 double h = c * e[i];
658 if (FastMath.abs(p) >= FastMath.abs(q)) {
659 c = q / p;
660 t = FastMath.sqrt(c * c + 1.0);
661 e[i + 1] = p * t;
662 s = 1.0 / t;
663 c *= s;
664 } else {
665 s = p / q;
666 t = FastMath.sqrt(s * s + 1.0);
667 e[i + 1] = q * t;
668 c = 1.0 / t;
669 s *= c;
670 }
671 if (e[i + 1] == 0.0) {
672 realEigenvalues[i + 1] -= u;
673 e[m] = 0.0;
674 break;
675 }
676 q = realEigenvalues[i + 1] - u;
677 t = (realEigenvalues[i] - q) * s + 2.0 * c * h;
678 u = s * t;
679 realEigenvalues[i + 1] = q + u;
680 q = c * t - h;
681 for (int ia = 0; ia < n; ia++) {
682 p = z[ia][i + 1];
683 z[ia][i + 1] = s * z[ia][i] + c * p;
684 z[ia][i] = c * z[ia][i] - s * p;
685 }
686 }
687 if (t == 0.0 && i >= j) {
688 continue;
689 }
690 realEigenvalues[j] -= u;
691 e[j] = q;
692 e[m] = 0.0;
693 }
694 } while (m != j);
695 }
696
697
698 for (int i = 0; i < n; i++) {
699 int k = i;
700 double p = realEigenvalues[i];
701 for (int j = i + 1; j < n; j++) {
702 if (realEigenvalues[j] > p) {
703 k = j;
704 p = realEigenvalues[j];
705 }
706 }
707 if (k != i) {
708 realEigenvalues[k] = realEigenvalues[i];
709 realEigenvalues[i] = p;
710 for (int j = 0; j < n; j++) {
711 p = z[j][i];
712 z[j][i] = z[j][k];
713 z[j][k] = p;
714 }
715 }
716 }
717
718
719 maxAbsoluteValue = 0;
720 for (int i = 0; i < n; i++) {
721 if (FastMath.abs(realEigenvalues[i]) > maxAbsoluteValue) {
722 maxAbsoluteValue=FastMath.abs(realEigenvalues[i]);
723 }
724 }
725
726 if (maxAbsoluteValue != 0.0) {
727 for (int i=0; i < n; i++) {
728 if (FastMath.abs(realEigenvalues[i]) < Precision.EPSILON * maxAbsoluteValue) {
729 realEigenvalues[i] = 0;
730 }
731 }
732 }
733 eigenvectors = new ArrayRealVector[n];
734 final double[] tmp = new double[n];
735 for (int i = 0; i < n; i++) {
736 for (int j = 0; j < n; j++) {
737 tmp[j] = z[j][i];
738 }
739 eigenvectors[i] = new ArrayRealVector(tmp);
740 }
741 }
742
743
744
745
746
747
748
749 private SchurTransformer transformToSchur(final RealMatrix matrix) {
750 final SchurTransformer schurTransform = new SchurTransformer(matrix);
751 final double[][] matT = schurTransform.getT().getData();
752
753 realEigenvalues = new double[matT.length];
754 imagEigenvalues = new double[matT.length];
755
756 for (int i = 0; i < realEigenvalues.length; i++) {
757 if (i == (realEigenvalues.length - 1) ||
758 Precision.equals(matT[i + 1][i], 0.0, EPSILON)) {
759 realEigenvalues[i] = matT[i][i];
760 } else {
761 final double x = matT[i + 1][i + 1];
762 final double p = 0.5 * (matT[i][i] - x);
763 final double z = FastMath.sqrt(FastMath.abs(p * p + matT[i + 1][i] * matT[i][i + 1]));
764 realEigenvalues[i] = x + p;
765 imagEigenvalues[i] = z;
766 realEigenvalues[i + 1] = x + p;
767 imagEigenvalues[i + 1] = -z;
768 i++;
769 }
770 }
771 return schurTransform;
772 }
773
774
775
776
777
778
779
780
781
782
783 private Complex cdiv(final double xr, final double xi,
784 final double yr, final double yi) {
785 return new Complex(xr, xi).divide(new Complex(yr, yi));
786 }
787
788
789
790
791
792
793
794 private void findEigenVectorsFromSchur(final SchurTransformer schur)
795 throws MathArithmeticException {
796 final double[][] matrixT = schur.getT().getData();
797 final double[][] matrixP = schur.getP().getData();
798
799 final int n = matrixT.length;
800
801
802 double norm = 0.0;
803 for (int i = 0; i < n; i++) {
804 for (int j = FastMath.max(i - 1, 0); j < n; j++) {
805 norm += FastMath.abs(matrixT[i][j]);
806 }
807 }
808
809
810 if (Precision.equals(norm, 0.0, EPSILON)) {
811 throw new MathArithmeticException(LocalizedFormats.ZERO_NORM);
812 }
813
814
815
816 double r = 0.0;
817 double s = 0.0;
818 double z = 0.0;
819
820 for (int idx = n - 1; idx >= 0; idx--) {
821 double p = realEigenvalues[idx];
822 double q = imagEigenvalues[idx];
823
824 if (Precision.equals(q, 0.0)) {
825
826 int l = idx;
827 matrixT[idx][idx] = 1.0;
828 for (int i = idx - 1; i >= 0; i--) {
829 double w = matrixT[i][i] - p;
830 r = 0.0;
831 for (int j = l; j <= idx; j++) {
832 r += matrixT[i][j] * matrixT[j][idx];
833 }
834 if (Precision.compareTo(imagEigenvalues[i], 0.0, EPSILON) < 0) {
835 z = w;
836 s = r;
837 } else {
838 l = i;
839 if (Precision.equals(imagEigenvalues[i], 0.0)) {
840 if (w != 0.0) {
841 matrixT[i][idx] = -r / w;
842 } else {
843 matrixT[i][idx] = -r / (Precision.EPSILON * norm);
844 }
845 } else {
846
847 double x = matrixT[i][i + 1];
848 double y = matrixT[i + 1][i];
849 q = (realEigenvalues[i] - p) * (realEigenvalues[i] - p) +
850 imagEigenvalues[i] * imagEigenvalues[i];
851 double t = (x * s - z * r) / q;
852 matrixT[i][idx] = t;
853 if (FastMath.abs(x) > FastMath.abs(z)) {
854 matrixT[i + 1][idx] = (-r - w * t) / x;
855 } else {
856 matrixT[i + 1][idx] = (-s - y * t) / z;
857 }
858 }
859
860
861 double t = FastMath.abs(matrixT[i][idx]);
862 if ((Precision.EPSILON * t) * t > 1) {
863 for (int j = i; j <= idx; j++) {
864 matrixT[j][idx] /= t;
865 }
866 }
867 }
868 }
869 } else if (q < 0.0) {
870
871 int l = idx - 1;
872
873
874 if (FastMath.abs(matrixT[idx][idx - 1]) > FastMath.abs(matrixT[idx - 1][idx])) {
875 matrixT[idx - 1][idx - 1] = q / matrixT[idx][idx - 1];
876 matrixT[idx - 1][idx] = -(matrixT[idx][idx] - p) / matrixT[idx][idx - 1];
877 } else {
878 final Complex result = cdiv(0.0, -matrixT[idx - 1][idx],
879 matrixT[idx - 1][idx - 1] - p, q);
880 matrixT[idx - 1][idx - 1] = result.getReal();
881 matrixT[idx - 1][idx] = result.getImaginary();
882 }
883
884 matrixT[idx][idx - 1] = 0.0;
885 matrixT[idx][idx] = 1.0;
886
887 for (int i = idx - 2; i >= 0; i--) {
888 double ra = 0.0;
889 double sa = 0.0;
890 for (int j = l; j <= idx; j++) {
891 ra += matrixT[i][j] * matrixT[j][idx - 1];
892 sa += matrixT[i][j] * matrixT[j][idx];
893 }
894 double w = matrixT[i][i] - p;
895
896 if (Precision.compareTo(imagEigenvalues[i], 0.0, EPSILON) < 0) {
897 z = w;
898 r = ra;
899 s = sa;
900 } else {
901 l = i;
902 if (Precision.equals(imagEigenvalues[i], 0.0)) {
903 final Complex c = cdiv(-ra, -sa, w, q);
904 matrixT[i][idx - 1] = c.getReal();
905 matrixT[i][idx] = c.getImaginary();
906 } else {
907
908 double x = matrixT[i][i + 1];
909 double y = matrixT[i + 1][i];
910 double vr = (realEigenvalues[i] - p) * (realEigenvalues[i] - p) +
911 imagEigenvalues[i] * imagEigenvalues[i] - q * q;
912 final double vi = (realEigenvalues[i] - p) * 2.0 * q;
913 if (Precision.equals(vr, 0.0) && Precision.equals(vi, 0.0)) {
914 vr = Precision.EPSILON * norm *
915 (FastMath.abs(w) + FastMath.abs(q) + FastMath.abs(x) +
916 FastMath.abs(y) + FastMath.abs(z));
917 }
918 final Complex c = cdiv(x * r - z * ra + q * sa,
919 x * s - z * sa - q * ra, vr, vi);
920 matrixT[i][idx - 1] = c.getReal();
921 matrixT[i][idx] = c.getImaginary();
922
923 if (FastMath.abs(x) > (FastMath.abs(z) + FastMath.abs(q))) {
924 matrixT[i + 1][idx - 1] = (-ra - w * matrixT[i][idx - 1] +
925 q * matrixT[i][idx]) / x;
926 matrixT[i + 1][idx] = (-sa - w * matrixT[i][idx] -
927 q * matrixT[i][idx - 1]) / x;
928 } else {
929 final Complex c2 = cdiv(-r - y * matrixT[i][idx - 1],
930 -s - y * matrixT[i][idx], z, q);
931 matrixT[i + 1][idx - 1] = c2.getReal();
932 matrixT[i + 1][idx] = c2.getImaginary();
933 }
934 }
935
936
937 double t = FastMath.max(FastMath.abs(matrixT[i][idx - 1]),
938 FastMath.abs(matrixT[i][idx]));
939 if ((Precision.EPSILON * t) * t > 1) {
940 for (int j = i; j <= idx; j++) {
941 matrixT[j][idx - 1] /= t;
942 matrixT[j][idx] /= t;
943 }
944 }
945 }
946 }
947 }
948 }
949
950
951 for (int j = n - 1; j >= 0; j--) {
952 for (int i = 0; i <= n - 1; i++) {
953 z = 0.0;
954 for (int k = 0; k <= FastMath.min(j, n - 1); k++) {
955 z += matrixP[i][k] * matrixT[k][j];
956 }
957 matrixP[i][j] = z;
958 }
959 }
960
961 eigenvectors = new ArrayRealVector[n];
962 final double[] tmp = new double[n];
963 for (int i = 0; i < n; i++) {
964 for (int j = 0; j < n; j++) {
965 tmp[j] = matrixP[j][i];
966 }
967 eigenvectors[i] = new ArrayRealVector(tmp);
968 }
969 }
970 }