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.junit.jupiter.api.Test;
27  
28  import static org.junit.jupiter.api.Assertions.assertEquals;
29  import static org.junit.jupiter.api.Assertions.assertFalse;
30  import static org.junit.jupiter.api.Assertions.assertTrue;
31  import static org.junit.jupiter.api.Assertions.fail;
32  
33  class LUSolverTest {
34      private double[][] testData = {
35              { 1.0, 2.0, 3.0},
36              { 2.0, 5.0, 3.0},
37              { 1.0, 0.0, 8.0}
38      };
39      private double[][] luData = {
40              { 2.0, 3.0, 3.0 },
41              { 0.0, 5.0, 7.0 },
42              { 6.0, 9.0, 8.0 }
43      };
44  
45      // singular matrices
46      private double[][] singular = {
47              { 2.0, 3.0 },
48              { 2.0, 3.0 }
49      };
50      private double[][] bigSingular = {
51              { 1.0, 2.0,   3.0,    4.0 },
52              { 2.0, 5.0,   3.0,    4.0 },
53              { 7.0, 3.0, 256.0, 1930.0 },
54              { 3.0, 7.0,   6.0,    8.0 }
55      }; // 4th row = 1st + 2nd
56  
57      /** test threshold impact */
58      @Test
59      void testThreshold() {
60          final RealMatrix matrix = MatrixUtils.createRealMatrix(new double[][] {
61                                                         { 1.0, 2.0, 3.0},
62                                                         { 2.0, 5.0, 3.0},
63                                                         { 4.000001, 9.0, 9.0}
64                                                       });
65          assertFalse(new LUDecomposer(1.0e-5).decompose(matrix).isNonSingular());
66          assertTrue(new LUDecomposer(1.0e-10).decompose(matrix).isNonSingular());
67      }
68  
69      /** test singular */
70      @Test
71      void testSingular() {
72          DecompositionSolver solver =
73              new LUDecomposition(MatrixUtils.createRealMatrix(testData)).getSolver();
74          assertTrue(solver.isNonSingular());
75          solver = new LUDecomposition(MatrixUtils.createRealMatrix(singular)).getSolver();
76          assertFalse(solver.isNonSingular());
77          solver = new LUDecomposition(MatrixUtils.createRealMatrix(bigSingular)).getSolver();
78          assertFalse(solver.isNonSingular());
79      }
80  
81      /** test solve dimension errors */
82      @Test
83      void testSolveDimensionErrors() {
84          DecompositionSolver solver =
85              new LUDecomposition(MatrixUtils.createRealMatrix(testData)).getSolver();
86          RealMatrix b = MatrixUtils.createRealMatrix(new double[2][2]);
87          try {
88              solver.solve(b);
89              fail("an exception should have been thrown");
90          } catch (MathIllegalArgumentException iae) {
91              // expected behavior
92          }
93          try {
94              solver.solve(b.getColumnVector(0));
95              fail("an exception should have been thrown");
96          } catch (MathIllegalArgumentException iae) {
97              // expected behavior
98          }
99          try {
100             solver.solve(new ArrayRealVectorTest.RealVectorTestImpl(b.getColumn(0)));
101             fail("an exception should have been thrown");
102         } catch (MathIllegalArgumentException iae) {
103             // expected behavior
104         }
105     }
106 
107     /** test solve singularity errors */
108     @Test
109     void testSolveSingularityErrors() {
110         DecompositionSolver solver =
111             new LUDecomposition(MatrixUtils.createRealMatrix(singular)).getSolver();
112         RealMatrix b = MatrixUtils.createRealMatrix(new double[2][2]);
113         try {
114             solver.solve(b);
115             fail("an exception should have been thrown");
116         } catch (MathIllegalArgumentException ime) {
117             // expected behavior
118         }
119         try {
120             solver.solve(b.getColumnVector(0));
121             fail("an exception should have been thrown");
122         } catch (MathIllegalArgumentException ime) {
123             // expected behavior
124         }
125         try {
126             solver.solve(new ArrayRealVectorTest.RealVectorTestImpl(b.getColumn(0)));
127             fail("an exception should have been thrown");
128         } catch (MathIllegalArgumentException ime) {
129             // expected behavior
130         }
131     }
132 
133     /** test solve */
134     @Test
135     void testSolve() {
136         DecompositionSolver solver =
137             new LUDecomposition(MatrixUtils.createRealMatrix(testData)).getSolver();
138         RealMatrix b = MatrixUtils.createRealMatrix(new double[][] {
139                 { 1, 0 }, { 2, -5 }, { 3, 1 }
140         });
141         RealMatrix xRef = MatrixUtils.createRealMatrix(new double[][] {
142                 { 19, -71 }, { -6, 22 }, { -2, 9 }
143         });
144 
145         // using RealMatrix
146         assertEquals(0, solver.solve(b).subtract(xRef).getNorm1(), 1.0e-13);
147 
148         // using ArrayRealVector
149         for (int i = 0; i < b.getColumnDimension(); ++i) {
150             assertEquals(0,
151                          solver.solve(b.getColumnVector(i)).subtract(xRef.getColumnVector(i)).getNorm(),
152                          1.0e-13);
153         }
154 
155         // using RealVector with an alternate implementation
156         for (int i = 0; i < b.getColumnDimension(); ++i) {
157             ArrayRealVectorTest.RealVectorTestImpl v =
158                 new ArrayRealVectorTest.RealVectorTestImpl(b.getColumn(i));
159             assertEquals(0,
160                          solver.solve(v).subtract(xRef.getColumnVector(i)).getNorm(),
161                          1.0e-13);
162         }
163     }
164 
165     /** test determinant */
166     @Test
167     void testDeterminant() {
168         assertEquals( -1, getDeterminant(MatrixUtils.createRealMatrix(testData)), 1.0e-15);
169         assertEquals(-10, getDeterminant(MatrixUtils.createRealMatrix(luData)), 1.0e-14);
170         assertEquals(  0, getDeterminant(MatrixUtils.createRealMatrix(singular)), 1.0e-17);
171         assertEquals(  0, getDeterminant(MatrixUtils.createRealMatrix(bigSingular)), 1.0e-10);
172     }
173 
174     private double getDeterminant(RealMatrix m) {
175         return new LUDecomposition(m).getDeterminant();
176     }
177 }