/*
* File: MathUtilTest.java
* Authors: Justin Basilico
* Company: Sandia National Laboratories
* Project: Cognitive Foundry
*
* Copyright November 13, 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.math;
import gov.sandia.cognition.math.matrix.Vector;
import gov.sandia.cognition.math.matrix.VectorFactory;
import java.util.Random;
import junit.framework.TestCase;
/**
* This class implements JUnit tests for the following classes:
*
* MathUtil
*
* @author Justin Basilico
* @since 2.0
*/
public class MathUtilTest
extends TestCase
{
/**
* Random number generator
*/
public Random RANDOM = new Random(1);
/**
* Tolerance
*/
public double TOLERANCE = 1e-5;
/**
* Constructor
* @param testName
* Name
*/
public MathUtilTest(
String testName)
{
super(testName);
}
/**
* Test of log2 method, of class gov.sandia.cognition.math.MathUtil.
*/
public void testLog2()
{
assertEquals(0.0, MathUtil.log2(1.0));
assertEquals(1.0, MathUtil.log2(2.0));
assertEquals(2.0, MathUtil.log2(4.0));
}
/**
* Test of log method, of class MathUtil.
*/
public void testLog()
{
assertEquals(0.0, MathUtil.log(1.0, 4.0));
assertEquals(1.0, MathUtil.log(2.0, 2.0));
assertEquals(2.0, MathUtil.log(4.0, 2.0));
assertEquals(0.5, MathUtil.log(2.0, 4.0));
}
/**
* Test of binomialCoefficient method, of class BinomialDistribution.
*/
public void testBinomialCoefficient()
{
System.out.println( "binomialCoefficient" );
assertEquals( 3, MathUtil.binomialCoefficient( 3, 2 ) );
assertEquals( 6, MathUtil.binomialCoefficient( 4, 2 ) );
assertEquals( 4, MathUtil.binomialCoefficient( 4, 3 ) );
assertEquals( 1, MathUtil.binomialCoefficient( 1, 0 ) );
try
{
MathUtil.binomialCoefficient( 4, 5 );
fail( "k must be <= N" );
}
catch (Exception e)
{
System.out.println( "Good: " + e );
}
}
/**
* Test of logFactorial method, of class BinomialDistribution.
*/
public void testLogFactorial()
{
System.out.println( "logFactorial" );
assertEquals( Math.log( 6 ), MathUtil.logFactorial( 3 ), TOLERANCE );
assertEquals( Math.log( 720 ), MathUtil.logFactorial( 6 ), TOLERANCE );
assertEquals( Math.log( 3628800 ), MathUtil.logFactorial( 10 ), TOLERANCE );
}
/**
* Test of evaluate method, of class gov.sandia.cognition.learning.util.function.cost.StudentTTest.LogGammaFunction.
*/
public void testLogGammaEvaluate()
{
System.out.println( "GammaDistribution.GammaFunction.LogGammaFunction.evaluate" );
assertEquals( 2.252713, MathUtil.logGammaFunction( 0.1 ), TOLERANCE );
assertEquals( 1.524064, MathUtil.logGammaFunction( 0.2 ), TOLERANCE );
assertEquals( 1.095798, MathUtil.logGammaFunction( 0.3 ), TOLERANCE );
assertEquals( 0.796678, MathUtil.logGammaFunction( 0.4 ), TOLERANCE );
assertEquals( 0.572365, MathUtil.logGammaFunction( 0.5 ), TOLERANCE );
assertEquals( 0.398234, MathUtil.logGammaFunction( 0.6 ), TOLERANCE );
assertEquals( 0.260867, MathUtil.logGammaFunction( 0.7 ), TOLERANCE );
assertEquals( 0.152060, MathUtil.logGammaFunction( 0.8 ), TOLERANCE );
assertEquals( 0.066376, MathUtil.logGammaFunction( 0.9 ), TOLERANCE );
assertEquals( 0.000000, MathUtil.logGammaFunction( 1.0 ), TOLERANCE );
assertEquals( 12.801827, MathUtil.logGammaFunction( 10.0 ), TOLERANCE );
try
{
MathUtil.logGammaFunction( 0.0 );
fail( "x > 0" );
}
catch (Exception e)
{
System.out.println( "Good: " + e );
}
}
/**
* Test of evaluate method, of class gov.sandia.cognition.learning.util.statistics.Statistics.GammaFunction.Incomplete.
*/
public void testGammaFunctionIncompleteEvaluate()
{
System.out.println( "GammaDistribution.GammaFunction.Incomplete.evaluate" );
// These were checked using octave's gammainc() function...
// however, we use evaluate(a,x), octave uses gammainc(x,a)
// so just remember to switch the arguments when checking
assertEquals( 0.8275517596, MathUtil.lowerIncompleteGammaFunction( 0.1, 0.1 ), TOLERANCE );
assertEquals( 0.9414024459, MathUtil.lowerIncompleteGammaFunction( 0.1, 0.5 ), TOLERANCE );
assertEquals( 0.9943261760, MathUtil.lowerIncompleteGammaFunction( 0.1, 2.0 ), TOLERANCE );
assertEquals( 0.0951625820, MathUtil.lowerIncompleteGammaFunction( 1.0, 0.1 ), TOLERANCE );
assertEquals( 0.1987480431, MathUtil.lowerIncompleteGammaFunction( 1.5, 0.5 ), TOLERANCE );
assertEquals( 0.5939941503, MathUtil.lowerIncompleteGammaFunction( 2.0, 2.0 ), TOLERANCE );
}
/**
* Beta function evaluate
*/
public void testBetaFunctionEvaluate()
{
System.out.println( "BetaFunction.evaluate" );
// These values come from octave's beta() function
assertEquals( Math.log(1.0), MathUtil.logBetaFunction( 1, 1 ), TOLERANCE );
assertEquals( Math.log(0.5), MathUtil.logBetaFunction( 2, 1 ), TOLERANCE );
assertEquals( Math.log(0.5), MathUtil.logBetaFunction( 1, 2 ), TOLERANCE );
assertEquals( Math.log(0.0095238095), MathUtil.logBetaFunction( 3, 5 ), TOLERANCE );
assertEquals( Math.log(0.0095238095), MathUtil.logBetaFunction( 5, 3 ), TOLERANCE );
}
/**
* Test of function method, of class gov.sandia.cognition.learning.util.function.cost.StudentTTest.BetaFunction.Incomplete.
*/
public void testBetaFunctionIncompleteFunction()
{
System.out.println( "BetaFunction.Incomplete.evaluate" );
assertEquals( 0.0, MathUtil.regularizedIncompleteBetaFunction( RANDOM.nextDouble(), RANDOM.nextDouble(), 0.0 ) );
assertEquals( 1.0, MathUtil.regularizedIncompleteBetaFunction( RANDOM.nextDouble(), RANDOM.nextDouble(), 1.0 ) );
assertEquals( 0.133975, MathUtil.regularizedIncompleteBetaFunction( 1.0, 0.5, 0.25 ), TOLERANCE );
assertEquals( 0.292893, MathUtil.regularizedIncompleteBetaFunction( 1.0, 0.5, 0.50 ), TOLERANCE );
assertEquals( 0.500000, MathUtil.regularizedIncompleteBetaFunction( 1.0, 0.5, 0.75 ), TOLERANCE );
assertEquals( 0.000977, MathUtil.regularizedIncompleteBetaFunction( 5.0, 1.0, 0.25 ), TOLERANCE );
assertEquals( 0.590490, MathUtil.regularizedIncompleteBetaFunction( 5.0, 1.0, 0.90 ), TOLERANCE );
assertEquals( 0.018535, MathUtil.regularizedIncompleteBetaFunction( 4.0, 8.0, 0.10 ), TOLERANCE );
assertEquals( 0.886719, MathUtil.regularizedIncompleteBetaFunction( 4.0, 8.0, 0.50 ), TOLERANCE );
assertEquals( 0.999999, MathUtil.regularizedIncompleteBetaFunction( 4.0, 8.0, 0.90 ), TOLERANCE );
try
{
MathUtil.regularizedIncompleteBetaFunction( RANDOM.nextDouble(), RANDOM.nextDouble(), -1.0 );
fail( "x must be >= 0.0" );
}
catch (Exception e)
{
System.out.println( "Good: " + e );
}
try
{
MathUtil.regularizedIncompleteBetaFunction( RANDOM.nextDouble(), RANDOM.nextDouble(), 1.01 );
fail( "x must be <= 1.0" );
}
catch (Exception e)
{
System.out.println( "Good: " + e );
}
}
/**
* MathUtil.logMultinomialBetaFunction
*/
public void testLogMultinomialBetaFunction()
{
System.out.println( "logMultinomialBetaFunction" );
Vector a = VectorFactory.getDefault().copyValues(1.0, 2.0, 3.0 );
assertEquals( -4.094344562222101, MathUtil.logMultinomialBetaFunction(a), TOLERANCE );
a = VectorFactory.getDefault().copyValues(1.0, 2.0, 3.0, 4.0 );
assertEquals( -10.316920830293707, MathUtil.logMultinomialBetaFunction(a), TOLERANCE );
a = VectorFactory.getDefault().copyValues(4.0, 2.0, 3.0, 4.0 );
assertEquals( -15.710548376647242, MathUtil.logMultinomialBetaFunction(a), TOLERANCE );
a = VectorFactory.getDefault().copyValues(1.0, 0.0, 3.0 );
try
{
MathUtil.logMultinomialBetaFunction(a);
fail( "a must be positive!" );
}
catch (Exception e)
{
System.out.println( "Good: " + e );
}
}
/**
* Test of checkedAdd method, of class MathUtil
*/
public void testCheckedAdd()
{
System.out.println("checkedAdd");
final int a1 = Integer.MAX_VALUE;
final int b1 = -3;
int result1 = MathUtil.checkedAdd(a1, b1);
assertEquals(result1, 2147483644);
final int a2 = Integer.MIN_VALUE;
final int b2 = 4;
int result2 = MathUtil.checkedAdd(a2, b2);
assertEquals(result2, -2147483644);
final int a3 = Integer.MAX_VALUE;
final int b3 = 1;
try
{
final int result3 = MathUtil.checkedAdd(a3, b3);
fail("An ArithmeticException should have been thrown!");
}
catch (ArithmeticException e)
{
System.out.println("Good: " + e);
}
catch (Exception e)
{
fail("An ArithmeticException should have been thrown!");
}
final int a4 = Integer.MIN_VALUE;
final int b4 = -1;
try
{
final int result4 = MathUtil.checkedAdd(a4, b4);
fail("An ArithmeticException should have been thrown!");
}
catch (ArithmeticException e)
{
System.out.println("Good: " + e);
}
catch (Exception e)
{
fail("An ArithmeticException should have been thrown!");
}
}
public void testCheckedMultiply()
{
System.out.println("checkedMultiply");
final int a1 = 3;
final int b1 = 4;
int result1 = MathUtil.checkedMultiply(a1, b1);
assertEquals(result1, 12);
final int a2 = -5;
final int b2 = 6;
int result2 = MathUtil.checkedMultiply(a2, b2);
assertEquals(result2, -30);
final int a3 = -7;
final int b3 = -8;
int result3 = MathUtil.checkedMultiply(a3, b3);
assertEquals(result3, 56);
final int a4 = Integer.MAX_VALUE;
final int b4 = 2;
try
{
final int result4 = MathUtil.checkedMultiply(a4, b4);
fail("An ArithmeticException should have been thrown!");
}
catch (ArithmeticException e)
{
System.out.println("Good: " + e);
}
catch (Exception e)
{
fail("An ArithmeticException should have been thrown!");
}
final int a5 = Integer.MIN_VALUE;
final int b5 = 2;
try
{
final int result5 = MathUtil.checkedMultiply(a5, b5);
fail("An ArithmeticException should have been thrown!");
}
catch (ArithmeticException e)
{
System.out.println("Good: " + e);
}
catch (Exception e)
{
fail("An ArithmeticException should have been thrown!");
}
}
/**
* Gets the good values that the object can take.
*
* @return
* The good values the object can take.
*/
protected double[] getGoodValues()
{
return new double[] { 1.0, 0.0, 0.1, 3.14, Math.PI, Math.E, 10.0,
Double.POSITIVE_INFINITY,
Double.MIN_VALUE, this.randomDouble(),
this.randomDouble(), this.randomDouble(), this.randomDouble(),
this.RANDOM.nextGaussian(), this.RANDOM.nextGaussian(),
this.RANDOM.nextGaussian(), this.RANDOM.nextGaussian(),
-1.0, -0.1, -3.14, -10.0, -Math.PI, -Math.E,
Double.MAX_VALUE, -Double.MAX_VALUE, Double.MIN_NORMAL,
Double.NEGATIVE_INFINITY,
-Double.MIN_VALUE, -RANDOM.nextDouble(), -this.randomDouble()};
}
/**
* Creates a random double in the range of the test.
*
* @return
* A random double for the test.
*/
protected double randomDouble()
{
return (this.RANDOM.nextDouble() - 0.5) * 100000.0;
}
public void testLog1Plus()
{
for (double value : getGoodValues())
{
assertEquals(Math.log1p(value), MathUtil.log1Plus(value), 0.0);
}
}
public void testExpMinus1Plus()
{
for (double value : getGoodValues())
{
assertEquals(Math.expm1(value), MathUtil.expMinus1Plus(value), 0.0);
}
}
public void testLog1MinusExp()
{
System.out.println("testLog1MinusExp");
for (double x : getGoodValues())
{
double expected = Math.log(1 - Math.exp(x));
double actual = MathUtil.log1MinusExp(x);
System.out.println("x = " + x + " " + expected + " " + actual + " " + -MathUtil.expMinus1Plus(x));
if (x < 0.0 && !Double.isInfinite(x) && Double.isInfinite(expected))
{
// For overflows for expected just make sure actual isn't overflowed.
assertTrue(!Double.isInfinite(actual));
}
else
{
assertEquals(expected, actual, TOLERANCE);
}
}
}
public void testLog1PlusExp()
{
System.out.println("testLog1PlusExp");
for (double x : getGoodValues())
{
double expected = Math.log(1 + Math.exp(x));
double actual = MathUtil.log1PlusExp(x);
System.out.println("x = " + x + " " + expected + " " + actual);
if (x > 0.0 && !Double.isInfinite(x) && Double.isInfinite(expected))
{
// For overflows for expected just make sure actual isn't overflowed.
assertTrue(!Double.isInfinite(actual));
}
else
{
assertEquals(expected, actual, TOLERANCE);
}
}
}
}