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