Author: tn Date: Fri Oct 18 21:19:18 2013 New Revision: 1533638 URL: http://svn.apache.org/r1533638 Log: [MATH-1004] Added new methods to compute the inverse of a matrix to DiagonalMatrix and MatrixUtils.
Modified: commons/proper/math/trunk/src/changes/changes.xml commons/proper/math/trunk/src/main/java/org/apache/commons/math3/linear/DiagonalMatrix.java commons/proper/math/trunk/src/main/java/org/apache/commons/math3/linear/MatrixUtils.java commons/proper/math/trunk/src/test/java/org/apache/commons/math3/linear/DiagonalMatrixTest.java commons/proper/math/trunk/src/test/java/org/apache/commons/math3/linear/MatrixUtilsTest.java Modified: commons/proper/math/trunk/src/changes/changes.xml URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/changes/changes.xml?rev=1533638&r1=1533637&r2=1533638&view=diff ============================================================================== --- commons/proper/math/trunk/src/changes/changes.xml (original) +++ commons/proper/math/trunk/src/changes/changes.xml Fri Oct 18 21:19:18 2013 @@ -51,6 +51,10 @@ If the output is not quite correct, chec </properties> <body> <release version="x.y" date="TBD" description="TBD"> + <action dev="tn" type="add" issue="MATH-1004" due-to="Ajo Fod"> + Added new methods to compute the inverse of a matrix to "DiagonalMatrix" + and "MatrixUtils". + </action> <action dev="tn" type="fix" issue="MATH-1029"> The "BigFraction" constructor will throw a "FractionConversionException" also in case negative values are provided which exceed the allowed range Modified: commons/proper/math/trunk/src/main/java/org/apache/commons/math3/linear/DiagonalMatrix.java URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math3/linear/DiagonalMatrix.java?rev=1533638&r1=1533637&r2=1533638&view=diff ============================================================================== --- commons/proper/math/trunk/src/main/java/org/apache/commons/math3/linear/DiagonalMatrix.java (original) +++ commons/proper/math/trunk/src/main/java/org/apache/commons/math3/linear/DiagonalMatrix.java Fri Oct 18 21:19:18 2013 @@ -319,4 +319,53 @@ public class DiagonalMatrix extends Abst } } + /** + * Computes the inverse of this diagonal matrix. + * <p> + * Note: this method will use a singularity threshold of 0, + * use {@link #inverse(double)} if a different threshold is needed. + * + * @return the inverse of {@code m} + * @throws SingularMatrixException if the matrix is singular + * @since 3.3 + */ + public DiagonalMatrix inverse() throws SingularMatrixException { + return inverse(0); + } + + /** + * Computes the inverse of this diagonal matrix. + * + * @param threshold Singularity threshold. + * @return the inverse of {@code m} + * @throws SingularMatrixException if the matrix is singular + * @since 3.3 + */ + public DiagonalMatrix inverse(double threshold) throws SingularMatrixException { + if (isSingular(threshold)) { + throw new SingularMatrixException(); + } + + final double[] result = new double[data.length]; + for (int i = 0; i < data.length; i++) { + result[i] = 1.0 / data[i]; + } + return new DiagonalMatrix(result, false); + } + + /** Returns whether this diagonal matrix is singular, i.e. any diagonal entry + * is equal to {@code 0} within the given threshold. + * + * @param threshold Singularity threshold. + * @return {@code true} if the matrix is singular, {@code false} otherwise + * @since 3.3 + */ + public boolean isSingular(double threshold) { + for (int i = 0; i < data.length; i++) { + if (Precision.equals(data[i], 0.0, threshold)) { + return true; + } + } + return false; + } } Modified: commons/proper/math/trunk/src/main/java/org/apache/commons/math3/linear/MatrixUtils.java URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math3/linear/MatrixUtils.java?rev=1533638&r1=1533637&r2=1533638&view=diff ============================================================================== --- commons/proper/math/trunk/src/main/java/org/apache/commons/math3/linear/MatrixUtils.java (original) +++ commons/proper/math/trunk/src/main/java/org/apache/commons/math3/linear/MatrixUtils.java Fri Oct 18 21:19:18 2013 @@ -36,6 +36,7 @@ import org.apache.commons.math3.fraction import org.apache.commons.math3.fraction.Fraction; import org.apache.commons.math3.util.FastMath; import org.apache.commons.math3.util.MathArrays; +import org.apache.commons.math3.util.MathUtils; import org.apache.commons.math3.util.Precision; /** @@ -1063,4 +1064,57 @@ public class MatrixUtils { return result; } + + /** + * Computes the inverse of the given matrix. + * <p> + * By default, the inverse of the matrix is computed using the QR-decomposition, + * unless a more efficient method can be determined for the input matrix. + * <p> + * Note: this method will use a singularity threshold of 0, + * use {@link #inverse(RealMatrix, double)} if a different threshold is needed. + * + * @param matrix Matrix whose inverse shall be computed + * @return the inverse of {@code matrix} + * @throws NullArgumentException if {@code matrix} is {@code null} + * @throws SingularMatrixException if m is singular + * @throws NonSquareMatrixException if matrix is not square + * @since 3.3 + */ + public static RealMatrix inverse(RealMatrix matrix) + throws NullArgumentException, SingularMatrixException, NonSquareMatrixException { + return inverse(matrix, 0); + } + + /** + * Computes the inverse of the given matrix. + * <p> + * By default, the inverse of the matrix is computed using the QR-decomposition, + * unless a more efficient method can be determined for the input matrix. + * + * @param matrix Matrix whose inverse shall be computed + * @param threshold Singularity threshold + * @return the inverse of {@code m} + * @throws NullArgumentException if {@code matrix} is {@code null} + * @throws SingularMatrixException if matrix is singular + * @throws NonSquareMatrixException if matrix is not square + * @since 3.3 + */ + public static RealMatrix inverse(RealMatrix matrix, double threshold) + throws NullArgumentException, SingularMatrixException, NonSquareMatrixException { + + MathUtils.checkNotNull(matrix); + + if (!matrix.isSquare()) { + throw new NonSquareMatrixException(matrix.getRowDimension(), + matrix.getColumnDimension()); + } + + if (matrix instanceof DiagonalMatrix) { + return ((DiagonalMatrix) matrix).inverse(threshold); + } else { + QRDecomposition decomposition = new QRDecomposition(matrix, threshold); + return decomposition.getSolver().getInverse(); + } + } } Modified: commons/proper/math/trunk/src/test/java/org/apache/commons/math3/linear/DiagonalMatrixTest.java URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math3/linear/DiagonalMatrixTest.java?rev=1533638&r1=1533637&r2=1533638&view=diff ============================================================================== --- commons/proper/math/trunk/src/test/java/org/apache/commons/math3/linear/DiagonalMatrixTest.java (original) +++ commons/proper/math/trunk/src/test/java/org/apache/commons/math3/linear/DiagonalMatrixTest.java Fri Oct 18 21:19:18 2013 @@ -340,4 +340,29 @@ public class DiagonalMatrixTest { Assert.assertEquals( 6.0, diag.getEntry(2, 2), 1.0e-20); } + @Test(expected=SingularMatrixException.class) + public void testInverseError() { + final double[] data = { 1, 2, 0 }; + final DiagonalMatrix diag = new DiagonalMatrix(data); + diag.inverse(); + } + + @Test(expected=SingularMatrixException.class) + public void testInverseError2() { + final double[] data = { 1, 2, 1e-6 }; + final DiagonalMatrix diag = new DiagonalMatrix(data); + diag.inverse(1e-5); + } + + @Test + public void testInverse() { + final double[] data = { 1, 2, 3 }; + final DiagonalMatrix m = new DiagonalMatrix(data); + final DiagonalMatrix inverse = m.inverse(); + + final DiagonalMatrix result = m.multiply(inverse); + TestUtils.assertEquals("DiagonalMatrix.inverse() returns wrong result", + MatrixUtils.createRealIdentityMatrix(data.length), result, Math.ulp(1d)); + } + } Modified: commons/proper/math/trunk/src/test/java/org/apache/commons/math3/linear/MatrixUtilsTest.java URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math3/linear/MatrixUtilsTest.java?rev=1533638&r1=1533637&r2=1533638&view=diff ============================================================================== --- commons/proper/math/trunk/src/test/java/org/apache/commons/math3/linear/MatrixUtilsTest.java (original) +++ commons/proper/math/trunk/src/test/java/org/apache/commons/math3/linear/MatrixUtilsTest.java Fri Oct 18 21:19:18 2013 @@ -17,6 +17,7 @@ package org.apache.commons.math3.linear; import java.math.BigDecimal; + import org.apache.commons.math3.TestUtils; import org.apache.commons.math3.fraction.BigFraction; import org.apache.commons.math3.fraction.Fraction; @@ -37,6 +38,8 @@ import org.junit.Test; public final class MatrixUtilsTest { protected double[][] testData = { {1d,2d,3d}, {2d,5d,3d}, {1d,0d,8d} }; + protected double[][] testData3x3Singular = { { 1, 4, 7, }, { 2, 5, 8, }, { 3, 6, 9, } }; + protected double[][] testData3x4 = { { 12, -51, 4, 1 }, { 6, 167, -68, 2 }, { -4, 24, -41, 3 } }; protected double[][] nullMatrix = null; protected double[] row = {1,2,3}; protected BigDecimal[] bigRow = @@ -445,4 +448,38 @@ public final class MatrixUtilsTest { }; MatrixUtils.checkSymmetric(MatrixUtils.createRealMatrix(dataNonSym), Math.ulp(1d)); } + + @Test(expected=SingularMatrixException.class) + public void testInverseSingular() { + RealMatrix m = MatrixUtils.createRealMatrix(testData3x3Singular); + MatrixUtils.inverse(m); + } + + @Test(expected=NonSquareMatrixException.class) + public void testInverseNonSquare() { + RealMatrix m = MatrixUtils.createRealMatrix(testData3x4); + MatrixUtils.inverse(m); + } + + @Test + public void testInverseDiagonalMatrix() { + final double[] data = { 1, 2, 3 }; + final RealMatrix m = new DiagonalMatrix(data); + final RealMatrix inverse = MatrixUtils.inverse(m); + + final RealMatrix result = m.multiply(inverse); + TestUtils.assertEquals("MatrixUtils.inverse() returns wrong result", + MatrixUtils.createRealIdentityMatrix(data.length), result, Math.ulp(1d)); + } + + @Test + public void testInverseRealMatrix() { + RealMatrix m = MatrixUtils.createRealMatrix(testData); + final RealMatrix inverse = MatrixUtils.inverse(m); + + final RealMatrix result = m.multiply(inverse); + TestUtils.assertEquals("MatrixUtils.inverse() returns wrong result", + MatrixUtils.createRealIdentityMatrix(testData.length), result, 1e-12); + } + }