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 org.hipparchus.complex.Complex;
20  import org.hipparchus.complex.ComplexField;
21  import org.hipparchus.exception.LocalizedCoreFormats;
22  import org.hipparchus.exception.MathIllegalArgumentException;
23  import org.hipparchus.util.FastMath;
24  import org.junit.jupiter.api.Test;
25  
26  import static org.junit.jupiter.api.Assertions.assertEquals;
27  import static org.junit.jupiter.api.Assertions.assertFalse;
28  import static org.junit.jupiter.api.Assertions.assertTrue;
29  import static org.junit.jupiter.api.Assertions.fail;
30  
31  class ComplexEigenDecompositionTest {
32  
33      @Test
34      void testNonSquare() {
35          try {
36              new ComplexEigenDecomposition(MatrixUtils.createRealMatrix(2, 3), 1.0e-5, 1.0e-12, 1.0e-6);
37              fail("an axception should have been thrown");
38          } catch (MathIllegalArgumentException miae) {
39              assertEquals(LocalizedCoreFormats.NON_SQUARE_MATRIX, miae.getSpecifier());
40          }
41      }
42  
43      @Test
44      void testRealEigenValues() {
45          final RealMatrix m = MatrixUtils.createRealMatrix(new double[][] { { 2, 0 }, { 0, 3 } });
46          ComplexEigenDecomposition eigenDecomp = new ComplexEigenDecomposition(m);
47          assertFalse(eigenDecomp.hasComplexEigenvalues());
48      }
49  
50      @Test
51      void testGetEigenValues() {
52          final RealMatrix A = MatrixUtils.createRealMatrix(new double[][] { { 3, -2 }, { 4, -1 } });
53          ComplexEigenDecomposition eigenDecomp = new ComplexEigenDecomposition(A);
54          Complex ev1 = eigenDecomp.getEigenvalues()[0];
55          Complex ev2 = eigenDecomp.getEigenvalues()[1];
56          assertEquals(new Complex(1, +2), ev1);
57          assertEquals(new Complex(1, -2), ev2);
58      }
59  
60      @Test
61      void testHasComplexEigenValues() {
62          final RealMatrix A = MatrixUtils.createRealMatrix(new double[][] { { 3, -2 }, { 4, -1 } });
63          ComplexEigenDecomposition eigenDecomp = new ComplexEigenDecomposition(A);
64          assertTrue(eigenDecomp.hasComplexEigenvalues());
65      }
66  
67      @Test
68      void testGetDeterminant() {
69          final RealMatrix A = MatrixUtils.createRealMatrix(new double[][] { { 3, -2 }, { 4, -1 } });
70          ComplexEigenDecomposition eigenDecomp = new ComplexEigenDecomposition(A);
71          assertEquals(5, eigenDecomp.getDeterminant(), 1.0e-12);
72      }
73  
74      @Test
75      void testGetEigenVectors() {
76          final RealMatrix A = MatrixUtils.createRealMatrix(new double[][] { { 3, -2 }, { 4, -1 } });
77          ComplexEigenDecomposition eigenDecomp = new ComplexEigenDecomposition(A);
78          checkScaledVector(eigenDecomp.getEigenvector(0), buildVector(new Complex(1), new Complex(1, -1)), 1.0e-15);
79          checkScaledVector(eigenDecomp.getEigenvector(1), buildVector(new Complex(1), new Complex(1, +1)), 1.0e-15);
80      }
81  
82      @Test
83      void testEigenValuesAndVectors() {
84          final RealMatrix aR = MatrixUtils.createRealMatrix(new double[][] { { 3, -2 }, { 4, -1 } });
85          final ComplexEigenDecomposition eigenDecomp = new ComplexEigenDecomposition(aR);
86          final FieldMatrix<Complex> aC = toComplex(aR);
87          for (int i = 0; i < aR.getRowDimension(); ++i) {
88              final Complex              lambda = eigenDecomp.getEigenvalues()[i];
89              final FieldVector<Complex> u      = eigenDecomp.getEigenvector(i);
90              final FieldVector<Complex> v      = aC.operate(u);
91              checkScaledVector(v, u.mapMultiplyToSelf(lambda), 1.0e-12);
92          }
93      }
94  
95      @Test
96      void testGetV() {
97          final RealMatrix A = MatrixUtils.createRealMatrix(new double[][] { { 3, -2 }, { 4, -1 } });
98          ComplexEigenDecomposition eigenDecomp = new ComplexEigenDecomposition(A);
99          FieldMatrix<Complex> V = eigenDecomp.getV();        
100         assertEquals(0.0, new Complex(.5, .5).subtract(V.getEntry(0, 0)).norm(), 1.0e-15);
101         assertEquals(0.0, new Complex(.5, -.5).subtract(V.getEntry(0, 1)).norm(), 1.0e-15);
102         assertEquals(0.0, new Complex(1).subtract(V.getEntry(1, 0)).norm(), 1.0e-15);
103         assertEquals(0.0, new Complex(1).subtract(V.getEntry(1, 1)).norm(), 1.0e-15);
104     }
105 
106     @Test
107     void testGetVT() {
108         final RealMatrix A = MatrixUtils.createRealMatrix(new double[][] { { 3, -2 }, { 4, -1 } });
109         ComplexEigenDecomposition eigenDecomp = new ComplexEigenDecomposition(A);
110         FieldMatrix<Complex> V = eigenDecomp.getVT();
111         assertEquals(0.0, new Complex(.5, .5).subtract(V.getEntry(0, 0)).norm(), 1.0e-15);
112         assertEquals(0.0, new Complex(.5, -.5).subtract(V.getEntry(1, 0)).norm(), 1.0e-15);
113         assertEquals(0.0, new Complex(1).subtract(V.getEntry(0, 1)).norm(), 1.0e-15);
114         assertEquals(0.0, new Complex(1).subtract(V.getEntry(1, 1)).norm(), 1.0e-15);
115     }
116 
117     @Test
118     void testGetD() {
119         final RealMatrix A = MatrixUtils.createRealMatrix(new double[][] { { 3, -2 }, { 4, -1 } });
120         ComplexEigenDecomposition eigenDecomp = new ComplexEigenDecomposition(A);
121         FieldMatrix<Complex> D = eigenDecomp.getD();
122         assertEquals(0.0, new Complex(1, +2).subtract(D.getEntry(0, 0)).norm(), 1.0e-15);
123         assertEquals(0.0, new Complex(0).subtract(D.getEntry(0, 1)).norm(), 1.0e-15);
124         assertEquals(0.0, new Complex(0).subtract(D.getEntry(0, 1)).norm(), 1.0e-15);
125         assertEquals(0.0, new Complex(1, -2).subtract(D.getEntry(1, 1)).norm(), 1.0e-15);
126     }
127 
128     @Test
129     void testEqualEigenvalues() {
130 
131         final RealMatrix A = MatrixUtils.createRealMatrix(new double[][] {
132             { 1.0, 0.0, 0.0 },
133             { 0.0, 1.0, 0.0 },
134             { 0.0, 0.0, 1.0 }
135         });
136         ComplexEigenDecomposition eigenDecomp = new ComplexEigenDecomposition(A);
137 
138         assertEquals(3, eigenDecomp.getEigenvalues().length);
139         for (Complex z : eigenDecomp.getEigenvalues()) {
140             assertEquals(Complex.ONE, z);
141         }
142 
143         checkScaledVector(buildVector(Complex.ONE,  Complex.ZERO, Complex.ZERO), eigenDecomp.getEigenvector(0), 1.0e-12);
144         checkScaledVector(buildVector(Complex.ZERO, Complex.ONE,  Complex.ZERO), eigenDecomp.getEigenvector(1), 1.0e-12);
145         checkScaledVector(buildVector(Complex.ZERO, Complex.ZERO, Complex.ONE),  eigenDecomp.getEigenvector(2), 1.0e-12);
146 
147     }
148 
149     @Test
150     void testDefinition() {
151 
152         final RealMatrix aR = MatrixUtils.createRealMatrix(new double[][] { { 3, -2 }, { 4, -1 } });
153         ComplexEigenDecomposition eigenDecomp = new ComplexEigenDecomposition(aR);
154         FieldMatrix<Complex> aC = toComplex(aR);
155 
156         // testing AV = lamba V - [0]
157         checkScaledVector(aC.operate(eigenDecomp.getEigenvector(0)),
158                     eigenDecomp.getEigenvector(0).mapMultiply(eigenDecomp.getEigenvalues()[0]),
159                     1.0e-12);
160 
161         // testing AV = lamba V - [1]
162         checkScaledVector(aC.operate(eigenDecomp.getEigenvector(1)),
163                     eigenDecomp.getEigenvector(1).mapMultiply(eigenDecomp.getEigenvalues()[1]),
164                     1.0e-12);
165 
166         // checking definition of the decomposition A*V = V*D
167         checkMatrix(aC.multiply(eigenDecomp.getV()),
168                     eigenDecomp.getV().multiply(eigenDecomp.getD()),
169                     1.0e-12);
170     }
171 
172     @Test
173     void testIssue249() {
174 
175         // the characteristic polynomial of this matrix is (1-λ)³,
176         // so the matrix has a single eigen value λ=1 and algebraic multiplicity 3.
177         // for this eigen value, there are only two eigen vectors: v₁ = (0, 1, 0) and v₂ = (0, 0, 1)
178         // as the geometric multiplicity is only 2
179         final RealMatrix matrix = new Array2DRowRealMatrix(new double[][] {
180             {  1.0, 0.0, 0.0 },
181             { -2.0, 1.0, 0.0 },
182             {  0.0, 0.0, 1.0 }
183         });
184 
185         final ComplexEigenDecomposition ced = new OrderedComplexEigenDecomposition(matrix,
186                   ComplexEigenDecomposition.DEFAULT_EIGENVECTORS_EQUALITY,
187                   ComplexEigenDecomposition.DEFAULT_EPSILON,
188                   ComplexEigenDecomposition.DEFAULT_EPSILON_AV_VD_CHECK,
189                   (c1, c2) -> Double.compare(c2.norm(), c1.norm()));
190 
191         assertEquals(3, ced.getEigenvalues().length);
192         assertEquals(1.0, ced.getEigenvector(0).dotProduct(ced.getEigenvector(0)).norm(), 1.0e-15);
193         assertEquals(1.0, ced.getEigenvector(1).dotProduct(ced.getEigenvector(1)).norm(), 1.0e-15);
194         assertEquals(0.0, ced.getEigenvector(2).dotProduct(ced.getEigenvector(2)).norm(), 1.0e-15);
195 
196     }
197 
198     private FieldMatrix<Complex> toComplex(final RealMatrix m) {
199         FieldMatrix<Complex> c = MatrixUtils.createFieldMatrix(ComplexField.getInstance(),
200                                                                m.getRowDimension(),
201                                                                m.getColumnDimension());
202         for (int i = 0; i < m.getRowDimension(); ++i) {
203             for (int j = 0; j < m.getColumnDimension(); ++j) {
204                 c.setEntry(i, j, new Complex(m.getEntry(i, j)));
205             }
206         }
207 
208         return c;
209 
210     }
211 
212     private FieldVector<Complex> buildVector(final Complex... vi) {
213         return new ArrayFieldVector<>(vi);
214     }
215 
216     private void checkScaledVector(final FieldVector<Complex> v, final FieldVector<Complex> reference, final double tol) {
217 
218         assertEquals(reference.getDimension(), v.getDimension());
219 
220         // find the global scaling factor, using the maximum reference component
221         Complex scale = Complex.NaN;
222         double  norm  = Double.NEGATIVE_INFINITY;
223         for (int i = 0; i < reference.getDimension(); i++) {
224             final Complex ri = reference.getEntry(i);
225             final double  ni = FastMath.hypot(ri.getReal(), ri.getImaginary());
226             if (ni > norm) {
227                 scale = ri.divide(v.getEntry(i));
228                 norm  = ni;
229             }
230         }
231 
232         // check vector, applying the scaling factor
233         for (int i = 0; i < reference.getDimension(); ++i) {
234             final Complex ri = reference.getEntry(i);
235             final Complex vi = v.getEntry(i);
236             final Complex si = vi.multiply(scale);
237             assertEquals(ri.getReal(), si.getReal(), tol, "" + (ri.getReal() - si.getReal()));
238             assertEquals(ri.getImaginary(), si.getImaginary(), tol, "" + (ri.getImaginary() - si.getImaginary()));
239         }
240 
241     }
242 
243     private void checkMatrix(final FieldMatrix<Complex> m, final FieldMatrix<Complex> reference, final double tol) {
244         assertEquals(reference.getRowDimension(), m.getRowDimension());
245         assertEquals(reference.getColumnDimension(), m.getColumnDimension());
246         for (int i = 0; i < reference.getRowDimension(); ++i) {
247             for (int j = 0; j < reference.getColumnDimension(); ++j) {
248                 assertEquals(reference.getEntry(i, j).getReal(), m.getEntry(i, j).getReal(), tol, "" + (reference.getEntry(i, j).getReal() - m.getEntry(i, j).getReal()));
249                 assertEquals(reference.getEntry(i, j).getImaginary(), m.getEntry(i, j).getImaginary(), tol, "" + (reference.getEntry(i, j).getImaginary() - m.getEntry(i, j).getImaginary()));
250             }
251         }
252     }
253 
254 }