View Javadoc
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 }