/*
* File: SmoothUnivariateDistributionTestHarness.java
* Authors: Kevin R. Dixon
* Company: Sandia National Laboratories
* Project: Cognitive Foundry
*
* Copyright Feb 3, 2009, 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.statistics;
import gov.sandia.cognition.learning.algorithm.BatchLearner;
import gov.sandia.cognition.math.matrix.NumericalDifferentiator;
import gov.sandia.cognition.statistics.distribution.UniformDistribution;
import gov.sandia.cognition.statistics.method.ImportanceSampling;
import gov.sandia.cognition.statistics.method.KolmogorovSmirnovConfidence;
import gov.sandia.cognition.util.DefaultWeightedValue;
import gov.sandia.cognition.util.WeightedValue;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Random;
/**
* Unit tests for SmoothUnivariateDistribution.
*
* @author krdixon
*/
public abstract class SmoothUnivariateDistributionTestHarness
extends ClosedFormUnivariateDistributionTestHarness<Double>
{
/**
* Tests for class SmoothScalarDistributionTestHarness2.
* @param testName Name of the test.
*/
public SmoothUnivariateDistributionTestHarness(
String testName)
{
super(testName);
}
public abstract SmoothUnivariateDistribution createInstance();
/**
* Tests the PDF constructors
*/
public abstract void testPDFConstructors();
/**
* Tests the PDF against known values
*/
public abstract void testPDFKnownValues();
/**
* Test of getProbabilityFunction method, of class SmoothUnivariateDistribution.
*/
public void testDistributionGetDistributionFunction()
{
System.out.println("Distribution.getDistributionFunction");
SmoothUnivariateDistribution instance = this.createInstance();
UnivariateProbabilityDensityFunction pdf = instance.getProbabilityFunction();
assertNotNull( pdf );
assertNotSame( instance, pdf );
assertEquals( instance.getMean().doubleValue(), pdf.getMean().doubleValue() );
assertEquals( instance.getVariance(), pdf.getVariance() );
Random r11 = new Random( 1 );
Random r12 = new Random( 1 );
ArrayList<? extends Number> s1 = instance.sample(r11, NUM_SAMPLES);
ArrayList<? extends Number> s2 = pdf.sample(r12, NUM_SAMPLES);
assertTrue( this.distributionSamplesEqual(s1, s2) );
}
/**
* Test of sampleAsDouble method, of class SmoothUnivariateDistribution.
*/
public void testDistributionSampleAsDouble()
{
System.out.println("SmoothUnivariateDistribution.sampleAsDouble(random)");
// Make sure that when we re-feed an identical RANDOM seed, then
// we get an equal sample from the distribution. But different seeds
// should give us different results... maybe.
Random random1a = new Random(1);
SmoothUnivariateDistribution instance = this.createInstance();
double r11 = instance.sampleAsDouble(random1a);
double rx2 = instance.sampleAsDouble(random1a);
assertFalse(r11 == rx2);
Random random1b = new Random(1);
double r13 = instance.sampleAsDouble(random1b);
assertEquals(r11, r13, TOLERANCE);
double r21 = instance.sampleAsDouble(new Random(2));
assertEquals(instance.sample(new Random(2)), r21, TOLERANCE);
assertFalse(r13 == r21);
assertFalse(r11 == r21);
Random random1c = new Random(1);
double r14 = instance.sampleAsDouble(random1c);
assertEquals(r11, r14, TOLERANCE);
assertEquals(r13, r14, TOLERANCE);
}
/**
* Test of sampleAsDoubles method, of class SmoothUnivariateDistribution.
*/
public void testDistributionSampleAsDoubles()
{
System.out.println("SmoothUnivariateDistribution.sampleAsDoubles(random,int)");
SmoothUnivariateDistribution instance = this.createInstance();
// Identical RANDOM seeds should produce equal squences.
// (Can't say anything about different seeds because deterministic
// distributions always return the same result, regardless of seed.)
Random r1a = new Random(1);
double[] s1a = instance.sampleAsDoubles(r1a, NUM_SAMPLES);
assertEquals(NUM_SAMPLES, s1a.length);
Random r1b = new Random(1);
double[] s1b = instance.sampleAsDoubles(r1b, NUM_SAMPLES);
assertEquals(NUM_SAMPLES, s1b.length);
assertEquals(s1a.length, s1b.length);
assertTrue(this.distributionSamplesEqual(s1a, s1b));
}
/**
* Test of sampleInto method, of class SmoothUnivariateDistribution.
*/
public void testDistributionSampleInto_doubles()
{
System.out.println("SmoothUnivariateDistribution.sampleInto(random,double[],int,int)");
SmoothUnivariateDistribution instance = this.createInstance();
// Identical RANDOM seeds should produce equal squences.
// (Can't say anything about different seeds because deterministic
// distributions always return the same result, regardless of seed.)
Random r1a = new Random(1);
double[] s1a = new double[NUM_SAMPLES];
instance.sampleInto(r1a, s1a, 0, NUM_SAMPLES);
Random r1b = new Random(1);
double[] s1b = instance.sampleAsDoubles(r1b, NUM_SAMPLES);
assertEquals(s1a.length, s1b.length);
assertTrue(this.distributionSamplesEqual(s1a, s1b));
// Make sure the data isn't over-written.
double[] r1c = new double[3 * NUM_SAMPLES + 5];
for (int i = 0; i < r1c.length; i++)
{
r1c[i] = i;
}
instance.sampleInto(new Random(1), r1c, 2, 3 * NUM_SAMPLES);
assertEquals(0.0, r1c[0], 0.0);
assertEquals(1.0, r1c[1], 0.0);
assertEquals(r1c.length - 3, r1c[r1c.length - 3], 0.0);
assertEquals(r1c.length - 2, r1c[r1c.length - 2], 0.0);
assertEquals(r1c.length - 1, r1c[r1c.length - 1], 0.0);
}
/**
* PDF.getProbabilityFunction
*/
public void testPDFGetDistributionFunction()
{
System.out.println( "PDF.getDistributionFunction" );
SmoothUnivariateDistribution instance = this.createInstance();
UnivariateProbabilityDensityFunction pdf = instance.getProbabilityFunction();
UnivariateProbabilityDensityFunction pdf2 = pdf.getProbabilityFunction();
assertNotNull( pdf2 );
assertNotSame( instance, pdf );
assertSame( pdf, pdf2 );
}
/**
* PDF.sample
*/
public void testPDFSample()
{
System.out.println( "PDF.sample" );
// Make sure that when we re-feed an identical RANDOM seed, then
// we get an equal sample from the distribution. But different seeds
// should give us different results... maybe.
Random random1a = new Random( 1 );
SmoothUnivariateDistribution instance = this.createInstance();
UnivariateProbabilityDensityFunction pdf = instance.getProbabilityFunction();
Double r11 = pdf.sample( random1a );
Double rx2 = pdf.sample( random1a );
assertNotNull( r11 );
assertNotNull( rx2 );
assertNotSame( r11, rx2 );
assertFalse( r11.equals( rx2 ) );
Random random1b = new Random( 1 );
Double r13 = pdf.sample( random1b );
assertNotNull( r13 );
assertNotSame( r11, r13 );
assertEquals( r11.doubleValue(), r13.doubleValue(), TOLERANCE );
Random random2 = new Random( 2 );
Double r21 = pdf.sample( random2 );
assertNotNull( r21 );
assertNotSame( r13, r21 );
assertNotSame( r11, r21 );
Random random1c = new Random( 1 );
Double r14 = pdf.sample( random1c );
assertNotNull( r14 );
assertNotSame( r11, r14 );
assertEquals( r11.doubleValue(), r14.doubleValue(), TOLERANCE );
assertNotSame( r13, r14 );
assertEquals( r13.doubleValue(), r14.doubleValue(), TOLERANCE );
}
/**
* PDF.evaluate(Double)
*/
public void testPDFEvaluate_Double()
{
System.out.println( "PDF.evaluate(Double)" );
SmoothUnivariateDistribution instance = this.createInstance();
UnivariateProbabilityDensityFunction pdf = instance.getProbabilityFunction();
ArrayList<? extends Double> samples = pdf.sample( RANDOM, NUM_SAMPLES );
for( Double sample : samples )
{
assertEquals( pdf.evaluate( sample ).doubleValue(), pdf.evaluate( sample.doubleValue() ) );
}
}
/**
* Tests PDF.nonnegative
*/
public void testPDFNonNegative()
{
System.out.println( "PDF.nonnegative" );
SmoothUnivariateDistribution instance = this.createInstance();
UnivariateProbabilityDensityFunction pdf = instance.getProbabilityFunction();
ArrayList<? extends Double> samples = instance.sample( RANDOM, NUM_SAMPLES );
for( Double sample : samples )
{
assertTrue( pdf.evaluate( sample ) >= 0.0 );
}
}
/**
* logEvaluate
*/
public void testPDFLogEvaluate()
{
System.out.println( "PDF.logEvaluate" );
ProbabilityDensityFunction<Double> pdf = this.createInstance().getProbabilityFunction();
ArrayList<? extends Double> samples = pdf.sample(RANDOM, NUM_SAMPLES);
for( Double sample : samples )
{
double plog = pdf.logEvaluate(sample);
double p = pdf.evaluate(sample);
double phat = Math.exp(plog);
assertEquals( p, phat, TOLERANCE );
}
}
/**
* PDF Monte Carlo
*/
public void testPDFMonteCarlo()
{
System.out.println( "PDF Monte Carlo" );
SmoothUnivariateDistribution instance = this.createInstance();
UnivariateProbabilityDensityFunction pdf = instance.getProbabilityFunction();
ArrayList<DefaultWeightedValue<Double>> samples =
ImportanceSampling.sample( pdf, pdf, RANDOM, NUM_SAMPLES );
double sum = 0.0;
double weightSum = 0.0;
for( WeightedValue<Double> value : samples )
{
double weight = value.getWeight();
sum += weight * value.getValue();
weightSum += weight;
}
double meanHat = sum / weightSum;
double meanEst = pdf.getMean();
double max = Math.max( Math.abs(meanEst), Math.abs(meanHat) );
assertEquals( pdf.getMean(), meanHat, MONTE_CARLO_FACTOR * max / Math.sqrt(NUM_SAMPLES) );
}
/**
* Computes an estimate of the probability distribution using the
* PDF as the importance sampling weighting function.
* @param learner
* Weighted learner.
*/
public void weightedDistributionEstimatorTest(
BatchLearner<Collection<? extends WeightedValue<? extends Double>>, ? extends Distribution<Double>> learner )
{
System.out.println( "Test weighted learner" );
SmoothUnivariateDistribution instance = this.createInstance();
UnivariateProbabilityDensityFunction pdf = instance.getProbabilityFunction();
System.out.println( "Target Distribution:\n" + pdf.toString() );
// Sample according to some unrelated distribution.
double std = Math.sqrt(pdf.getVariance());
double r = 6.0;
UniformDistribution.PDF background =
new UniformDistribution.PDF( instance.getMean()-r*std, instance.getMean()+r*std );
ArrayList<DefaultWeightedValue<Double>> weightedSamples =
ImportanceSampling.sample(background, pdf, RANDOM, NUM_SAMPLES);
Random r1 = new Random(1);
Collection<? extends Double> samples = pdf.sample(r1, NUM_SAMPLES);
Distribution<Double> estimate = learner.learn(weightedSamples);
System.out.println( "Estimated Distribution:\n" + estimate.toString() );
r1 = new Random(1);
Collection<? extends Double> estimatedSamples =
estimate.sample(r1, NUM_SAMPLES);
KolmogorovSmirnovConfidence.Statistic kstest =
KolmogorovSmirnovConfidence.INSTANCE.evaluateNullHypothesis(samples,estimatedSamples);
System.out.println( "K-S Test Results:\n" + kstest );
assertEquals( 1.0, kstest.getNullHypothesisProbability(), CONFIDENCE );
}
/**
* CDF.evaluates
*/
public void testCDFEvaluates()
{
System.out.println( "CDF.evaluate(Double) == CDF.evaluate(double)" );
SmoothUnivariateDistribution instance = this.createInstance();
SmoothCumulativeDistributionFunction cdf = instance.getCDF();
ArrayList<? extends Double> samples = cdf.sample( RANDOM, NUM_SAMPLES );
for( Double sample : samples )
{
assertEquals( cdf.evaluate( sample ).doubleValue(), cdf.evaluate( sample.doubleValue() ) );
}
}
/**
* Tests differentiate
*/
public void testCDFDifferentiate()
{
System.out.println( "CDF.differentiate" );
SmoothUnivariateDistribution instance = this.createInstance();
SmoothCumulativeDistributionFunction cdf = instance.getCDF();
UnivariateProbabilityDensityFunction pdf = instance.getProbabilityFunction();
UnivariateProbabilityDensityFunction dfdx = cdf.getDerivative();
NumericalDifferentiator.DoubleJacobian differ =
new NumericalDifferentiator.DoubleJacobian( cdf, TOLERANCE );
ArrayList<? extends Double> samples = cdf.sample( RANDOM, NUM_SAMPLES );
for( Double sample : samples )
{
double result = cdf.differentiate(sample);
double actual = pdf.evaluate(sample);
double approx = differ.differentiate(sample);
assertEquals( actual, result );
assertEquals( approx, result, Math.sqrt(TOLERANCE) );
}
}
/**
* CDF.getDerivative
*/
public void testCDFGetDerivative()
{
System.out.println( "CDF.getDerivative" );
SmoothUnivariateDistribution instance = this.createInstance();
SmoothCumulativeDistributionFunction cdf = instance.getCDF();
UnivariateProbabilityDensityFunction pdf = instance.getProbabilityFunction();
UnivariateProbabilityDensityFunction dfdx = cdf.getDerivative();
assertTrue( pdf.getClass().isInstance(dfdx) );
assertEquals( pdf.convertToVector(), dfdx.convertToVector() );
}
/**
* Tests the support bound
*/
public void testPDFSupport()
{
System.out.println( "PDF.Support" );
SmoothUnivariateDistribution instance = this.createInstance();
UnivariateProbabilityDensityFunction pdf = instance.getProbabilityFunction();
assertEquals( instance.getMinSupport(), pdf.getMinSupport() );
assertEquals( instance.getMaxSupport(), pdf.getMaxSupport() );
}
/**
* PDF Boundary Conditions
*/
public void testPDFBoundaryConditions()
{
System.out.println( "PDF Boundary Conditions" );
SmoothUnivariateDistribution instance = this.createInstance();
UnivariateProbabilityDensityFunction pdf = instance.getProbabilityFunction();
Double min = pdf.getMinSupport();
if( !Double.isInfinite(min) )
{
double mm1 = min-TOLERANCE;
assertEquals( 0.0, pdf.evaluate(mm1) );
}
Double max = pdf.getMaxSupport();
if( !Double.isInfinite(max) )
{
double mp1 = max+TOLERANCE;
assertEquals( 0.0, pdf.evaluate(mp1) );
}
}
/**
* CDF.inverse
*/
public void testCDFInverse()
{
System.out.println( "CDF.inverse" );
SmoothCumulativeDistributionFunction cdf = this.createInstance().getCDF();
if( cdf instanceof InvertibleCumulativeDistributionFunction )
{
System.out.println( "Found InvertibleCumulativeDistributionFunction... testing." );
ArrayList<? extends Double> samples = cdf.sample(RANDOM, NUM_SAMPLES);
InvertibleCumulativeDistributionFunction<Double> icdf =
(InvertibleCumulativeDistributionFunction<Double>) cdf;
for( int n = 0; n < samples.size(); n++ )
{
double p = icdf.evaluate(samples.get(n));
double xhat = icdf.inverse(p);
double phat = icdf.evaluate(xhat);
assertEquals( p, phat, TOLERANCE );
}
}
}
/**
* Determines if two distribution samples are equal within tolerance.
*
* @param s1
* The first set of samples.
* @param s2
* The second set of samples.
* @return
* True if the distributions are approximately equal
*/
public boolean distributionSamplesEqual(
double[] s1,
double[] s2 )
{
if (s1.length != s2.length)
{
return false;
}
for (int n = 0; n < s1.length; n++)
{
if (Math.abs(s1[n] - s2[n]) > TOLERANCE)
{
return false;
}
}
return true;
}
}