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,
39               ComplexEigenDecomposition.DEFAULT_EIGENVECTORS_EQUALITY,
40               ComplexEigenDecomposition.DEFAULT_EPSILON,
41               ComplexEigenDecomposition.DEFAULT_EPSILON_AV_VD_CHECK);
42      }
43  
44      /**
45       * Constructor for decomposition.
46       * <p>
47       * The {@code eigenVectorsEquality} threshold is used to ensure the L∞-normalized
48       * eigenvectors found using inverse iteration are different from each other.
49       * if \(min(|e_i-e_j|,|e_i+e_j|)\) is smaller than this threshold, the algorithm
50       * considers it has found again an already known vector, so it drops it and attempts
51       * a new inverse iteration with a different start vector. This value should be
52       * much larger than {@code epsilon} which is used for convergence
53       * </p>
54       * <p>
55       * This constructor calls {@link #OrderedComplexEigenDecomposition(RealMatrix, double,
56       * double, double, Comparator)} with a comparator using real ordering as the primary
57       * sort order and imaginary ordering as the secondary sort order..
58       * </p>
59       * @param matrix real matrix.
60       * @param eigenVectorsEquality threshold below which eigenvectors are considered equal
61       * @param epsilon Epsilon used for internal tests (e.g. is singular, eigenvalue ratio, etc.)
62       * @param epsilonAVVDCheck Epsilon criteria for final AV=VD check
63       * @since 1.9
64       */
65      public OrderedComplexEigenDecomposition(final RealMatrix matrix, final double eigenVectorsEquality,
66                                              final double epsilon, final double epsilonAVVDCheck) {
67          this(matrix, eigenVectorsEquality, epsilon, epsilonAVVDCheck,
68               (c1, c2) -> {
69                   final int cR = Double.compare(c1.getReal(), c2.getReal());
70                   if (cR == 0) {
71                       return Double.compare(c1.getImaginary(), c2.getImaginary());
72                   } else {
73                       return cR;
74                   }
75               });
76      }
77  
78      /**
79       * Constructor for decomposition.
80       * <p>
81       * The {@code eigenVectorsEquality} threshold is used to ensure the L∞-normalized
82       * eigenvectors found using inverse iteration are different from each other.
83       * if \(min(|e_i-e_j|,|e_i+e_j|)\) is smaller than this threshold, the algorithm
84       * considers it has found again an already known vector, so it drops it and attempts
85       * a new inverse iteration with a different start vector. This value should be
86       * much larger than {@code epsilon} which is used for convergence
87       * </p>
88       * @param matrix real matrix.
89       * @param eigenVectorsEquality threshold below which eigenvectors are considered equal
90       * @param epsilon Epsilon used for internal tests (e.g. is singular, eigenvalue ratio, etc.)
91       * @param epsilonAVVDCheck Epsilon criteria for final AV=VD check
92       * @param eigenValuesComparator comparator for sorting eigen values
93       * @since 3.0
94       */
95      public OrderedComplexEigenDecomposition(final RealMatrix matrix, final double eigenVectorsEquality,
96                                              final double epsilon, final double epsilonAVVDCheck,
97                                              final Comparator<Complex> eigenValuesComparator) {
98          super(matrix, eigenVectorsEquality, epsilon, epsilonAVVDCheck);
99          final FieldMatrix<Complex> D = this.getD();
100         final FieldMatrix<Complex> V = this.getV();
101 
102         // getting eigen values
103         IndexedEigenvalue[] eigenValues = new IndexedEigenvalue[D.getRowDimension()];
104         for (int ij = 0; ij < matrix.getRowDimension(); ij++) {
105             eigenValues[ij] = new IndexedEigenvalue(ij, D.getEntry(ij, ij));
106         }
107 
108         // ordering
109         Arrays.sort(eigenValues, (v1, v2) -> eigenValuesComparator.compare(v1.eigenValue, v2.eigenValue));
110         for (int ij = 0; ij < matrix.getRowDimension() - 1; ij++) {
111             final IndexedEigenvalue eij = eigenValues[ij];
112 
113             if (ij == eij.index) {
114                 continue;
115             }
116 
117             // exchanging D
118             final Complex previousValue = D.getEntry(ij, ij);
119             D.setEntry(ij, ij, eij.eigenValue);
120             D.setEntry(eij.index, eij.index, previousValue);
121 
122             // exchanging V
123             for (int k = 0; k  < matrix.getRowDimension(); ++k) {
124                 final Complex previous = V.getEntry(k, ij);
125                 V.setEntry(k, ij, V.getEntry(k, eij.index));
126                 V.setEntry(k, eij.index, previous);
127             }
128 
129             // exchanging eigenvalue
130             for (int k = ij + 1; k < matrix.getRowDimension(); ++k) {
131                 if (eigenValues[k].index == ij) {
132                     eigenValues[k].index = eij.index;
133                     break;
134                 }
135             }
136         }
137 
138         // reorder the eigenvalues and eigenvector s array in base class
139         matricesToEigenArrays();
140 
141         checkDefinition(matrix);
142 
143     }
144 
145     /** {@inheritDoc} */
146     @Override
147     public FieldMatrix<Complex> getVT() {
148         return getV().transpose();
149     }
150 
151     /** Container for index and eigenvalue pair. */
152     private static class IndexedEigenvalue {
153 
154         /** Index in the diagonal matrix. */
155         private int index;
156 
157         /** Eigenvalue. */
158         private final Complex eigenValue;
159 
160         /** Build the container from its fields.
161          * @param index index in the diagonal matrix
162          * @param eigenvalue eigenvalue
163          */
164         IndexedEigenvalue(final int index, final Complex eigenvalue) {
165             this.index      = index;
166             this.eigenValue = eigenvalue;
167         }
168 
169         /** {@inheritDoc} */
170         @Override
171         public boolean equals(final Object other) {
172 
173             if (this == other) {
174                 return true;
175             }
176 
177             if (other instanceof IndexedEigenvalue) {
178                 final IndexedEigenvalue rhs = (IndexedEigenvalue) other;
179                 return eigenValue.equals(rhs.eigenValue);
180             }
181 
182             return false;
183 
184         }
185 
186         /**
187          * Get a hashCode for the pair.
188          * @return a hash code value for this object
189          */
190         @Override
191         public int hashCode() {
192             return 4563 + index + eigenValue.hashCode();
193         }
194 
195     }
196 
197 }