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 java.util.Random;
26  
27  import org.hipparchus.exception.MathIllegalArgumentException;
28  import org.junit.Assert;
29  import org.junit.Test;
30  
31  public class RRQRSolverTest {
32      double[][] testData3x3NonSingular = {
33              { 12, -51,   4 },
34              {  6, 167, -68 },
35              { -4,  24, -41 }
36      };
37  
38      double[][] testData3x3Singular = {
39              { 1, 2,  2 },
40              { 2, 4,  6 },
41              { 4, 8, 12 }
42      };
43  
44      double[][] testData3x4 = {
45              { 12, -51,   4, 1 },
46              {  6, 167, -68, 2 },
47              { -4,  24, -41, 3 }
48      };
49  
50      double[][] testData4x3 = {
51              { 12, -51,   4 },
52              {  6, 167, -68 },
53              { -4,  24, -41 },
54              { -5,  34,   7 }
55      };
56  
57      /** test rank */
58      @Test
59      public void testRank() {
60          DecompositionSolver solver =
61              new RRQRDecomposition(MatrixUtils.createRealMatrix(testData3x3NonSingular), 1.0e-16).getSolver();
62          Assert.assertTrue(solver.isNonSingular());
63  
64          solver = new RRQRDecomposition(MatrixUtils.createRealMatrix(testData3x3Singular), 1.0e-16).getSolver();
65          Assert.assertFalse(solver.isNonSingular());
66  
67          solver = new RRQRDecomposition(MatrixUtils.createRealMatrix(testData3x4), 1.0e-16).getSolver();
68          Assert.assertTrue(solver.isNonSingular());
69  
70          solver = new RRQRDecomposition(MatrixUtils.createRealMatrix(testData4x3), 1.0e-16).getSolver();
71          Assert.assertTrue(solver.isNonSingular());
72  
73      }
74  
75      /** test solve dimension errors */
76      @Test
77      public void testSolveDimensionErrors() {
78          DecompositionSolver solver =
79              new RRQRDecomposition(MatrixUtils.createRealMatrix(testData3x3NonSingular)).getSolver();
80          RealMatrix b = MatrixUtils.createRealMatrix(new double[2][2]);
81          try {
82              solver.solve(b);
83              Assert.fail("an exception should have been thrown");
84          } catch (MathIllegalArgumentException iae) {
85              // expected behavior
86          }
87          try {
88              solver.solve(b.getColumnVector(0));
89              Assert.fail("an exception should have been thrown");
90          } catch (MathIllegalArgumentException iae) {
91              // expected behavior
92          }
93  
94      }
95  
96      /** test solve rank errors */
97      @Test
98      public void testSolveRankErrors() {
99          DecompositionSolver solver =
100             new RRQRDecomposition(MatrixUtils.createRealMatrix(testData3x3Singular), 1.0e-16).getSolver();
101         RealMatrix b = MatrixUtils.createRealMatrix(new double[3][2]);
102         try {
103             solver.solve(b);
104             Assert.fail("an exception should have been thrown");
105         } catch (MathIllegalArgumentException iae) {
106             // expected behavior
107         }
108         try {
109             solver.solve(b.getColumnVector(0));
110             Assert.fail("an exception should have been thrown");
111         } catch (MathIllegalArgumentException iae) {
112             // expected behavior
113         }
114 
115     }
116 
117     /** test solve */
118     @Test
119     public void testSolve() {
120         RealMatrix b = MatrixUtils.createRealMatrix(new double[][] {
121                 { -102, 12250 }, { 544, 24500 }, { 167, -36750 }
122         });
123         RealMatrix xRef = MatrixUtils.createRealMatrix(new double[][] {
124                 { 1, 2515 }, { 2, 422 }, { -3, 898 }
125         });
126 
127 
128         RRQRDecomposition decomposition = new RRQRDecomposition(MatrixUtils.createRealMatrix(testData3x3NonSingular));
129         DecompositionSolver solver = decomposition.getSolver();
130         Assert.assertEquals(testData3x3NonSingular.length, solver.getRowDimension());
131         Assert.assertEquals(testData3x3NonSingular[0].length, solver.getColumnDimension());
132 
133         // using RealMatrix
134         Assert.assertEquals(0, solver.solve(b).subtract(xRef).getNorm1(), 4.0e-16 * xRef.getNorm1());
135 
136         // using ArrayRealVector
137         for (int i = 0; i < b.getColumnDimension(); ++i) {
138             final RealVector x = solver.solve(b.getColumnVector(i));
139             final double error = x.subtract(xRef.getColumnVector(i)).getNorm();
140             Assert.assertEquals(0, error, 3.0e-16 * xRef.getColumnVector(i).getNorm());
141         }
142 
143         // using RealVector with an alternate implementation
144         for (int i = 0; i < b.getColumnDimension(); ++i) {
145             ArrayRealVectorTest.RealVectorTestImpl v =
146                 new ArrayRealVectorTest.RealVectorTestImpl(b.getColumn(i));
147             final RealVector x = solver.solve(v);
148             final double error = x.subtract(xRef.getColumnVector(i)).getNorm();
149             Assert.assertEquals(0, error, 3.0e-16 * xRef.getColumnVector(i).getNorm());
150         }
151 
152     }
153 
154     @Test
155     public void testOverdetermined() {
156         final Random r    = new Random(5559252868205245l);
157         int          p    = (7 * BlockRealMatrix.BLOCK_SIZE) / 4;
158         int          q    = (5 * BlockRealMatrix.BLOCK_SIZE) / 4;
159         RealMatrix   a    = createTestMatrix(r, p, q);
160         RealMatrix   xRef = createTestMatrix(r, q, BlockRealMatrix.BLOCK_SIZE + 3);
161 
162         // build a perturbed system: A.X + noise = B
163         RealMatrix b = a.multiply(xRef);
164         final double noise = 0.001;
165         b.walkInOptimizedOrder(new DefaultRealMatrixChangingVisitor() {
166             @Override
167             public double visit(int row, int column, double value) {
168                 return value * (1.0 + noise * (2 * r.nextDouble() - 1));
169             }
170         });
171 
172         // despite perturbation, the least square solution should be pretty good
173         RealMatrix x = new RRQRDecomposition(a).getSolver().solve(b);
174         Assert.assertEquals(0, x.subtract(xRef).getNorm1(), 0.01 * noise * p * q);
175 
176     }
177 
178     @Test
179     public void testUnderdetermined() {
180         final Random r    = new Random(42185006424567123l);
181         int          p    = (5 * BlockRealMatrix.BLOCK_SIZE) / 4;
182         int          q    = (7 * BlockRealMatrix.BLOCK_SIZE) / 4;
183         RealMatrix   a    = createTestMatrix(r, p, q);
184         RealMatrix   xRef = createTestMatrix(r, q, BlockRealMatrix.BLOCK_SIZE + 3);
185         RealMatrix   b    = a.multiply(xRef);
186         RRQRDecomposition rrqrd = new RRQRDecomposition(a);
187         RealMatrix   x = rrqrd.getSolver().solve(b);
188 
189         // too many equations, the system cannot be solved at all
190         Assert.assertTrue(x.subtract(xRef).getNorm1() / (p * q) > 0.01);
191 
192         // the last permuted unknown should have been set to 0
193         RealMatrix permuted = rrqrd.getP().transposeMultiply(x);
194         Assert.assertEquals(0.0, permuted.getSubMatrix(p, q - 1, 0, permuted.getColumnDimension() - 1).getNorm1(), 0);
195 
196     }
197 
198     private RealMatrix createTestMatrix(final Random r, final int rows, final int columns) {
199         RealMatrix m = MatrixUtils.createRealMatrix(rows, columns);
200         m.walkInOptimizedOrder(new DefaultRealMatrixChangingVisitor() {
201                 @Override
202                     public double visit(int row, int column, double value) {
203                     return 2.0 * r.nextDouble() - 1.0;
204                 }
205             });
206         return m;
207     }
208 }