001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.commons.math.linear;
019    
020    import junit.framework.Test;
021    import junit.framework.TestCase;
022    import junit.framework.TestSuite;
023    
024    import org.apache.commons.math.linear.DecompositionSolver;
025    import org.apache.commons.math.linear.InvalidMatrixException;
026    import org.apache.commons.math.linear.LUDecompositionImpl;
027    import org.apache.commons.math.linear.MatrixUtils;
028    import org.apache.commons.math.linear.RealMatrix;
029    import org.apache.commons.math.linear.ArrayRealVector;
030    
031    public class LUSolverTest extends TestCase {
032        private double[][] testData = {
033                { 1.0, 2.0, 3.0},
034                { 2.0, 5.0, 3.0},
035                { 1.0, 0.0, 8.0}
036        };
037        private double[][] luData = {
038                { 2.0, 3.0, 3.0 },
039                { 0.0, 5.0, 7.0 },
040                { 6.0, 9.0, 8.0 }
041        };
042        
043        // singular matrices
044        private double[][] singular = {
045                { 2.0, 3.0 },
046                { 2.0, 3.0 }
047        };
048        private double[][] bigSingular = {
049                { 1.0, 2.0,   3.0,    4.0 },
050                { 2.0, 5.0,   3.0,    4.0 },
051                { 7.0, 3.0, 256.0, 1930.0 },
052                { 3.0, 7.0,   6.0,    8.0 }
053        }; // 4th row = 1st + 2nd
054    
055        public LUSolverTest(String name) {
056            super(name);
057        }
058    
059        public static Test suite() {
060            TestSuite suite = new TestSuite(LUSolverTest.class);
061            suite.setName("LUSolver Tests");
062            return suite;
063        }
064    
065        /** test threshold impact */
066        public void testThreshold() {
067            final RealMatrix matrix = MatrixUtils.createRealMatrix(new double[][] {
068                                                           { 1.0, 2.0, 3.0},
069                                                           { 2.0, 5.0, 3.0},
070                                                           { 4.000001, 9.0, 9.0}
071                                                         });
072            assertFalse(new LUDecompositionImpl(matrix, 1.0e-5).getSolver().isNonSingular());
073            assertTrue(new LUDecompositionImpl(matrix, 1.0e-10).getSolver().isNonSingular());
074        }
075    
076        /** test singular */
077        public void testSingular() {
078            DecompositionSolver solver =
079                new LUDecompositionImpl(MatrixUtils.createRealMatrix(testData)).getSolver();
080            assertTrue(solver.isNonSingular());
081            solver = new LUDecompositionImpl(MatrixUtils.createRealMatrix(singular)).getSolver();
082            assertFalse(solver.isNonSingular());
083            solver = new LUDecompositionImpl(MatrixUtils.createRealMatrix(bigSingular)).getSolver();
084            assertFalse(solver.isNonSingular());
085        }
086    
087        /** test solve dimension errors */
088        public void testSolveDimensionErrors() {
089            DecompositionSolver solver =
090                new LUDecompositionImpl(MatrixUtils.createRealMatrix(testData)).getSolver();
091            RealMatrix b = MatrixUtils.createRealMatrix(new double[2][2]);
092            try {
093                solver.solve(b);
094                fail("an exception should have been thrown");
095            } catch (IllegalArgumentException iae) {
096                // expected behavior
097            } catch (Exception e) {
098                fail("wrong exception caught");
099            }
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    }