544
451
* Transform matrix to tridiagonal.
545
* @param matrix matrix to transform
453
* matrix to transform
547
455
private void transformToTridiagonal(final RealMatrix matrix) {
549
457
// transform the matrix to tridiagonal
550
458
transformer = new TriDiagonalTransformer(matrix);
551
main = transformer.getMainDiagonalRef();
459
main = transformer.getMainDiagonalRef();
552
460
secondary = transformer.getSecondaryDiagonalRef();
554
// pre-compute some elements
555
squaredSecondary = new double[secondary.length];
556
for (int i = 0; i < squaredSecondary.length; ++i) {
557
final double s = secondary[i];
558
squaredSecondary[i] = s * s;
564
* Compute the Gershgorin circles for all rows.
566
private void computeGershgorinCircles() {
568
final int m = main.length;
569
final int lowerStart = 4 * m;
570
final int upperStart = 5 * m;
571
lowerSpectra = Double.POSITIVE_INFINITY;
572
upperSpectra = Double.NEGATIVE_INFINITY;
576
for (int i = 0; i < m - 1; ++i) {
578
final double dCurrent = main[i];
579
final double ePrevious = eCurrent;
580
eCurrent = Math.abs(secondary[i]);
581
eMax = Math.max(eMax, eCurrent);
582
final double radius = ePrevious + eCurrent;
584
final double lower = dCurrent - radius;
585
work[lowerStart + i] = lower;
586
lowerSpectra = Math.min(lowerSpectra, lower);
588
final double upper = dCurrent + radius;
589
work[upperStart + i] = upper;
590
upperSpectra = Math.max(upperSpectra, upper);
594
final double dCurrent = main[m - 1];
595
work[lowerStart + m - 1] = dCurrent - eCurrent;
596
work[upperStart + m - 1] = dCurrent + eCurrent;
597
minPivot = MathUtils.SAFE_MIN * Math.max(1.0, eMax * eMax);
602
* Find the realEigenvalues.
603
* @exception InvalidMatrixException if a block cannot be diagonalized
605
private void findEigenvalues()
606
throws InvalidMatrixException {
608
// compute splitting points
609
List<Integer> splitIndices = computeSplits();
611
// find realEigenvalues in each block
612
realEigenvalues = new double[main.length];
613
imagEigenvalues = new double[main.length];
615
for (final int end : splitIndices) {
616
final int n = end - begin;
620
// apply dedicated method for dimension 1
621
process1RowBlock(begin);
625
// apply dedicated method for dimension 2
626
process2RowsBlock(begin);
630
// apply dedicated method for dimension 3
631
process3RowsBlock(begin);
636
// choose an initial shift for LDL<sup>T</sup> decomposition
637
final double[] range = eigenvaluesRange(begin, n);
638
final double oneFourth = 0.25 * (3 * range[0] + range[1]);
639
final int oneFourthCount = countEigenValues(oneFourth, begin, n);
640
final double threeFourth = 0.25 * (range[0] + 3 * range[1]);
641
final int threeFourthCount = countEigenValues(threeFourth, begin, n);
642
final boolean chooseLeft = (oneFourthCount - 1) >= (n - threeFourthCount);
643
final double lambda = chooseLeft ? range[0] : range[1];
645
tau = (range[1] - range[0]) * MathUtils.EPSILON * n + 2 * minPivot;
647
// decompose TλI as LDL<sup>T</sup>
648
ldlTDecomposition(lambda, begin, n);
650
// apply general dqd/dqds method
651
processGeneralBlock(n);
653
// extract realEigenvalues
655
for (int i = 0; i < n; ++i) {
656
realEigenvalues[begin + i] = lambda + work[4 * i];
659
for (int i = 0; i < n; ++i) {
660
realEigenvalues[begin + i] = lambda - work[4 * i];
668
// sort the realEigenvalues in decreasing order
669
Arrays.sort(realEigenvalues);
670
for (int i = 0, j = realEigenvalues.length - 1; i < j; ++i, --j) {
671
final double tmp = realEigenvalues[i];
672
realEigenvalues[i] = realEigenvalues[j];
673
realEigenvalues[j] = tmp;
679
* Compute splitting points.
680
* @return list of indices after matrix can be split
682
private List<Integer> computeSplits() {
684
final List<Integer> list = new ArrayList<Integer>();
686
// splitting preserving relative accuracy
687
double absDCurrent = Math.abs(main[0]);
688
for (int i = 0; i < secondary.length; ++i) {
689
final double absDPrevious = absDCurrent;
690
absDCurrent = Math.abs(main[i + 1]);
691
final double max = splitTolerance * Math.sqrt(absDPrevious * absDCurrent);
692
if (Math.abs(secondary[i]) <= max) {
695
squaredSecondary[i] = 0;
699
list.add(secondary.length + 1);
705
* Find eigenvalue in a block with 1 row.
706
* <p>In low dimensions, we simply solve the characteristic polynomial.</p>
707
* @param index index of the first row of the block
709
private void process1RowBlock(final int index) {
710
realEigenvalues[index] = main[index];
714
* Find realEigenvalues in a block with 2 rows.
715
* <p>In low dimensions, we simply solve the characteristic polynomial.</p>
716
* @param index index of the first row of the block
717
* @exception InvalidMatrixException if characteristic polynomial cannot be solved
719
private void process2RowsBlock(final int index)
720
throws InvalidMatrixException {
722
// the characteristic polynomial is
723
// X^2 - (q0 + q1) X + q0 q1 - e1^2
724
final double q0 = main[index];
725
final double q1 = main[index + 1];
726
final double e12 = squaredSecondary[index];
728
final double s = q0 + q1;
729
final double p = q0 * q1 - e12;
730
final double delta = s * s - 4 * p;
732
throw new InvalidMatrixException("cannot solve degree {0} equation", 2);
735
final double largestRoot = 0.5 * (s + Math.sqrt(delta));
736
realEigenvalues[index] = largestRoot;
737
realEigenvalues[index + 1] = p / largestRoot;
742
* Find realEigenvalues in a block with 3 rows.
743
* <p>In low dimensions, we simply solve the characteristic polynomial.</p>
744
* @param index index of the first row of the block
745
* @exception InvalidMatrixException if diagonal elements are not positive
747
private void process3RowsBlock(final int index)
748
throws InvalidMatrixException {
750
// the characteristic polynomial is
751
// X^3 - (q0 + q1 + q2) X^2 + (q0 q1 + q0 q2 + q1 q2 - e1^2 - e2^2) X + q0 e2^2 + q2 e1^2 - q0 q1 q2
752
final double q0 = main[index];
753
final double q1 = main[index + 1];
754
final double q2 = main[index + 2];
755
final double e12 = squaredSecondary[index];
756
final double q1q2Me22 = q1 * q2 - squaredSecondary[index + 1];
758
// compute coefficients of the cubic equation as: x^3 + b x^2 + c x + d = 0
759
final double b = -(q0 + q1 + q2);
760
final double c = q0 * q1 + q0 * q2 + q1q2Me22 - e12;
761
final double d = q2 * e12 - q0 * q1q2Me22;
763
// solve cubic equation
764
final double b2 = b * b;
765
final double q = (3 * c - b2) / 9;
766
final double r = ((9 * c - 2 * b2) * b - 27 * d) / 54;
767
final double delta = q * q * q + r * r;
769
// in fact, there are solutions to the equation, but in the context
770
// of symmetric realEigenvalues problem, there should be three distinct
771
// real roots, so we throw an error if this condition is not met
772
throw new InvalidMatrixException("cannot solve degree {0} equation", 3);
774
final double sqrtMq = Math.sqrt(-q);
775
final double theta = Math.acos(r / (-q * sqrtMq));
776
final double alpha = 2 * sqrtMq;
777
final double beta = b / 3;
779
double z0 = alpha * Math.cos(theta / 3) - beta;
780
double z1 = alpha * Math.cos((theta + 2 * Math.PI) / 3) - beta;
781
double z2 = alpha * Math.cos((theta + 4 * Math.PI) / 3) - beta;
797
realEigenvalues[index] = z0;
798
realEigenvalues[index + 1] = z1;
799
realEigenvalues[index + 2] = z2;
804
* Find realEigenvalues using dqd/dqds algorithms.
805
* <p>This implementation is based on Beresford N. Parlett
806
* and Osni A. Marques paper <a
807
* href="http://www.netlib.org/lapack/lawnspdf/lawn155.pdf">An
808
* Implementation of the dqds Algorithm (Positive Case)</a> and on the
809
* corresponding LAPACK routine DLASQ2.</p>
810
* @param n number of rows of the block
811
* @exception InvalidMatrixException if block cannot be diagonalized
812
* after 30 * n iterations
814
private void processGeneralBlock(final int n)
815
throws InvalidMatrixException {
817
// check decomposed matrix data range
818
double sumOffDiag = 0;
819
for (int i = 0; i < n - 1; ++i) {
820
final int fourI = 4 * i;
821
final double ei = work[fourI + 2];
825
if (sumOffDiag == 0) {
826
// matrix is already diagonal
830
// initial checks for splits (see Parlett & Marques section 3.3)
831
flipIfWarranted(n, 2);
833
// two iterations with Li's test for initial splits
836
// initialize parameters used by goodStep
845
// process split segments
850
// retrieve shift that was temporarily stored as a negative off-diagonal element
851
sigma = (n0 == n) ? 0 : -work[4 * n0 - 2];
854
// find start of a new split segment to process
855
double eMin = (i0 == n0) ? 0 : work[4 * n0 - 6];
857
double qMax = work[4 * n0 - 4];
860
for (int i = 4 * (n0 - 2); i >= 0; i -= 4) {
861
if (work[i + 2] <= 0) {
865
if (qMin >= 4 * eMax) {
866
qMin = Math.min(qMin, work[i + 4]);
867
eMax = Math.max(eMax, work[i + 2]);
869
qMax = Math.max(qMax, work[i] + work[i + 2]);
870
eMin = Math.min(eMin, work[i + 2]);
872
work[4 * n0 - 2] = eMin;
874
// lower bound of Gershgorin disk
875
dMin = -Math.max(0, qMin - 2 * Math.sqrt(qMin * eMax));
878
int maxIter = 30 * (n0 - i0);
879
for (int k = 0; i0 < n0; ++k) {
881
throw new InvalidMatrixException(new MaxIterationsExceededException(maxIter));
885
n0 = goodStep(i0, n0);
886
pingPong = 1 - pingPong;
888
// check for new splits after "ping" steps
889
// when the last elements of qd array are very small
890
if ((pingPong == 0) && (n0 - i0 > 3) &&
891
(work[4 * n0 - 1] <= TOLERANCE_2 * qMax) &&
892
(work[4 * n0 - 2] <= TOLERANCE_2 * sigma)) {
895
eMin = work[4 * i0 + 2];
896
double previousEMin = work[4 * i0 + 3];
897
for (int i = 4 * i0; i < 4 * n0 - 11; i += 4) {
898
if ((work[i + 3] <= TOLERANCE_2 * work[i]) &&
899
(work[i + 2] <= TOLERANCE_2 * sigma)) {
901
work[i + 2] = -sigma;
905
previousEMin = work[i + 7];
465
* Find eigenvalues and eigenvectors (Dubrulle et al., 1971)
466
* @param householderMatrix Householder matrix of the transformation
467
* to tri-diagonal form.
469
private void findEigenVectors(double[][] householderMatrix) {
471
double[][]z = householderMatrix.clone();
472
final int n = main.length;
473
realEigenvalues = new double[n];
474
imagEigenvalues = new double[n];
475
double[] e = new double[n];
476
for (int i = 0; i < n - 1; i++) {
477
realEigenvalues[i] = main[i];
480
realEigenvalues[n - 1] = main[n - 1];
483
// Determine the largest main and secondary value in absolute term.
484
double maxAbsoluteValue=0.0;
485
for (int i = 0; i < n; i++) {
486
if (Math.abs(realEigenvalues[i])>maxAbsoluteValue) {
487
maxAbsoluteValue=Math.abs(realEigenvalues[i]);
489
if (Math.abs(e[i])>maxAbsoluteValue) {
490
maxAbsoluteValue=Math.abs(e[i]);
493
// Make null any main and secondary value too small to be significant
494
if (maxAbsoluteValue!=0.0) {
495
for (int i=0; i < n; i++) {
496
if (Math.abs(realEigenvalues[i])<=MathUtils.EPSILON*maxAbsoluteValue) {
497
realEigenvalues[i]=0.0;
499
if (Math.abs(e[i])<=MathUtils.EPSILON*maxAbsoluteValue) {
505
for (int j = 0; j < n; j++) {
509
for (m = j; m < n - 1; m++) {
510
double delta = Math.abs(realEigenvalues[m]) + Math.abs(realEigenvalues[m + 1]);
511
if (Math.abs(e[m]) + delta == delta) {
517
throw new InvalidMatrixException(
518
new MaxIterationsExceededException(maxIter));
520
double q = (realEigenvalues[j + 1] - realEigenvalues[j]) / (2 * e[j]);
521
double t = Math.sqrt(1 + q * q);
523
q = realEigenvalues[m] - realEigenvalues[j] + e[j] / (q - t);
525
q = realEigenvalues[m] - realEigenvalues[j] + e[j] / (q + t);
531
for (i = m - 1; i >= j; i--) {
534
if (Math.abs(p) >= Math.abs(q)) {
536
t = Math.sqrt(c * c + 1.0);
907
qMax = Math.max(qMax, work[i + 4]);
908
eMin = Math.min(eMin, work[i + 2]);
909
previousEMin = Math.min(previousEMin, work[i + 3]);
912
work[4 * n0 - 2] = eMin;
913
work[4 * n0 - 1] = previousEMin;
923
* Perform two iterations with Li's tests for initial splits.
924
* @param n number of rows of the matrix to process
926
private void initialSplits(final int n) {
929
for (int k = 0; k < 2; ++k) {
931
// apply Li's reverse test
932
double d = work[4 * (n - 1) + pingPong];
933
for (int i = 4 * (n - 2) + pingPong; i >= 0; i -= 4) {
934
if (work[i + 2] <= TOLERANCE_2 * d) {
938
d *= work[i] / (d + work[i + 2]);
942
// apply dqd plus Li's forward test.
944
for (int i = 2 + pingPong; i < 4 * n - 2; i += 4) {
945
final int j = i - 2 * pingPong - 1;
946
work[j] = d + work[i];
947
if (work[i] <= TOLERANCE_2 * d) {
952
} else if ((MathUtils.SAFE_MIN * work[i + 2] < work[j]) &&
953
(MathUtils.SAFE_MIN * work[j] < work[i + 2])) {
954
final double tmp = work[i + 2] / work[j];
955
work[j + 2] = work[i] * tmp;
958
work[j + 2] = work[i + 2] * (work[i] / work[j]);
959
d *= work[i + 2] / work[j];
962
work[4 * n - 3 - pingPong] = d;
965
pingPong = 1 - pingPong;
972
* Perform one "good" dqd/dqds step.
973
* <p>This implementation is based on Beresford N. Parlett
974
* and Osni A. Marques paper <a
975
* href="http://www.netlib.org/lapack/lawnspdf/lawn155.pdf">An
976
* Implementation of the dqds Algorithm (Positive Case)</a> and on the
977
* corresponding LAPACK routine DLAZQ3.</p>
978
* @param start start index
979
* @param end end index
980
* @return new end (maybe deflated)
982
private int goodStep(final int start, final int end) {
986
// step 1: accepting realEigenvalues
987
int deflatedEnd = end;
988
for (boolean deflating = true; deflating;) {
990
if (start >= deflatedEnd) {
991
// the array has been completely deflated
995
final int k = 4 * deflatedEnd + pingPong - 1;
997
if ((start == deflatedEnd - 1) ||
998
((start != deflatedEnd - 2) &&
999
((work[k - 5] <= TOLERANCE_2 * (sigma + work[k - 3])) ||
1000
(work[k - 2 * pingPong - 4] <= TOLERANCE_2 * work[k - 7])))) {
1002
// one eigenvalue found, deflate array
1003
work[4 * deflatedEnd - 4] = sigma + work[4 * deflatedEnd - 4 + pingPong];
1006
} else if ((start == deflatedEnd - 2) ||
1007
(work[k - 9] <= TOLERANCE_2 * sigma) ||
1008
(work[k - 2 * pingPong - 8] <= TOLERANCE_2 * work[k - 11])) {
1010
// two realEigenvalues found, deflate array
1011
if (work[k - 3] > work[k - 7]) {
1012
final double tmp = work[k - 3];
1013
work[k - 3] = work[k - 7];
1017
if (work[k - 5] > TOLERANCE_2 * work[k - 3]) {
1018
double t = 0.5 * ((work[k - 7] - work[k - 3]) + work[k - 5]);
1019
double s = work[k - 3] * (work[k - 5] / t);
1021
s = work[k - 3] * work[k - 5] / (t * (1 + Math.sqrt(1 + s / t)));
1023
s = work[k - 3] * work[k - 5] / (t + Math.sqrt(t * (t + s)));
1025
t = work[k - 7] + (s + work[k - 5]);
1026
work[k - 3] *= work[k - 7] / t;
1029
work[4 * deflatedEnd - 8] = sigma + work[k - 7];
1030
work[4 * deflatedEnd - 4] = sigma + work[k - 3];
1034
// no more realEigenvalues found, we need to iterate
1041
final int l = 4 * deflatedEnd + pingPong - 1;
1043
// step 2: flip array if needed
1044
if ((dMin <= 0) || (deflatedEnd < end)) {
1045
if (flipIfWarranted(deflatedEnd, 1)) {
1046
dMin2 = Math.min(dMin2, work[l - 1]);
1048
Math.min(work[l - 1],
1049
Math.min(work[3 + pingPong], work[7 + pingPong]));
1050
work[l - 2 * pingPong] =
1051
Math.min(work[l - 2 * pingPong],
1052
Math.min(work[6 + pingPong], work[6 + pingPong]));
1053
qMax = Math.max(qMax, Math.max(work[3 + pingPong], work[7 + pingPong]));
1059
(MathUtils.SAFE_MIN * qMax < Math.min(work[l - 1],
1060
Math.min(work[l - 9],
1061
dMin2 + work[l - 2 * pingPong])))) {
1062
// step 3: choose a shift
1063
computeShiftIncrement(start, deflatedEnd, end - deflatedEnd);
1066
for (boolean loop = true; loop;) {
1068
// perform one dqds step with the chosen shift
1069
dqds(start, deflatedEnd);
1071
// check result of the dqds step
1072
if ((dMin >= 0) && (dMin1 > 0)) {
1073
// the shift was good
1076
} else if ((dMin < 0.0) &&
1078
(work[4 * deflatedEnd - 5 - pingPong] < TOLERANCE * (sigma + dN1)) &&
1079
(Math.abs(dN) < TOLERANCE * sigma)) {
1080
// convergence hidden by negative DN.
1081
work[4 * deflatedEnd - 3 - pingPong] = 0.0;
1085
} else if (dMin < 0.0) {
1086
// tau too big. Select new tau and try again.
1088
// failed twice. Play it safe.
1090
} else if (dMin1 > 0.0) {
1091
// late failure. Gives excellent shift.
1092
tau = (tau + dMin) * (1.0 - 2.0 * MathUtils.EPSILON);
1095
// early failure. Divide by 4.
1099
} else if (Double.isNaN(dMin)) {
1102
// possible underflow. Play it safe.
1109
// perform a dqd step (i.e. no shift)
1110
dqd(start, deflatedEnd);
1117
* Flip qd array if warranted.
1118
* @param n number of rows in the block
1119
* @param step within the array (1 for flipping all elements, 2 for flipping
1120
* only every other element)
1121
* @return true if qd array was flipped
1123
private boolean flipIfWarranted(final int n, final int step) {
1124
if (1.5 * work[pingPong] < work[4 * (n - 1) + pingPong]) {
1126
for (int i = 0, j = 4 * n - 1; i < j; i += 4, j -= 4) {
1127
for (int k = 0; k < 4; k += step) {
1128
final double tmp = work[i + k];
1129
work[i + k] = work[j - k];
1139
* Compute an interval containing all realEigenvalues of a block.
1140
* @param index index of the first row of the block
1141
* @param n number of rows of the block
1142
* @return an interval containing the realEigenvalues
1144
private double[] eigenvaluesRange(final int index, final int n) {
1146
// find the bounds of the spectra of the local block
1147
final int lowerStart = 4 * main.length;
1148
final int upperStart = 5 * main.length;
1149
double lower = Double.POSITIVE_INFINITY;
1150
double upper = Double.NEGATIVE_INFINITY;
1151
for (int i = 0; i < n; ++i) {
1152
lower = Math.min(lower, work[lowerStart + index +i]);
1153
upper = Math.max(upper, work[upperStart + index +i]);
1157
final double tNorm = Math.max(Math.abs(lower), Math.abs(upper));
1158
final double relativeTolerance = Math.sqrt(MathUtils.EPSILON);
1159
final double absoluteTolerance = 4 * minPivot;
1161
2 + (int) ((Math.log(tNorm + minPivot) - Math.log(minPivot)) / Math.log(2.0));
1162
final double margin = 2 * (tNorm * MathUtils.EPSILON * n + 2 * minPivot);
1164
// search lower eigenvalue
1165
double left = lower - margin;
1166
double right = upper + margin;
1167
for (int i = 0; i < maxIter; ++i) {
1169
final double range = right - left;
1170
if ((range < absoluteTolerance) ||
1171
(range < relativeTolerance * Math.max(Math.abs(left), Math.abs(right)))) {
1172
// search has converged
1176
final double middle = 0.5 * (left + right);
1177
if (countEigenValues(middle, index, n) >= 1) {
1184
lower = Math.max(lower, left - 100 * MathUtils.EPSILON * Math.abs(left));
1186
// search upper eigenvalue
1187
left = lower - margin;
1188
right = upper + margin;
1189
for (int i = 0; i < maxIter; ++i) {
1191
final double range = right - left;
1192
if ((range < absoluteTolerance) ||
1193
(range < relativeTolerance * Math.max(Math.abs(left), Math.abs(right)))) {
1194
// search has converged
1198
final double middle = 0.5 * (left + right);
1199
if (countEigenValues(middle, index, n) >= n) {
1206
upper = Math.min(upper, right + 100 * MathUtils.EPSILON * Math.abs(right));
1208
return new double[] { lower, upper };
1213
* Count the number of realEigenvalues below a point.
1214
* @param t value below which we must count the number of realEigenvalues
1215
* @param index index of the first row of the block
1216
* @param n number of rows of the block
1217
* @return number of realEigenvalues smaller than t
1219
private int countEigenValues(final double t, final int index, final int n) {
1220
double ratio = main[index] - t;
1221
int count = (ratio > 0) ? 0 : 1;
1222
for (int i = 1; i < n; ++i) {
1223
ratio = main[index + i] - squaredSecondary[index + i - 1] / ratio - t;
1232
* Decompose the shifted tridiagonal matrix T-λI as LDL<sup>T</sup>.
1233
* <p>A shifted symmetric tridiagonal matrix T can be decomposed as
1234
* LDL<sup>T</sup> where L is a lower bidiagonal matrix with unit diagonal
1235
* and D is a diagonal matrix. This method is an implementation of
1236
* algorithm 4.4.7 from Dhillon's thesis.</p>
1237
* @param lambda shift to add to the matrix before decomposing it
1238
* to ensure it is positive definite
1239
* @param index index of the first row of the block
1240
* @param n number of rows of the block
1242
private void ldlTDecomposition(final double lambda, final int index, final int n) {
1243
double di = main[index] - lambda;
1244
work[0] = Math.abs(di);
1245
for (int i = 1; i < n; ++i) {
1246
final int fourI = 4 * i;
1247
final double eiM1 = secondary[index + i - 1];
1248
final double ratio = eiM1 / di;
1249
work[fourI - 2] = ratio * ratio * Math.abs(di);
1250
di = (main[index + i] - lambda) - eiM1 * ratio;
1251
work[fourI] = Math.abs(di);
1256
* Perform a dqds step, using current shift increment.
1257
* <p>This implementation is a translation of the LAPACK routine DLASQ5.</p>
1258
* @param start start index
1259
* @param end end index
1261
private void dqds(final int start, final int end) {
1263
eMin = work[4 * start + pingPong + 4];
1264
double d = work[4 * start + pingPong] - tau;
1266
dMin1 = -work[4 * start + pingPong];
1268
if (pingPong == 0) {
1269
for (int j4 = 4 * start + 3; j4 <= 4 * (end - 3); j4 += 4) {
1270
work[j4 - 2] = d + work[j4 - 1];
1271
final double tmp = work[j4 + 1] / work[j4 - 2];
1273
dMin = Math.min(dMin, d);
1274
work[j4] = work[j4 - 1] * tmp;
1275
eMin = Math.min(work[j4], eMin);
1278
for (int j4 = 4 * start + 3; j4 <= 4 * (end - 3); j4 += 4) {
1279
work[j4 - 3] = d + work[j4];
1280
final double tmp = work[j4 + 2] / work[j4 - 3];
1282
dMin = Math.min(dMin, d);
1283
work[j4 - 1] = work[j4] * tmp;
1284
eMin = Math.min(work[j4 - 1], eMin);
1288
// unroll last two steps.
1291
int j4 = 4 * (end - 2) - pingPong - 1;
1292
int j4p2 = j4 + 2 * pingPong - 1;
1293
work[j4 - 2] = dN2 + work[j4p2];
1294
work[j4] = work[j4p2 + 2] * (work[j4p2] / work[j4 - 2]);
1295
dN1 = work[j4p2 + 2] * (dN2 / work[j4 - 2]) - tau;
1296
dMin = Math.min(dMin, dN1);
1300
j4p2 = j4 + 2 * pingPong - 1;
1301
work[j4 - 2] = dN1 + work[j4p2];
1302
work[j4] = work[j4p2 + 2] * (work[j4p2] / work[j4 - 2]);
1303
dN = work[j4p2 + 2] * (dN1 / work[j4 - 2]) - tau;
1304
dMin = Math.min(dMin, dN);
1307
work[4 * end - pingPong - 1] = eMin;
1313
* Perform a dqd step.
1314
* <p>This implementation is a translation of the LAPACK routine DLASQ6.</p>
1315
* @param start start index
1316
* @param end end index
1318
private void dqd(final int start, final int end) {
1320
eMin = work[4 * start + pingPong + 4];
1321
double d = work[4 * start + pingPong];
1324
if (pingPong == 0) {
1325
for (int j4 = 4 * start + 3; j4 < 4 * (end - 3); j4 += 4) {
1326
work[j4 - 2] = d + work[j4 - 1];
1327
if (work[j4 - 2] == 0.0) {
1332
} else if ((MathUtils.SAFE_MIN * work[j4 + 1] < work[j4 - 2]) &&
1333
(MathUtils.SAFE_MIN * work[j4 - 2] < work[j4 + 1])) {
1334
final double tmp = work[j4 + 1] / work[j4 - 2];
1335
work[j4] = work[j4 - 1] * tmp;
1338
work[j4] = work[j4 + 1] * (work[j4 - 1] / work[j4 - 2]);
1339
d *= work[j4 + 1] / work[j4 - 2];
1341
dMin = Math.min(dMin, d);
1342
eMin = Math.min(eMin, work[j4]);
1345
for (int j4 = 4 * start + 3; j4 < 4 * (end - 3); j4 += 4) {
1346
work[j4 - 3] = d + work[j4];
1347
if (work[j4 - 3] == 0.0) {
1352
} else if ((MathUtils.SAFE_MIN * work[j4 + 2] < work[j4 - 3]) &&
1353
(MathUtils.SAFE_MIN * work[j4 - 3] < work[j4 + 2])) {
1354
final double tmp = work[j4 + 2] / work[j4 - 3];
1355
work[j4 - 1] = work[j4] * tmp;
1358
work[j4 - 1] = work[j4 + 2] * (work[j4] / work[j4 - 3]);
1359
d *= work[j4 + 2] / work[j4 - 3];
1361
dMin = Math.min(dMin, d);
1362
eMin = Math.min(eMin, work[j4 - 1]);
1366
// Unroll last two steps
1369
int j4 = 4 * (end - 2) - pingPong - 1;
1370
int j4p2 = j4 + 2 * pingPong - 1;
1371
work[j4 - 2] = dN2 + work[j4p2];
1372
if (work[j4 - 2] == 0.0) {
1374
dN1 = work[j4p2 + 2];
1377
} else if ((MathUtils.SAFE_MIN * work[j4p2 + 2] < work[j4 - 2]) &&
1378
(MathUtils.SAFE_MIN * work[j4 - 2] < work[j4p2 + 2])) {
1379
final double tmp = work[j4p2 + 2] / work[j4 - 2];
1380
work[j4] = work[j4p2] * tmp;
1383
work[j4] = work[j4p2 + 2] * (work[j4p2] / work[j4 - 2]);
1384
dN1 = work[j4p2 + 2] * (dN2 / work[j4 - 2]);
1386
dMin = Math.min(dMin, dN1);
1390
j4p2 = j4 + 2 * pingPong - 1;
1391
work[j4 - 2] = dN1 + work[j4p2];
1392
if (work[j4 - 2] == 0.0) {
1394
dN = work[j4p2 + 2];
1397
} else if ((MathUtils.SAFE_MIN * work[j4p2 + 2] < work[j4 - 2]) &&
1398
(MathUtils.SAFE_MIN * work[j4 - 2] < work[j4p2 + 2])) {
1399
final double tmp = work[j4p2 + 2] / work[j4 - 2];
1400
work[j4] = work[j4p2] * tmp;
1403
work[j4] = work[j4p2 + 2] * (work[j4p2] / work[j4 - 2]);
1404
dN = work[j4p2 + 2] * (dN1 / work[j4 - 2]);
1406
dMin = Math.min(dMin, dN);
1409
work[4 * end - pingPong - 1] = eMin;
1414
* Compute the shift increment as an estimate of the smallest eigenvalue.
1415
* <p>This implementation is a translation of the LAPACK routine DLAZQ4.</p>
1416
* @param start start index
1417
* @param end end index
1418
* @param deflated number of realEigenvalues just deflated
1420
private void computeShiftIncrement(final int start, final int end, final int deflated) {
1422
final double cnst1 = 0.563;
1423
final double cnst2 = 1.010;
1424
final double cnst3 = 1.05;
1426
// a negative dMin forces the shift to take that absolute value
1427
// tType records the type of shift.
1434
int nn = 4 * end + pingPong - 1;
1437
case 0 : // no realEigenvalues deflated.
1438
if (dMin == dN || dMin == dN1) {
1440
double b1 = Math.sqrt(work[nn - 3]) * Math.sqrt(work[nn - 5]);
1441
double b2 = Math.sqrt(work[nn - 7]) * Math.sqrt(work[nn - 9]);
1442
double a2 = work[nn - 7] + work[nn - 5];
1444
if (dMin == dN && dMin1 == dN1) {
1446
final double gap2 = dMin2 - a2 - dMin2 * 0.25;
1447
final double gap1 = a2 - dN - ((gap2 > 0.0 && gap2 > b2) ? (b2 / gap2) * b2 : (b1 + b2));
1448
if (gap1 > 0.0 && gap1 > b1) {
1449
tau = Math.max(dN - (b1 / gap1) * b1, 0.5 * dMin);
1456
if (a2 > (b1 + b2)) {
1457
s = Math.min(s, a2 - (b1 + b2));
1459
tau = Math.max(s, 0.333 * dMin);
1465
double s = 0.25 * dMin;
1471
if (work[nn - 5] > work[nn - 7]) {
1474
b2 = work[nn - 5] / work[nn - 7];
1477
np = nn - 2 * pingPong;
1480
if (work[np - 4] > work[np - 2]) {
1483
a2 = work[np - 4] / work[np - 2];
1484
if (work[nn - 9] > work[nn - 11]) {
1487
b2 = work[nn - 9] / work[nn - 11];
1491
// approximate contribution to norm squared from i < nn-1.
1493
for (int i4 = np; i4 >= 4 * start + 2 + pingPong; i4 -= 4) {
1498
if (work[i4] > work[i4 - 2]) {
1501
b2 = b2 * (work[i4] / work[i4 - 2]);
1503
if (100 * Math.max(b2, b1) < a2 || cnst1 < a2) {
1509
// rayleigh quotient residual bound.
1511
s = gam * (1 - Math.sqrt(a2)) / (1 + a2);
1516
} else if (dMin == dN2) {
1520
double s = 0.25 * dMin;
1522
// compute contribution to norm squared from i > nn-2.
1523
final int np = nn - 2 * pingPong;
1524
double b1 = work[np - 2];
1525
double b2 = work[np - 6];
1526
final double gam = dN2;
1527
if (work[np - 8] > b2 || work[np - 4] > b1) {
1530
double a2 = (work[np - 8] / b2) * (1 + work[np - 4] / b1);
1532
// approximate contribution to norm squared from i < nn-2.
1533
if (end - start > 2) {
1534
b2 = work[nn - 13] / work[nn - 15];
1536
for (int i4 = nn - 17; i4 >= 4 * start + 2 + pingPong; i4 -= 4) {
1541
if (work[i4] > work[i4 - 2]) {
1544
b2 = b2 * (work[i4] / work[i4 - 2]);
1546
if (100 * Math.max(b2, b1) < a2 || cnst1 < a2) {
1554
tau = gam * (1 - Math.sqrt(a2)) / (1 + a2);
1561
// case 6, no information to guide us.
1563
g += 0.333 * (1 - g);
1564
} else if (tType == -18) {
1575
case 1 : // one eigenvalue just deflated. use dMin1, dN1 for dMin and dN.
1576
if (dMin1 == dN1 && dMin2 == dN2) {
1580
double s = 0.333 * dMin1;
1581
if (work[nn - 5] > work[nn - 7]) {
1584
double b1 = work[nn - 5] / work[nn - 7];
1587
for (int i4 = 4 * end - 10 + pingPong; i4 >= 4 * start + 2 + pingPong; i4 -= 4) {
1588
final double oldB1 = b1;
1589
if (work[i4] > work[i4 - 2]) {
1592
b1 = b1 * (work[i4] / work[i4 - 2]);
1594
if (100 * Math.max(b1, oldB1) < b2) {
1599
b2 = Math.sqrt(cnst3 * b2);
1600
final double a2 = dMin1 / (1 + b2 * b2);
1601
final double gap2 = 0.5 * dMin2 - a2;
1602
if (gap2 > 0.0 && gap2 > b2 * a2) {
1603
tau = Math.max(s, a2 * (1 - cnst2 * a2 * (b2 / gap2) * b2));
1605
tau = Math.max(s, a2 * (1 - cnst2 * b2));
1619
case 2 : // two realEigenvalues deflated. use dMin2, dN2 for dMin and dN.
1622
if (dMin2 == dN2 && 2 * work[nn - 5] < work[nn - 7]) {
1624
final double s = 0.333 * dMin2;
1625
if (work[nn - 5] > work[nn - 7]) {
1628
double b1 = work[nn - 5] / work[nn - 7];
1631
for (int i4 = 4 * end - 9 + pingPong; i4 >= 4 * start + 2 + pingPong; i4 -= 4) {
1632
if (work[i4] > work[i4 - 2]) {
1635
b1 *= work[i4] / work[i4 - 2];
1637
if (100 * b1 < b2) {
1642
b2 = Math.sqrt(cnst3 * b2);
1643
final double a2 = dMin2 / (1 + b2 * b2);
1644
final double gap2 = work[nn - 7] + work[nn - 9] -
1645
Math.sqrt(work[nn - 11]) * Math.sqrt(work[nn - 9]) - a2;
1646
if (gap2 > 0.0 && gap2 > b2 * a2) {
1647
tau = Math.max(s, a2 * (1 - cnst2 * a2 * (b2 / gap2) * b2));
1649
tau = Math.max(s, a2 * (1 - cnst2 * b2));
1657
default : // case 12, more than two realEigenvalues deflated. no information.
1666
* @param tau shift to apply to sigma
1668
private void updateSigma(final double tau) {
1669
// BEWARE: do NOT attempt to simplify the following statements
1670
// the expressions below take care to accumulate the part of sigma
1671
// that does not fit within a double variable into sigmaLow
1674
final double t = sigma + sigmaLow;
1675
sigmaLow -= t - sigma;
1678
final double t = sigma + tau;
1679
sigmaLow += sigma - (t - tau);
1685
* Find eigenvectors.
1687
private void findEigenVectors() {
1689
final int m = main.length;
1690
eigenvectors = new ArrayRealVector[m];
1692
// perform an initial non-shifted LDLt decomposition
1693
final double[] d = new double[m];
1694
final double[] l = new double[m - 1];
1695
double di = main[0];
1697
for (int i = 1; i < m; ++i) {
1698
final double eiM1 = secondary[i - 1];
1699
final double ratio = eiM1 / di;
1700
di = main[i] - eiM1 * ratio;
1705
// compute eigenvectors
1706
for (int i = 0; i < m; ++i) {
1707
eigenvectors[i] = findEigenvector(realEigenvalues[i], d, l);
1713
* Find an eigenvector corresponding to an eigenvalue, using bidiagonals.
1714
* <p>This method corresponds to algorithm X from Dhillon's thesis.</p>
1716
* @param eigenvalue eigenvalue for which eigenvector is desired
1717
* @param d diagonal elements of the initial non-shifted D matrix
1718
* @param l off-diagonal elements of the initial non-shifted L matrix
1719
* @return an eigenvector
1721
private ArrayRealVector findEigenvector(final double eigenvalue,
1722
final double[] d, final double[] l) {
1724
// compute the LDLt and UDUt decompositions of the
1725
// perfectly shifted tridiagonal matrix
1726
final int m = main.length;
1727
stationaryQuotientDifferenceWithShift(d, l, eigenvalue);
1728
progressiveQuotientDifferenceWithShift(d, l, eigenvalue);
1730
// select the twist index leading to
1731
// the least diagonal element in the twisted factorization
1733
double minG = Math.abs(work[6 * r] + work[6 * r + 3] + eigenvalue);
1734
for (int i = 0, sixI = 0; i < m - 1; ++i, sixI += 6) {
1735
final double g = work[sixI] + d[i] * work[sixI + 9] / work[sixI + 10];
1736
final double absG = Math.abs(g);
1743
// solve the singular system by ignoring the equation
1744
// at twist index and propagating upwards and downwards
1745
double[] eigenvector = new double[m];
1749
for (int i = r - 1; i >= 0; --i) {
1750
z *= -work[6 * i + 2];
1755
for (int i = r + 1; i < m; ++i) {
1756
z *= -work[6 * i - 1];
1762
final double inv = 1.0 / Math.sqrt(n2);
1763
for (int i = 0; i < m; ++i) {
1764
eigenvector[i] *= inv;
1767
return (transformer == null) ?
1768
new ArrayRealVector(eigenvector, false) :
1769
new ArrayRealVector(transformer.getQ().operate(eigenvector), false);
1774
* Decompose matrix LDL<sup>T</sup> - λ I as
1775
* L<sub>+</sub>D<sub>+</sub>L<sub>+</sub><sup>T</sup>.
1776
* <p>This method corresponds to algorithm 4.4.3 (dstqds) from Dhillon's thesis.</p>
1777
* @param d diagonal elements of D,
1778
* @param l off-diagonal elements of L
1779
* @param lambda shift to apply
1781
private void stationaryQuotientDifferenceWithShift(final double[] d, final double[] l,
1782
final double lambda) {
1783
final int nM1 = d.length - 1;
1784
double si = -lambda;
1785
for (int i = 0, sixI = 0; i < nM1; ++i, sixI += 6) {
1786
final double di = d[i];
1787
final double li = l[i];
1788
final double diP1 = di + si;
1789
final double liP1 = li * di / diP1;
1791
work[sixI + 1] = diP1;
1792
work[sixI + 2] = liP1;
1793
si = li * liP1 * si - lambda;
1795
work[6 * nM1 + 1] = d[nM1] + si;
1800
* Decompose matrix LDL<sup>T</sup> - λ I as
1801
* U<sub>-</sub>D<sub>-</sub>U<sub>-</sub><sup>T</sup>.
1802
* <p>This method corresponds to algorithm 4.4.5 (dqds) from Dhillon's thesis.</p>
1803
* @param d diagonal elements of D
1804
* @param l off-diagonal elements of L
1805
* @param lambda shift to apply
1807
private void progressiveQuotientDifferenceWithShift(final double[] d, final double[] l,
1808
final double lambda) {
1809
final int nM1 = d.length - 1;
1810
double pi = d[nM1] - lambda;
1811
for (int i = nM1 - 1, sixI = 6 * i; i >= 0; --i, sixI -= 6) {
1812
final double di = d[i];
1813
final double li = l[i];
1814
final double diP1 = di * li * li + pi;
1815
final double t = di / diP1;
1816
work[sixI + 9] = pi;
1817
work[sixI + 10] = diP1;
1818
work[sixI + 5] = li * t;
1819
pi = pi * t - lambda;
542
t = Math.sqrt(s * s + 1.0);
547
if (e[i + 1] == 0.0) {
548
realEigenvalues[i + 1] -= u;
552
q = realEigenvalues[i + 1] - u;
553
t = (realEigenvalues[i] - q) * s + 2.0 * c * h;
555
realEigenvalues[i + 1] = q + u;
557
for (int ia = 0; ia < n; ia++) {
559
z[ia][i + 1] = s * z[ia][i] + c * p;
560
z[ia][i] = c * z[ia][i] - s * p;
563
if (e[i + 1] == 0.0 && i >= j)
565
realEigenvalues[j] -= u;
572
//Sort the eigen values (and vectors) in increase order
573
for (int i = 0; i < n; i++) {
575
double p = realEigenvalues[i];
576
for (int j = i + 1; j < n; j++) {
577
if (realEigenvalues[j] > p) {
579
p = realEigenvalues[j];
583
realEigenvalues[k] = realEigenvalues[i];
584
realEigenvalues[i] = p;
585
for (int j = 0; j < n; j++) {
593
// Determine the largest eigen value in absolute term.
594
maxAbsoluteValue=0.0;
595
for (int i = 0; i < n; i++) {
596
if (Math.abs(realEigenvalues[i])>maxAbsoluteValue) {
597
maxAbsoluteValue=Math.abs(realEigenvalues[i]);
600
// Make null any eigen value too small to be significant
601
if (maxAbsoluteValue!=0.0) {
602
for (int i=0; i < n; i++) {
603
if (Math.abs(realEigenvalues[i])<MathUtils.EPSILON*maxAbsoluteValue) {
604
realEigenvalues[i]=0.0;
608
eigenvectors = new ArrayRealVector[n];
609
double[] tmp = new double[n];
610
for (int i = 0; i < n; i++) {
611
for (int j = 0; j < n; j++) {
614
eigenvectors[i] = new ArrayRealVector(tmp);