Package mikera.matrixx.decompose.impl.eigen

Source Code of mikera.matrixx.decompose.impl.eigen.TestSymmetricQRAlgorithmDecomposition

/*
* 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.eigen;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import mikera.matrixx.AMatrix;
import mikera.matrixx.Matrix;
import mikera.matrixx.Matrixx;
import mikera.matrixx.algo.Multiplications;
import mikera.matrixx.decompose.impl.lu.AltLU;
import mikera.vectorz.AVector;
import mikera.vectorz.Vector2;

import org.junit.Test;


/**
* @author Peter Abeles
*/
public class TestSymmetricQRAlgorithmDecomposition {
    boolean together;
    private boolean computeVectors;
   
    public static double EPS = Math.pow(2,-52);

    public SymmetricQRAlgorithmDecomposition createDecomposition() {
        SymmetricQRAlgorithmDecomposition alg = new SymmetricQRAlgorithmDecomposition(computeVectors);
        if( computeVectors )
            alg.setComputeVectorsWithValues(together);

        return alg;
    }

    @Test
    public void justSymmetricTests_separate() {
        together = false;
        computeVectors = true;

        checkRandomSymmetric();
        checkIdentity();
        checkAllZeros();
        checkLargeValue(true);
//
        computeVectors = false;
        checkKnownSymmetric_JustValue();
    }

    @Test
    public void justSymmetricTests_together() {
        together = true;
        computeVectors = true;

        checkRandomSymmetric();
        checkIdentity();
        checkAllZeros();
        checkLargeValue(true);

        computeVectors = false;
        checkKnownSymmetric_JustValue();
    }
   
    /**
     * Create a variety of different random matrices of different sizes and sees if they pass the standard
     * eigen decompositions tests.
     */
    public void checkRandom() {
        int sizes[] = new int[]{1,2,5,10,20,50,100,200};

        SymmetricQRAlgorithmDecomposition alg = createDecomposition();

        for( int s = 2; s < sizes.length; s++ ) {
            int N = sizes[s];
//            System.out.println("N = "+N);

            for( int i = 0; i < 2; i++ ) {
                Matrix A = Matrix.createRandom(N,N);
                A.multiply(2);
                A.sub(1);
               
                assertNotNull(alg.decompose(A));

                performStandardTests(alg,A,-1);
            }
        }
    }

    /**
     * Sees if it correctly computed the eigenvalues.  Does not check eigenvectors.
     */
    public void checkKnownSymmetric_JustValue() {
        Matrix A = Matrix.create(new double[][]
               {{0.98139,   0.78650,   0.78564},
                {0.78650,   1.03207,   0.29794},
                {0.78564,   0.29794,   0.91926}});
        SymmetricQRAlgorithmDecomposition alg = createDecomposition();

        assertNotNull(alg.decompose(A));

        testForEigenvalue(alg,A,0.00426,0,1);
        testForEigenvalue(alg,A,0.67856,0,1);
        testForEigenvalue(alg,A,2.24989,0,1);
    }

    /**
     * Compare results against a simple matrix with known results where some the eigenvalues
     * are real and some are complex.
     */
    public void checkKnownComplex() {
        Matrix A = Matrix.create(new double[][] {{-0.418284, 0.279875, 0.452912},
                                                 {-0.093748, -0.045179, 0.310949},
                                                 {0.250513, -0.304077, -0.031414}});

        SymmetricQRAlgorithmDecomposition alg = createDecomposition();

        assertNotNull(alg.decompose(A));
        performStandardTests(alg,A,-1);

        testForEigenpair(alg,-0.39996,0,0.87010,0.43425,-0.23314);
        testForEigenpair(alg,-0.04746,0.02391);
        testForEigenpair(alg,-0.04746,-0.02391);
    }

    /**
     * Check results against symmetric matrices that are randomly generated
     */
    public void checkRandomSymmetric() {
        for( int N = 1; N <= 15; N++ ) {
            for( int i = 0; i < 20; i++ ) {
//                DenseMatrix64F A = RandomMatrices.createSymmetric(N,-1,1,rand);
                AMatrix z = Matrixx.createRandomMatrix(3, 3);
                AMatrix A = z.innerProduct(z.getTranspose());

                SymmetricQRAlgorithmDecomposition alg = createDecomposition();

                assertNotNull(alg.decompose(A));
               
                performStandardTests(alg,A.toMatrix(),-1);
            }
        }
    }

    /**
     * For some eigenvector algorithms this is a difficult matrix that requires a special
     * check for.  If it fails that check it will either loop forever or exit before converging.
     */
    public void checkExceptional() {
        Matrix A = Matrix.create(new double [][]
                {{0, 0, 0, 0, 1},
                {1, 0, 0, 0, 0},
                {0, 1, 0, 0, 0},
                {0, 0, 1, 0, 0},
                {0, 0, 0, 1, 0}});

        SymmetricQRAlgorithmDecomposition alg = createDecomposition();

        assertNotNull(alg.decompose(A));
       
        performStandardTests(alg,A,1);
    }

    public void checkIdentity() {
        Matrix I = Matrix.createIdentity(4);

        SymmetricQRAlgorithmDecomposition alg = createDecomposition();

        assertNotNull(alg.decompose(I));

        performStandardTests(alg,I,4);

        testForEigenpair(alg,1,0,1,0,0,0);
        testForEigenpair(alg,1,0,0,1,0,0);
        testForEigenpair(alg,1,0,0,0,1,0);
        testForEigenpair(alg,1,0,0,0,0,1);
    }

    public void checkAllZeros() {
        Matrix A = Matrix.create(5,5);

        SymmetricQRAlgorithmDecomposition alg = createDecomposition();

        assertNotNull(alg.decompose(A));

        performStandardTests(alg,A,5);
        testEigenvalues(alg,0);
    }

//    public void checkWithSomeRepeatedValuesSymm() {
//        DoubleStepQRDecomposition alg = createDecomposition();
//
//        checkSymmetricMatrix(alg,2,-3,-3,-3);
//        checkSymmetricMatrix(alg,2,-3,2,2);
//        checkSymmetricMatrix(alg,1,1,1,2);
//    }
//
//    public void checkWithSingularSymm() {
//
//        DoubleStepQRDecomposition alg = createDecomposition();
//
//        checkSymmetricMatrix(alg,1,0,1,2);
//    }
//
//    /**
//     * Creates a random symmetric matrix with the specified eigenvalues.  It then
//     * checks to see if it has the expected results.
//     */
//    private void checkSymmetricMatrix(DoubleStepQRDecomposition alg , double ...ev ) {
//        int numRepeated[] = new int[ ev.length ];
//
//        for( int i = 0; i < ev.length ; i++ ) {
//            int num = 0;
//
//            for (double anEv : ev) {
//                if (ev[i] == anEv)
//                    num++;
//            }
//            numRepeated[i] = num;
//        }
//
//        for( int i = 0; i < 200; i++ ) {
//            Matrix A = RandomMatrices.createEigenvaluesSymm(ev.length,rand,ev);
//
//            assertTrue(safeDecomposition(alg,A));
//
//            performStandardTests(alg,A,ev.length);
//
//            for( int j = 0; j < ev.length; j++ ) {
//                testForEigenvalue(alg,A,ev[j],0,numRepeated[j]);
//            }
//        }
//    }

    public void checkLargeValue( boolean symmetric) {
        for( int i = 0; i < 20; i++ ) {
            SymmetricQRAlgorithmDecomposition alg = createDecomposition();
            Matrix z = Matrix.createRandom(3, 3);
            Matrix A = z.innerProduct(z.getTranspose())// symmetric

//            CommonOps.scale(1e-200,A);
//            A.scale(1e100);

            assertNotNull(alg.decompose(A));

            performStandardTests(alg,A,-1);
        }
    }

    /**
     * If the eigenvalues are all known, real, and the same this can be used to check them.
     */
    public void testEigenvalues( SymmetricQRAlgorithmDecomposition alg , double expected ) {

        for( int i = 0; i < alg.getNumberOfEigenvalues(); i++ ) {
            Vector2 c = alg.getEigenvalue(i);

            assertTrue(c.y==0);

            assertEquals(expected,c.x,1e-8);
        }
    }

    /**
     * Preforms standard tests that can be performed on any decomposition without prior knowledge of
     * what the results should be.
     */
    public void performStandardTests( SymmetricQRAlgorithmDecomposition alg , Matrix A , int numReal )
    {

        // basic sanity tests
        assertEquals(A.rowCount(),alg.getNumberOfEigenvalues());

        if( numReal >= 0 ) {
            for( int i = 0; i < A.rowCount(); i++ ) {
                Vector2 v = alg.getEigenvalue(i);

                assertFalse( Double.isNaN(v.x ));
                if( v.y==0 )
                    numReal--;
                else if( Math.abs(v.y) < 10*EPS)
                    numReal--;
            }

            // if there are more than the expected number of real eigenvalues this will
            // be negative
            assertEquals(0,numReal);
        }

//        checkCharacteristicEquation(alg,A);
        if( computeVectors ) {
            testPairsConsistent(alg,A);
            testVectorsLinearlyIndependent(alg);
        }
    }

    /**
     * Checks to see if an eigenvalue is complex then the eigenvector is null.  If it is real it
     * then checks to see if the equation A*v = lambda*v holds true.
     */
    public void testPairsConsistent( SymmetricQRAlgorithmDecomposition alg , Matrix A )
    {
//        System.out.println("-------------------------------------------------------------------------");
        int N = alg.getNumberOfEigenvalues();

        for( int i = 0; i < N; i++ ) {
            Vector2 c = alg.getEigenvalue(i);
            AVector v = alg.getEigenVector(i);

            if( Double.isInfinite(c.x) || Double.isNaN(c.x) ||
                    Double.isInfinite(c.y) || Double.isNaN(c.y))
                fail("Uncountable eigenvalue");

            if( Math.abs(c.y) > 1e-20 ) {
                assertTrue(v==null);
            } else {
                assertTrue(v != null );
//                if( MatrixFeatures.hasUncountable(v)) {
//                    throw new RuntimeException("Egads");
//                }
                assertFalse(v.hasUncountable());

//                CommonOps.mult(A,v,tempA);
                Matrix ta = Matrix.create(A.rowCount(), 1);
                ta.setColumn(0, (v));
                AMatrix tempA = Multiplications.multiply(A, ta);
//                CommonOps.scale(c.real,v,tempB);
                Matrix tb = Matrix.create(v.length(), 1);
                tb.setColumn(0, v);
                AMatrix tempB = tb.multiplyCopy(c.x);
//                double max = NormOps.normPInf(A);
                double max = normPInf(A);
                if( max == 0 ) max = 1;
               
                double error = diffNormF(tempA,tempB)/max;
               
                if( error > 1e-12 ) {
//                    System.out.println("Original matrix:");
//                    System.out.println(A);
//                    A.print();
//                    System.out.println("Eigenvalue = "+c.x);
//                    Eigenpair p = EigenOps.computeEigenVector(A,c.real);
//                    p.vector.print();
//                    v.print();
//
//
//                    CommonOps.mult(A,p.vector,tempA);
//                    CommonOps.scale(c.real,p.vector,tempB);
//
//                    max = NormOps.normPInf(A);
//
//                    System.out.println("error before = "+error);
//                    error = SpecializedOps.diffNormF(tempA,tempB)/max;
//                    System.out.println("error after = "+error);
//                    A.print("%f");
//                    System.out.println();
                    fail("Error was too large");
                }

                assertTrue(error <= 1e-12);
            }
        }
    }
   
    private static double normPInf( AMatrix A ) {
        return A.absCopy().elementMax();
    }

    private double diffNormF(AMatrix tempA, AMatrix tempB)
    {
        AMatrix temp = tempA.copy();
        temp.sub(tempB);
        double total = temp.elementSquaredSum();
        temp.abs();
        double scale = temp.elementMax();
        return Math.abs(scale-0) > 1e-12 ? total/scale : 0;
    }

    /**
     * See if eigenvalues cause the characteristic equation to have a value of zero
     */
    public void checkCharacteristicEquation( SymmetricQRAlgorithmDecomposition alg ,
                                             Matrix A ) {
        int N = alg.getNumberOfEigenvalues();

        Matrix a = Matrix.create(A);

        for( int i = 0; i < N; i++ ) {
            Vector2 c = alg.getEigenvalue(i);

            if( Math.abs(c.y - 0) < 1e-8 ) {
                // test using the characteristic equation
                Matrix temp = Matrix.createIdentity(A.columnCount());
                temp.scale(c.x);
                temp.sub(a);
                double det = temp.determinant();

                // extremely crude test.  given perfect data this is probably considered a failure...  However,
                // its hard to tell what a good test value actually is.
                assertEquals(0, det, 0.1);
            }
        }
    }

    /**
     * Checks to see if all the real eigenvectors are linearly independent of each other.
     */
    public void testVectorsLinearlyIndependent( SymmetricQRAlgorithmDecomposition alg ) {
        int N = alg.getNumberOfEigenvalues();

        // create a matrix out of the eigenvectors
        Matrix A = Matrix.create(N,N);

        int off = 0;
        for( int i = 0; i < N; i++ ) {
            AVector v = alg.getEigenVector(i);

            // it can only handle real eigenvectors
            if( v == null )
                off++;
            else {
                for( int j = 0; j < N; j++ ) {
                    A.set(i-off,j,v.get(j));
                }
            }
        }

        // see if there are any real eigenvectors
        if( N == off )
            return;

//        A.reshape(N-off,N, false);
        A = A.reshape(N-off, N);
       
        AltLU lu = new AltLU();
        lu._decompose(A);
        assertFalse(lu.isSingular());
//        assertTrue(MatrixFeatures.isRowsLinearIndependent(A));
    }

    /**
     * Sees if the pair of eigenvalue and eigenvector was found in the decomposition.
     */
    public void testForEigenpair( SymmetricQRAlgorithmDecomposition alg , double valueReal ,
                                  double valueImg , double... vector )
    {
        int N = alg.getNumberOfEigenvalues();

        int numMatched = 0;
        for( int i = 0; i < N; i++ ) {
            Vector2 c = alg.getEigenvalue(i);
           
            if( Math.abs(c.x-valueReal) < 1e-4 && Math.abs(c.y-valueImg) < 1e-4) {

//                if( c.isReal() ) {
                if(Math.abs(c.y - 0) < 1e-8)
                    if( vector.length > 0 ) {
                        AVector v = alg.getEigenVector(i);
                        AMatrix e = Matrix.createFromRows(vector);
                        e = e.getTranspose();
                       
                        Matrix t = Matrix.create(v.length(), 1);
                        t.setColumn(0, v);
                        double error = diffNormF(e,t);
//                        CommonOps.changeSign(e);
                        e.multiply(-1);
                        double error2 = diffNormF(e,t);


                        if(error < 1e-3 || error2 < 1e-3)
                            numMatched++;
                    } else {
                        numMatched++;
                } else if( Math.abs(c.y-0) > 1e-8 ) {
                    numMatched++;
                }
            }
        }

        assertEquals(1,numMatched);
    }

    public void testForEigenvalue( SymmetricQRAlgorithmDecomposition alg ,
                                   Matrix A,
                                   double valueReal ,
                                   double valueImg , int numMatched ) {
        int N = alg.getNumberOfEigenvalues();

        int numFound = 0;
        for( int i = 0; i < N; i++ ) {
            Vector2 c = alg.getEigenvalue(i);

            if( Math.abs(c.x-valueReal) < 1e-4 && Math.abs(c.y-valueImg) < 1e-4) {
                numFound++;
            }
        }

        assertEquals(numMatched,numFound);
    }
}
TOP

Related Classes of mikera.matrixx.decompose.impl.eigen.TestSymmetricQRAlgorithmDecomposition

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.