View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) 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 ASF 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  
18  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  
23  package org.hipparchus.linear;
24  
25  import org.hipparchus.UnitTestUtils;
26  import org.hipparchus.complex.Complex;
27  import org.hipparchus.complex.ComplexComparator;
28  import org.hipparchus.complex.ComplexField;
29  import org.hipparchus.random.RandomDataGenerator;
30  import org.hipparchus.random.RandomGenerator;
31  import org.hipparchus.random.Well1024a;
32  import org.hipparchus.util.FastMath;
33  import org.hipparchus.util.Precision;
34  import org.junit.jupiter.api.AfterEach;
35  import org.junit.jupiter.api.BeforeEach;
36  import org.junit.jupiter.api.Test;
37  
38  import java.util.Arrays;
39  import java.util.Random;
40  
41  import static org.junit.jupiter.api.Assertions.assertEquals;
42  import static org.junit.jupiter.api.Assertions.assertNotNull;
43  import static org.junit.jupiter.api.Assertions.assertTrue;
44  import static org.junit.jupiter.api.Assertions.fail;
45  
46  public class EigenDecompositionNonSymmetricTest {
47  
48      private Complex[] refValues;
49      private RealMatrix matrix;
50  
51      /** test dimensions */
52      @Test
53      void testDimensions() {
54          final int m = matrix.getRowDimension();
55          EigenDecompositionNonSymmetric ed = new EigenDecompositionNonSymmetric(matrix);
56          assertEquals(m, ed.getV().getRowDimension());
57          assertEquals(m, ed.getV().getColumnDimension());
58          assertEquals(m, ed.getD().getColumnDimension());
59          assertEquals(m, ed.getD().getColumnDimension());
60          assertEquals(m, ed.getVInv().getRowDimension());
61          assertEquals(m, ed.getVInv().getColumnDimension());
62      }
63  
64      /** test eigenvalues */
65      @Test
66      void testEigenvalues() {
67          EigenDecompositionNonSymmetric ed = new EigenDecompositionNonSymmetric(matrix);
68          Complex[] eigenValues = ed.getEigenvalues();
69          assertEquals(refValues.length, eigenValues.length);
70          for (int i = 0; i < refValues.length; ++i) {
71              assertEquals(refValues[i].getRealPart(),      eigenValues[i].getRealPart(), 3.0e-15);
72              assertEquals(refValues[i].getImaginaryPart(), eigenValues[i].getImaginaryPart(), 3.0e-15);
73          }
74      }
75  
76      @Test
77      void testNonSymmetric() {
78          // Vandermonde matrix V(x;i,j) = x_i^{n - j} with x = (-1,-2,3,4)
79          double[][] vData = { { -1.0, 1.0, -1.0, 1.0 },
80                               { -8.0, 4.0, -2.0, 1.0 },
81                               { 27.0, 9.0,  3.0, 1.0 },
82                               { 64.0, 16.0, 4.0, 1.0 } };
83          checkNonSymmetricMatrix(MatrixUtils.createRealMatrix(vData));
84  
85          RealMatrix randMatrix = MatrixUtils.createRealMatrix(new double[][] {
86                  {0,  1,     0,     0},
87                  {1,  0,     2.e-7, 0},
88                  {0, -2.e-7, 0,     1},
89                  {0,  0,     1,     0}
90          });
91          checkNonSymmetricMatrix(randMatrix);
92  
93          // from http://eigen.tuxfamily.org/dox/classEigen_1_1RealSchur.html
94          double[][] randData2 = {
95                  {  0.680, -0.3300, -0.2700, -0.717, -0.687,  0.0259 },
96                  { -0.211,  0.5360,  0.0268,  0.214, -0.198,  0.6780 },
97                  {  0.566, -0.4440,  0.9040, -0.967, -0.740,  0.2250 },
98                  {  0.597,  0.1080,  0.8320, -0.514, -0.782, -0.4080 },
99                  {  0.823, -0.0452,  0.2710, -0.726,  0.998,  0.2750 },
100                 { -0.605,  0.2580,  0.4350,  0.608, -0.563,  0.0486 }
101         };
102         checkNonSymmetricMatrix(MatrixUtils.createRealMatrix(randData2));
103     }
104 
105     @Test
106     void testRandomNonSymmetricMatrix() {
107         for (int run = 0; run < 100; run++) {
108             RandomGenerator r = new Well1024a(0x171956baefeac83el);
109 
110             // matrix size
111             int size = r.nextInt(20) + 4;
112 
113             double[][] data = new double[size][size];
114             for (int i = 0; i < size; i++) {
115                 for (int j = 0; j < size; j++) {
116                     data[i][j] = r.nextInt(100);
117                 }
118             }
119 
120             RealMatrix m = MatrixUtils.createRealMatrix(data);
121             checkNonSymmetricMatrix(m);
122         }
123     }
124 
125     /**
126      * Tests the porting of a bugfix in Jama-1.0.3 (from changelog):
127      *
128      *  Patched hqr2 method in Jama.EigenvalueDecomposition to avoid infinite loop;
129      *  Thanks Frederic Devernay &lt;frederic.devernay@m4x.org&gt;
130      */
131     @Test
132     void testMath1051() {
133         double[][] data = {
134                 {0,0,0,0,0},
135                 {0,0,0,0,1},
136                 {0,0,0,1,0},
137                 {1,1,0,0,1},
138                 {1,0,1,0,1}
139         };
140 
141         RealMatrix m = MatrixUtils.createRealMatrix(data);
142         checkNonSymmetricMatrix(m);
143     }
144 
145     @Test
146     void testNormalDistributionNonSymmetricMatrix() {
147         for (int run = 0; run < 100; run++) {
148             final RandomGenerator r = new Well1024a(0x511d8551a5641ea2l);
149             final RandomDataGenerator gen = new RandomDataGenerator(100);
150 
151             // matrix size
152             int size = r.nextInt(20) + 4;
153 
154             double[][] data = new double[size][size];
155             for (int i = 0; i < size; i++) {
156                 for (int j = 0; j < size; j++) {
157                     data[i][j] = gen.nextNormal(0.0, r.nextDouble() * 5);
158                 }
159             }
160 
161             RealMatrix m = MatrixUtils.createRealMatrix(data);
162             checkNonSymmetricMatrix(m);
163         }
164     }
165 
166     @Test
167     void testMath848() {
168         double[][] data = {
169                 { 0.1849449280, -0.0646971046,  0.0774755812, -0.0969651755, -0.0692648806,  0.3282344352, -0.0177423074,  0.2063136340},
170                 {-0.0742700134, -0.0289063030, -0.0017269460, -0.0375550146, -0.0487737922, -0.2616837868, -0.0821201295, -0.2530000167},
171                 { 0.2549910127,  0.0995733692, -0.0009718388,  0.0149282808,  0.1791878897, -0.0823182816,  0.0582629256,  0.3219545182},
172                 {-0.0694747557, -0.1880649148, -0.2740630911,  0.0720096468, -0.1800836914, -0.3518996425,  0.2486747833,  0.6257938167},
173                 { 0.0536360918, -0.1339297778,  0.2241579764, -0.0195327484, -0.0054103808,  0.0347564518,  0.5120802482, -0.0329902864},
174                 {-0.5933332356, -0.2488721082,  0.2357173629,  0.0177285473,  0.0856630593, -0.3567126300, -0.1600668126, -0.1010899621},
175                 {-0.0514349819, -0.0854319435,  0.1125050061,  0.0063453560, -0.2250000688, -0.2209343090,  0.1964623477, -0.1512329924},
176                 { 0.0197395947, -0.1997170581, -0.1425959019, -0.2749477910, -0.0969467073,  0.0603688520, -0.2826905192,  0.1794315473}};
177         RealMatrix m = MatrixUtils.createRealMatrix(data);
178         checkNonSymmetricMatrix(m);
179     }
180 
181     /**
182      * Checks that the eigen decomposition of a general (non-symmetric) matrix is valid by
183      * checking: A*V = V*D
184      */
185     private void checkNonSymmetricMatrix(final RealMatrix m) {
186         try {
187             EigenDecompositionNonSymmetric ed = new EigenDecompositionNonSymmetric(m, 1.0e-15);
188 
189             RealMatrix d = ed.getD();
190             RealMatrix v = ed.getV();
191 
192             RealMatrix x = m.multiply(v);
193             RealMatrix y = v.multiply(d);
194 
195             double diffNorm = x.subtract(y).getNorm1();
196             assertTrue(x.subtract(y).getNorm1() < 1000 * Precision.EPSILON * FastMath.max(x.getNorm1(), y.getNorm1()),
197                     "The norm of (X-Y) is too large: " + diffNorm + ", matrix=" + m.toString());
198 
199             RealMatrix invV = new LUDecomposition(v).getSolver().getInverse();
200             double norm = v.multiply(d).multiply(invV).subtract(m).getNorm1();
201             assertEquals(0.0, norm, 1.0e-10);
202         } catch (Exception e) {
203             fail("Failed to create EigenDecomposition for matrix " + m.toString() + ", ex=" + e.toString());
204         }
205     }
206 
207     /** test eigenvectors */
208     @Test
209     void testEigenvectors() {
210         EigenDecompositionNonSymmetric ed = new EigenDecompositionNonSymmetric(matrix);
211         FieldMatrix<Complex> cMatrix = MatrixUtils.createFieldMatrix(ComplexField.getInstance(),
212                                                                      matrix.getRowDimension(),
213                                                                      matrix.getColumnDimension());
214         for (int i = 0; i < matrix.getRowDimension(); ++i) {
215             for (int j = 0; j < matrix.getColumnDimension(); ++j) {
216                 cMatrix.setEntry(i, j, new Complex(matrix.getEntry(j, i)));
217             }
218         }
219 
220         for (int i = 0; i < matrix.getRowDimension(); ++i) {
221             final Complex              lambda = ed.getEigenvalue(i);
222             final FieldVector<Complex> v      = ed.getEigenvector(i);
223             final FieldVector<Complex> mV     = cMatrix.operate(v);
224             for (int k = 0; k < v.getDimension(); ++k) {
225                 assertEquals(0, mV.getEntry(k).subtract(v.getEntry(k).multiply(lambda)).norm(), 1.0e-13);
226             }
227         }
228     }
229 
230     /** test A = VDV⁻¹ */
231     @Test
232     void testAEqualVDVInv() {
233         EigenDecompositionNonSymmetric ed = new EigenDecompositionNonSymmetric(matrix);
234         RealMatrix v  = ed.getV();
235         RealMatrix d  = ed.getD();
236         RealMatrix vI = MatrixUtils.inverse(v);
237         double norm = v.multiply(d).multiply(vI).subtract(matrix).getNorm1();
238         assertEquals(0, norm, 6.0e-13);
239     }
240 
241     /**
242      * Matrix with eigenvalues {2, 0, 12}
243      */
244     @Test
245     void testDistinctEigenvalues() {
246         RealMatrix distinct = MatrixUtils.createRealMatrix(new double[][] {
247                 {3, 1, -4},
248                 {1, 3, -4},
249                 {-4, -4, 8}
250         });
251         EigenDecompositionNonSymmetric ed = new EigenDecompositionNonSymmetric(distinct);
252         checkEigenValues((new Complex[] { new Complex( 2), new Complex( 0), new Complex(12) }), ed, 1E-12);
253         checkEigenVector((new Complex[] { new Complex( 1), new Complex(-1), new Complex( 0) }), ed, 1E-12);
254         checkEigenVector((new Complex[] { new Complex( 1), new Complex( 1), new Complex( 1) }), ed, 1E-12);
255         checkEigenVector((new Complex[] { new Complex(-1), new Complex(-1), new Complex( 2) }), ed, 1E-12);
256     }
257 
258     /**
259      * Verifies operation on indefinite matrix
260      */
261     @Test
262     void testZeroDivide() {
263         RealMatrix indefinite = MatrixUtils.createRealMatrix(new double [][] {
264                 { 0.0, 1.0, -1.0 },
265                 { 1.0, 1.0, 0.0 },
266                 { -1.0,0.0, 1.0 }
267         });
268         EigenDecompositionNonSymmetric ed = new EigenDecompositionNonSymmetric(indefinite);
269         checkEigenValues((new Complex[] { new Complex(2), new Complex(1), new Complex(-1) }), ed, 1E-12);
270         Complex isqrt3 = new Complex(1 / FastMath.sqrt(3.0));
271         checkEigenVector((new Complex[] {isqrt3, isqrt3, isqrt3.negate() }), ed, 1E-12);
272         Complex isqrt2 = new Complex(1 / FastMath.sqrt(2.0));
273         checkEigenVector((new Complex[] { new Complex(0.0), isqrt2.negate(), isqrt2.negate() }), ed, 1E-12);
274         Complex isqrt6 = new Complex(1 / FastMath.sqrt(6.0));
275         checkEigenVector((new Complex[] { isqrt6.multiply(2), isqrt6.negate(), isqrt6 }), ed, 1E-12);
276     }
277 
278     /** test eigenvalues for a big matrix. */
279     @Test
280     void testBigMatrix() {
281         Random r = new Random(17748333525117l);
282         double[] bigValues = new double[200];
283         for (int i = 0; i < bigValues.length; ++i) {
284             bigValues[i] = 2 * r.nextDouble() - 1;
285         }
286         Arrays.sort(bigValues);
287         EigenDecompositionNonSymmetric ed = new EigenDecompositionNonSymmetric(createTestMatrix(r, bigValues));
288         Complex[] eigenValues = ed.getEigenvalues();
289         Arrays.sort(eigenValues, new ComplexComparator());
290         assertEquals(bigValues.length, eigenValues.length);
291         for (int i = 0; i < bigValues.length; ++i) {
292             assertEquals(bigValues[i], eigenValues[i].getRealPart(), 2.0e-14);
293         }
294     }
295 
296     /**
297      * Verifies operation on very small values.
298      * Matrix with eigenvalues {2e-100, 0, 12e-100}
299      */
300     @Test
301     void testTinyValues() {
302         final double tiny = 1.0e-100;
303         RealMatrix distinct = MatrixUtils.createRealMatrix(new double[][] {
304                 {3, 1, -4},
305                 {1, 3, -4},
306                 {-4, -4, 8}
307         });
308         distinct = distinct.scalarMultiply(tiny);
309 
310         final EigenDecompositionNonSymmetric ed = new EigenDecompositionNonSymmetric(distinct);
311         checkEigenValues(new Complex[] { new Complex(2).multiply(tiny), new Complex(0).multiply(tiny), new Complex(12).multiply(tiny) },
312                          ed, 1e-12 * tiny);
313         checkEigenVector(new Complex[] { new Complex( 1), new Complex(-1), new Complex(0) }, ed, 1e-12);
314         checkEigenVector(new Complex[] { new Complex( 1), new Complex( 1), new Complex(1) }, ed, 1e-12);
315         checkEigenVector(new Complex[] { new Complex(-1), new Complex(-1), new Complex(2) }, ed, 1e-12);
316     }
317 
318     @Test
319     void testDeterminantWithCompleEigenValues() {
320         final RealMatrix m = MatrixUtils.createRealMatrix(new double[][] { { -3, -1.5, -3 }, { 0, -1, 0 }, { 1, 0, 0 } });
321         EigenDecompositionNonSymmetric decomposition = new EigenDecompositionNonSymmetric(m);
322         assertEquals(-3.0, decomposition.getDeterminant().getRealPart(),      1.0e-15);
323         assertEquals( 0.0, decomposition.getDeterminant().getImaginaryPart(), 1.0e-15);
324     }
325 
326     @Test
327     void testReal() {
328         // AA = [1 2;1 -3];
329 
330         RealMatrix A = MatrixUtils.createRealMatrix(new double[][] {
331             { 1, 2 }, { 1, -3 }
332         });
333 
334         EigenDecompositionNonSymmetric ed = new EigenDecompositionNonSymmetric(A);
335 
336         assertNotNull(ed.getD());
337         assertNotNull(ed.getV());
338 
339         final double s2 = FastMath.sqrt(2);
340         final double s3 = FastMath.sqrt(3);
341         RealMatrix D_expected = MatrixUtils.createRealMatrix(new double[][] {
342             { s2 * s3 - 1.0, 0 }, { 0,  -1 - s2 * s3 }
343         });
344 
345         RealMatrix V_expected = MatrixUtils.createRealMatrix(new double[][] {
346             { (s2 + 2 * s3) / 5, (s3 - 3 * s2)     /  6 },
347             { (2 * s2 - s3) / 5, (4 * s3 + 3 * s2) / 12 }
348         });
349 
350         UnitTestUtils.customAssertEquals("D", D_expected, ed.getD(), 1.0e-15);
351         UnitTestUtils.customAssertEquals("V", V_expected, ed.getV(), 1.0e-15);
352 
353         // checking definition of the decomposition A = V*D*inv(V)
354         UnitTestUtils.customAssertEquals("A", A,
355                                          ed.getV().multiply(ed.getD()).multiply(MatrixUtils.inverse(ed.getV())),
356                                          2.0e-15);
357 
358     }
359 
360     @Test
361     void testImaginary() {
362         // AA = [3 -2;4 -1];
363 
364         RealMatrix A = MatrixUtils.createRealMatrix(new double[][] {
365             { 3, -2 }, { 4, -1 }
366         });
367 
368         EigenDecompositionNonSymmetric ordEig = new EigenDecompositionNonSymmetric(A);
369 
370         assertNotNull(ordEig.getD());
371         assertNotNull(ordEig.getV());
372 
373         RealMatrix D_expected = MatrixUtils.createRealMatrix(new double[][] {
374             { 1, 2 }, { -2, 1 }
375         });
376 
377         RealMatrix V_expected = MatrixUtils.createRealMatrix(new double[][] {
378             { -0.5, 0.5 }, { 0, 1 }
379         });
380 
381         UnitTestUtils.customAssertEquals("D", D_expected, ordEig.getD(), 1.0e-15);
382         UnitTestUtils.customAssertEquals("V", V_expected, ordEig.getV(), 1.0e-15);
383 
384         // checking definition of the decomposition A = V*D*inv(V)
385         UnitTestUtils.customAssertEquals("A", A, ordEig.getV().multiply(ordEig.getD()).multiply(MatrixUtils.inverse(ordEig.getV())), 1.0e-15);
386 
387         // checking A*V = D*V
388         FieldMatrix<Complex> ac = new Array2DRowFieldMatrix<>(ComplexField.getInstance(), 2, 2);
389         for (int i = 0; i < ac.getRowDimension(); ++i) {
390             for (int j = 0; j < ac.getColumnDimension(); ++j) {
391                 ac.setEntry(i, j, new Complex(A.getEntry(i, j)));
392             }
393         }
394         for (int i = 0; i < ordEig.getEigenvalues().length; ++i) {
395             final Complex              li  = ordEig.getEigenvalue(i);
396             final FieldVector<Complex> ei  = ordEig.getEigenvector(i);
397             final FieldVector<Complex> aei = ac.operate(ei);
398             for (int j = 0; j < ei.getDimension(); ++j) {
399                 assertEquals(aei.getEntry(j).getRealPart(),      li.multiply(ei.getEntry(j)).getRealPart(),      1.0e-10);
400                 assertEquals(aei.getEntry(j).getImaginaryPart(), li.multiply(ei.getEntry(j)).getImaginaryPart(), 1.0e-10);
401             }
402         }
403 
404     }
405 
406     @Test
407     void testImaginary33() {
408         // AA = [3 -2 0;4 -1 0;1 1 1];
409 
410         RealMatrix A = MatrixUtils.createRealMatrix(new double[][] {
411             { 3, -2, 0 }, { 4, -1, 0 }, { 1, 1, 1 }
412         });
413 
414         EigenDecompositionNonSymmetric ordEig = new EigenDecompositionNonSymmetric(A);
415 
416         assertNotNull(ordEig.getD());
417         assertNotNull(ordEig.getV());
418 
419         RealMatrix D_expected = MatrixUtils.createRealMatrix(new double[][] {
420             { 1, 2, 0 }, { -2, 1, 0}, {0, 0, 1 }
421         });
422 
423         final double a = FastMath.sqrt(8.0 / 17.0);
424         final double b = FastMath.sqrt(17.0) / 2.0;
425         RealMatrix V_expected = MatrixUtils.createRealMatrix(new double[][] {
426             { 0,        a, 0 },
427             { a,        a, 0 },
428             { a, -0.5 * a, b }
429         });
430         UnitTestUtils.customAssertEquals("D", D_expected, ordEig.getD(), 1.0e-15);
431         UnitTestUtils.customAssertEquals("V", V_expected, ordEig.getV(), 1.0e-15);
432 
433         // checking definition of the decomposition A = V*D*inv(V)
434         UnitTestUtils.customAssertEquals("A", A,
435                                          ordEig.getV().multiply(ordEig.getD()).multiply(MatrixUtils.inverse(ordEig.getV())),
436                                          8.0e-15);
437 
438         // checking A*V = D*V
439         FieldMatrix<Complex> ac = new Array2DRowFieldMatrix<>(ComplexField.getInstance(), 3, 3);
440         for (int i = 0; i < ac.getRowDimension(); ++i) {
441             for (int j = 0; j < ac.getColumnDimension(); ++j) {
442                 ac.setEntry(i, j, new Complex(A.getEntry(i, j)));
443             }
444         }
445         for (int i = 0; i < ordEig.getEigenvalues().length; ++i) {
446             final Complex              li  = ordEig.getEigenvalue(i);
447             final FieldVector<Complex> ei  = ordEig.getEigenvector(i);
448             final FieldVector<Complex> aei = ac.operate(ei);
449             for (int j = 0; j < ei.getDimension(); ++j) {
450                 assertEquals(aei.getEntry(j).getRealPart(),      li.multiply(ei.getEntry(j)).getRealPart(),      1.0e-10);
451                 assertEquals(aei.getEntry(j).getImaginaryPart(), li.multiply(ei.getEntry(j)).getImaginaryPart(), 1.0e-10);
452             }
453         }
454 
455     }
456 
457     @Test
458     void testImaginaryNullEigenvalue() {
459         // AA = [3 -2 0;4 -1 0;3 -2 0];
460 
461         RealMatrix A = MatrixUtils.createRealMatrix(new double[][] {
462             { 3, -2, 0 }, { 4, -1, 0 }, { 3, -2, 0 }
463         });
464 
465         EigenDecompositionNonSymmetric ordEig = new EigenDecompositionNonSymmetric(A);
466 
467         assertNotNull(ordEig.getD());
468         assertNotNull(ordEig.getV());
469 
470         RealMatrix D_expected = MatrixUtils.createRealMatrix(new double[][] {
471             { 1, 2, 0 }, { -2, 1, 0 }, { 0, 0, 0 }
472         });
473 
474         final double a  = FastMath.sqrt(11.0 / 50.0);
475         final double s2 = FastMath.sqrt(2.0);
476         RealMatrix V_expected = MatrixUtils.createRealMatrix(new double[][] {
477             {      -a, -2 * a,  0.0 },
478             {  -3 * a,     -a,  0.0 },
479             {      -a, -2 * a,   s2 }
480         });
481 
482         UnitTestUtils.customAssertEquals("D", D_expected, ordEig.getD(), 1.0e-15);
483         UnitTestUtils.customAssertEquals("V", V_expected, ordEig.getV(), 2.0e-15);
484 
485         // checking definition of the decomposition A = V*D*inv(V)
486         UnitTestUtils.customAssertEquals("A", A,
487                                          ordEig.getV().multiply(ordEig.getD()).multiply(MatrixUtils.inverse(ordEig.getV())),
488                                          1.0e-14);
489 
490         // checking A*V = D*V
491         FieldMatrix<Complex> ac = new Array2DRowFieldMatrix<>(ComplexField.getInstance(), 3, 3);
492         for (int i = 0; i < ac.getRowDimension(); ++i) {
493             for (int j = 0; j < ac.getColumnDimension(); ++j) {
494                 ac.setEntry(i, j, new Complex(A.getEntry(i, j)));
495             }
496         }
497 
498         for (int i = 0; i < ordEig.getEigenvalues().length; ++i) {
499             final Complex              li  = ordEig.getEigenvalue(i);
500             final FieldVector<Complex> ei  = ordEig.getEigenvector(i);
501             final FieldVector<Complex> aei = ac.operate(ei);
502             for (int j = 0; j < ei.getDimension(); ++j) {
503                 assertEquals(aei.getEntry(j).getRealPart(),      li.multiply(ei.getEntry(j)).getRealPart(),      1.0e-10);
504                 assertEquals(aei.getEntry(j).getImaginaryPart(), li.multiply(ei.getEntry(j)).getImaginaryPart(), 1.0e-10);
505             }
506         }
507 
508     }
509 
510     /**
511      * Verifies that the given EigenDecomposition has eigenvalues equivalent to
512      * the targetValues, ignoring the order of the values and allowing
513      * values to differ by tolerance.
514      */
515     protected void checkEigenValues(Complex[] targetValues,
516                                     EigenDecompositionNonSymmetric ed, double tolerance) {
517         Complex[] observed = ed.getEigenvalues();
518         for (int i = 0; i < observed.length; i++) {
519             assertTrue(isIncludedValue(observed[i], targetValues, tolerance));
520             assertTrue(isIncludedValue(targetValues[i], observed, tolerance));
521         }
522     }
523 
524 
525     /**
526      * Returns true iff there is an entry within tolerance of value in
527      * searchArray.
528      */
529     private boolean isIncludedValue(Complex value, Complex[] searchArray, double tolerance) {
530        boolean found = false;
531        int i = 0;
532        while (!found && i < searchArray.length) {
533            if (value.subtract(searchArray[i]).norm() < tolerance) {
534                found = true;
535            }
536            i++;
537        }
538        return found;
539     }
540 
541     /**
542      * Returns true iff eigenVector is a scalar multiple of one of the columns
543      * of ed.getV().  Does not try linear combinations - i.e., should only be
544      * used to find vectors in one-dimensional eigenspaces.
545      */
546     protected void checkEigenVector(Complex[] eigenVector, EigenDecompositionNonSymmetric ed, double tolerance) {
547         assertTrue(isIncludedColumn(eigenVector, ed.getV(), tolerance));
548     }
549 
550     /**
551      * Returns true iff there is a column that is a scalar multiple of column
552      * in searchMatrix (modulo tolerance)
553      */
554     private boolean isIncludedColumn(Complex[] column, RealMatrix searchMatrix, double tolerance) {
555         boolean found = false;
556         int i = 0;
557         while (!found && i < searchMatrix.getColumnDimension()) {
558             Complex multiplier = Complex.ONE;
559             boolean matching = true;
560             int j = 0;
561             while (matching && j < searchMatrix.getRowDimension()) {
562                 double colEntry = searchMatrix.getEntry(j, i);
563                 // Use the first entry where both are non-zero as scalar
564                 if (multiplier.subtract(1).norm() <= FastMath.ulp(1.0) && FastMath.abs(colEntry) > 1E-14
565                         && column[j].norm() > 1e-14) {
566                     multiplier = column[j].reciprocal().multiply(colEntry);
567                 }
568                 if (column[j].multiply(multiplier).subtract(colEntry).norm() > tolerance) {
569                     matching = false;
570                 }
571                 j++;
572             }
573             found = matching;
574             i++;
575         }
576         return found;
577     }
578 
579     @BeforeEach
580     void setUp() {
581         double[] real = {
582         		0.001, 1.000, 1.001, 2.001, 2.002, 2.003
583         };
584         refValues = new Complex[real.length];
585         for (int i = 0; i < refValues.length; ++i) {
586             refValues[i] = new Complex(real[i]);
587         }
588         matrix = createTestMatrix(new Random(35992629946426l), real);
589     }
590 
591     @AfterEach
592     void tearDown() {
593         refValues = null;
594         matrix    = null;
595     }
596 
597     static RealMatrix createTestMatrix(final Random r, final double[] eigenValues) {
598         final int n = eigenValues.length;
599         final RealMatrix v = createOrthogonalMatrix(r, n);
600         final RealMatrix d = MatrixUtils.createRealDiagonalMatrix(eigenValues);
601         return v.multiply(d).multiplyTransposed(v);
602     }
603 
604     public static RealMatrix createOrthogonalMatrix(final Random r, final int size) {
605 
606         final double[][] data = new double[size][size];
607 
608         for (int i = 0; i < size; ++i) {
609             final double[] dataI = data[i];
610             double norm2 = 0;
611             do {
612 
613                 // generate randomly row I
614                 for (int j = 0; j < size; ++j) {
615                     dataI[j] = 2 * r.nextDouble() - 1;
616                 }
617 
618                 // project the row in the subspace orthogonal to previous rows
619                 for (int k = 0; k < i; ++k) {
620                     final double[] dataK = data[k];
621                     double dotProduct = 0;
622                     for (int j = 0; j < size; ++j) {
623                         dotProduct += dataI[j] * dataK[j];
624                     }
625                     for (int j = 0; j < size; ++j) {
626                         dataI[j] -= dotProduct * dataK[j];
627                     }
628                 }
629 
630                 // normalize the row
631                 norm2 = 0;
632                 for (final double dataIJ : dataI) {
633                     norm2 += dataIJ * dataIJ;
634                 }
635                 final double inv = 1.0 / FastMath.sqrt(norm2);
636                 for (int j = 0; j < size; ++j) {
637                     dataI[j] *= inv;
638                 }
639 
640             } while (norm2 * size < 0.01);
641         }
642 
643         return MatrixUtils.createRealMatrix(data);
644 
645     }
646 }