Author: celestin Date: Sat Sep 24 05:13:53 2011 New Revision: 1175108 URL: http://svn.apache.org/viewvc?rev=1175108&view=rev Log: Merged SingularValueDecomposition and SingularValueDecompositionImpl (see MATH-662).
Added: commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/SingularValueDecomposition.java - copied, changed from r1175099, commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/SingularValueDecompositionImpl.java commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/SingularValueDecompositionTest.java - copied, changed from r1175099, commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/SingularValueDecompositionImplTest.java Removed: commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/SingularValueDecompositionImpl.java commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/SingularValueDecompositionImplTest.java Modified: commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/SingularValueSolverTest.java Copied: commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/SingularValueDecomposition.java (from r1175099, commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/SingularValueDecompositionImpl.java) URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/SingularValueDecomposition.java?p2=commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/SingularValueDecomposition.java&p1=commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/SingularValueDecompositionImpl.java&r1=1175099&r2=1175108&rev=1175108&view=diff ============================================================================== --- commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/SingularValueDecompositionImpl.java (original) +++ commons/proper/math/trunk/src/main/java/org/apache/commons/math/linear/SingularValueDecomposition.java Sat Sep 24 05:13:53 2011 @@ -31,10 +31,27 @@ import org.apache.commons.math.util.Math * n orthogonal matrix (hence V<sup>T</sup> is also orthogonal) where * p=min(m,n). * </p> + * <p>This class is similar to the class with similar name from the + * <a href="http://math.nist.gov/javanumerics/jama/">JAMA</a> library, with the + * following changes:</p> + * <ul> + * <li>the {@code norm2} method which has been renamed as {@link #getNorm() + * getNorm},</li> + * <li>the {@code cond} method which has been renamed as {@link + * #getConditionNumber() getConditionNumber},</li> + * <li>the {@code rank} method which has been renamed as {@link #getRank() + * getRank},</li> + * <li>a {@link #getUT() getUT} method has been added,</li> + * <li>a {@link #getVT() getVT} method has been added,</li> + * <li>a {@link #getSolver() getSolver} method has been added,</li> + * <li>a {@link #getCovariance(double) getCovariance} method has been added.</li> + * </ul> + * @see <a href="http://mathworld.wolfram.com/SingularValueDecomposition.html">MathWorld</a> + * @see <a href="http://en.wikipedia.org/wiki/Singular_value_decomposition">Wikipedia</a> * @version $Id$ - * @since 2.0 + * @since 2.0 (changed to concrete class in 3.0) */ -public class SingularValueDecompositionImpl implements SingularValueDecomposition { +public class SingularValueDecomposition { /** Relative threshold for small singular values. */ private static final double EPS = 0x1.0p-52; /** Absolute threshold for small singular values. */ @@ -68,7 +85,7 @@ public class SingularValueDecompositionI * * @param matrix Matrix to decompose. */ - public SingularValueDecompositionImpl(final RealMatrix matrix) { + public SingularValueDecomposition(final RealMatrix matrix) { final double[][] A; // "m" is always the largest dimension. @@ -459,14 +476,24 @@ public class SingularValueDecompositionI } } - /** {@inheritDoc} */ + /** + * Returns the matrix U of the decomposition. + * <p>U is an orthogonal matrix, i.e. its transpose is also its inverse.</p> + * @return the U matrix + * @see #getUT() + */ public RealMatrix getU() { // return the cached matrix return cachedU; } - /** {@inheritDoc} */ + /** + * Returns the transpose of the matrix U of the decomposition. + * <p>U is an orthogonal matrix, i.e. its transpose is also its inverse.</p> + * @return the U matrix (or null if decomposed matrix is singular) + * @see #getU() + */ public RealMatrix getUT() { if (cachedUt == null) { cachedUt = getU().transpose(); @@ -475,7 +502,12 @@ public class SingularValueDecompositionI return cachedUt; } - /** {@inheritDoc} */ + /** + * Returns the diagonal matrix Σ of the decomposition. + * <p>Σ is a diagonal matrix. The singular values are provided in + * non-increasing order, for compatibility with Jama.</p> + * @return the Σ matrix + */ public RealMatrix getS() { if (cachedS == null) { // cache the matrix for subsequent calls @@ -484,18 +516,33 @@ public class SingularValueDecompositionI return cachedS; } - /** {@inheritDoc} */ + /** + * Returns the diagonal elements of the matrix Σ of the decomposition. + * <p>The singular values are provided in non-increasing order, for + * compatibility with Jama.</p> + * @return the diagonal elements of the Σ matrix + */ public double[] getSingularValues() { return singularValues.clone(); } - /** {@inheritDoc} */ + /** + * Returns the matrix V of the decomposition. + * <p>V is an orthogonal matrix, i.e. its transpose is also its inverse.</p> + * @return the V matrix (or null if decomposed matrix is singular) + * @see #getVT() + */ public RealMatrix getV() { // return the cached matrix return cachedV; } - /** {@inheritDoc} */ + /** + * Returns the transpose of the matrix V of the decomposition. + * <p>V is an orthogonal matrix, i.e. its transpose is also its inverse.</p> + * @return the V matrix (or null if decomposed matrix is singular) + * @see #getV() + */ public RealMatrix getVT() { if (cachedVt == null) { cachedVt = getV().transpose(); @@ -504,7 +551,17 @@ public class SingularValueDecompositionI return cachedVt; } - /** {@inheritDoc} */ + /** + * Returns the n × n covariance matrix. + * <p>The covariance matrix is V × J × V<sup>T</sup> + * where J is the diagonal matrix of the inverse of the squares of + * the singular values.</p> + * @param minSingularValue value below which singular values are ignored + * (a 0 or negative value implies all singular value will be used) + * @return covariance matrix + * @exception IllegalArgumentException if minSingularValue is larger than + * the largest singular value, meaning all singular values are ignored + */ public RealMatrix getCovariance(final double minSingularValue) { // get the number of singular values to consider final int p = singularValues.length; @@ -533,12 +590,21 @@ public class SingularValueDecompositionI return jv.transpose().multiply(jv); } - /** {@inheritDoc} */ + /** + * Returns the L<sub>2</sub> norm of the matrix. + * <p>The L<sub>2</sub> norm is max(|A × u|<sub>2</sub> / + * |u|<sub>2</sub>), where |.|<sub>2</sub> denotes the vectorial 2-norm + * (i.e. the traditional euclidian norm).</p> + * @return norm + */ public double getNorm() { return singularValues[0]; } - /** {@inheritDoc} */ + /** + * Return the condition number of the matrix. + * @return condition number of the matrix + */ public double getConditionNumber() { return singularValues[0] / singularValues[n - 1]; } @@ -554,7 +620,14 @@ public class SingularValueDecompositionI return singularValues[n - 1] / singularValues[0]; } - /** {@inheritDoc} */ + /** + * Return the effective numerical matrix rank. + * <p>The effective numerical rank is the number of non-negligible + * singular values. The threshold used to identify non-negligible + * terms is max(m,n) × ulp(s<sub>1</sub>) where ulp(s<sub>1</sub>) + * is the least significant bit of the largest singular value.</p> + * @return effective numerical matrix rank + */ public int getRank() { int r = 0; for (int i = 0; i < singularValues.length; i++) { @@ -565,7 +638,10 @@ public class SingularValueDecompositionI return r; } - /** {@inheritDoc} */ + /** + * Get a solver for finding the A × X = B solution in least square sense. + * @return a solver + */ public DecompositionSolver getSolver() { return new Solver(singularValues, getUT(), getV(), getRank() == m, tol); } Copied: commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/SingularValueDecompositionTest.java (from r1175099, commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/SingularValueDecompositionImplTest.java) URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/SingularValueDecompositionTest.java?p2=commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/SingularValueDecompositionTest.java&p1=commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/SingularValueDecompositionImplTest.java&r1=1175099&r2=1175108&rev=1175108&view=diff ============================================================================== --- commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/SingularValueDecompositionImplTest.java (original) +++ commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/SingularValueDecompositionTest.java Sat Sep 24 05:13:53 2011 @@ -27,7 +27,7 @@ import org.junit.Assert; import org.junit.Test; -public class SingularValueDecompositionImplTest { +public class SingularValueDecompositionTest { private double[][] testSquare = { { 24.0 / 25.0, 43.0 / 25.0 }, @@ -50,7 +50,7 @@ public class SingularValueDecompositionI final int columns = singularValues.length; Random r = new Random(15338437322523l); SingularValueDecomposition svd = - new SingularValueDecompositionImpl(createTestMatrix(r, rows, columns, singularValues)); + new SingularValueDecomposition(createTestMatrix(r, rows, columns, singularValues)); double[] computedSV = svd.getSingularValues(); Assert.assertEquals(singularValues.length, computedSV.length); for (int i = 0; i < singularValues.length; ++i) { @@ -65,7 +65,7 @@ public class SingularValueDecompositionI final int columns = singularValues.length + 2; Random r = new Random(732763225836210l); SingularValueDecomposition svd = - new SingularValueDecompositionImpl(createTestMatrix(r, rows, columns, singularValues)); + new SingularValueDecomposition(createTestMatrix(r, rows, columns, singularValues)); double[] computedSV = svd.getSingularValues(); Assert.assertEquals(singularValues.length, computedSV.length); for (int i = 0; i < singularValues.length; ++i) { @@ -79,7 +79,7 @@ public class SingularValueDecompositionI RealMatrix matrix = MatrixUtils.createRealMatrix(testSquare); final int m = matrix.getRowDimension(); final int n = matrix.getColumnDimension(); - SingularValueDecomposition svd = new SingularValueDecompositionImpl(matrix); + SingularValueDecomposition svd = new SingularValueDecomposition(matrix); Assert.assertEquals(m, svd.getU().getRowDimension()); Assert.assertEquals(m, svd.getU().getColumnDimension()); Assert.assertEquals(m, svd.getS().getColumnDimension()); @@ -98,7 +98,7 @@ public class SingularValueDecompositionI { 9.0 / 2.0, 3.0 / 2.0, 15.0 / 2.0, 5.0 / 2.0 }, { 3.0 / 2.0, 9.0 / 2.0, 5.0 / 2.0, 15.0 / 2.0 } }, false); - SingularValueDecomposition svd = new SingularValueDecompositionImpl(matrix); + SingularValueDecomposition svd = new SingularValueDecomposition(matrix); Assert.assertEquals(16.0, svd.getSingularValues()[0], 1.0e-14); Assert.assertEquals( 8.0, svd.getSingularValues()[1], 1.0e-14); Assert.assertEquals( 4.0, svd.getSingularValues()[2], 1.0e-14); @@ -135,7 +135,7 @@ public class SingularValueDecompositionI } public void checkAEqualUSVt(final RealMatrix matrix) { - SingularValueDecomposition svd = new SingularValueDecompositionImpl(matrix); + SingularValueDecomposition svd = new SingularValueDecomposition(matrix); RealMatrix u = svd.getU(); RealMatrix s = svd.getS(); RealMatrix v = svd.getV(); @@ -147,17 +147,17 @@ public class SingularValueDecompositionI /** test that U is orthogonal */ @Test public void testUOrthogonal() { - checkOrthogonal(new SingularValueDecompositionImpl(MatrixUtils.createRealMatrix(testSquare)).getU()); - checkOrthogonal(new SingularValueDecompositionImpl(MatrixUtils.createRealMatrix(testNonSquare)).getU()); - checkOrthogonal(new SingularValueDecompositionImpl(MatrixUtils.createRealMatrix(testNonSquare).transpose()).getU()); + checkOrthogonal(new SingularValueDecomposition(MatrixUtils.createRealMatrix(testSquare)).getU()); + checkOrthogonal(new SingularValueDecomposition(MatrixUtils.createRealMatrix(testNonSquare)).getU()); + checkOrthogonal(new SingularValueDecomposition(MatrixUtils.createRealMatrix(testNonSquare).transpose()).getU()); } /** test that V is orthogonal */ @Test public void testVOrthogonal() { - checkOrthogonal(new SingularValueDecompositionImpl(MatrixUtils.createRealMatrix(testSquare)).getV()); - checkOrthogonal(new SingularValueDecompositionImpl(MatrixUtils.createRealMatrix(testNonSquare)).getV()); - checkOrthogonal(new SingularValueDecompositionImpl(MatrixUtils.createRealMatrix(testNonSquare).transpose()).getV()); + checkOrthogonal(new SingularValueDecomposition(MatrixUtils.createRealMatrix(testSquare)).getV()); + checkOrthogonal(new SingularValueDecomposition(MatrixUtils.createRealMatrix(testNonSquare)).getV()); + checkOrthogonal(new SingularValueDecomposition(MatrixUtils.createRealMatrix(testNonSquare).transpose()).getV()); } public void checkOrthogonal(final RealMatrix m) { @@ -171,7 +171,7 @@ public class SingularValueDecompositionI // together, the actual triplet (U,S,V) is not uniquely defined. public void testMatricesValues1() { SingularValueDecomposition svd = - new SingularValueDecompositionImpl(MatrixUtils.createRealMatrix(testSquare)); + new SingularValueDecomposition(MatrixUtils.createRealMatrix(testSquare)); RealMatrix uRef = MatrixUtils.createRealMatrix(new double[][] { { 3.0 / 5.0, -4.0 / 5.0 }, { 4.0 / 5.0, 3.0 / 5.0 } @@ -224,7 +224,7 @@ public class SingularValueDecompositionI // check values against known references SingularValueDecomposition svd = - new SingularValueDecompositionImpl(MatrixUtils.createRealMatrix(testNonSquare)); + new SingularValueDecomposition(MatrixUtils.createRealMatrix(testNonSquare)); RealMatrix u = svd.getU(); Assert.assertEquals(0, u.subtract(uRef).getNorm(), normTolerance); RealMatrix s = svd.getS(); @@ -244,34 +244,34 @@ public class SingularValueDecompositionI public void testRank() { double[][] d = { { 1, 1, 1 }, { 0, 0, 0 }, { 1, 2, 3 } }; RealMatrix m = new Array2DRowRealMatrix(d); - SingularValueDecomposition svd = new SingularValueDecompositionImpl(m); - Assert.assertEquals(2, svd.getRank()); + SingularValueDecomposition svd = new SingularValueDecomposition(m); + Assert.assertEquals(2, svd.getRank()); } - + /** test MATH-583 */ @Test public void testStability1() { RealMatrix m = new Array2DRowRealMatrix(201, 201); loadRealMatrix(m,"matrix1.csv"); try { - new SingularValueDecompositionImpl(m); + new SingularValueDecomposition(m); } catch (Exception e) { Assert.fail("Exception whilst constructing SVD"); - } + } } - + /** test MATH-327 */ @Test public void testStability2() { RealMatrix m = new Array2DRowRealMatrix(7, 168); loadRealMatrix(m,"matrix2.csv"); try { - new SingularValueDecompositionImpl(m); + new SingularValueDecomposition(m); } catch (Throwable e) { Assert.fail("Exception whilst constructing SVD"); - } + } } - + private void loadRealMatrix(RealMatrix m, String resourceName) { try { DataInputStream in = new DataInputStream(getClass().getResourceAsStream(resourceName)); @@ -286,25 +286,25 @@ public class SingularValueDecompositionI row++; } in.close(); - } catch (IOException e) {} + } catch (IOException e) {} } - + /** test condition number */ @Test public void testConditionNumber() { - SingularValueDecompositionImpl svd = - new SingularValueDecompositionImpl(MatrixUtils.createRealMatrix(testSquare)); + SingularValueDecomposition svd = + new SingularValueDecomposition(MatrixUtils.createRealMatrix(testSquare)); // replace 1.0e-15 with 1.5e-15 Assert.assertEquals(3.0, svd.getConditionNumber(), 1.5e-15); } @Test public void testInverseConditionNumber() { - SingularValueDecompositionImpl svd = - new SingularValueDecompositionImpl(MatrixUtils.createRealMatrix(testSquare)); + SingularValueDecomposition svd = + new SingularValueDecomposition(MatrixUtils.createRealMatrix(testSquare)); Assert.assertEquals(1.0/3.0, svd.getInverseConditionNumber(), 1.5e-15); } - + private RealMatrix createTestMatrix(final Random r, final int rows, final int columns, final double[] singularValues) { final RealMatrix u = Modified: commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/SingularValueSolverTest.java URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/SingularValueSolverTest.java?rev=1175108&r1=1175107&r2=1175108&view=diff ============================================================================== --- commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/SingularValueSolverTest.java (original) +++ commons/proper/math/trunk/src/test/java/org/apache/commons/math/linear/SingularValueSolverTest.java Sat Sep 24 05:13:53 2011 @@ -35,7 +35,7 @@ public class SingularValueSolverTest { @Test public void testSolveDimensionErrors() { DecompositionSolver solver = - new SingularValueDecompositionImpl(MatrixUtils.createRealMatrix(testSquare)).getSolver(); + new SingularValueDecomposition(MatrixUtils.createRealMatrix(testSquare)).getSolver(); RealMatrix b = MatrixUtils.createRealMatrix(new double[3][2]); try { solver.solve(b); @@ -65,7 +65,7 @@ public class SingularValueSolverTest { { 1.0, 0.0 }, { 0.0, 0.0 } }); - DecompositionSolver solver = new SingularValueDecompositionImpl(m).getSolver(); + DecompositionSolver solver = new SingularValueDecomposition(m).getSolver(); RealMatrix b = MatrixUtils.createRealMatrix(new double[][] { { 11, 12 }, { 21, 22 } }); @@ -86,7 +86,7 @@ public class SingularValueSolverTest { @Test public void testSolve() { DecompositionSolver solver = - new SingularValueDecompositionImpl(MatrixUtils.createRealMatrix(testSquare)).getSolver(); + new SingularValueDecomposition(MatrixUtils.createRealMatrix(testSquare)).getSolver(); RealMatrix b = MatrixUtils.createRealMatrix(new double[][] { { 1, 2, 3 }, { 0, -5, 1 } }); @@ -119,8 +119,8 @@ public class SingularValueSolverTest { /** test condition number */ @Test public void testConditionNumber() { - SingularValueDecompositionImpl svd = - new SingularValueDecompositionImpl(MatrixUtils.createRealMatrix(testSquare)); + SingularValueDecomposition svd = + new SingularValueDecomposition(MatrixUtils.createRealMatrix(testSquare)); // replace 1.0e-15 with 1.5e-15 Assert.assertEquals(3.0, svd.getConditionNumber(), 1.5e-15); } @@ -131,7 +131,7 @@ public class SingularValueSolverTest { { 1.0, 2.0 }, { 1.0, 2.0 } }); SingularValueDecomposition svd = - new SingularValueDecompositionImpl(rm); + new SingularValueDecomposition(rm); RealMatrix recomposed = svd.getU().multiply(svd.getS()).multiply(svd.getVT()); Assert.assertEquals(0.0, recomposed.subtract(rm).getNorm(), 2.0e-15); }