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    *      http://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  package org.apache.commons.math4.legacy.linear;
18  
19  import java.math.BigDecimal;
20  
21  import org.apache.commons.math4.legacy.TestUtils;
22  import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException;
23  import org.apache.commons.math4.legacy.exception.NotStrictlyPositiveException;
24  import org.apache.commons.math4.legacy.exception.NullArgumentException;
25  import org.apache.commons.math4.legacy.exception.OutOfRangeException;
26  import org.apache.commons.math4.legacy.core.dfp.Dfp;
27  import org.junit.Assert;
28  import org.junit.Test;
29  
30  /**
31   * Test cases for the {@link MatrixUtils} class.
32   *
33   */
34  
35  public final class MatrixUtilsTest {
36  
37      protected double[][] testData = { {1d,2d,3d}, {2d,5d,3d}, {1d,0d,8d} };
38      protected double[][] testData3x3Singular = { { 1, 4, 7, }, { 2, 5, 8, }, { 3, 6, 9, } };
39      protected double[][] testData3x4 = { { 12, -51, 4, 1 }, { 6, 167, -68, 2 }, { -4, 24, -41, 3 } };
40      protected double[][] nullMatrix = null;
41      protected double[] row = {1,2,3};
42      protected BigDecimal[] bigRow =
43          {new BigDecimal(1),new BigDecimal(2),new BigDecimal(3)};
44      protected String[] stringRow = {"1", "2", "3"};
45      protected Dfp[] fractionRow =
46          {Dfp25.of(1),Dfp25.of(2),Dfp25.of(3)};
47      protected double[][] rowMatrix = {{1,2,3}};
48      protected BigDecimal[][] bigRowMatrix =
49          {{new BigDecimal(1), new BigDecimal(2), new BigDecimal(3)}};
50      protected String[][] stringRowMatrix = {{"1", "2", "3"}};
51      protected Dfp[][] fractionRowMatrix =
52          {{Dfp25.of(1), Dfp25.of(2), Dfp25.of(3)}};
53      protected double[] col = {0,4,6};
54      protected BigDecimal[] bigCol =
55          {new BigDecimal(0),new BigDecimal(4),new BigDecimal(6)};
56      protected String[] stringCol = {"0","4","6"};
57      protected Dfp[] fractionCol =
58          {Dfp25.of(0),Dfp25.of(4),Dfp25.of(6)};
59      protected double[] nullDoubleArray = null;
60      protected double[][] colMatrix = {{0},{4},{6}};
61      protected BigDecimal[][] bigColMatrix =
62          {{new BigDecimal(0)},{new BigDecimal(4)},{new BigDecimal(6)}};
63      protected String[][] stringColMatrix = {{"0"}, {"4"}, {"6"}};
64      protected Dfp[][] fractionColMatrix =
65          {{Dfp25.of(0)},{Dfp25.of(4)},{Dfp25.of(6)}};
66  
67      @Test
68      public void testCreateRealMatrix() {
69          Assert.assertEquals(new BlockRealMatrix(testData),
70                  MatrixUtils.createRealMatrix(testData));
71          try {
72              MatrixUtils.createRealMatrix(new double[][] {{1}, {1,2}});  // ragged
73              Assert.fail("Expecting MathIllegalArgumentException");
74          } catch (MathIllegalArgumentException ex) {
75              // expected
76          }
77          try {
78              MatrixUtils.createRealMatrix(new double[][] {{}, {}});  // no columns
79              Assert.fail("Expecting MathIllegalArgumentException");
80          } catch (MathIllegalArgumentException ex) {
81              // expected
82          }
83          try {
84              MatrixUtils.createRealMatrix(null);  // null
85              Assert.fail("Expecting NullArgumentException");
86          } catch (NullArgumentException ex) {
87              // expected
88          }
89      }
90  
91      @Test
92      public void testcreateFieldMatrix() {
93          Assert.assertEquals(new Array2DRowFieldMatrix<>(asDfp(testData)),
94                       MatrixUtils.createFieldMatrix(asDfp(testData)));
95          Assert.assertEquals(new Array2DRowFieldMatrix<>(Dfp25.getField(), fractionColMatrix),
96                       MatrixUtils.createFieldMatrix(fractionColMatrix));
97          try {
98              MatrixUtils.createFieldMatrix(asDfp(new double[][] {{1}, {1,2}}));  // ragged
99              Assert.fail("Expecting MathIllegalArgumentException");
100         } catch (MathIllegalArgumentException ex) {
101             // expected
102         }
103         try {
104             MatrixUtils.createFieldMatrix(asDfp(new double[][] {{}, {}}));  // no columns
105             Assert.fail("Expecting MathIllegalArgumentException");
106         } catch (MathIllegalArgumentException ex) {
107             // expected
108         }
109         try {
110             MatrixUtils.createFieldMatrix((Dfp[][])null);  // null
111             Assert.fail("Expecting NullArgumentException");
112         } catch (NullArgumentException ex) {
113             // expected
114         }
115     }
116 
117     @Test
118     public void testCreateRowRealMatrix() {
119         Assert.assertEquals(MatrixUtils.createRowRealMatrix(row),
120                      new BlockRealMatrix(rowMatrix));
121         try {
122             MatrixUtils.createRowRealMatrix(new double[] {});  // empty
123             Assert.fail("Expecting NotStrictlyPositiveException");
124         } catch (NotStrictlyPositiveException ex) {
125             // expected
126         }
127         try {
128             MatrixUtils.createRowRealMatrix(null);  // null
129             Assert.fail("Expecting NullArgumentException");
130         } catch (NullArgumentException ex) {
131             // expected
132         }
133     }
134 
135     @Test
136     public void testCreateRowFieldMatrix() {
137         Assert.assertEquals(MatrixUtils.createRowFieldMatrix(asDfp(row)),
138                      new Array2DRowFieldMatrix<>(asDfp(rowMatrix)));
139         Assert.assertEquals(MatrixUtils.createRowFieldMatrix(fractionRow),
140                      new Array2DRowFieldMatrix<>(fractionRowMatrix));
141         try {
142             MatrixUtils.createRowFieldMatrix(new Dfp[] {});  // empty
143             Assert.fail("Expecting MathIllegalArgumentException");
144         } catch (MathIllegalArgumentException ex) {
145             // expected
146         }
147         try {
148             MatrixUtils.createRowFieldMatrix((Dfp[]) null);  // null
149             Assert.fail("Expecting NullArgumentException");
150         } catch (NullArgumentException ex) {
151             // expected
152         }
153     }
154 
155     @Test
156     public void testCreateColumnRealMatrix() {
157         Assert.assertEquals(MatrixUtils.createColumnRealMatrix(col),
158                      new BlockRealMatrix(colMatrix));
159         try {
160             MatrixUtils.createColumnRealMatrix(new double[] {});  // empty
161             Assert.fail("Expecting MathIllegalArgumentException");
162         } catch (MathIllegalArgumentException ex) {
163             // expected
164         }
165         try {
166             MatrixUtils.createColumnRealMatrix(null);  // null
167             Assert.fail("Expecting NullArgumentException");
168         } catch (NullArgumentException ex) {
169             // expected
170         }
171     }
172 
173     @Test
174     public void testCreateColumnFieldMatrix() {
175         Assert.assertEquals(MatrixUtils.createColumnFieldMatrix(asDfp(col)),
176                      new Array2DRowFieldMatrix<>(asDfp(colMatrix)));
177         Assert.assertEquals(MatrixUtils.createColumnFieldMatrix(fractionCol),
178                      new Array2DRowFieldMatrix<>(fractionColMatrix));
179 
180         try {
181             MatrixUtils.createColumnFieldMatrix(new Dfp[] {});  // empty
182             Assert.fail("Expecting MathIllegalArgumentException");
183         } catch (MathIllegalArgumentException ex) {
184             // expected
185         }
186         try {
187             MatrixUtils.createColumnFieldMatrix((Dfp[]) null);  // null
188             Assert.fail("Expecting NullArgumentException");
189         } catch (NullArgumentException ex) {
190             // expected
191         }
192     }
193 
194     /**
195      * Verifies that the matrix is an identity matrix
196      */
197     protected void checkIdentityMatrix(RealMatrix m) {
198         for (int i = 0; i < m.getRowDimension(); i++) {
199             for (int j =0; j < m.getColumnDimension(); j++) {
200                 if (i == j) {
201                     Assert.assertEquals(m.getEntry(i, j), 1d, 0);
202                 } else {
203                     Assert.assertEquals(m.getEntry(i, j), 0d, 0);
204                 }
205             }
206         }
207     }
208 
209     @Test
210     public void testCreateIdentityMatrix() {
211         checkIdentityMatrix(MatrixUtils.createRealIdentityMatrix(3));
212         checkIdentityMatrix(MatrixUtils.createRealIdentityMatrix(2));
213         checkIdentityMatrix(MatrixUtils.createRealIdentityMatrix(1));
214         try {
215             MatrixUtils.createRealIdentityMatrix(0);
216             Assert.fail("Expecting MathIllegalArgumentException");
217         } catch (MathIllegalArgumentException ex) {
218             // expected
219         }
220     }
221 
222     /**
223      * Verifies that the matrix is an identity matrix
224      */
225     protected void checkIdentityFieldMatrix(FieldMatrix<Dfp> m) {
226         for (int i = 0; i < m.getRowDimension(); i++) {
227             for (int j =0; j < m.getColumnDimension(); j++) {
228                 if (i == j) {
229                     Assert.assertEquals(m.getEntry(i, j), Dfp25.ONE);
230                 } else {
231                     Assert.assertEquals(m.getEntry(i, j), Dfp25.ZERO);
232                 }
233             }
234         }
235     }
236 
237     @Test
238     public void testcreateFieldIdentityMatrix() {
239         checkIdentityFieldMatrix(MatrixUtils.createFieldIdentityMatrix(Dfp25.getField(), 3));
240         checkIdentityFieldMatrix(MatrixUtils.createFieldIdentityMatrix(Dfp25.getField(), 2));
241         checkIdentityFieldMatrix(MatrixUtils.createFieldIdentityMatrix(Dfp25.getField(), 1));
242         try {
243             MatrixUtils.createRealIdentityMatrix(0);
244             Assert.fail("Expecting MathIllegalArgumentException");
245         } catch (MathIllegalArgumentException ex) {
246             // expected
247         }
248     }
249 
250     public static Dfp[][] asDfp(double[][] data) {
251         Dfp d[][] = new Dfp[data.length][];
252         for (int i = 0; i < data.length; ++i) {
253             double[] dataI = data[i];
254             Dfp[] dI  = new Dfp[dataI.length];
255             for (int j = 0; j < dataI.length; ++j) {
256                 dI[j] = Dfp25.of(dataI[j]);
257             }
258             d[i] = dI;
259         }
260         return d;
261     }
262 
263     public static Dfp[] asDfp(double[] data) {
264         Dfp d[] = new Dfp[data.length];
265         for (int i = 0; i < data.length; ++i) {
266             d[i] = Dfp25.of(data[i]);
267         }
268 
269         return d;
270     }
271 
272     @Test
273     public void testSolveLowerTriangularSystem(){
274         RealMatrix rm = new Array2DRowRealMatrix(
275                 new double[][] { {2,0,0,0 }, { 1,1,0,0 }, { 3,3,3,0 }, { 3,3,3,4 } },
276                        false);
277         RealVector b = new ArrayRealVector(new double[] { 2,3,4,8 }, false);
278         MatrixUtils.solveLowerTriangularSystem(rm, b);
279         TestUtils.assertEquals( new double[]{1,2,-1.66666666666667, 1.0}  , b.toArray() , 1.0e-12);
280     }
281 
282 
283     /*
284      * Taken from R manual http://stat.ethz.ch/R-manual/R-patched/library/base/html/backsolve.html
285      */
286     @Test
287     public void testSolveUpperTriangularSystem(){
288         RealMatrix rm = new Array2DRowRealMatrix(
289                 new double[][] { {1,2,3 }, { 0,1,1 }, { 0,0,2 } },
290                        false);
291         RealVector b = new ArrayRealVector(new double[] { 8,4,2 }, false);
292         MatrixUtils.solveUpperTriangularSystem(rm, b);
293         TestUtils.assertEquals( new double[]{-1,3,1}  , b.toArray() , 1.0e-12);
294     }
295 
296     /**
297      * This test should probably be replaced by one that could show
298      * whether this algorithm can sometimes perform better (precision- or
299      * performance-wise) than the direct inversion of the whole matrix.
300      */
301     @Test
302     public void testBlockInverse() {
303         final double[][] data = {
304             { -1, 0, 123, 4 },
305             { -56, 78.9, -0.1, -23.4 },
306             { 5.67, 8, -9, 1011 },
307             { 12, 345, -67.8, 9 },
308         };
309 
310         final RealMatrix m = new Array2DRowRealMatrix(data);
311         final int len = data.length;
312         final double tol = 1e-14;
313 
314         for (int splitIndex = 0; splitIndex < 3; splitIndex++) {
315             final RealMatrix mInv = MatrixUtils.blockInverse(m, splitIndex);
316             final RealMatrix id = m.multiply(mInv);
317 
318             // Check that we recovered the identity matrix.
319             for (int i = 0; i < len; i++) {
320                 for (int j = 0; j < len; j++) {
321                     final double entry = id.getEntry(i, j);
322                     if (i == j) {
323                         Assert.assertEquals("[" + i + "][" + j + "]",
324                                             1, entry, tol);
325                     } else {
326                         Assert.assertEquals("[" + i + "][" + j + "]",
327                                             0, entry, tol);
328                     }
329                 }
330             }
331         }
332     }
333 
334     @Test(expected=SingularMatrixException.class)
335     public void testBlockInverseNonInvertible() {
336         final double[][] data = {
337             { -1, 0, 123, 4 },
338             { -56, 78.9, -0.1, -23.4 },
339             { 5.67, 8, -9, 1011 },
340             { 5.67, 8, -9, 1011 },
341         };
342 
343         MatrixUtils.blockInverse(new Array2DRowRealMatrix(data), 2);
344     }
345 
346     @Test
347     public void testIsSymmetric() {
348         final double eps = Math.ulp(1d);
349 
350         final double[][] dataSym = {
351             { 1, 2, 3 },
352             { 2, 2, 5 },
353             { 3, 5, 6 },
354         };
355         Assert.assertTrue(MatrixUtils.isSymmetric(MatrixUtils.createRealMatrix(dataSym), eps));
356 
357         final double[][] dataNonSym = {
358             { 1, 2, -3 },
359             { 2, 2, 5 },
360             { 3, 5, 6 },
361         };
362         Assert.assertFalse(MatrixUtils.isSymmetric(MatrixUtils.createRealMatrix(dataNonSym), eps));
363     }
364 
365     @Test
366     public void testIsSymmetricTolerance() {
367         final double eps = 1e-4;
368 
369         final double[][] dataSym1 = {
370             { 1,   1, 1.00009 },
371             { 1,   1, 1       },
372             { 1.0, 1, 1       },
373         };
374         Assert.assertTrue(MatrixUtils.isSymmetric(MatrixUtils.createRealMatrix(dataSym1), eps));
375         final double[][] dataSym2 = {
376             { 1,   1, 0.99990 },
377             { 1,   1, 1       },
378             { 1.0, 1, 1       },
379         };
380         Assert.assertTrue(MatrixUtils.isSymmetric(MatrixUtils.createRealMatrix(dataSym2), eps));
381 
382         final double[][] dataNonSym1 = {
383             { 1,   1, 1.00011 },
384             { 1,   1, 1       },
385             { 1.0, 1, 1       },
386         };
387         Assert.assertFalse(MatrixUtils.isSymmetric(MatrixUtils.createRealMatrix(dataNonSym1), eps));
388         final double[][] dataNonSym2 = {
389             { 1,   1, 0.99989 },
390             { 1,   1, 1       },
391             { 1.0, 1, 1       },
392         };
393         Assert.assertFalse(MatrixUtils.isSymmetric(MatrixUtils.createRealMatrix(dataNonSym2), eps));
394     }
395 
396     @Test
397     public void testCheckSymmetric1() {
398         final double[][] dataSym = {
399             { 1, 2, 3 },
400             { 2, 2, 5 },
401             { 3, 5, 6 },
402         };
403         MatrixUtils.checkSymmetric(MatrixUtils.createRealMatrix(dataSym), Math.ulp(1d));
404     }
405 
406     @Test(expected=NonSymmetricMatrixException.class)
407     public void testCheckSymmetric2() {
408         final double[][] dataNonSym = {
409             { 1, 2, -3 },
410             { 2, 2, 5 },
411             { 3, 5, 6 },
412         };
413         MatrixUtils.checkSymmetric(MatrixUtils.createRealMatrix(dataNonSym), Math.ulp(1d));
414     }
415 
416     @Test(expected=SingularMatrixException.class)
417     public void testInverseSingular() {
418         RealMatrix m = MatrixUtils.createRealMatrix(testData3x3Singular);
419         MatrixUtils.inverse(m);
420     }
421 
422     @Test(expected=NonSquareMatrixException.class)
423     public void testInverseNonSquare() {
424         RealMatrix m = MatrixUtils.createRealMatrix(testData3x4);
425         MatrixUtils.inverse(m);
426     }
427 
428     @Test
429     public void testInverseDiagonalMatrix() {
430         final double[] data = { 1, 2, 3 };
431         final RealMatrix m = new DiagonalMatrix(data);
432         final RealMatrix inverse = MatrixUtils.inverse(m);
433 
434         final RealMatrix result = m.multiply(inverse);
435         TestUtils.assertEquals("MatrixUtils.inverse() returns wrong result",
436                 MatrixUtils.createRealIdentityMatrix(data.length), result, Math.ulp(1d));
437     }
438 
439     @Test
440     public void testInverseRealMatrix() {
441         RealMatrix m = MatrixUtils.createRealMatrix(testData);
442         final RealMatrix inverse = MatrixUtils.inverse(m);
443 
444         final RealMatrix result = m.multiply(inverse);
445         TestUtils.assertEquals("MatrixUtils.inverse() returns wrong result",
446                 MatrixUtils.createRealIdentityMatrix(testData.length), result, 1e-12);
447     }
448 
449     @Test
450     public void testCheckMatrixRowIndexError() {
451         try {
452             AnyMatrix m = MatrixUtils.createRealMatrix(new double[][] {{9,9}, {9,9}, {9,9}});
453             MatrixUtils.checkRowIndex(m, 4);
454             Assert.fail("expected an OutOfRangeException");
455         } catch (OutOfRangeException e) {
456             String s = e.getMessage();
457             int topIx = s.indexOf('2');
458             int botIx = s.indexOf('0');
459             int rowIx = s.indexOf('4');
460             if (topIx < 0 || botIx < 0 || rowIx < 0) {
461                 Assert.fail("expected a message like index 4 is not in 0..3, not: " + s);
462             }
463         } catch (Exception e) {
464             Assert.fail("expected an OutOfRange exception, not: " +
465                         e.getClass().getName() + ": " + e.getMessage());
466         }
467     }
468 
469     @Test
470     public void testCheckMatrixColIndexError() {
471         try {
472             AnyMatrix m = MatrixUtils.createRealMatrix(new double[][] {{9,9}, {9,9}, {9,9}});
473             MatrixUtils.checkColumnIndex(m, 4);
474             Assert.fail("expected an OutOfRangeException");
475         } catch (OutOfRangeException e) {
476             String s = e.getMessage();
477             int topIx = s.indexOf('1');
478             int botIx = s.indexOf('0');
479             int rowIx = s.indexOf('4');
480             if (topIx < 0 || botIx < 0 || rowIx < 0) {
481                 Assert.fail("expected a message like index 4 is not in 0..3, not: " + s);
482             }
483         } catch (Exception e) {
484             Assert.fail("expected an OutOfRange exception, not: " +
485                         e.getClass().getName() + ": " + e.getMessage());
486         }
487     }
488 }