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