/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.math4.linear; import org.junit.Test; import org.junit.Assert; import org.apache.commons.math4.TestUtils; import org.apache.commons.math4.fraction.Fraction; import org.apache.commons.math4.fraction.FractionField; import org.apache.commons.math4.linear.Array2DRowFieldMatrix; import org.apache.commons.math4.linear.FieldLUDecomposition; import org.apache.commons.math4.linear.FieldMatrix; import org.apache.commons.math4.linear.NonSquareMatrixException; public class FieldLUDecompositionTest { private Fraction[][] testData = { { new Fraction(1), new Fraction(2), new Fraction(3)}, { new Fraction(2), new Fraction(5), new Fraction(3)}, { new Fraction(1), new Fraction(0), new Fraction(8)} }; private Fraction[][] testDataMinus = { { new Fraction(-1), new Fraction(-2), new Fraction(-3)}, { new Fraction(-2), new Fraction(-5), new Fraction(-3)}, { new Fraction(-1), new Fraction(0), new Fraction(-8)} }; private Fraction[][] luData = { { new Fraction(2), new Fraction(3), new Fraction(3) }, { new Fraction(2), new Fraction(3), new Fraction(7) }, { new Fraction(6), new Fraction(6), new Fraction(8) } }; // singular matrices private Fraction[][] singular = { { new Fraction(2), new Fraction(3) }, { new Fraction(2), new Fraction(3) } }; private Fraction[][] bigSingular = { { new Fraction(1), new Fraction(2), new Fraction(3), new Fraction(4) }, { new Fraction(2), new Fraction(5), new Fraction(3), new Fraction(4) }, { new Fraction(7), new Fraction(3), new Fraction(256), new Fraction(1930) }, { new Fraction(3), new Fraction(7), new Fraction(6), new Fraction(8) } }; // 4th row = 1st + 2nd /** test dimensions */ @Test public void testDimensions() { FieldMatrix<Fraction> matrix = new Array2DRowFieldMatrix<>(FractionField.getInstance(), testData); FieldLUDecomposition<Fraction> LU = new FieldLUDecomposition<>(matrix); Assert.assertEquals(testData.length, LU.getL().getRowDimension()); Assert.assertEquals(testData.length, LU.getL().getColumnDimension()); Assert.assertEquals(testData.length, LU.getU().getRowDimension()); Assert.assertEquals(testData.length, LU.getU().getColumnDimension()); Assert.assertEquals(testData.length, LU.getP().getRowDimension()); Assert.assertEquals(testData.length, LU.getP().getColumnDimension()); } /** test non-square matrix */ @Test public void testNonSquare() { try { // we don't use FractionField.getInstance() for testing purposes new FieldLUDecomposition<>(new Array2DRowFieldMatrix<>(new Fraction[][] { { Fraction.ZERO, Fraction.ZERO }, { Fraction.ZERO, Fraction.ZERO }, { Fraction.ZERO, Fraction.ZERO } })); Assert.fail("Expected NonSquareMatrixException"); } catch (NonSquareMatrixException ime) { // expected behavior } } /** test PA = LU */ @Test public void testPAEqualLU() { FieldMatrix<Fraction> matrix = new Array2DRowFieldMatrix<>(FractionField.getInstance(), testData); FieldLUDecomposition<Fraction> lu = new FieldLUDecomposition<>(matrix); FieldMatrix<Fraction> l = lu.getL(); FieldMatrix<Fraction> u = lu.getU(); FieldMatrix<Fraction> p = lu.getP(); TestUtils.assertEquals(p.multiply(matrix), l.multiply(u)); matrix = new Array2DRowFieldMatrix<>(FractionField.getInstance(), testDataMinus); lu = new FieldLUDecomposition<>(matrix); l = lu.getL(); u = lu.getU(); p = lu.getP(); TestUtils.assertEquals(p.multiply(matrix), l.multiply(u)); matrix = new Array2DRowFieldMatrix<>(FractionField.getInstance(), 17, 17); for (int i = 0; i < matrix.getRowDimension(); ++i) { matrix.setEntry(i, i, Fraction.ONE); } lu = new FieldLUDecomposition<>(matrix); l = lu.getL(); u = lu.getU(); p = lu.getP(); TestUtils.assertEquals(p.multiply(matrix), l.multiply(u)); matrix = new Array2DRowFieldMatrix<>(FractionField.getInstance(), singular); lu = new FieldLUDecomposition<>(matrix); Assert.assertFalse(lu.getSolver().isNonSingular()); Assert.assertNull(lu.getL()); Assert.assertNull(lu.getU()); Assert.assertNull(lu.getP()); matrix = new Array2DRowFieldMatrix<>(FractionField.getInstance(), bigSingular); lu = new FieldLUDecomposition<>(matrix); Assert.assertFalse(lu.getSolver().isNonSingular()); Assert.assertNull(lu.getL()); Assert.assertNull(lu.getU()); Assert.assertNull(lu.getP()); } /** test that L is lower triangular with unit diagonal */ @Test public void testLLowerTriangular() { FieldMatrix<Fraction> matrix = new Array2DRowFieldMatrix<>(FractionField.getInstance(), testData); FieldMatrix<Fraction> l = new FieldLUDecomposition<>(matrix).getL(); for (int i = 0; i < l.getRowDimension(); i++) { Assert.assertEquals(Fraction.ONE, l.getEntry(i, i)); for (int j = i + 1; j < l.getColumnDimension(); j++) { Assert.assertEquals(Fraction.ZERO, l.getEntry(i, j)); } } } /** test that U is upper triangular */ @Test public void testUUpperTriangular() { FieldMatrix<Fraction> matrix = new Array2DRowFieldMatrix<>(FractionField.getInstance(), testData); FieldMatrix<Fraction> u = new FieldLUDecomposition<>(matrix).getU(); for (int i = 0; i < u.getRowDimension(); i++) { for (int j = 0; j < i; j++) { Assert.assertEquals(Fraction.ZERO, u.getEntry(i, j)); } } } /** test that P is a permutation matrix */ @Test public void testPPermutation() { FieldMatrix<Fraction> matrix = new Array2DRowFieldMatrix<>(FractionField.getInstance(), testData); FieldMatrix<Fraction> p = new FieldLUDecomposition<>(matrix).getP(); FieldMatrix<Fraction> ppT = p.multiply(p.transpose()); FieldMatrix<Fraction> id = new Array2DRowFieldMatrix<>(FractionField.getInstance(), p.getRowDimension(), p.getRowDimension()); for (int i = 0; i < id.getRowDimension(); ++i) { id.setEntry(i, i, Fraction.ONE); } TestUtils.assertEquals(id, ppT); for (int i = 0; i < p.getRowDimension(); i++) { int zeroCount = 0; int oneCount = 0; int otherCount = 0; for (int j = 0; j < p.getColumnDimension(); j++) { final Fraction e = p.getEntry(i, j); if (e.equals(Fraction.ZERO)) { ++zeroCount; } else if (e.equals(Fraction.ONE)) { ++oneCount; } else { ++otherCount; } } Assert.assertEquals(p.getColumnDimension() - 1, zeroCount); Assert.assertEquals(1, oneCount); Assert.assertEquals(0, otherCount); } for (int j = 0; j < p.getColumnDimension(); j++) { int zeroCount = 0; int oneCount = 0; int otherCount = 0; for (int i = 0; i < p.getRowDimension(); i++) { final Fraction e = p.getEntry(i, j); if (e.equals(Fraction.ZERO)) { ++zeroCount; } else if (e.equals(Fraction.ONE)) { ++oneCount; } else { ++otherCount; } } Assert.assertEquals(p.getRowDimension() - 1, zeroCount); Assert.assertEquals(1, oneCount); Assert.assertEquals(0, otherCount); } } /** test singular */ @Test public void testSingular() { FieldLUDecomposition<Fraction> lu = new FieldLUDecomposition<>(new Array2DRowFieldMatrix<>(FractionField.getInstance(), testData)); Assert.assertTrue(lu.getSolver().isNonSingular()); lu = new FieldLUDecomposition<>(new Array2DRowFieldMatrix<>(FractionField.getInstance(), singular)); Assert.assertFalse(lu.getSolver().isNonSingular()); lu = new FieldLUDecomposition<>(new Array2DRowFieldMatrix<>(FractionField.getInstance(), bigSingular)); Assert.assertFalse(lu.getSolver().isNonSingular()); } /** test matrices values */ @Test public void testMatricesValues1() { FieldLUDecomposition<Fraction> lu = new FieldLUDecomposition<>(new Array2DRowFieldMatrix<>(FractionField.getInstance(), testData)); FieldMatrix<Fraction> lRef = new Array2DRowFieldMatrix<>(FractionField.getInstance(), new Fraction[][] { { new Fraction(1), new Fraction(0), new Fraction(0) }, { new Fraction(2), new Fraction(1), new Fraction(0) }, { new Fraction(1), new Fraction(-2), new Fraction(1) } }); FieldMatrix<Fraction> uRef = new Array2DRowFieldMatrix<>(FractionField.getInstance(), new Fraction[][] { { new Fraction(1), new Fraction(2), new Fraction(3) }, { new Fraction(0), new Fraction(1), new Fraction(-3) }, { new Fraction(0), new Fraction(0), new Fraction(-1) } }); FieldMatrix<Fraction> pRef = new Array2DRowFieldMatrix<>(FractionField.getInstance(), new Fraction[][] { { new Fraction(1), new Fraction(0), new Fraction(0) }, { new Fraction(0), new Fraction(1), new Fraction(0) }, { new Fraction(0), new Fraction(0), new Fraction(1) } }); int[] pivotRef = { 0, 1, 2 }; // check values against known references FieldMatrix<Fraction> l = lu.getL(); TestUtils.assertEquals(lRef, l); FieldMatrix<Fraction> u = lu.getU(); TestUtils.assertEquals(uRef, u); FieldMatrix<Fraction> p = lu.getP(); TestUtils.assertEquals(pRef, p); int[] pivot = lu.getPivot(); for (int i = 0; i < pivotRef.length; ++i) { Assert.assertEquals(pivotRef[i], pivot[i]); } // check the same cached instance is returned the second time Assert.assertTrue(l == lu.getL()); Assert.assertTrue(u == lu.getU()); Assert.assertTrue(p == lu.getP()); } /** test matrices values */ @Test public void testMatricesValues2() { FieldLUDecomposition<Fraction> lu = new FieldLUDecomposition<>(new Array2DRowFieldMatrix<>(FractionField.getInstance(), luData)); FieldMatrix<Fraction> lRef = new Array2DRowFieldMatrix<>(FractionField.getInstance(), new Fraction[][] { { new Fraction(1), new Fraction(0), new Fraction(0) }, { new Fraction(3), new Fraction(1), new Fraction(0) }, { new Fraction(1), new Fraction(0), new Fraction(1) } }); FieldMatrix<Fraction> uRef = new Array2DRowFieldMatrix<>(FractionField.getInstance(), new Fraction[][] { { new Fraction(2), new Fraction(3), new Fraction(3) }, { new Fraction(0), new Fraction(-3), new Fraction(-1) }, { new Fraction(0), new Fraction(0), new Fraction(4) } }); FieldMatrix<Fraction> pRef = new Array2DRowFieldMatrix<>(FractionField.getInstance(), new Fraction[][] { { new Fraction(1), new Fraction(0), new Fraction(0) }, { new Fraction(0), new Fraction(0), new Fraction(1) }, { new Fraction(0), new Fraction(1), new Fraction(0) } }); int[] pivotRef = { 0, 2, 1 }; // check values against known references FieldMatrix<Fraction> l = lu.getL(); TestUtils.assertEquals(lRef, l); FieldMatrix<Fraction> u = lu.getU(); TestUtils.assertEquals(uRef, u); FieldMatrix<Fraction> p = lu.getP(); TestUtils.assertEquals(pRef, p); int[] pivot = lu.getPivot(); for (int i = 0; i < pivotRef.length; ++i) { Assert.assertEquals(pivotRef[i], pivot[i]); } // check the same cached instance is returned the second time Assert.assertTrue(l == lu.getL()); Assert.assertTrue(u == lu.getU()); Assert.assertTrue(p == lu.getP()); } }