/* * 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 java.util.Random; import org.apache.commons.math4.exception.MathIllegalArgumentException; import org.apache.commons.math4.linear.DecompositionSolver; import org.apache.commons.math4.linear.EigenDecomposition; import org.apache.commons.math4.linear.MatrixUtils; import org.apache.commons.math4.linear.RealMatrix; import org.apache.commons.math4.linear.SingularMatrixException; import org.apache.commons.numbers.core.Precision; import org.junit.Test; import org.junit.Assert; public class EigenSolverTest { private double[][] bigSingular = { { 1.0, 2.0, 3.0, 4.0 }, { 2.0, 5.0, 3.0, 4.0 }, { 7.0, 3.0, 256.0, 1930.0 }, { 3.0, 7.0, 6.0, 8.0 } }; // 4th row = 1st + 2nd /** test non invertible matrix */ @Test public void testNonInvertible() { Random r = new Random(9994100315209l); RealMatrix m = EigenDecompositionTest.createTestMatrix(r, new double[] { 1.0, 0.0, -1.0, -2.0, -3.0 }); DecompositionSolver es = new EigenDecomposition(m).getSolver(); Assert.assertFalse(es.isNonSingular()); try { es.getInverse(); Assert.fail("an exception should have been thrown"); } catch (SingularMatrixException ime) { // expected behavior } } /** test invertible matrix */ @Test public void testInvertible() { Random r = new Random(9994100315209l); RealMatrix m = EigenDecompositionTest.createTestMatrix(r, new double[] { 1.0, 0.5, -1.0, -2.0, -3.0 }); DecompositionSolver es = new EigenDecomposition(m).getSolver(); Assert.assertTrue(es.isNonSingular()); RealMatrix inverse = es.getInverse(); RealMatrix error = m.multiply(inverse).subtract(MatrixUtils.createRealIdentityMatrix(m.getRowDimension())); Assert.assertEquals(0, error.getNorm(), 4.0e-15); } /** * Verifies operation on very small values. * Matrix with eigenvalues {8e-100, -1e-100, -1e-100} */ @Test public void testInvertibleTinyValues() { final double tiny = 1e-100; RealMatrix m = MatrixUtils.createRealMatrix(new double[][] { {3, 2, 4}, {2, 0, 2}, {4, 2, 3} }); m = m.scalarMultiply(tiny); final EigenDecomposition ed = new EigenDecomposition(m); RealMatrix inv = ed.getSolver().getInverse(); final RealMatrix id = m.multiply(inv); for (int i = 0; i < m.getRowDimension(); i++) { for (int j = 0; j < m.getColumnDimension(); j++) { if (i == j) { Assert.assertTrue(Precision.equals(1, id.getEntry(i, j), 1e-15)); } else { Assert.assertTrue(Precision.equals(0, id.getEntry(i, j), 1e-15)); } } } } @Test(expected=SingularMatrixException.class) public void testNonInvertibleMath1045() { EigenDecomposition eigen = new EigenDecomposition(MatrixUtils.createRealMatrix(bigSingular)); eigen.getSolver().getInverse(); } @Test(expected=SingularMatrixException.class) public void testZeroMatrix() { EigenDecomposition eigen = new EigenDecomposition(MatrixUtils.createRealMatrix(new double[][] {{0}})); eigen.getSolver().getInverse(); } @Test public void testIsNonSingularTinyOutOfOrderEigenvalue() { final EigenDecomposition eigen = new EigenDecomposition(MatrixUtils.createRealMatrix(new double[][] { { 1e-13, 0 }, { 1, 1 }, })); Assert.assertFalse("Singular matrix not detected", eigen.getSolver().isNonSingular()); } /** test solve dimension errors */ @Test public void testSolveDimensionErrors() { final double[] refValues = new double[] { 2.003, 2.002, 2.001, 1.001, 1.000, 0.001 }; final RealMatrix matrix = EigenDecompositionTest.createTestMatrix(new Random(35992629946426l), refValues); DecompositionSolver es = new EigenDecomposition(matrix).getSolver(); RealMatrix b = MatrixUtils.createRealMatrix(new double[2][2]); try { es.solve(b); Assert.fail("an exception should have been thrown"); } catch (MathIllegalArgumentException iae) { // expected behavior } try { es.solve(b.getColumnVector(0)); Assert.fail("an exception should have been thrown"); } catch (MathIllegalArgumentException iae) { // expected behavior } try { es.solve(new ArrayRealVectorTest.RealVectorTestImpl(b.getColumn(0))); Assert.fail("an exception should have been thrown"); } catch (MathIllegalArgumentException iae) { // expected behavior } } /** test solve */ @Test public void testSolve() { RealMatrix m = MatrixUtils.createRealMatrix(new double[][] { { 91, 5, 29, 32, 40, 14 }, { 5, 34, -1, 0, 2, -1 }, { 29, -1, 12, 9, 21, 8 }, { 32, 0, 9, 14, 9, 0 }, { 40, 2, 21, 9, 51, 19 }, { 14, -1, 8, 0, 19, 14 } }); DecompositionSolver es = new EigenDecomposition(m).getSolver(); RealMatrix b = MatrixUtils.createRealMatrix(new double[][] { { 1561, 269, 188 }, { 69, -21, 70 }, { 739, 108, 63 }, { 324, 86, 59 }, { 1624, 194, 107 }, { 796, 69, 36 } }); RealMatrix xRef = MatrixUtils.createRealMatrix(new double[][] { { 1, 2, 1 }, { 2, -1, 2 }, { 4, 2, 3 }, { 8, -1, 0 }, { 16, 2, 0 }, { 32, -1, 0 } }); // using RealMatrix RealMatrix solution=es.solve(b); Assert.assertEquals(0, solution.subtract(xRef).getNorm(), 2.5e-12); // using RealVector for (int i = 0; i < b.getColumnDimension(); ++i) { Assert.assertEquals(0, es.solve(b.getColumnVector(i)).subtract(xRef.getColumnVector(i)).getNorm(), 2.0e-11); } // using RealVector with an alternate implementation for (int i = 0; i < b.getColumnDimension(); ++i) { ArrayRealVectorTest.RealVectorTestImpl v = new ArrayRealVectorTest.RealVectorTestImpl(b.getColumn(i)); Assert.assertEquals(0, es.solve(v).subtract(xRef.getColumnVector(i)).getNorm(), 2.0e-11); } } }