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  
18  package org.apache.commons.math4.legacy.field.linalg;
19  
20  import org.junit.Test;
21  import org.junit.Assert;
22  import org.apache.commons.numbers.fraction.Fraction;
23  import org.apache.commons.numbers.field.FractionField;
24  import org.apache.commons.math4.legacy.linear.SingularMatrixException;
25  import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
26  
27  public class FieldLUDecompositionTest {
28      private final int[][] testData = {
29          { 1, 2, 3 },
30          { 2, 5, 3 },
31          { 1, 0, 8 }
32      };
33      private final int[][] testDataMinus = {
34          { -1, -2, -3 },
35          { -2, -5, -3 },
36          { -1, 0, -8 }
37      };
38      private final int[][] luData = {
39          { 2, 3, 3 },
40          { 2, 3, 7 },
41          { 6, 6, 8 }
42      };
43      private final int[][] luData2 = {
44          { 2, 3, 3 },
45          { 0, 5, 7 },
46          { 6, 9, 8 }
47      };
48  
49      // singular matrices
50      private int[][] singular = {
51          { 2, 3 },
52          { 2, 3 }
53      };
54      private final int[][] bigSingular = {
55          { 1, 2,   3,    4 },
56          { 2, 5,   3,    4 },
57          { 7, 3, 256, 1930 },
58          { 3, 7,   6,    8 }
59      }; // 4th row = 1st + 2nd
60  
61      @Test
62      public void testDimensions() {
63          FieldDenseMatrix<Fraction> matrix = create(testData);
64          FieldLUDecomposition<Fraction> LU = FieldLUDecomposition.of(matrix);
65          Assert.assertEquals(testData.length, LU.getL().getRowDimension());
66          Assert.assertEquals(testData.length, LU.getU().getRowDimension());
67          Assert.assertEquals(testData.length, LU.getP().getRowDimension());
68      }
69  
70      @Test
71      public void testPAEqualLU() {
72          FieldDenseMatrix<Fraction> matrix = create(testData);
73          FieldLUDecomposition<Fraction> lu = FieldLUDecomposition.of(matrix);
74          FieldDenseMatrix<Fraction> l = lu.getL();
75          FieldDenseMatrix<Fraction> u = lu.getU();
76          FieldDenseMatrix<Fraction> p = lu.getP();
77          Assert.assertEquals(p.multiply(matrix), l.multiply(u));
78  
79          matrix = create(testDataMinus);
80          lu = FieldLUDecomposition.of(matrix);
81          l = lu.getL();
82          u = lu.getU();
83          p = lu.getP();
84          Assert.assertEquals(p.multiply(matrix), l.multiply(u));
85  
86          matrix = FieldDenseMatrix.identity(FractionField.get(), 17);
87          lu = FieldLUDecomposition.of(matrix);
88          l = lu.getL();
89          u = lu.getU();
90          p = lu.getP();
91          Assert.assertEquals(p.multiply(matrix), l.multiply(u));
92      }
93  
94      /* L is lower triangular with unit diagonal */
95      @Test
96      public void testLLowerTriangular() {
97          FieldDenseMatrix<Fraction> matrix = create(testData);
98          FieldDenseMatrix<Fraction> l = FieldLUDecomposition.of(matrix).getL();
99          for (int i = 0; i < l.getRowDimension(); i++) {
100             Assert.assertEquals(Fraction.ONE, l.get(i, i));
101             for (int j = i + 1; j < l.getColumnDimension(); j++) {
102                 Assert.assertEquals(Fraction.ZERO, l.get(i, j));
103             }
104         }
105     }
106 
107     /* U is upper triangular */
108     @Test
109     public void testUUpperTriangular() {
110         FieldDenseMatrix<Fraction> matrix = create(testData);
111         FieldDenseMatrix<Fraction> u = FieldLUDecomposition.of(matrix).getU();
112         for (int i = 0; i < u.getRowDimension(); i++) {
113             for (int j = 0; j < i; j++) {
114                 Assert.assertEquals(Fraction.ZERO, u.get(i, j));
115             }
116         }
117     }
118 
119     /* P is a permutation matrix */
120     @Test
121     public void testPPermutation() {
122         FieldDenseMatrix<Fraction> matrix = create(testData);
123         FieldDenseMatrix<Fraction> p = FieldLUDecomposition.of(matrix).getP();
124 
125         FieldDenseMatrix<Fraction> ppT = p.multiply(p.transpose());
126         FieldDenseMatrix<Fraction> id = FieldDenseMatrix.identity(FractionField.get(),
127                                                                   p.getRowDimension());
128         Assert.assertEquals(id, ppT);
129 
130         for (int i = 0; i < p.getRowDimension(); i++) {
131             int zeroCount = 0;
132             int oneCount = 0;
133             int otherCount = 0;
134             for (int j = 0; j < p.getColumnDimension(); j++) {
135                 final Fraction e = p.get(i, j);
136                 if (e.equals(Fraction.ZERO)) {
137                     ++zeroCount;
138                 } else if (e.equals(Fraction.ONE)) {
139                     ++oneCount;
140                 } else {
141                     ++otherCount;
142                 }
143             }
144             Assert.assertEquals(p.getRowDimension() - 1, zeroCount);
145             Assert.assertEquals(1, oneCount);
146             Assert.assertEquals(0, otherCount);
147         }
148 
149         for (int j = 0; j < p.getRowDimension(); j++) {
150             int zeroCount = 0;
151             int oneCount = 0;
152             int otherCount = 0;
153             for (int i = 0; i < p.getColumnDimension(); i++) {
154                 final Fraction e = p.get(i, j);
155                 if (e.equals(Fraction.ZERO)) {
156                     ++zeroCount;
157                 } else if (e.equals(Fraction.ONE)) {
158                     ++oneCount;
159                 } else {
160                     ++otherCount;
161                 }
162             }
163             Assert.assertEquals(p.getRowDimension() - 1, zeroCount);
164             Assert.assertEquals(1, oneCount);
165             Assert.assertEquals(0, otherCount);
166         }
167     }
168 
169     @Test
170     public void testIsSingular1() {
171         FieldLUDecomposition<Fraction> lu = FieldLUDecomposition.of(create(testData));
172         Assert.assertFalse(lu.isSingular());
173         lu.getSolver();
174     }
175     @Test(expected=SingularMatrixException.class)
176     public void testIsSingular2() {
177         FieldLUDecomposition<Fraction> lu = FieldLUDecomposition.of(create(singular));
178         Assert.assertTrue(lu.isSingular());
179         lu.getSolver();
180     }
181     @Test(expected=SingularMatrixException.class)
182     public void testIsSingular3() {
183         FieldLUDecomposition<Fraction> lu = FieldLUDecomposition.of(create(bigSingular));
184         Assert.assertTrue(lu.isSingular());
185         lu.getSolver();
186     }
187 
188     @Test
189     public void testMatricesValues1() {
190         FieldLUDecomposition<Fraction> lu = FieldLUDecomposition.of(create(testData));
191         FieldDenseMatrix<Fraction> lRef = create(new int[][] {
192                 { 1, 0, 0 },
193                 { 2, 1, 0 },
194                 { 1, -2, 1 }
195             });
196         FieldDenseMatrix<Fraction> uRef = create(new int[][] {
197                 { 1,  2, 3 },
198                 { 0, 1, -3 },
199                 { 0,  0, -1 }
200             });
201         FieldDenseMatrix<Fraction> pRef = create(new int[][] {
202                 { 1, 0, 0 },
203                 { 0, 1, 0 },
204                 { 0, 0, 1 }
205             });
206         int[] pivotRef = { 0, 1, 2 };
207 
208         // check values against known references
209         FieldDenseMatrix<Fraction> l = lu.getL();
210         Assert.assertEquals(lRef, l);
211         FieldDenseMatrix<Fraction> u = lu.getU();
212         Assert.assertEquals(uRef, u);
213         FieldDenseMatrix<Fraction> p = lu.getP();
214         Assert.assertEquals(pRef, p);
215         int[] pivot = lu.getPivot();
216         for (int i = 0; i < pivotRef.length; ++i) {
217             Assert.assertEquals(pivotRef[i], pivot[i]);
218         }
219     }
220 
221     @Test
222     public void testMatricesValues2() {
223         final FieldLUDecomposition<Fraction> lu = FieldLUDecomposition.of(create(luData));
224         final FieldDenseMatrix<Fraction> lRef = create(new int[][] {
225                 { 1, 0, 0 },
226                 { 3, 1, 0 },
227                 { 1, 0, 1 }
228             });
229         final FieldDenseMatrix<Fraction> uRef = create(new int[][] {
230                 { 2, 3, 3 },
231                 { 0, -3, -1 },
232                 { 0, 0, 4 }
233             });
234         final FieldDenseMatrix<Fraction> pRef = create(new int[][] {
235                 { 1, 0, 0 },
236                 { 0, 0, 1 },
237                 { 0, 1, 0 }
238             });
239         int[] pivotRef = { 0, 2, 1 };
240 
241         // check values against known references
242         final FieldDenseMatrix<Fraction> l = lu.getL();
243         Assert.assertEquals(lRef, l);
244         final FieldDenseMatrix<Fraction> u = lu.getU();
245         Assert.assertEquals(uRef, u);
246         final FieldDenseMatrix<Fraction> p = lu.getP();
247         Assert.assertEquals(pRef, p);
248         int[] pivot = lu.getPivot();
249         for (int i = 0; i < pivotRef.length; i++) {
250             Assert.assertEquals(pivotRef[i], pivot[i]);
251         }
252     }
253 
254     @Test(expected=DimensionMismatchException.class)
255     public void testSolveDimensionErrors() {
256         FieldLUDecomposition
257             .of(create(testData))
258             .getSolver()
259             .solve(create(new int[2][2]));
260     }
261 
262     @Test
263     public void testSolve() {
264         final FieldDecompositionSolver<Fraction> solver = FieldLUDecomposition
265             .of(create(testData))
266             .getSolver();
267         final FieldDenseMatrix<Fraction> b = create(new int[][] {
268                 { 1, 0 },
269                 { 2, -5 },
270                 { 3, 1 }
271             });
272         final FieldDenseMatrix<Fraction> xRef = create(new int[][] {
273                 { 19, -71 },
274                 { -6, 22 },
275                 { -2, 9 }
276             });
277 
278         final FieldDenseMatrix<Fraction> x = solver.solve(b);
279         for (int i = 0; i < x.getRowDimension(); i++){
280             for (int j = 0; j < x.getColumnDimension(); j++){
281                 Assert.assertEquals("(" + i + ", " + j + ")",
282                                     xRef.get(i, j), x.get(i, j));
283             }
284         }
285     }
286 
287     @Test
288     public void testDeterminant() {
289         Assert.assertEquals(-1, determinant(testData), 1e-15);
290         Assert.assertEquals(24, determinant(luData), 1e-14);
291         Assert.assertEquals(-10, determinant(luData2), 1e-14);
292         Assert.assertEquals(0, determinant(singular), 1e-15);
293         Assert.assertEquals(0, determinant(bigSingular), 1e-15);
294     }
295 
296     private static double determinant(int[][] data) {
297         return FieldLUDecomposition.of(create(data)).getDeterminant().doubleValue();
298     }
299 
300     private static FieldDenseMatrix<Fraction> create(final int[][] data) {
301         final int numRows = data.length;
302         final int numCols = data[0].length;
303         final FieldDenseMatrix<Fraction> m = FieldDenseMatrix
304             .create(FractionField.get(), numRows, numCols);
305 
306         for (int i = 0; i < numRows; i++) {
307             for (int j = 0; j < numCols; j++) {
308                 m.set(i, j, Fraction.of(data[i][j], 1));
309             }
310         }
311 
312         return m;
313     }
314 }