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.junit.jupiter.api.Test;
26
27 import java.io.BufferedReader;
28 import java.io.DataInputStream;
29 import java.io.IOException;
30 import java.io.InputStreamReader;
31 import java.util.Random;
32
33 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
34 import static org.junit.jupiter.api.Assertions.assertEquals;
35 import static org.junit.jupiter.api.Assertions.assertFalse;
36 import static org.junit.jupiter.api.Assertions.assertTrue;
37
38
39 public class SingularValueDecompositionTest {
40
41 private double[][] testSquare = {
42 { 24.0 / 25.0, 43.0 / 25.0 },
43 { 57.0 / 25.0, 24.0 / 25.0 }
44 };
45
46 private double[][] testNonSquare = {
47 { -540.0 / 625.0, 963.0 / 625.0, -216.0 / 625.0 },
48 { -1730.0 / 625.0, -744.0 / 625.0, 1008.0 / 625.0 },
49 { -720.0 / 625.0, 1284.0 / 625.0, -288.0 / 625.0 },
50 { -360.0 / 625.0, 192.0 / 625.0, 1756.0 / 625.0 },
51 };
52
53 private static final double normTolerance = 10e-14;
54
55 @Test
56 void testMoreRows() {
57 final double[] singularValues = { 123.456, 2.3, 1.001, 0.999 };
58 final int rows = singularValues.length + 2;
59 final int columns = singularValues.length;
60 Random r = new Random(15338437322523l);
61 SingularValueDecomposition svd =
62 new SingularValueDecomposition(createTestMatrix(r, rows, columns, singularValues));
63 double[] computedSV = svd.getSingularValues();
64 assertEquals(singularValues.length, computedSV.length);
65 for (int i = 0; i < singularValues.length; ++i) {
66 assertEquals(singularValues[i], computedSV[i], 1.0e-10);
67 }
68 }
69
70 @Test
71 void testMoreColumns() {
72 final double[] singularValues = { 123.456, 2.3, 1.001, 0.999 };
73 final int rows = singularValues.length;
74 final int columns = singularValues.length + 2;
75 Random r = new Random(732763225836210l);
76 SingularValueDecomposition svd =
77 new SingularValueDecomposition(createTestMatrix(r, rows, columns, singularValues));
78 double[] computedSV = svd.getSingularValues();
79 assertEquals(singularValues.length, computedSV.length);
80 for (int i = 0; i < singularValues.length; ++i) {
81 assertEquals(singularValues[i], computedSV[i], 1.0e-10);
82 }
83 }
84
85
86 @Test
87 void testDimensions() {
88 RealMatrix matrix = MatrixUtils.createRealMatrix(testSquare);
89 final int m = matrix.getRowDimension();
90 final int n = matrix.getColumnDimension();
91 SingularValueDecomposition svd = new SingularValueDecomposition(matrix);
92 assertEquals(m, svd.getU().getRowDimension());
93 assertEquals(m, svd.getU().getColumnDimension());
94 assertEquals(m, svd.getS().getColumnDimension());
95 assertEquals(n, svd.getS().getColumnDimension());
96 assertEquals(n, svd.getV().getRowDimension());
97 assertEquals(n, svd.getV().getColumnDimension());
98
99 }
100
101 @Test
102 void testDecomposer() {
103 MatrixDecomposer decomposer = new SingularValueDecomposer();
104 assertTrue(decomposer.decompose(MatrixUtils.createRealMatrix(testSquare)).isNonSingular());
105 assertFalse(decomposer.decompose(MatrixUtils.createRealMatrix(testNonSquare)).isNonSingular());
106 }
107
108
109 @Test
110 void testHadamard() {
111 RealMatrix matrix = new Array2DRowRealMatrix(new double[][] {
112 {15.0 / 2.0, 5.0 / 2.0, 9.0 / 2.0, 3.0 / 2.0 },
113 { 5.0 / 2.0, 15.0 / 2.0, 3.0 / 2.0, 9.0 / 2.0 },
114 { 9.0 / 2.0, 3.0 / 2.0, 15.0 / 2.0, 5.0 / 2.0 },
115 { 3.0 / 2.0, 9.0 / 2.0, 5.0 / 2.0, 15.0 / 2.0 }
116 }, false);
117 SingularValueDecomposition svd = new SingularValueDecomposition(matrix);
118 assertEquals(16.0, svd.getSingularValues()[0], 1.0e-14);
119 assertEquals( 8.0, svd.getSingularValues()[1], 1.0e-14);
120 assertEquals( 4.0, svd.getSingularValues()[2], 1.0e-14);
121 assertEquals( 2.0, svd.getSingularValues()[3], 1.0e-14);
122
123 RealMatrix fullCovariance = new Array2DRowRealMatrix(new double[][] {
124 { 85.0 / 1024, -51.0 / 1024, -75.0 / 1024, 45.0 / 1024 },
125 { -51.0 / 1024, 85.0 / 1024, 45.0 / 1024, -75.0 / 1024 },
126 { -75.0 / 1024, 45.0 / 1024, 85.0 / 1024, -51.0 / 1024 },
127 { 45.0 / 1024, -75.0 / 1024, -51.0 / 1024, 85.0 / 1024 }
128 }, false);
129 assertEquals(0.0,
130 fullCovariance.subtract(svd.getCovariance(0.0)).getNorm1(),
131 1.0e-14);
132
133 RealMatrix halfCovariance = new Array2DRowRealMatrix(new double[][] {
134 { 5.0 / 1024, -3.0 / 1024, 5.0 / 1024, -3.0 / 1024 },
135 { -3.0 / 1024, 5.0 / 1024, -3.0 / 1024, 5.0 / 1024 },
136 { 5.0 / 1024, -3.0 / 1024, 5.0 / 1024, -3.0 / 1024 },
137 { -3.0 / 1024, 5.0 / 1024, -3.0 / 1024, 5.0 / 1024 }
138 }, false);
139 assertEquals(0.0,
140 halfCovariance.subtract(svd.getCovariance(6.0)).getNorm1(),
141 1.0e-14);
142
143 }
144
145
146 @Test
147 void testAEqualUSVt() {
148 checkAEqualUSVt(MatrixUtils.createRealMatrix(testSquare));
149 checkAEqualUSVt(MatrixUtils.createRealMatrix(testNonSquare));
150 checkAEqualUSVt(MatrixUtils.createRealMatrix(testNonSquare).transpose());
151 }
152
153 public void checkAEqualUSVt(final RealMatrix matrix) {
154 SingularValueDecomposition svd = new SingularValueDecomposition(matrix);
155 RealMatrix u = svd.getU();
156 RealMatrix s = svd.getS();
157 RealMatrix v = svd.getV();
158 double norm = u.multiply(s).multiplyTransposed(v).subtract(matrix).getNorm1();
159 assertEquals(0, norm, normTolerance);
160
161 }
162
163
164 @Test
165 void testUOrthogonal() {
166 checkOrthogonal(new SingularValueDecomposition(MatrixUtils.createRealMatrix(testSquare)).getU());
167 checkOrthogonal(new SingularValueDecomposition(MatrixUtils.createRealMatrix(testNonSquare)).getU());
168 checkOrthogonal(new SingularValueDecomposition(MatrixUtils.createRealMatrix(testNonSquare).transpose()).getU());
169 }
170
171
172 @Test
173 void testVOrthogonal() {
174 checkOrthogonal(new SingularValueDecomposition(MatrixUtils.createRealMatrix(testSquare)).getV());
175 checkOrthogonal(new SingularValueDecomposition(MatrixUtils.createRealMatrix(testNonSquare)).getV());
176 checkOrthogonal(new SingularValueDecomposition(MatrixUtils.createRealMatrix(testNonSquare).transpose()).getV());
177 }
178
179 public void checkOrthogonal(final RealMatrix m) {
180 RealMatrix mTm = m.transposeMultiply(m);
181 RealMatrix id = MatrixUtils.createRealIdentityMatrix(mTm.getRowDimension());
182 assertEquals(0, mTm.subtract(id).getNorm1(), normTolerance);
183 }
184
185
186
187
188 public void testMatricesValues1() {
189 SingularValueDecomposition svd =
190 new SingularValueDecomposition(MatrixUtils.createRealMatrix(testSquare));
191 RealMatrix uRef = MatrixUtils.createRealMatrix(new double[][] {
192 { 3.0 / 5.0, -4.0 / 5.0 },
193 { 4.0 / 5.0, 3.0 / 5.0 }
194 });
195 RealMatrix sRef = MatrixUtils.createRealMatrix(new double[][] {
196 { 3.0, 0.0 },
197 { 0.0, 1.0 }
198 });
199 RealMatrix vRef = MatrixUtils.createRealMatrix(new double[][] {
200 { 4.0 / 5.0, 3.0 / 5.0 },
201 { 3.0 / 5.0, -4.0 / 5.0 }
202 });
203
204
205 RealMatrix u = svd.getU();
206 assertEquals(0, u.subtract(uRef).getNorm1(), normTolerance);
207 RealMatrix s = svd.getS();
208 assertEquals(0, s.subtract(sRef).getNorm1(), normTolerance);
209 RealMatrix v = svd.getV();
210 assertEquals(0, v.subtract(vRef).getNorm1(), normTolerance);
211
212
213 assertTrue(u == svd.getU());
214 assertTrue(s == svd.getS());
215 assertTrue(v == svd.getV());
216
217 }
218
219
220
221
222 public void useless_testMatricesValues2() {
223
224 RealMatrix uRef = MatrixUtils.createRealMatrix(new double[][] {
225 { 0.0 / 5.0, 3.0 / 5.0, 0.0 / 5.0 },
226 { -4.0 / 5.0, 0.0 / 5.0, -3.0 / 5.0 },
227 { 0.0 / 5.0, 4.0 / 5.0, 0.0 / 5.0 },
228 { -3.0 / 5.0, 0.0 / 5.0, 4.0 / 5.0 }
229 });
230 RealMatrix sRef = MatrixUtils.createRealMatrix(new double[][] {
231 { 4.0, 0.0, 0.0 },
232 { 0.0, 3.0, 0.0 },
233 { 0.0, 0.0, 2.0 }
234 });
235 RealMatrix vRef = MatrixUtils.createRealMatrix(new double[][] {
236 { 80.0 / 125.0, -60.0 / 125.0, 75.0 / 125.0 },
237 { 24.0 / 125.0, 107.0 / 125.0, 60.0 / 125.0 },
238 { -93.0 / 125.0, -24.0 / 125.0, 80.0 / 125.0 }
239 });
240
241
242 SingularValueDecomposition svd =
243 new SingularValueDecomposition(MatrixUtils.createRealMatrix(testNonSquare));
244 RealMatrix u = svd.getU();
245 assertEquals(0, u.subtract(uRef).getNorm1(), normTolerance);
246 RealMatrix s = svd.getS();
247 assertEquals(0, s.subtract(sRef).getNorm1(), normTolerance);
248 RealMatrix v = svd.getV();
249 assertEquals(0, v.subtract(vRef).getNorm1(), normTolerance);
250
251
252 assertTrue(u == svd.getU());
253 assertTrue(s == svd.getS());
254 assertTrue(v == svd.getV());
255
256 }
257
258
259 @Test
260 void testRank() {
261 double[][] d = { { 1, 1, 1 }, { 0, 0, 0 }, { 1, 2, 3 } };
262 RealMatrix m = new Array2DRowRealMatrix(d);
263 SingularValueDecomposition svd = new SingularValueDecomposition(m);
264 assertEquals(2, svd.getRank());
265 }
266
267
268 @Test
269 void testStability1() {
270 RealMatrix m = new Array2DRowRealMatrix(201, 201);
271 loadRealMatrix(m,"matrix1.csv");
272 assertDoesNotThrow(() -> {
273 new SingularValueDecomposition(m);
274 }, "Exception whilst constructing SVD");
275 }
276
277
278 @Test
279 void testStability2() {
280 RealMatrix m = new Array2DRowRealMatrix(7, 168);
281 loadRealMatrix(m,"matrix2.csv");
282 assertDoesNotThrow(() -> {
283 new SingularValueDecomposition(m);
284 }, "Exception whilst constructing SVD");
285 }
286
287 private void loadRealMatrix(RealMatrix m, String resourceName) {
288 try {
289 DataInputStream in = new DataInputStream(getClass().getResourceAsStream(resourceName));
290 BufferedReader br = new BufferedReader(new InputStreamReader(in));
291 String strLine;
292 int row = 0;
293 while ((strLine = br.readLine()) != null) {
294 if (!strLine.startsWith("#")) {
295 int col = 0;
296 for (String entry : strLine.split(",")) {
297 m.setEntry(row, col++, Double.parseDouble(entry));
298 }
299 row++;
300 }
301 }
302 in.close();
303 } catch (IOException e) {}
304 }
305
306
307 @Test
308 void testConditionNumber() {
309 SingularValueDecomposition svd =
310 new SingularValueDecomposition(MatrixUtils.createRealMatrix(testSquare));
311
312 assertEquals(3.0, svd.getConditionNumber(), 1.5e-15);
313 }
314
315 @Test
316 void testInverseConditionNumber() {
317 SingularValueDecomposition svd =
318 new SingularValueDecomposition(MatrixUtils.createRealMatrix(testSquare));
319 assertEquals(1.0/3.0, svd.getInverseConditionNumber(), 1.5e-15);
320 }
321
322 private RealMatrix createTestMatrix(final Random r, final int rows, final int columns,
323 final double[] singularValues) {
324 final RealMatrix u = EigenDecompositionSymmetricTest.createOrthogonalMatrix(r, rows);
325 final RealMatrix d = new Array2DRowRealMatrix(rows, columns);
326 d.setSubMatrix(MatrixUtils.createRealDiagonalMatrix(singularValues).getData(), 0, 0);
327 final RealMatrix v = EigenDecompositionSymmetricTest.createOrthogonalMatrix(r, columns);
328 return u.multiply(d).multiply(v);
329 }
330
331 @Test
332 void testIssue947() {
333 double[][] nans = new double[][] {
334 { Double.NaN, Double.NaN },
335 { Double.NaN, Double.NaN }
336 };
337 RealMatrix m = new Array2DRowRealMatrix(nans, false);
338 SingularValueDecomposition svd = new SingularValueDecomposition(m);
339 assertTrue(Double.isNaN(svd.getSingularValues()[0]));
340 assertTrue(Double.isNaN(svd.getSingularValues()[1]));
341 }
342
343 }