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