/*
* File: PolynomialFunctionTest.java
* Authors: Kevin R. Dixon
* Company: Sandia National Laboratories
* Project: Cognitive Foundry
*
* Copyright September 4, 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.function.scalar;
import gov.sandia.cognition.learning.algorithm.minimization.line.InputOutputSlopeTriplet;
import gov.sandia.cognition.learning.data.DefaultInputOutputPair;
import gov.sandia.cognition.learning.data.InputOutputPair;
import gov.sandia.cognition.learning.function.vector.ScalarBasisSet;
import gov.sandia.cognition.math.matrix.VectorFactory;
import gov.sandia.cognition.math.matrix.Vector;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Random;
import junit.framework.TestCase;
/**
*
* @author Kevin R. Dixon
*/
public class PolynomialFunctionTest extends TestCase
{
/** The random number generator for the tests. */
protected Random random = new Random(1234567);
/**
*
* @param testName
*/
public PolynomialFunctionTest( String testName )
{
super( testName );
}
/**
*
* @return
*/
protected PolynomialFunction createInstance()
{
double exponent = (int) (4.0 * random.nextDouble() - 2.0);
return new PolynomialFunction( exponent );
}
/**
* Test of clone method, of class gov.sandia.cognition.learning.util.function.PolynomialFunction.
*/
public void testClone()
{
System.out.println( "clone" );
double exponent = random.nextDouble();
PolynomialFunction instance = new PolynomialFunction( exponent );
PolynomialFunction clone = instance.clone();
assertNotSame( instance, clone );
assertEquals( instance.getExponent(), clone.getExponent() );
}
/**
* Test of convertToVector method, of class gov.sandia.cognition.learning.util.function.PolynomialFunction.
*/
public void testConvertToVector()
{
System.out.println( "convertToVector" );
double exponent = random.nextDouble();
PolynomialFunction instance = new PolynomialFunction( exponent );
Vector params = instance.convertToVector();
assertEquals( 1, params.getDimensionality() );
assertEquals( exponent, params.getElement( 0 ) );
}
/**
* Test of convertFromVector method, of class gov.sandia.cognition.learning.util.function.PolynomialFunction.
*/
public void testConvertFromVector()
{
System.out.println( "convertFromVector" );
double exponent = random.nextDouble();
PolynomialFunction instance = new PolynomialFunction( exponent );
assertEquals( exponent, instance.getExponent() );
Vector params = instance.convertToVector();
assertEquals( 1, params.getDimensionality() );
assertEquals( exponent, params.getElement( 0 ) );
Vector p2 = params.clone();
assertNotSame( p2, params );
assertEquals( p2, params );
double e2 = exponent + 1;
p2.setElement( 0, e2 );
instance.convertFromVector( p2 );
assertEquals( e2, instance.getExponent() );
}
/**
* Test of differentiate method, of class gov.sandia.cognition.learning.util.function.PolynomialFunction.
*/
public void testDifferentiate()
{
System.out.println( "differentiate" );
for (int i = 0; i < 10; i++)
{
double e1 = random.nextDouble() * 10 + 1;
PolynomialFunction p1 = new PolynomialFunction( e1 );
final double EPS = 1e-5;
System.out.println( p1.toString() );
double x1 = random.nextDouble();
double d1 = e1 * Math.pow( x1, e1 - 1.0 );
assertEquals( d1, p1.differentiate( x1 ), EPS );
}
}
/**
* Test of evaluate method, of class gov.sandia.cognition.learning.util.function.PolynomialFunction.
*/
public void testEvaluate()
{
System.out.println( "evaluate" );
final double EPS = 1e-5;
for (int i = 0; i < 10; i++)
{
PolynomialFunction p1 = new PolynomialFunction( 0.0 );
assertEquals( 1.0, p1.evaluate( random.nextDouble() ) );
PolynomialFunction p2 = new PolynomialFunction( 1.0 );
double x2 = random.nextDouble();
assertEquals( x2, p2.evaluate( x2 ) );
PolynomialFunction p3 = new PolynomialFunction( 10 * random.nextDouble() - 5 );
double x3 = 10 * random.nextDouble();
assertEquals( Math.pow( x3, p3.getExponent() ), p3.evaluate( x3 ), EPS );
}
}
/**
* Test of computeParameterGradient method, of class gov.sandia.cognition.learning.util.function.PolynomialFunction.
*/
public void testComputeParameterGradient()
{
System.out.println( "computeParameterGradient" );
PolynomialFunction p2 = new PolynomialFunction( random.nextDouble() );
double x = random.nextDouble() * 20 - 10;
Vector g = VectorFactory.getDefault().createVector( 1 );
double v = Math.log( x ) * p2.evaluate( x );
g.setElement( 0, v );
final double EPS = 1e-5;
Vector ghat = p2.computeParameterGradient( x );
assertEquals( g.getDimensionality(), ghat.getDimensionality() );
assertTrue( g.equals( p2.computeParameterGradient( x ), EPS ) );
}
/**
* Test of learn method, of class gov.sandia.cognition.learning.util.function.PolynomialFunction.Regression.
*/
public void testRegressionLearn()
{
System.out.println( "static Regression.learn" );
Collection<InputOutputPair<Double, Double>> d1 =
new LinkedList<InputOutputPair<Double, Double>>();
int o1 = 5;
VectorFunctionLinearDiscriminant<Double> t1 = new VectorFunctionLinearDiscriminant<Double>(
new ScalarBasisSet<Double>( PolynomialFunction.createPolynomials( 1.0, 2.0, 3.0, 4.0, 5.0 ) ),
new LinearDiscriminantWithBias( VectorFactory.getDefault().createUniformRandom( o1, -10, 10, random ), random.nextGaussian() ) );
double r = 5;
for (int n = 0; n < 10; n++)
{
double x = random.nextDouble() * 2 * r - r;
double y = t1.evaluate( x );
d1.add( new DefaultInputOutputPair<Double, Double>( x, y ) );
}
VectorFunctionLinearDiscriminant<Double> e1 =
PolynomialFunction.Regression.learn( o1, d1 );
System.out.println( "Target: " + t1.convertToVector() );
System.out.println( "Regression: " + e1.convertToVector() );
double EPS = 1e-5;
assertTrue( t1.convertToVector().equals( e1.convertToVector(), EPS ) );
}
/**
* Test of getExponent method, of class gov.sandia.cognition.learning.util.function.PolynomialFunction.
*/
public void testGetExponent()
{
System.out.println( "getExponent" );
double exponent = random.nextDouble();
PolynomialFunction instance = new PolynomialFunction( exponent );
assertEquals( exponent, instance.getExponent() );
}
/**
* Test of setExponent method, of class gov.sandia.cognition.learning.util.function.PolynomialFunction.
*/
public void testSetExponent()
{
System.out.println( "setExponent" );
double exponent = random.nextDouble();
PolynomialFunction instance = new PolynomialFunction( exponent );
assertEquals( exponent, instance.getExponent() );
double e2 = exponent + random.nextDouble();
instance.setExponent( e2 );
assertEquals( e2, instance.getExponent() );
}
/**
* Test of createPolynomials method, of class gov.sandia.cognition.learning.util.function.PolynomialFunction.
*/
public void testCreatePolynomials()
{
System.out.println( "createPolynomials" );
int num = 10;
double[] polynomialExponents = new double[num];
for (int i = 0; i < num; i++)
{
polynomialExponents[i] = random.nextDouble();
}
ArrayList<PolynomialFunction> result = PolynomialFunction.createPolynomials( polynomialExponents );
assertEquals( num, result.size() );
for (int i = 0; i < num; i++)
{
assertEquals( polynomialExponents[i], result.get( i ).getExponent() );
}
}
/**
*
* @return
*/
protected PolynomialFunction createFunction()
{
return this.createInstance();
}
/**
*
*/
public void testEvaluateScalar()
{
System.out.println( "evaluateScalar" );
this.testEvaluate();
}
/**
* Test of differentiate method, of class gov.sandia.isrc.learning.util.function.ElementWiseDifferentiableFunction.
*/
public void testDifferentiateScalar()
{
System.out.println( "differentiate (Scalar)" );
// Just perform a Reimann sum integral over part of the space using
// the mid-point rule
for (int n = 0; n < 10; n++)
{
double min = random.nextDouble() * 4.0;
double max = min + random.nextDouble() * 4.0;
double step = 1e-3;
double sum = 0.0;
PolynomialFunction f = this.createFunction();
int num = 0;
for (double x = min; x < max; x += step)
{
sum += f.differentiate( x + (0.5 * step) );
num++;
}
double estimated = sum * step;
double fmin = f.evaluate( min );
double fmax = f.evaluate( max );
double expected = fmax - fmin;
System.out.printf( "f(%f) = %f, f(%f) = %f, difference = %f, estimate = %f\n", min, fmin, max, fmax, expected, estimated );
assertEquals( expected, estimated, 1 / Math.log( num ) );
}
}
/**
* Test of differentiate method, of class gov.sandia.isrc.learning.util.function.ElementWiseDifferentiableFunction.
*/
public void testQuadtraticFormula()
{
final double EPS = 1e-5;
// Let's find the roots of:
// y(x) = 2*(x-1)*(x-2) = 2*(2-3*x+x^2) = 4-6x+2x^2
// The roots should be {1,2}
double q0 = 4.0;
double q1 = -6.0;
double q2 = 2.0;
Double[] r1 = PolynomialFunction.Quadratic.roots( q0, q1, q2 );
assertEquals( 2, r1.length );
assertEquals( 1.0, r1[0], EPS );
assertEquals( 2.0, r1[1], EPS );
assertEquals( 0.0, PolynomialFunction.Quadratic.evaluate( r1[0], q0, q1, q2 ), EPS );
assertEquals( 0.0, PolynomialFunction.Quadratic.evaluate( r1[1], q0, q1, q2 ), EPS );
assertEquals( 24.0, PolynomialFunction.Quadratic.evaluate( -2.0, q0, q1, q2 ), EPS );
// Let's find the roots of:
// y(x) = (x-1)*(x-1) = 1-2x+x^2
Double[] r2 = PolynomialFunction.Quadratic.roots( 1.0, -2.0, 1.0 );
assertEquals( 1, r2.length );
assertEquals( 1.0, r2[0], EPS );
// Here's a complex root:
// y(x) = 2 + x + x^2
Double[] r6 = PolynomialFunction.Quadratic.roots(
2.0, 1.0, 1.0 );
assertEquals( 0, r6.length );
// Let's find the roots of:
// y(x) = 8+2x
Double[] r3 = PolynomialFunction.Quadratic.roots( 8.0, 2.0, 0.0 );
assertEquals( 1, r3.length );
assertEquals( -4.0, r3[0], EPS );
// This shouldn't return any roots
Double[] r4 = PolynomialFunction.Quadratic.roots( 8.0, 0.0, 0.0 );
assertEquals( 0, r4.length );
// This shouldn't return roots either
Double[] r5 = PolynomialFunction.Quadratic.roots( 0.0, 0.0, 0.0 );
assertEquals( 0, r5.length );
}
/**
* Test of differentiate method, of class gov.sandia.isrc.learning.util.function.ElementWiseDifferentiableFunction.
*/
public void testCubicStationaryPoint()
{
final double EPS = 1e-5;
// Let's find the stationary points of
// y(x) = (x-1)*(x-2)*(x+2) = (2 - 3*x + x^2) * (2+x)
// y(x) = (4 - 6*x + 2*x^2) + (2*x - 3*x^2 + x^3)
// y(x) = 4 - 4*x - x^2 + x^3
double q0 = 4.0;
double q1 = -4.0;
double q2 = -1.0;
double q3 = 1.0;
Double[] stat = PolynomialFunction.Cubic.stationaryPoints( q0, q1, q2, q3 );
assertEquals( 2, stat.length );
assertEquals( -0.86851709182, stat[0], EPS );
assertEquals( 1.53519, stat[1], EPS );
assertEquals( -0.87942, PolynomialFunction.Cubic.evaluate( stat[1], q0, q1, q2, q3), EPS );
}
/**
* Test of PolynomialFunction.Linear class
*/
public void testLinear()
{
System.out.println( "Linear test" );
// f(x) = 1-2x
// dx(x) = -2
PolynomialFunction.Linear f1 = new PolynomialFunction.Linear( 1.0, -2.0 );
assertEquals( 1.0, f1.getQ0() );
assertEquals( -2.0, f1.getQ1() );
final double EPS = 1e-5;
assertEquals( 1.0, f1.evaluate( 0.0 ), EPS );
assertEquals( -1.0, f1.evaluate( 1.0 ), EPS );
assertEquals( 5.0, f1.evaluate( -2.0 ), EPS );
assertEquals( -2.0, f1.differentiate( 0.0 ), EPS );
assertEquals( -2.0, f1.differentiate( 2.0 ), EPS );
assertEquals( -2.0, f1.differentiate( -3.0 ), EPS );
assertEquals( 0, f1.stationaryPoints().length );
Double[] roots = f1.roots();
assertEquals( 1, roots.length );
assertEquals( 0.5, roots[0], EPS );
}
/**
* Test the linear fit
*/
public void testLinearFit2Points()
{
System.out.println( "Linear fit 2-points" );
double q0 = -1.0;
double q1 = 3.0;
PolynomialFunction.Linear f1 = new PolynomialFunction.Linear( q0, q1 );
System.out.println( "f1: " + f1 );
double x0 = 1.0;
InputOutputPair<Double,Double> p0 = new DefaultInputOutputPair<Double, Double>( x0, f1.evaluate( x0 ) );
double x1 = 10.0;
InputOutputPair<Double,Double> p1 = new DefaultInputOutputPair<Double, Double>( x1, f1.evaluate( x1 ) );
PolynomialFunction.Linear fhat = PolynomialFunction.Linear.fit( p0, p1 );
final double EPS = 1e-5;
assertEquals( f1.getQ0(), fhat.getQ0(), EPS );
assertEquals( f1.getQ1(), fhat.getQ1(), EPS );
System.out.println( "fhat: " + fhat );
try
{
PolynomialFunction.Linear.fit( p0, p0 );
fail( "Points are collinear and should have thrown exception" );
}
catch (Exception e)
{
System.out.println( "Good: " + e );
}
}
/**
* Test the linear fit
*/
public void testLinearFit1Point()
{
System.out.println( "Linear fit 1-point" );
double q0 = -1.0;
double q1 = 3.0;
PolynomialFunction.Linear f1 = new PolynomialFunction.Linear( q0, q1 );
System.out.println( "f1: " + f1 );
double x0 = 1.0;
InputOutputSlopeTriplet p0 = new InputOutputSlopeTriplet( x0, f1.evaluate( x0 ), f1.differentiate( x0 ) );
PolynomialFunction.Linear fhat1 = PolynomialFunction.Linear.fit( p0 );
System.out.println( "fhat1: " + fhat1 );
assertEquals( q0, fhat1.getQ0() );
assertEquals( q1, fhat1.getQ1() );
double x1 = 10.0;
InputOutputSlopeTriplet p1 = new InputOutputSlopeTriplet( x1, f1.evaluate( x1 ), f1.differentiate( x1 ) );
PolynomialFunction.Linear fhat2 = PolynomialFunction.Linear.fit( p1 );
System.out.println( "fhat2: " + fhat2 );
assertEquals( q0, fhat2.getQ0() );
assertEquals( q1, fhat2.getQ1() );
}
/**
* Test of differentiate method, of class gov.sandia.isrc.learning.util.function.ElementWiseDifferentiableFunction.
*/
public void testQuadratic()
{
System.out.println( "Quadratic test" );
// f(x) = 4 + 3x + 2x^2
// dx(x) = 3 + 4x
PolynomialFunction.Quadratic f1 = new PolynomialFunction.Quadratic(
4.0, 3.0, 2.0 );
assertEquals( 4.0, f1.getQ0() );
assertEquals( 3.0, f1.getQ1() );
assertEquals( 2.0, f1.getQ2() );
final double EPS = 1e-5;
assertEquals( 4.0, f1.evaluate( 0.0 ), EPS );
assertEquals( 3.0, f1.differentiate( 0.0 ), EPS );
assertEquals( 4.0+3.0+2.0, f1.evaluate( 1.0 ), EPS );
assertEquals( 3.0+4.0, f1.differentiate( 1.0 ), EPS );
assertEquals( 4.0-3.0+2.0, f1.evaluate( -1.0 ), EPS );
assertEquals( 3.0-4.0, f1.differentiate( -1.0 ), EPS );
assertEquals( 4.0+6.0+8.0, f1.evaluate( 2.0 ), EPS );
assertEquals( 3.0+8.0, f1.differentiate( 2.0 ), EPS );
Double[] p = f1.stationaryPoints();
assertEquals( 1, p.length );
assertEquals( -3.0/4.0, p[0], EPS );
assertEquals( 0, f1.roots().length );
}
public void testCubic()
{
System.out.println( "Cubic test" );
// f(x) = 4 + 3x - 2x^2 + 2x^3
// dx(x) = 3 - 4x + 6x^2
PolynomialFunction.Cubic f1 = new PolynomialFunction.Cubic(
4.0, 3.0, -2.0, 2.0 );
assertEquals( 4.0, f1.getQ0() );
assertEquals( 3.0, f1.getQ1() );
assertEquals( -2.0, f1.getQ2() );
assertEquals( 2.0, f1.getQ3() );
final double EPS = 1e-5;
assertEquals( 4.0, f1.evaluate( 0.0 ), EPS );
assertEquals( 3.0, f1.differentiate( 0.0 ), EPS );
assertEquals( 4.0+3.0-2.0+2.0, f1.evaluate( 1.0 ), EPS );
assertEquals( 3.0-4.0+6.0, f1.differentiate( 1.0 ), EPS );
assertEquals( 4.0-3.0-2.0-2.0, f1.evaluate( -1.0 ), EPS );
assertEquals( 3.0+4.0+6.0, f1.differentiate( -1.0 ), EPS );
assertEquals( 4.0+6.0-8.0+16.0, f1.evaluate( 2.0 ), EPS );
assertEquals( 3.0-8.0+24.0, f1.differentiate( 2.0 ), EPS );
Double[] p = f1.stationaryPoints();
assertEquals( 0, p.length );
try
{
f1.roots();
fail( "Cubic roots should not be implemented!" );
}
catch (Exception e)
{
System.out.println( "Good: " + e );
}
}
public void testQuadratic3PointFit()
{
System.out.println( "Quadratic 3-point fit" );
// f(x) = 4 + 3x + 2x^2
// dx(x) = 3 + 4x
double q0 = 4.0;
double q1 = 3.0;
double q2 = 2.0;
PolynomialFunction.Quadratic f1 = new PolynomialFunction.Quadratic(
q0, q1, q2 );
double x0 = 1.0;
InputOutputPair<Double,Double> p0 = new DefaultInputOutputPair<Double, Double>( x0, f1.evaluate( x0 ) );
double x1 = 2.0;
InputOutputPair<Double,Double> p1 = new DefaultInputOutputPair<Double, Double>( x1, f1.evaluate( x1 ) );
double x2 = 5.0;
InputOutputPair<Double,Double> p2 = new DefaultInputOutputPair<Double, Double>( x2, f1.evaluate( x2 ) );
PolynomialFunction.Quadratic fhat = PolynomialFunction.Quadratic.fit(
p0, p1, p2 );
System.out.println( "fhat: " + fhat );
final double EPS = 1e-5;
assertEquals( q0, fhat.getQ0(), EPS );
assertEquals( q1, fhat.getQ1(), EPS );
assertEquals( q2, fhat.getQ2(), EPS );
}
/**
* Test of the 2-point quadratic fit
*/
public void testQuadratic2PointFit()
{
System.out.println( "Quadratic 2-point fit" );
// f(x) = 4 + 3x + 2x^2
// dx(x) = 3 + 4x
double q0 = 4.0;
double q1 = 3.0;
double q2 = 2.0;
PolynomialFunction.Quadratic f1 = new PolynomialFunction.Quadratic(
q0, q1, q2 );
double x0 = -1.0;
InputOutputSlopeTriplet p0 = new InputOutputSlopeTriplet(
x0, f1.evaluate( x0 ), f1.differentiate( x0 ) );
double x1 = 3.0;
InputOutputPair<Double,Double> p1 = new DefaultInputOutputPair<Double, Double>(
x1, f1.evaluate( x1 ) );
PolynomialFunction.Quadratic fhat = PolynomialFunction.Quadratic.fit(
p0, p1 );
System.out.println( "fhat: " + fhat );
final double EPS = 1e-5;
assertEquals( q0, fhat.getQ0(), EPS );
assertEquals( q1, fhat.getQ1(), EPS );
assertEquals( q2, fhat.getQ2(), EPS );
}
/**
* Test of the 2-point quadratic fit
*/
public void testCubic2PointFit()
{
System.out.println( "Cubic 2-point fit" );
// f(x) = 4 + 3x + 2x^2
// dx(x) = 3 + 4x
double q0 = 4.0;
double q1 = 3.0;
double q2 = -2.0;
double q3 = 0.5;
PolynomialFunction.Cubic f1 = new PolynomialFunction.Cubic(
q0, q1, q2, q3 );
double x0 = -1.0;
InputOutputSlopeTriplet p0 = new InputOutputSlopeTriplet(
x0, f1.evaluate( x0 ), f1.differentiate( x0 ) );
double x1 = 2.5;
InputOutputSlopeTriplet p1 = new InputOutputSlopeTriplet(
x1, f1.evaluate( x1 ), f1.differentiate( x1 ) );
PolynomialFunction.Cubic fhat = PolynomialFunction.Cubic.fit( p0, p1 );
System.out.println( "fhat: " + fhat );
}
}