1 /*
2 * Licensed to the Hipparchus project under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The Hipparchus project licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.hipparchus.linear;
18
19 import java.util.Arrays;
20 import java.util.Comparator;
21
22 import org.hipparchus.complex.Complex;
23
24 /**
25 * Given a matrix A, it computes a complex eigen decomposition A = VDV^{T}.
26 *
27 * It ensures that eigen values in the diagonal of D are in ascending order.
28 *
29 */
30 public class OrderedComplexEigenDecomposition extends ComplexEigenDecomposition {
31
32 /**
33 * Constructor for the decomposition.
34 *
35 * @param matrix real matrix.
36 */
37 public OrderedComplexEigenDecomposition(final RealMatrix matrix) {
38 this(matrix, DEFAULT_EIGENVECTORS_EQUALITY, DEFAULT_EPSILON, DEFAULT_EPSILON_AV_VD_CHECK);
39 }
40
41 /**
42 * Constructor for decomposition.
43 * <p>
44 * The {@code eigenVectorsEquality} threshold is used to ensure the L∞-normalized
45 * eigenvectors found using inverse iteration are different from each other.
46 * if \(min(|e_i-e_j|,|e_i+e_j|)\) is smaller than this threshold, the algorithm
47 * considers it has found again an already known vector, so it drops it and attempts
48 * a new inverse iteration with a different start vector. This value should be
49 * much larger than {@code epsilon} which is used for convergence
50 * </p>
51 * <p>
52 * This constructor calls {@link #OrderedComplexEigenDecomposition(RealMatrix, double,
53 * double, double, Comparator)} with a comparator using real ordering as the primary
54 * sort order and imaginary ordering as the secondary sort order..
55 * </p>
56 * @param matrix real matrix.
57 * @param eigenVectorsEquality threshold below which eigenvectors are considered equal
58 * @param epsilon Epsilon used for internal tests (e.g. is singular, eigenvalue ratio, etc.)
59 * @param epsilonAVVDCheck Epsilon criteria for final AV=VD check
60 * @since 1.9
61 */
62 public OrderedComplexEigenDecomposition(final RealMatrix matrix, final double eigenVectorsEquality,
63 final double epsilon, final double epsilonAVVDCheck) {
64 this(matrix, eigenVectorsEquality, epsilon, epsilonAVVDCheck,
65 (c1, c2) -> {
66 final int cR = Double.compare(c1.getReal(), c2.getReal());
67 if (cR == 0) {
68 return Double.compare(c1.getImaginary(), c2.getImaginary());
69 } else {
70 return cR;
71 }
72 });
73 }
74
75 /**
76 * Constructor for decomposition.
77 * <p>
78 * The {@code eigenVectorsEquality} threshold is used to ensure the L∞-normalized
79 * eigenvectors found using inverse iteration are different from each other.
80 * if \(min(|e_i-e_j|,|e_i+e_j|)\) is smaller than this threshold, the algorithm
81 * considers it has found again an already known vector, so it drops it and attempts
82 * a new inverse iteration with a different start vector. This value should be
83 * much larger than {@code epsilon} which is used for convergence
84 * </p>
85 * @param matrix real matrix.
86 * @param eigenVectorsEquality threshold below which eigenvectors are considered equal
87 * @param epsilon Epsilon used for internal tests (e.g. is singular, eigenvalue ratio, etc.)
88 * @param epsilonAVVDCheck Epsilon criteria for final AV=VD check
89 * @param eigenValuesComparator comparator for sorting eigen values
90 * @since 3.0
91 */
92 public OrderedComplexEigenDecomposition(final RealMatrix matrix, final double eigenVectorsEquality,
93 final double epsilon, final double epsilonAVVDCheck,
94 final Comparator<Complex> eigenValuesComparator) {
95 super(matrix, eigenVectorsEquality, epsilon, epsilonAVVDCheck);
96 final FieldMatrix<Complex> D = this.getD();
97 final FieldMatrix<Complex> V = this.getV();
98
99 // getting eigen values
100 IndexedEigenvalue[] eigenValues = new IndexedEigenvalue[D.getRowDimension()];
101 for (int ij = 0; ij < matrix.getRowDimension(); ij++) {
102 eigenValues[ij] = new IndexedEigenvalue(ij, D.getEntry(ij, ij));
103 }
104
105 // ordering
106 Arrays.sort(eigenValues, (v1, v2) -> eigenValuesComparator.compare(v1.eigenValue, v2.eigenValue));
107 for (int ij = 0; ij < matrix.getRowDimension() - 1; ij++) {
108 final IndexedEigenvalue eij = eigenValues[ij];
109
110 if (ij == eij.index) {
111 continue;
112 }
113
114 // exchanging D
115 final Complex previousValue = D.getEntry(ij, ij);
116 D.setEntry(ij, ij, eij.eigenValue);
117 D.setEntry(eij.index, eij.index, previousValue);
118
119 // exchanging V
120 for (int k = 0; k < matrix.getRowDimension(); ++k) {
121 final Complex previous = V.getEntry(k, ij);
122 V.setEntry(k, ij, V.getEntry(k, eij.index));
123 V.setEntry(k, eij.index, previous);
124 }
125
126 // exchanging eigenvalue
127 for (int k = ij + 1; k < matrix.getRowDimension(); ++k) {
128 if (eigenValues[k].index == ij) {
129 eigenValues[k].index = eij.index;
130 break;
131 }
132 }
133 }
134
135 // reorder the eigenvalues and eigenvector s array in base class
136 matricesToEigenArrays();
137
138 checkDefinition(matrix);
139
140 }
141
142 /** {@inheritDoc} */
143 @Override
144 public FieldMatrix<Complex> getVT() {
145 return getV().transpose();
146 }
147
148 /** Container for index and eigenvalue pair. */
149 private static class IndexedEigenvalue {
150
151 /** Index in the diagonal matrix. */
152 private int index;
153
154 /** Eigenvalue. */
155 private final Complex eigenValue;
156
157 /** Build the container from its fields.
158 * @param index index in the diagonal matrix
159 * @param eigenvalue eigenvalue
160 */
161 IndexedEigenvalue(final int index, final Complex eigenvalue) {
162 this.index = index;
163 this.eigenValue = eigenvalue;
164 }
165
166 /** {@inheritDoc} */
167 @Override
168 public boolean equals(final Object other) {
169
170 if (this == other) {
171 return true;
172 }
173
174 if (other instanceof IndexedEigenvalue) {
175 final IndexedEigenvalue rhs = (IndexedEigenvalue) other;
176 return eigenValue.equals(rhs.eigenValue);
177 }
178
179 return false;
180
181 }
182
183 /**
184 * Get a hashCode for the pair.
185 * @return a hash code value for this object
186 */
187 @Override
188 public int hashCode() {
189 return 4563 + index + eigenValue.hashCode();
190 }
191
192 }
193
194 }