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.MatrixUtils;
027    import org.apache.commons.math.linear.RealMatrix;
028    import org.apache.commons.math.linear.ArrayRealVector;
029    import org.apache.commons.math.linear.SingularValueDecompositionImpl;
030    
031    public class SingularValueSolverTest extends TestCase {
032    
033        private double[][] testSquare = {
034                { 24.0 / 25.0, 43.0 / 25.0 },
035                { 57.0 / 25.0, 24.0 / 25.0 }
036        };
037    
038        private static final double normTolerance = 10e-14;
039    
040        public SingularValueSolverTest(String name) {
041            super(name);
042        }
043    
044        public static Test suite() {
045            TestSuite suite = new TestSuite(SingularValueSolverTest.class);
046            suite.setName("SingularValueSolver Tests");
047            return suite;
048        }
049    
050        /** test solve dimension errors */
051        public void testSolveDimensionErrors() {
052            DecompositionSolver solver =
053                new SingularValueDecompositionImpl(MatrixUtils.createRealMatrix(testSquare)).getSolver();
054            RealMatrix b = MatrixUtils.createRealMatrix(new double[3][2]);
055            try {
056                solver.solve(b);
057                fail("an exception should have been thrown");
058            } catch (IllegalArgumentException iae) {
059                // expected behavior
060            } catch (Exception e) {
061                fail("wrong exception caught");
062            }
063            try {
064                solver.solve(b.getColumn(0));
065                fail("an exception should have been thrown");
066            } catch (IllegalArgumentException iae) {
067                // expected behavior
068            } catch (Exception e) {
069                fail("wrong exception caught");
070            }
071            try {
072                solver.solve(new ArrayRealVectorTest.RealVectorTestImpl(b.getColumn(0)));
073                fail("an exception should have been thrown");
074            } catch (IllegalArgumentException iae) {
075                // expected behavior
076            } catch (Exception e) {
077                fail("wrong exception caught");
078            }
079        }
080    
081        /** test solve singularity errors */
082        public void testSolveSingularityErrors() {
083            RealMatrix m =
084                MatrixUtils.createRealMatrix(new double[][] {
085                                       { 1.0, 0.0 },
086                                       { 0.0, 0.0 }
087                                   });
088            DecompositionSolver solver = new SingularValueDecompositionImpl(m).getSolver();
089            RealMatrix b = MatrixUtils.createRealMatrix(new double[2][2]);
090            try {
091                solver.solve(b);
092                fail("an exception should have been thrown");
093            } catch (InvalidMatrixException ime) {
094                // expected behavior
095            } catch (Exception e) {
096                fail("wrong exception caught");
097            }
098            try {
099                solver.solve(b.getColumn(0));
100                fail("an exception should have been thrown");
101            } catch (InvalidMatrixException ime) {
102                // expected behavior
103            } catch (Exception e) {
104                fail("wrong exception caught");
105            }
106            try {
107                solver.solve(b.getColumnVector(0));
108                fail("an exception should have been thrown");
109            } catch (InvalidMatrixException ime) {
110                // expected behavior
111            } catch (Exception e) {
112                fail("wrong exception caught");
113            }
114            try {
115                solver.solve(new ArrayRealVectorTest.RealVectorTestImpl(b.getColumn(0)));
116                fail("an exception should have been thrown");
117            } catch (InvalidMatrixException ime) {
118                // expected behavior
119            } catch (Exception e) {
120                fail("wrong exception caught");
121            }
122        }
123    
124        /** test solve */
125        public void testSolve() {
126            DecompositionSolver solver =
127                new SingularValueDecompositionImpl(MatrixUtils.createRealMatrix(testSquare)).getSolver();
128            RealMatrix b = MatrixUtils.createRealMatrix(new double[][] {
129                    { 1, 2, 3 }, { 0, -5, 1 }
130            });
131            RealMatrix xRef = MatrixUtils.createRealMatrix(new double[][] {
132                    { -8.0 / 25.0, -263.0 / 75.0, -29.0 / 75.0 },
133                    { 19.0 / 25.0,   78.0 / 25.0,  49.0 / 25.0 }
134            });
135    
136            // using RealMatrix
137            assertEquals(0, solver.solve(b).subtract(xRef).getNorm(), normTolerance);
138    
139            // using double[]
140            for (int i = 0; i < b.getColumnDimension(); ++i) {
141                assertEquals(0,
142                             new ArrayRealVector(solver.solve(b.getColumn(i))).subtract(xRef.getColumnVector(i)).getNorm(),
143                             1.0e-13);
144            }
145    
146            // using Array2DRowRealMatrix
147            for (int i = 0; i < b.getColumnDimension(); ++i) {
148                assertEquals(0,
149                             solver.solve(b.getColumnVector(i)).subtract(xRef.getColumnVector(i)).getNorm(),
150                             1.0e-13);
151            }
152    
153            // using RealMatrix with an alternate implementation
154            for (int i = 0; i < b.getColumnDimension(); ++i) {
155                ArrayRealVectorTest.RealVectorTestImpl v =
156                    new ArrayRealVectorTest.RealVectorTestImpl(b.getColumn(i));
157                assertEquals(0,
158                             solver.solve(v).subtract(xRef.getColumnVector(i)).getNorm(),
159                             1.0e-13);
160            }
161    
162        }
163    
164        /** test condition number */
165        public void testConditionNumber() {
166            SingularValueDecompositionImpl svd =
167                new SingularValueDecompositionImpl(MatrixUtils.createRealMatrix(testSquare));
168            assertEquals(3.0, svd.getConditionNumber(), 1.0e-15);
169        }
170    
171    }