1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package org.hipparchus.linear;
24
25 import org.hipparchus.exception.MathIllegalArgumentException;
26 import org.junit.jupiter.api.Test;
27
28 import java.util.Random;
29
30 import static org.junit.jupiter.api.Assertions.assertEquals;
31 import static org.junit.jupiter.api.Assertions.assertFalse;
32 import static org.junit.jupiter.api.Assertions.assertTrue;
33 import static org.junit.jupiter.api.Assertions.fail;
34
35 class QRSolverTest {
36 double[][] testData3x3NonSingular = {
37 { 12, -51, 4 },
38 { 6, 167, -68 },
39 { -4, 24, -41 }
40 };
41
42 double[][] testData3x3Singular = {
43 { 1, 2, 2 },
44 { 2, 4, 6 },
45 { 4, 8, 12 }
46 };
47
48 double[][] testData3x4 = {
49 { 12, -51, 4, 1 },
50 { 6, 167, -68, 2 },
51 { -4, 24, -41, 3 }
52 };
53
54 double[][] testData4x3 = {
55 { 12, -51, 4 },
56 { 6, 167, -68 },
57 { -4, 24, -41 },
58 { -5, 34, 7 }
59 };
60
61
62 @Test
63 void testRank() {
64 DecompositionSolver solver =
65 new QRDecomposition(MatrixUtils.createRealMatrix(testData3x3NonSingular)).getSolver();
66 assertTrue(solver.isNonSingular());
67
68 solver = new QRDecomposition(MatrixUtils.createRealMatrix(testData3x3Singular)).getSolver();
69 assertFalse(solver.isNonSingular());
70
71 solver = new QRDecomposition(MatrixUtils.createRealMatrix(testData3x4)).getSolver();
72 assertTrue(solver.isNonSingular());
73
74 solver = new QRDecomposition(MatrixUtils.createRealMatrix(testData4x3)).getSolver();
75 assertTrue(solver.isNonSingular());
76
77 }
78
79
80 @Test
81 void testSolveDimensionErrors() {
82 DecompositionSolver solver =
83 new QRDecomposition(MatrixUtils.createRealMatrix(testData3x3NonSingular)).getSolver();
84 RealMatrix b = MatrixUtils.createRealMatrix(new double[2][2]);
85 try {
86 solver.solve(b);
87 fail("an exception should have been thrown");
88 } catch (MathIllegalArgumentException iae) {
89
90 }
91 try {
92 solver.solve(b.getColumnVector(0));
93 fail("an exception should have been thrown");
94 } catch (MathIllegalArgumentException iae) {
95
96 }
97 }
98
99
100 @Test
101 void testSolveRankErrors() {
102 DecompositionSolver solver =
103 new QRDecomposition(MatrixUtils.createRealMatrix(testData3x3Singular)).getSolver();
104 RealMatrix b = MatrixUtils.createRealMatrix(new double[3][2]);
105 try {
106 solver.solve(b);
107 fail("an exception should have been thrown");
108 } catch (MathIllegalArgumentException iae) {
109
110 }
111 try {
112 solver.solve(b.getColumnVector(0));
113 fail("an exception should have been thrown");
114 } catch (MathIllegalArgumentException iae) {
115
116 }
117 }
118
119
120 @Test
121 void testSolve() {
122 QRDecomposition decomposition =
123 new QRDecomposition(MatrixUtils.createRealMatrix(testData3x3NonSingular));
124 DecompositionSolver solver = decomposition.getSolver();
125 RealMatrix b = MatrixUtils.createRealMatrix(new double[][] {
126 { -102, 12250 }, { 544, 24500 }, { 167, -36750 }
127 });
128 RealMatrix xRef = MatrixUtils.createRealMatrix(new double[][] {
129 { 1, 2515 }, { 2, 422 }, { -3, 898 }
130 });
131
132
133 assertEquals(0, solver.solve(b).subtract(xRef).getNorm1(), 2.1e-16 * xRef.getNorm1());
134
135
136 for (int i = 0; i < b.getColumnDimension(); ++i) {
137 final RealVector x = solver.solve(b.getColumnVector(i));
138 final double error = x.subtract(xRef.getColumnVector(i)).getNorm();
139 assertEquals(0, error, 3.0e-16 * xRef.getColumnVector(i).getNorm());
140 }
141
142
143 for (int i = 0; i < b.getColumnDimension(); ++i) {
144 ArrayRealVectorTest.RealVectorTestImpl v =
145 new ArrayRealVectorTest.RealVectorTestImpl(b.getColumn(i));
146 final RealVector x = solver.solve(v);
147 final double error = x.subtract(xRef.getColumnVector(i)).getNorm();
148 assertEquals(0, error, 3.0e-16 * xRef.getColumnVector(i).getNorm());
149 }
150
151 }
152
153 @Test
154 void testOverdetermined() {
155 final Random r = new Random(5559252868205245l);
156 int p = (7 * BlockRealMatrix.BLOCK_SIZE) / 4;
157 int q = (5 * BlockRealMatrix.BLOCK_SIZE) / 4;
158 RealMatrix a = createTestMatrix(r, p, q);
159 RealMatrix xRef = createTestMatrix(r, q, BlockRealMatrix.BLOCK_SIZE + 3);
160
161
162 RealMatrix b = a.multiply(xRef);
163 final double noise = 0.001;
164 b.walkInOptimizedOrder(new DefaultRealMatrixChangingVisitor() {
165 @Override
166 public double visit(int row, int column, double value) {
167 return value * (1.0 + noise * (2 * r.nextDouble() - 1));
168 }
169 });
170
171
172 RealMatrix x = new QRDecomposition(a).getSolver().solve(b);
173 assertEquals(0, x.subtract(xRef).getNorm1(), 0.01 * noise * p * q);
174
175 }
176
177 @Test
178 void testUnderdetermined() {
179 final Random r = new Random(42185006424567123l);
180 int p = (5 * BlockRealMatrix.BLOCK_SIZE) / 4;
181 int q = (7 * BlockRealMatrix.BLOCK_SIZE) / 4;
182 RealMatrix a = createTestMatrix(r, p, q);
183 RealMatrix xRef = createTestMatrix(r, q, BlockRealMatrix.BLOCK_SIZE + 3);
184 RealMatrix b = a.multiply(xRef);
185 RealMatrix x = new QRDecomposition(a).getSolver().solve(b);
186
187
188 assertTrue(x.subtract(xRef).getNorm1() / (p * q) > 0.01);
189
190
191 assertEquals(0.0, x.getSubMatrix(p, q - 1, 0, x.getColumnDimension() - 1).getNorm1(), 0);
192 }
193
194 private RealMatrix createTestMatrix(final Random r, final int rows, final int columns) {
195 RealMatrix m = MatrixUtils.createRealMatrix(rows, columns);
196 m.walkInOptimizedOrder(new DefaultRealMatrixChangingVisitor() {
197 @Override
198 public double visit(int row, int column, double value) {
199 return 2.0 * r.nextDouble() - 1.0;
200 }
201 });
202 return m;
203 }
204 }