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.math3.linear;
019    
020    import org.apache.commons.math3.exception.MathIllegalArgumentException;
021    
022    import org.junit.Test;
023    import org.junit.Assert;
024    
025    public class LUSolverTest {
026        private double[][] testData = {
027                { 1.0, 2.0, 3.0},
028                { 2.0, 5.0, 3.0},
029                { 1.0, 0.0, 8.0}
030        };
031        private double[][] luData = {
032                { 2.0, 3.0, 3.0 },
033                { 0.0, 5.0, 7.0 },
034                { 6.0, 9.0, 8.0 }
035        };
036    
037        // singular matrices
038        private double[][] singular = {
039                { 2.0, 3.0 },
040                { 2.0, 3.0 }
041        };
042        private double[][] bigSingular = {
043                { 1.0, 2.0,   3.0,    4.0 },
044                { 2.0, 5.0,   3.0,    4.0 },
045                { 7.0, 3.0, 256.0, 1930.0 },
046                { 3.0, 7.0,   6.0,    8.0 }
047        }; // 4th row = 1st + 2nd
048    
049        /** test threshold impact */
050        @Test
051        public void testThreshold() {
052            final RealMatrix matrix = MatrixUtils.createRealMatrix(new double[][] {
053                                                           { 1.0, 2.0, 3.0},
054                                                           { 2.0, 5.0, 3.0},
055                                                           { 4.000001, 9.0, 9.0}
056                                                         });
057            Assert.assertFalse(new LUDecomposition(matrix, 1.0e-5).getSolver().isNonSingular());
058            Assert.assertTrue(new LUDecomposition(matrix, 1.0e-10).getSolver().isNonSingular());
059        }
060    
061        /** test singular */
062        @Test
063        public void testSingular() {
064            DecompositionSolver solver =
065                new LUDecomposition(MatrixUtils.createRealMatrix(testData)).getSolver();
066            Assert.assertTrue(solver.isNonSingular());
067            solver = new LUDecomposition(MatrixUtils.createRealMatrix(singular)).getSolver();
068            Assert.assertFalse(solver.isNonSingular());
069            solver = new LUDecomposition(MatrixUtils.createRealMatrix(bigSingular)).getSolver();
070            Assert.assertFalse(solver.isNonSingular());
071        }
072    
073        /** test solve dimension errors */
074        @Test
075        public void testSolveDimensionErrors() {
076            DecompositionSolver solver =
077                new LUDecomposition(MatrixUtils.createRealMatrix(testData)).getSolver();
078            RealMatrix b = MatrixUtils.createRealMatrix(new double[2][2]);
079            try {
080                solver.solve(b);
081                Assert.fail("an exception should have been thrown");
082            } catch (MathIllegalArgumentException iae) {
083                // expected behavior
084            }
085            try {
086                solver.solve(b.getColumnVector(0));
087                Assert.fail("an exception should have been thrown");
088            } catch (MathIllegalArgumentException iae) {
089                // expected behavior
090            }
091            try {
092                solver.solve(new ArrayRealVectorTest.RealVectorTestImpl(b.getColumn(0)));
093                Assert.fail("an exception should have been thrown");
094            } catch (MathIllegalArgumentException iae) {
095                // expected behavior
096            }
097        }
098    
099        /** test solve singularity errors */
100        @Test
101        public void testSolveSingularityErrors() {
102            DecompositionSolver solver =
103                new LUDecomposition(MatrixUtils.createRealMatrix(singular)).getSolver();
104            RealMatrix b = MatrixUtils.createRealMatrix(new double[2][2]);
105            try {
106                solver.solve(b);
107                Assert.fail("an exception should have been thrown");
108            } catch (SingularMatrixException ime) {
109                // expected behavior
110            }
111            try {
112                solver.solve(b.getColumnVector(0));
113                Assert.fail("an exception should have been thrown");
114            } catch (SingularMatrixException ime) {
115                // expected behavior
116            }
117            try {
118                solver.solve(new ArrayRealVectorTest.RealVectorTestImpl(b.getColumn(0)));
119                Assert.fail("an exception should have been thrown");
120            } catch (SingularMatrixException ime) {
121                // expected behavior
122            }
123        }
124    
125        /** test solve */
126        @Test
127        public void testSolve() {
128            DecompositionSolver solver =
129                new LUDecomposition(MatrixUtils.createRealMatrix(testData)).getSolver();
130            RealMatrix b = MatrixUtils.createRealMatrix(new double[][] {
131                    { 1, 0 }, { 2, -5 }, { 3, 1 }
132            });
133            RealMatrix xRef = MatrixUtils.createRealMatrix(new double[][] {
134                    { 19, -71 }, { -6, 22 }, { -2, 9 }
135            });
136    
137            // using RealMatrix
138            Assert.assertEquals(0, solver.solve(b).subtract(xRef).getNorm(), 1.0e-13);
139    
140            // using ArrayRealVector
141            for (int i = 0; i < b.getColumnDimension(); ++i) {
142                Assert.assertEquals(0,
143                             solver.solve(b.getColumnVector(i)).subtract(xRef.getColumnVector(i)).getNorm(),
144                             1.0e-13);
145            }
146    
147            // using RealVector with an alternate implementation
148            for (int i = 0; i < b.getColumnDimension(); ++i) {
149                ArrayRealVectorTest.RealVectorTestImpl v =
150                    new ArrayRealVectorTest.RealVectorTestImpl(b.getColumn(i));
151                Assert.assertEquals(0,
152                             solver.solve(v).subtract(xRef.getColumnVector(i)).getNorm(),
153                             1.0e-13);
154            }
155        }
156    
157        /** test determinant */
158        @Test
159        public void testDeterminant() {
160            Assert.assertEquals( -1, getDeterminant(MatrixUtils.createRealMatrix(testData)), 1.0e-15);
161            Assert.assertEquals(-10, getDeterminant(MatrixUtils.createRealMatrix(luData)), 1.0e-14);
162            Assert.assertEquals(  0, getDeterminant(MatrixUtils.createRealMatrix(singular)), 1.0e-17);
163            Assert.assertEquals(  0, getDeterminant(MatrixUtils.createRealMatrix(bigSingular)), 1.0e-10);
164        }
165    
166        private double getDeterminant(RealMatrix m) {
167            return new LUDecomposition(m).getDeterminant();
168        }
169    }