/*
* File: PrincipalComponentsAnalysisTest.java
* Authors: Kevin R. Dixon
* Company: Sandia National Laboratories
* Project: Cognitive Foundry
*
* Copyright October 9, 2007, Sandia Corporation. Under the terms of Contract
* DE-AC04-94AL85000, there is a non-exclusive license for use of this work by
* or on behalf of the U.S. Government. Export of this program may require a
* license from the United States Government. See CopyrightHistory.txt for
* complete details.
*
*/
package gov.sandia.cognition.learning.algorithm.pca;
import gov.sandia.cognition.math.MultivariateStatisticsUtil;
import gov.sandia.cognition.math.matrix.VectorFactory;
import gov.sandia.cognition.math.matrix.decomposition.SingularValueDecomposition;
import gov.sandia.cognition.math.matrix.Vector;
import gov.sandia.cognition.math.matrix.mtj.DenseMatrix;
import gov.sandia.cognition.math.matrix.mtj.DenseMatrixFactoryMTJ;
import gov.sandia.cognition.math.matrix.mtj.decomposition.SingularValueDecompositionMTJ;
import gov.sandia.cognition.util.ObjectUtil;
import java.util.ArrayList;
import java.util.Random;
import junit.framework.TestCase;
/**
*
* @author Kevin R. Dixon
*/
public abstract class PrincipalComponentsAnalysisTestHarness extends TestCase
{
public PrincipalComponentsAnalysisTestHarness( String testName )
{
super( testName );
}
/** The random number generator for the tests. */
Random random = new Random(4);
public int INPUT_DIM = random.nextInt( 3 ) + 3;
public int OUTPUT_DIM = random.nextInt( 2 ) + 1;
/**
* Creates a PCA
* @return PCA
*/
abstract public PrincipalComponentsAnalysis createPCAInstance();
public void testPCAClone()
{
System.out.println( "PCA.clone" );
PrincipalComponentsAnalysis instance = this.createPCAInstance();
PrincipalComponentsAnalysis clone =
(PrincipalComponentsAnalysis) instance.clone();
assertNotNull( clone );
assertNotSame( instance, clone );
assertEquals( instance.getNumComponents(), clone.getNumComponents() );
}
/**
* Test of learn method, of class gov.sandia.cognition.learning.pca.PrincipalComponentsAnalysis.
*
* The example data is based on: http://www.kernel-machines.org/code/kpca_toy.m
*/
public void testPCALearn()
{
System.out.println( "PCA.learn" );
int num = random.nextInt( 100 ) + 10;
ArrayList<Vector> data = new ArrayList<Vector>( num );
final double r1 = random.nextDouble();
final double r2 = r1 / random.nextDouble();
for (int i = 0; i < num; i++)
{
data.add( VectorFactory.getDefault().createUniformRandom( INPUT_DIM, r1, r2, random ) );
}
Vector mean = MultivariateStatisticsUtil.computeMean( data );
DenseMatrix X = DenseMatrixFactoryMTJ.INSTANCE.createMatrix( INPUT_DIM, num );
for (int n = 0; n < num; n++)
{
X.setColumn( n, data.get( n ).minus( mean ) );
}
final ArrayList<Vector> dataCopy = ObjectUtil.cloneSmartElementsAsArrayList(data);
long startsvd = System.currentTimeMillis();
SingularValueDecomposition svd = SingularValueDecompositionMTJ.create( X );
long stopsvd = System.currentTimeMillis();
long start = System.currentTimeMillis();
PrincipalComponentsAnalysis instance = this.createPCAInstance();
PrincipalComponentsAnalysisFunction f = instance.learn( data );
long stop = System.currentTimeMillis();
assertEquals(dataCopy, data);
System.out.println( "Uhat:\n" + f.getDimensionReducer().getDiscriminant().transpose() );
System.out.println( "U:\n" + svd.getU() );
System.out.println( "Time taken: SVD = " + (stopsvd - startsvd) + ", PCA = " + (stop - start) );
// Make sure the PCA algorithm subtracted off the sample mean
if (mean.equals( f.getMean(), 1e-5 ) == false)
{
assertEquals( mean, f.getMean() );
}
assertEquals( OUTPUT_DIM, instance.getNumComponents() );
assertEquals( instance.getNumComponents(), f.getOutputDimensionality() );
assertEquals( INPUT_DIM, f.getInputDimensionality() );
if (mean.equals( f.getMean(), 1e-5 ) == false)
{
assertEquals( mean, f.getMean() );
}
double absnorm = 0.0;
int nc = instance.getNumComponents() * INPUT_DIM;
for (int i = 0; i < instance.getNumComponents(); i++)
{
Vector uihat = f.getDimensionReducer().getDiscriminant().getRow( i );
for (int j = 0; j < i; j++)
{
Vector ujhat = f.getDimensionReducer().getDiscriminant().getRow( j );
assertEquals( "Dot product between " + i + " and " + j + " is too large!", 0.0, uihat.dotProduct( ujhat ), 1e-2 );
}
assertEquals( 1.0, uihat.norm2(), 1e-5 );
Vector ui = svd.getU().getColumn( i );
absnorm += Math.min( ui.minus( uihat ).norm2(), ui.minus( uihat.scale( -1 ) ).norm2() );
}
absnorm /= nc;
System.out.println( "U 1-norm: " + absnorm );
assertEquals( 0.0, absnorm, 1e-1 );
}
}