gov.sandia.cognition.math.MathUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cognitive-foundry Show documentation
Show all versions of cognitive-foundry Show documentation
A single jar with all the Cognitive Foundry components.
/*
* File: MathUtil.java
* Authors: Justin Basilico, Kevin Dixon, Zachary Benz
* 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.annotation.CodeReview;
import gov.sandia.cognition.annotation.PublicationReference;
import gov.sandia.cognition.annotation.PublicationReferences;
import gov.sandia.cognition.annotation.PublicationType;
import gov.sandia.cognition.math.matrix.Vector;
/**
* The {@code MathUtil} class implements mathematical utility functions.
*
* @author Justin Basilico, Kevin Dixon, Zachary Benz
* @since 2.0
*/
@CodeReview(
reviewer="Kevin R. Dixon",
date="2008-02-26",
changesNeeded=false,
comments={
"Minor changes, log2 uses callback to main log() method.",
"Otherwise, looks fine."
}
)
public class MathUtil
{
/**
* Returns the log of the given base of the given value,
* y=log_b(x) such that x=b^y
* @param x The value.
* @param base The base for the logarithm
* @return The log of x using the given base.
*/
public static double log(
final double x,
final double base)
{
return Math.log(x) / Math.log(base);
}
/**
* Returns the base-2 logarithm of the given value. It is computed as
* log(x) / log(2).
*
* @param x The value.
* @return The base-2 logarithm.
*/
public static double log2(
final double x)
{
return Math.log(x) / LogMath.LOG_2;
}
/**
* Computes the logarithm of the Gamma function.
* @param input
* Input to evaluate the Natural Logarithm of the Gamma Function
* @return
* Natural Logarithm of the Gamma Function about the input
*/
@PublicationReferences(
references={
@PublicationReference(
author="Wikipedia",
title="Gamma Function",
type=PublicationType.WebPage,
year=2010,
url="http://en.wikipedia.org/wiki/Gamma_function"
)
,
@PublicationReference(
author="jdhedden",
title="Bug in 2nd edition version of gammln()",
type=PublicationType.WebPage,
year=2005,
url="http://www.numerical-recipes.com/forum/showthread.php?t=606"
)
}
)
public static double logGammaFunction(
final double input )
{
if (input <= 0.0)
{
throw new IllegalArgumentException( "Input must be > 0.0" );
}
double xx = input;
double tmp, ser;
tmp = xx + 4.5;
tmp -= (xx - 0.5) * Math.log( tmp );
ser = 1.000000000190015
+ (76.18009172947146 / xx)
- (86.50532032941677 / (xx + 1.0))
+ (24.01409824083091 / (xx + 2.0))
- (1.231739572450155 / (xx + 3.0))
+ (0.1208650973866179e-2 / (xx + 4.0))
- (0.5395239384953e-5 / (xx + 5.0));
return (Math.log( 2.5066282746310005 * ser ) - tmp);
}
/**
* Computes the Lower incomplete gamma function.
* Note that this has the reverse parameters order from octave.
* @param a
* Degrees of Freedom
* @param x
* Input value
* @return
* Value of the IncompleteGammaFunction(a,x)
*/
@PublicationReferences(
references={
@PublicationReference(
author="Wikipedia",
title="Incomplete gamma function",
type=PublicationType.WebPage,
year=2010,
url="http://en.wikipedia.org/wiki/Incomplete_gamma_function"
)
,
@PublicationReference(
author={
"William H. Press",
"Saul A. Teukolsky",
"William T. Vetterling",
"Brian P. Flannery"
},
title="Numerical Recipes in C, Second Edition",
type=PublicationType.Book,
year=1992,
pages=218,
notes="Function gammap",
url="http://www.nrbook.com/a/bookcpdf.php"
)
}
)
public static double lowerIncompleteGammaFunction(
final double a,
final double x )
{
if (a <= 0.0)
{
throw new IllegalArgumentException( "a must be > 0.0" );
}
double gammp;
if (x <= 0.0)
{
gammp = 0.0;
}
else if (x > 1e10)
{
gammp = 1.0;
}
else if (x < (a + 1.0))
{
gammp = incompleteGammaSeriesExpansion( a, x );
}
else
{
gammp = incompleteGammaContinuedFraction( a, x );
}
return gammp;
}
/**
* Computes the series expansion approximation to the incomplete
* gamma function.
* Note that this has the reverse parameters order from octave.
* @param a
* Degrees of Freedom
* @param x
* Input value
* @return
* Value of the IncompleteGammaFunction(a,x)
*/
@PublicationReference(
author={
"William H. Press",
"Saul A. Teukolsky",
"William T. Vetterling",
"Brian P. Flannery"
},
title="Numerical Recipes in C, Second Edition",
type=PublicationType.Book,
year=1992,
pages={218,219},
url="http://www.nrbook.com/a/bookcpdf.php",
notes="Function gser()"
)
protected static double incompleteGammaSeriesExpansion(
final double a,
final double x )
{
final int MAX_ITERATIONS = 1000;
final double EPS = 3e-7;
double gamser = 0.0;
if (x <= 0.0)
{
if (x < 0.0)
{
throw new IllegalArgumentException( "x must be >= 0.0" );
}
gamser = 0.0;
}
else
{
double gln = logGammaFunction( a );
double del, sum;
double ap = a;
del = sum = 1.0 / a;
int n;
for (n = 1; n <= MAX_ITERATIONS; n++)
{
ap++;
del *= x / ap;
sum += del;
if (Math.abs( del ) < (Math.abs( sum ) * EPS))
{
gamser = sum * Math.exp( -x + a * Math.log( x ) - gln );
break;
}
}
if (n > MAX_ITERATIONS)
{
throw new OperationNotConvergedException(
"a too large, MAX_ITERATIONS too small in seriesExpansion()" );
}
}
return gamser;
}
/**
* Returns the incomplete Gamma function using the continued
* fraction expansion evaluation using Lentz's method
* @param a
* Degrees of Freedom
* @param x
* Input value
* @return
* Value of the IncompleteGammaFunction(a,x)
*/
@PublicationReference(
author={
"William H. Press",
"Saul A. Teukolsky",
"William T. Vetterling",
"Brian P. Flannery"
},
title="Numerical Recipes in C, Second Edition",
type=PublicationType.Book,
year=1992,
pages={216,219},
url="http://www.nrbook.com/a/bookcpdf.php"
)
public static double incompleteGammaContinuedFraction(
final double a,
final double x )
{
LentzMethod lentz = new LentzMethod();
lentz.initializeAlgorithm(0.0);
lentz.iterate(1.0, (x+1-a) );
while( lentz.getKeepGoing() )
{
int i = lentz.getIteration();
double aterm = -i * (i-a);
double bterm = x + (2*i+1)-a;
lentz.iterate(aterm, bterm);
}
double gln = logGammaFunction( a );
double gcf = lentz.getResult();
double gamma = 1.0 - Math.exp( -x + a * Math.log( x ) - gln ) * gcf;
return gamma;
}
/**
* Returns the binomial coefficient for "N choose k". In other words, this
* is the number of different ways of choosing k objects from a total of
* N different ones, where order doesn't matter and without replacement.
* @param N Total number of objects in the bag
* @param k Total number of objects to choose, must be less than or equal
* to N
* @return Binomial coefficient for N choose k
*/
@PublicationReference(
author="Wikipedia",
title="Binomial coefficient",
type=PublicationType.WebPage,
year=2010,
url="http://en.wikipedia.org/wiki/Binomial_coefficient"
)
public static int binomialCoefficient(
final int N,
final int k )
{
return (int) Math.round( Math.exp( logBinomialCoefficient(N, k) ) );
}
/**
* Computes the natural logarithm of the binomial coefficient.
* @param N Total number of objects in the bag
* @param k Total number of objects to choose, must be less than or equal
* to N
* @return Natural logarithm of the binomial coefficient for N choose k
*/
public static double logBinomialCoefficient(
final int N,
final int k )
{
return logFactorial( N ) - logFactorial( k ) - logFactorial( N - k );
}
/**
* Returns the natural logarithm of n factorial log(n!) =
* log(n*(n-1)*...*3*2*1)
* @param n
* Parameter for choose for n factorial
* @return
* n factorial
*/
public static double logFactorial(
final int n )
{
if (n < 0)
{
throw new IllegalArgumentException( "Factorial must be >= 0" );
}
// Less than 1 is defined to be 1, taking its logarithm yields 0.0
else if (n <= 1)
{
return 0.0;
}
else
{
return logGammaFunction( n + 1.0 );
}
}
/**
* Compute the natural logarithm of the Beta Function.
* @param a
* First parameter to the Beta function
* @param b
* Second parameter to the Beta function
* @return
* Natural logarithm of the Beta Function.
*/
@PublicationReference(
author="Wikipedia",
title="Beta function",
type=PublicationType.WebPage,
year=2010,
url="http://en.wikipedia.org/wiki/Beta_function"
)
public static double logBetaFunction(
final double a,
final double b )
{
double ga = logGammaFunction( a );
double gb = logGammaFunction( b );
double gab = logGammaFunction( a + b );
return ga + gb - gab;
}
/**
* Computes the regularized incomplete Beta function.
* @param a
* Parameter a to the Beta function
* @param b
* Parameter b to the Beta function
* @param x
* Parameter x to for the integral from 0 to x
* @return
* Incomplete beta function for I_x(a,b)
*/
@PublicationReferences(
references={
@PublicationReference(
author="Wikipedia",
title="Beta function, Incomplete Beta function",
type=PublicationType.WebPage,
year=2010,
url="http://en.wikipedia.org/wiki/Beta_function#Incomplete_beta_function"
)
,
@PublicationReference(
author={
"William H. Press",
"Saul A. Teukolsky",
"William T. Vetterling",
"Brian P. Flannery"
},
title="Numerical Recipes in C, Second Edition",
type=PublicationType.Book,
year=1992,
pages=227,
notes="Function betai",
url="http://www.nrbook.com/a/bookcpdf.php"
)
}
)
public static double regularizedIncompleteBetaFunction(
final double a,
final double b,
final double x )
{
double bt;
if ((x < 0.0) || (x > 1.0))
{
throw new IllegalArgumentException( "0 <= x <= 1" );
}
if ((x == 0.0) || (x == 1.0))
{
bt = 0.0;
}
else
{
bt = Math.exp(
a*Math.log( x ) +
b*Math.log( 1.0-x ) -
logBetaFunction(a, b) );
}
if (x < ((a + 1.0) / (a + b + 2.0)))
{
return bt * incompleteBetaContinuedFraction( a, b, x ) / a;
}
else
{
return 1.0 - bt*incompleteBetaContinuedFraction( b, a, 1.0 - x )/b;
}
}
/**
* Evaluates the continued fraction of the incomplete beta function.
* Based on the math from NRC's 6.4 "Incomplete Beta Function"
* @param a
* Parameter a to the beta continued fraction
* @param b
* Parameter b to the beta continued fraction
* @param x
* Parameter x to the beta continued fraction
* @return
* Incomplete beta function continued fraction
*/
@PublicationReference(
author={
"William H. Press",
"Saul A. Teukolsky",
"William T. Vetterling",
"Brian P. Flannery"
},
title="Numerical Recipes in C, Second Edition",
type=PublicationType.Book,
year=1992,
pages=227,
notes="Incomplete Beta Function continued fraction terms for Lentz's method",
url="http://www.nrbook.com/a/bookcpdf.php"
)
protected static double incompleteBetaContinuedFraction(
final double a,
final double b,
final double x )
{
double apb = a+b;
LentzMethod lentz = new LentzMethod();
lentz.initializeAlgorithm( 0.0 );
lentz.iterate(1.0, 1.0);
while( lentz.getKeepGoing() )
{
int m = lentz.getIteration() / 2;
double ap2m = a + 2*m;
double aterm;
if( (lentz.getIteration() % 2) != 0 )
{
// Odd iteration cycle
double num = -(a+m) * (apb+m) * x;
double den = ap2m * (ap2m + 1);
aterm = num / den;
}
else
{
// Even iteration cycle
double num = m * (b-m) * x;
double den = (ap2m - 1) * ap2m;
aterm = num / den;
}
lentz.iterate(aterm, 1.0);
}
if( lentz.isResultValid() )
{
return lentz.getResult();
}
else
{
throw new OperationNotConvergedException(
"Lentz's Method failed in Beta continuous fraction: "
+ String.format( "a = %f, b = %f, x = %f", a, b, x));
}
}
/**
* Evaluates the natural logarithm of the multinomial beta function
* for the given input vector.
* @param input
* Input vector to consider.
* @return
* Natural logarithm of the Multinomial beta function evaluated at the
* given input.
*/
@PublicationReference(
author="Wikipedia",
title="Dirichlet distribution",
type=PublicationType.WebPage,
year=2009,
url="http://en.wikipedia.org/wiki/Dirichlet_distribution",
notes="Multinomial Beta Function found in the \"Probability density function\" section."
)
static public double logMultinomialBetaFunction(
final Vector input)
{
double logsum = 0.0;
double inputSum = 0.0;
for( int i = 0; i < input.getDimensionality(); i++ )
{
double ai = input.getElement(i);
inputSum += ai;
logsum += logGammaFunction(ai);
}
logsum -= logGammaFunction(inputSum);
return logsum;
}
/**
* Safely checks for underflow/overflow before adding two integers. If an
* underflow or overflow would occur as a result of the addition, an
* {@code ArithmeticException} is thrown.
*
* @param a
* The first integer to add
* @param b
* The second integer to add
* @return
* The sum of integers a and b
* @throws ArithmeticException
* If an underflow or overflow will occur upon adding a and b
*/
@PublicationReference(
author={
"Tov Are",
"Paul van Keep",
"Mike Cowlishaw",
"Pierre Baillargeon",
"Bill Wilkinson",
"Patricia Shanahan",
"Joseph Bowbeer",
"Charles Thomas",
"Joel Crisp",
"Eric Nagler",
"Daniel Leuck",
"William Brogden",
"Yves Bossu",
"Chad Loder"
},
title="Java Gotchas",
type=PublicationType.WebPage,
year=2011,
url="http://202.38.93.17/bookcd/285/1.iso/faq/gloss/gotchas.html#OVERFLOW",
notes="")
static public int checkedAdd(
final int a,
final int b)
throws ArithmeticException
{
if ((a > 0) && (b > Integer.MAX_VALUE - a))
{
throw new ArithmeticException("Integer Overflow: " +
a + " + " + b + " > Integer.MAX_VALUE");
}
else if ((a < 0) && (b < Integer.MIN_VALUE - a))
{
throw new ArithmeticException("Integer Underflow: " +
a + " + " + b + " < Integer.MIN_VALUE");
}
else
{
return a + b;
}
}
/**
* Safely checks for overflow before multiplying two integers.
* If an overflow would occur as a result of the
* multiplication, an {@code ArithmeticException} is thrown.
*
* @param a
* The first integer to multiply
* @param b
* The second integer to multiply
* @return
* The result of multiplying the integers a and b
* @throws ArithmeticException
* If an overflow will occur upon multiplying a and b
*/
@PublicationReference(
author={
"Tov Are",
"Paul van Keep",
"Mike Cowlishaw",
"Pierre Baillargeon",
"Bill Wilkinson",
"Patricia Shanahan",
"Joseph Bowbeer",
"Charles Thomas",
"Joel Crisp",
"Eric Nagler",
"Daniel Leuck",
"William Brogden",
"Yves Bossu",
"Chad Loder"
},
title="Java Gotchas",
type=PublicationType.WebPage,
year=2011,
url="http://202.38.93.17/bookcd/285/1.iso/faq/gloss/gotchas.html#OVERFLOW",
notes="")
static public int checkedMultiply(
final int a,
final int b)
throws ArithmeticException
{
final long result = (long)a * (long)b;
final int desiredHighBits = - ((int)( result >>> 31 ) & 1);
final int actualHighBits = (int)( result >>> 32 );
if (desiredHighBits == actualHighBits)
{
return(int)result;
}
else if (result > 0)
{
throw new ArithmeticException("Integer Overflow: " +
a + " * " + b + " > Integer.MAX_VALUE");
}
else
{
throw new ArithmeticException("Integer Underflow: " +
a + " * " + b + " < Integer.MIN_VALUE");
}
}
/**
* Computes log(1 + x). For small values, this is closer to the actual value
* than actually calling log(1 + x). This is an alias for Math.log1p(x).
*
* @param x
* The value.
* @return
* The result of log(1 + x).
*/
public static double log1Plus(
final double x)
{
return Math.log1p(x);
}
/**
* Computes exp(x - 1). For small values, this is closer to the actual value
* than actually calling exp(x - 1). This is an alias for Math.expm1(x).
*
* @param x
* The value.
* @return
* The result of exp(x - 1).
*/
public static double expMinus1Plus(
final double x)
{
return Math.expm1(x);
}
/**
* Computes log(1 - exp(x)). For small values, this is closer to the actual
* value than actually calling log(1 - exp(x)).
*
* @param x
* The value.
* @return
* The result of log(1 - exp(x)).
*/
@PublicationReference(
title="Accurately Computing log(1 − exp(−|a|)): Assessed by the Rmpfr package",
author="Martin Machler",
year=2012,
type=PublicationType.WebPage,
url="http://cran.r-project.org/web/packages/Rmpfr/vignettes/log1mexp-note.pdf")
public static double log1MinusExp(
final double x)
{
if (x < 0.0 && x >= -LogMath.LOG_2)
{
// For small x, use the -(exp(x) - 1) with higher precision.
return Math.log(-MathUtil.expMinus1Plus(x));
}
else
{
// For large x, use log(1 - exp(x)) with higher precision.
return Math.log1p(-Math.exp(x));
}
}
/**
* Computes log(1 + exp(x)). For small values, this is closer to the actual
* value than actually calling log(1 + exp(x)).
*
* @param x
* The value.
* @return
* The result of log(1 + exp(x)).
*/
@PublicationReference(
title="Accurately Computing log(1 − exp(−|a|)): Assessed by the Rmpfr package",
author="Martin Machler",
year=2012,
type=PublicationType.WebPage,
url="http://cran.r-project.org/web/packages/Rmpfr/vignettes/log1mexp-note.pdf")
public static double log1PlusExp(
final double x)
{
if (x <= 18.0)
{
// At this scale we can just perform the exponetiation.
return Math.log1p(Math.exp(x));
}
else if (x <= 33.3)
{
// Intermediate scale.
return x + Math.exp(-x);
}
else
{
// At this scale exp(x) is sufficiently large that the +1
// term goes away so log(1 + exp(x)) ~= log(exp(x)) = x.
return x;
}
}
}