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.exception.MathIllegalArgumentException;
26  import org.hipparchus.util.Precision;
27  import org.junit.jupiter.api.Test;
28  
29  import java.util.Random;
30  
31  import static org.junit.jupiter.api.Assertions.assertEquals;
32  import static org.junit.jupiter.api.Assertions.assertFalse;
33  import static org.junit.jupiter.api.Assertions.assertThrows;
34  import static org.junit.jupiter.api.Assertions.assertTrue;
35  import static org.junit.jupiter.api.Assertions.fail;
36  
37  class EigenSolverTest {
38  
39      private double[][] bigSingular = {
40          { 1.0, 2.0,   3.0,    4.0 },
41          { 2.0, 5.0,   3.0,    4.0 },
42          { 7.0, 3.0, 256.0, 1930.0 },
43          { 3.0, 7.0,   6.0,    8.0 }
44      }; // 4th row = 1st + 2nd
45  
46      /** test non invertible matrix */
47      @Test
48      void testNonInvertible() {
49          Random r = new Random(9994100315209l);
50          RealMatrix m =
51              EigenDecompositionSymmetricTest.createTestMatrix(r, new double[] { 1.0, 0.0, -1.0, -2.0, -3.0 });
52          DecompositionSolver es = new EigenDecompositionSymmetric(m).getSolver();
53          assertFalse(es.isNonSingular());
54          try {
55              es.getInverse();
56              fail("an exception should have been thrown");
57          } catch (MathIllegalArgumentException ime) {
58              // expected behavior
59          }
60      }
61  
62      /** test invertible matrix */
63      @Test
64      void testInvertible() {
65          Random r = new Random(9994100315209l);
66          RealMatrix m =
67              EigenDecompositionSymmetricTest.createTestMatrix(r, new double[] { 1.0, 0.5, -1.0, -2.0, -3.0 });
68          DecompositionSolver es = new EigenDecompositionSymmetric(m).getSolver();
69          assertTrue(es.isNonSingular());
70          RealMatrix inverse = es.getInverse();
71          RealMatrix error =
72              m.multiply(inverse).subtract(MatrixUtils.createRealIdentityMatrix(m.getRowDimension()));
73          assertEquals(0, error.getNorm1(), 4.0e-15);
74      }
75  
76      /**
77       * Verifies operation on very small values.
78       * Matrix with eigenvalues {8e-100, -1e-100, -1e-100}
79       */
80      @Test
81      void testInvertibleTinyValues() {
82          final double tiny = 1e-100;
83          RealMatrix m = MatrixUtils.createRealMatrix(new double[][] {
84                  {3,  2,  4},
85                  {2,  0,  2},
86                  {4,  2,  3}
87          });
88          m = m.scalarMultiply(tiny);
89  
90          final EigenDecompositionSymmetric ed = new EigenDecompositionSymmetric(m);
91          RealMatrix inv = ed.getSolver().getInverse();
92  
93          final RealMatrix id = m.multiply(inv);
94          for (int i = 0; i < m.getRowDimension(); i++) {
95              for (int j = 0; j < m.getColumnDimension(); j++) {
96                  if (i == j) {
97                      assertTrue(Precision.equals(1, id.getEntry(i, j), 1e-15));
98                  } else {
99                      assertTrue(Precision.equals(0, id.getEntry(i, j), 1e-15));
100                 }
101             }
102         }
103     }
104 
105     @Test
106     void testNonInvertibleMath1045() {
107         assertThrows(MathIllegalArgumentException.class, () -> {
108             EigenDecompositionSymmetric eigen =
109                 new EigenDecompositionSymmetric(MatrixUtils.createRealMatrix(bigSingular));
110             eigen.getSolver().getInverse();
111         });
112     }
113 
114     @Test
115     void testZeroMatrix() {
116         assertThrows(MathIllegalArgumentException.class, () -> {
117             EigenDecompositionSymmetric eigen =
118                 new EigenDecompositionSymmetric(MatrixUtils.createRealMatrix(new double[][]{{0}}));
119             eigen.getSolver().getInverse();
120         });
121     }
122 
123     /** test solve dimension errors */
124     @Test
125     void testSolveDimensionErrors() {
126         final double[] refValues = new double[] {
127             2.003, 2.002, 2.001, 1.001, 1.000, 0.001
128         };
129         final RealMatrix matrix = EigenDecompositionSymmetricTest.createTestMatrix(new Random(35992629946426l), refValues);
130 
131         DecompositionSolver es = new EigenDecompositionSymmetric(matrix).getSolver();
132         RealMatrix b = MatrixUtils.createRealMatrix(new double[2][2]);
133         try {
134             es.solve(b);
135             fail("an exception should have been thrown");
136         } catch (MathIllegalArgumentException iae) {
137             // expected behavior
138         }
139         try {
140             es.solve(b.getColumnVector(0));
141             fail("an exception should have been thrown");
142         } catch (MathIllegalArgumentException iae) {
143             // expected behavior
144         }
145         try {
146             es.solve(new ArrayRealVectorTest.RealVectorTestImpl(b.getColumn(0)));
147             fail("an exception should have been thrown");
148         } catch (MathIllegalArgumentException iae) {
149             // expected behavior
150         }
151     }
152 
153     /** test solve */
154     @Test
155     void testSolve() {
156         RealMatrix m = MatrixUtils.createRealMatrix(new double[][] {
157                 { 91,  5, 29, 32, 40, 14 },
158                 {  5, 34, -1,  0,  2, -1 },
159                 { 29, -1, 12,  9, 21,  8 },
160                 { 32,  0,  9, 14,  9,  0 },
161                 { 40,  2, 21,  9, 51, 19 },
162                 { 14, -1,  8,  0, 19, 14 }
163         });
164         DecompositionSolver es = new EigenDecompositionSymmetric(m).getSolver();
165         RealMatrix b = MatrixUtils.createRealMatrix(new double[][] {
166                 { 1561, 269, 188 },
167                 {   69, -21,  70 },
168                 {  739, 108,  63 },
169                 {  324,  86,  59 },
170                 { 1624, 194, 107 },
171                 {  796,  69,  36 }
172         });
173         RealMatrix xRef = MatrixUtils.createRealMatrix(new double[][] {
174                 { 1,   2, 1 },
175                 { 2,  -1, 2 },
176                 { 4,   2, 3 },
177                 { 8,  -1, 0 },
178                 { 16,  2, 0 },
179                 { 32, -1, 0 }
180         });
181 
182         // using RealMatrix
183         RealMatrix solution=es.solve(b);
184         assertEquals(0, solution.subtract(xRef).getNorm1(), 2.5e-12);
185 
186         // using RealVector
187         for (int i = 0; i < b.getColumnDimension(); ++i) {
188             assertEquals(0,
189                          es.solve(b.getColumnVector(i)).subtract(xRef.getColumnVector(i)).getNorm(),
190                          2.0e-11);
191         }
192 
193         // using RealVector with an alternate implementation
194         for (int i = 0; i < b.getColumnDimension(); ++i) {
195             ArrayRealVectorTest.RealVectorTestImpl v =
196                 new ArrayRealVectorTest.RealVectorTestImpl(b.getColumn(i));
197             assertEquals(0,
198                          es.solve(v).subtract(xRef.getColumnVector(i)).getNorm(),
199                          2.0e-11);
200         }
201     }
202 }