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