Package mikera.matrixx.decompose.impl.svd

Source Code of mikera.matrixx.decompose.impl.svd.StandardSvdChecks

/*
* Copyright (c) 2009-2013, Peter Abeles. All Rights Reserved.
*
* This file is part of Efficient Java Matrix Library (EJML).
*
* Licensed 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 mikera.matrixx.decompose.impl.svd;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import mikera.matrixx.Matrix;
import mikera.matrixx.algo.Multiplications;


/**
* @author Peter Abeles
*/
public abstract class StandardSvdChecks {

    private static double EPS = Math.pow(2,-52);

    public abstract SvdImplicitQr createSvd();

    boolean omitVerySmallValues = false;

    public void allTests() {
        testDecompositionOfTrivial();
        testWide();
        testTall();
        checkGetU_Transpose();

        if( !omitVerySmallValues )
            testVerySmallValue();
        testZero();
        testLargeToSmall();
        testIdentity();
        testLots();
    }

    public void testDecompositionOfTrivial()
    {
//      Test 1
      Matrix A = Matrix.create(new double[][] {{5,2,3},
          {1.5, -2, 8},
          {-3, 4.7, -0.5}});
      SvdImplicitQr alg = createSvd();
      assertNotNull(alg._decompose(A));
      assertEquals(3, rank(alg, EPS));
      assertEquals(0, nullity(alg, EPS));
      double []w = alg.getSingularValues().toDoubleArray();
      checkNumFound(1,1e-5,9.59186,w);
      checkNumFound(1,1e-5,5.18005,w);
      checkNumFound(1,1e-5,4.55558,w);
      checkComponents(alg,A);
//      Test 2
        Matrix B = Matrix.create(new double[][] {{1, 2, 3},
                         {4, 5, 6},
                         {7, 8, 9}});
        alg = createSvd();
        assertNotNull(alg._decompose(B));
        assertEquals(2, rank(alg, 10*EPS));
        assertEquals(0, nullity(alg, EPS));
        w = alg.getSingularValues().toDoubleArray();
        checkNumFound(1,1e-5,16.848103,w);
        checkNumFound(1,1e-5,1.068370,w);
        checkNumFound(1,1e-5,0,w);
        checkComponents(alg,B);
    }

    public void testWide() {
        Matrix A = Matrix.createRandom(1,1);
        A.sub(0.5);
        A.scale(2);
        SvdImplicitQr alg = createSvd();
        assertNotNull(alg._decompose(A));

        checkComponents(alg,A);
    }

    public void testTall() {
        Matrix A = Matrix.createRandom(21,5);
        A.sub(0.5);
        A.scale(2);

        SvdImplicitQr alg = createSvd();
        assertNotNull(alg._decompose(A));

        checkComponents(alg,A);
    }

    public void testZero() {

        for( int i = 1; i <= 11; i += 5 ) {
            for( int j = 1; j <= 11; j += 5 ) {
                Matrix A = Matrix.create(i,j);

                SvdImplicitQr alg = createSvd();
                assertNotNull(alg._decompose(A));

                int min = Math.min(i,j);

                assertEquals(min,checkOccurrence(0,alg.getSingularValues().toDoubleArray(),min),1e-5);

                checkComponents(alg,A);
            }
        }

    }

    public void testIdentity() {
        Matrix A = Matrix.createIdentity(6,6);

        SvdImplicitQr alg = createSvd();
        assertNotNull(alg._decompose(A));

        assertEquals(6,checkOccurrence(1,alg.getSingularValues().toDoubleArray(),6),1e-5);

        checkComponents(alg,A);
    }

    /**
     * See if it can handle very small values and not blow up.  This can some times
     * cause a zero to appear unexpectedly and thus a divided by zero.
     */
    public void testVerySmallValue() {
        Matrix A = Matrix.createRandom(5,5);
        A.sub(0.5);
        A.scale(2);

        A.scale(1e-200);

        SvdImplicitQr alg = createSvd();
        assertNotNull(alg._decompose(A));

        checkComponents(alg,A);
    }


    public void testLots() {
        SvdImplicitQr alg = createSvd();

        for( int i = 1; i < 8; i+=2 ) {
            for( int j = 1; j < 8; j+=2 ) {
                Matrix A = Matrix.createRandom(i,j);
                A.sub(0.5);
                A.scale(2);

                assertNotNull(alg._decompose(A));

                checkComponents(alg,A);
            }
        }
    }

    /**
     * Makes sure transposed flag is correctly handled.
     */
    public void checkGetU_Transpose() {
        Matrix A = Matrix.createRandom(5, 7);
        A.sub(0.5);
        A.scale(2);

        SvdImplicitQr alg = createSvd();
        assertNotNull(alg._decompose(A));

        Matrix U = alg.getU().toMatrix();
        Matrix Ut = alg.getU().getTranspose().toMatrix();

        Matrix found = U.getTransposeCopy().toMatrix();

        assertArrayEquals(Ut.getElements(), found.getElements(), 1e-6);
    }

    /**
     * Makes sure arrays are correctly set when it first computers a larger matrix
     * then a smaller one.  When going from small to large its often forces to declare
     * new memory, this way it actually uses memory.
     */
    public void testLargeToSmall() {
        SvdImplicitQr alg = createSvd();

        // first the larger one
        Matrix A = Matrix.createRandom(10,10);
        A.sub(0.5);
        A.scale(2);
        assertNotNull(alg._decompose(A));
        checkComponents(alg,A);

        // then the smaller one
        A = Matrix.createRandom(5,5);
        A.sub(0.5);
        A.scale(2);
       
        assertNotNull(alg._decompose(A));
        checkComponents(alg,A);
    }

    private int checkOccurrence( double check , double[]values , int numSingular ) {
        int num = 0;

        for( int i = 0; i < numSingular; i++ ) {
            if( Math.abs(values[i]-check)<1e-8)
                num++;
        }

        return num;
    }

    private void checkComponents( SvdImplicitQr svd , Matrix expected )
    {
        Matrix U = svd.getU().toMatrix();
        Matrix Vt = svd.getV().getTranspose().toMatrix();
        Matrix W = svd.getS().toMatrix();

        assertTrue( !U.hasUncountable() );
        assertTrue( !Vt.hasUncountable() );
        assertTrue( !W.hasUncountable() );

        if( svd.isCompact() ) {
          assertTrue(W.columnCount()==W.rowCount());
          assertTrue(U.columnCount()==W.rowCount());
            assertTrue(Vt.rowCount()==W.columnCount());
        } else {
          assertTrue(U.columnCount()==W.rowCount());
          assertTrue(W.columnCount()==Vt.rowCount());
          assertTrue(U.columnCount()==U.rowCount());
            assertTrue(Vt.columnCount()==Vt.rowCount());
        }

        Matrix found = Multiplications.multiply(U, Multiplications.multiply(W, Vt));

//        found.print();
//        expected.print();

//        assertTrue(expected.equals(found));
        assertArrayEquals(expected.toDoubleArray(), found.toDoubleArray(), 1e-6);
    }
   
    /**
     * Extracts the rank of a matrix using a preexisting decomposition.
     *
     * @param svd A precomputed decomposition.  Not modified.
     * @param threshold Tolerance used to determine of a singular value is singular.
     * @return The rank of the decomposed matrix.
     */
//    taken from SingularOps
    private static int rank( SvdImplicitQr svd , double threshold ) {
        int numRank=0;

        double w[]= svd.getSingularValues().toDoubleArray();

        int N = svd.numberOfSingularValues();

        for( int j = 0; j < N; j++ ) {
            if( w[j] > threshold)
                numRank++;
        }

        return numRank;
    }
   
    /**
     * Extracts the nullity of a matrix using a preexisting decomposition.
     *
     * @param svd A precomputed decomposition.  Not modified.
     * @param threshold Tolerance used to determine of a singular value is singular.
     * @return The nullity of the decomposed matrix.
     */
//    taken from SingularOps
    public static int nullity( SvdImplicitQr svd , double threshold ) {
        int ret = 0;

        double w[]= svd.getSingularValues().toDoubleArray();

        int N = svd.numberOfSingularValues();

        int numCol = svd.numCols();

        for( int j = 0; j < N; j++ ) {
            if( w[j] <= threshold) ret++;
        }
        return ret + numCol-N;
    }

//  taken from SingularOps
    private static void checkNumFound( int expected , double tol , double value , double data[] )
    {
        int numFound = 0;

        for( int i = 0; i < data.length; i++ ) {
            if( Math.abs(data[i]-value) <= tol )
                numFound++;
        }

        assertEquals(expected,numFound);
    }
}
TOP

Related Classes of mikera.matrixx.decompose.impl.svd.StandardSvdChecks

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.