gov.sandia.cognition.math.LogNumber 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: LogNumber.java
* Authors: Justin Basilico
* Project: Cognitive Foundry
*
* Copyright 2011 Cognitive Foundry. All rights reserved.
*/
package gov.sandia.cognition.math;
/**
* Represents a number in log-space, storing the log of the absolute value
* log(|value|) and the sign of the value sign(value). It is used to operate
* on a number as if it were not being represented in log space.
* Thus if you have two log numbers, a and b such that
* a = log(x) and b = log(y), doing c = a * b will be result in c = log(x * y),
* not c = log(x) * log(y). This means that when you do a.getValue() you will
* get x and if you do a.getLogValue() you will get log(x).
*
*
*
* This class is useful if you need to do a lot of operations on data with very
* large or very small exponents to avoid numerical overflow and underflow.
* This can be useful, for example, for probabilities that involve many
* products.
*
*
*
* All of the logarithms done by the class are done using the natural base (e),
* which is often denoted as ln(x) instead of log(x).
*
*
*
* If you know that all of your numbers are positive, such as if they are all
* probabilities, then you may want to use an {@link UnsignedLogNumber} instead,
* since it does not have to maintain the sign information, so it will consume
* less memory and will be faster.
*
* @author Justin Basilico
* @version 3.3.0
* @see UnsignedLogNumber
* @see LogMath
*/
public class LogNumber
extends Number
implements Field, Comparable
{
/** The sign of the value, sign(value). True for negative, and false for
* positive. */
protected boolean negative;
/** The log of the absolute value represented by this object, log(|value|).
*/
protected double logValue;
/**
* Creates the {@code LogNumber} representing zero.
*/
public LogNumber()
{
this(false, LogMath.LOG_0);
}
/**
* Creates a new {@code LogNumber} from the given value in log-space.
* This method is protected to avoid people calling it with being unsure if
* value or log(value) should be given. Thus, two separate static utility
* functions createFromValue and createFromLogValue are to be used instead.
*
* @param negative
* The flag indicating if the value is positive (false) or negative
* (true).
* @param logValue
* The log(value) for the value to be represented by this
* LogNumber.
*/
protected LogNumber(
final boolean negative,
final double logValue)
{
super();
this.negative = negative;
this.logValue = logValue;
}
/**
* Copies a given LogNumber.
*
* @param other
* The LogNumber to copy.
*/
public LogNumber(
final LogNumber other)
{
super();
this.negative = other.negative;
this.logValue = other.logValue;
}
/**
* Creates a new {@code LogNumber} from the given value.
*
* @param value
* A normal value.
* @return
* The LogNumber representation of that value.
*/
public static LogNumber createFromValue(
final double value)
{
if (value >= 0.0)
{
return new LogNumber(false, Math.log(value));
}
else
{
return new LogNumber(true, Math.log(-value));
}
}
/**
* Creates a new {@code LogNumber} from the given value that is
* already in log-space. Equivalent to calling createFromValue(
* exp(logValue)) or createFromLogValue(false, logValue).
*
* @param logValue
* The value that is already in log-space.
* @return
* The LogNumber representation of value = exp(logValue).
*/
public static LogNumber createFromLogValue(
final double logValue)
{
return new LogNumber(false, logValue);
}
/**
* Creates a new {@code LogNumber} from the given value that is
* already in log-space. Equivalent to calling createFromValue(
* (negative ? +1.0 : -1.0) * exp(logValue)).
*
* @param negative
* True if the value is negative.
* @param logValue
* The value that is already in log-space.
* @return
* The LogNumber representation of value = sign * exp(logValue).
*/
public static LogNumber createFromLogValue(
final boolean negative,
final double logValue)
{
return new LogNumber(negative, logValue);
}
@Override
public LogNumber clone()
{
try
{
return (LogNumber) super.clone();
}
catch (CloneNotSupportedException e)
{
throw new RuntimeException(e);
}
}
@Override
public boolean equals(
final Object other)
{
return other instanceof LogNumber
&& this.equals((LogNumber) other, 0.0);
}
@Override
public boolean equals(
final LogNumber other,
final double effectiveZero)
{
if (this.negative == other.negative)
{
// This if statement is to avoid creating negative numbers.
if (this.logValue == other.logValue)
{
// This check for exact equality helps deal with positive
// infinity.
return true;
}
else if (this.logValue > other.logValue)
{
return LogMath.subtract(this.logValue, other.logValue)
<= Math.log(effectiveZero);
}
else if (this.logValue < other.logValue)
{
return LogMath.subtract(other.logValue, this.logValue)
<= Math.log(effectiveZero);
}
else
{
// This is to deal with NaNs.
return false;
}
}
else
{
return LogMath.add(this.logValue, other.logValue)
<= Math.log(effectiveZero);
}
}
@Override
public int compareTo(
final LogNumber other)
{
if (this.negative)
{
if (other.negative)
{
return -Double.compare(this.logValue, other.logValue);
}
else
{
return -1;
}
}
else
{
if (other.negative)
{
return 1;
}
else
{
return Double.compare(this.logValue, other.logValue);
}
}
}
@Override
public int hashCode()
{
int hash = 7;
hash = 17 * hash + (this.negative ? 1 : 0);
hash =
17 * hash +
(int) (Double.doubleToLongBits(this.logValue)
^ (Double.doubleToLongBits(this.logValue) >>> 32));
return hash;
}
@Override
public String toString()
{
// Since the value is represented as a log, its true value is the
// exponentiation of the stored log value.
return (this.negative ? "-" : "+") + "exp(" + this.logValue + ")";
}
@Override
public LogNumber plus(
final LogNumber other)
{
final LogNumber result = this.clone();
result.plusEquals(other);
return result;
}
@Override
public void plusEquals(
final LogNumber other)
{
if (this.negative == other.negative)
{
this.logValue = LogMath.add(this.logValue, other.logValue);
}
else if (this.logValue > other.logValue)
{
this.logValue = LogMath.subtract(this.logValue, other.logValue);
}
else
{
this.negative = other.negative;
this.logValue = LogMath.subtract(other.logValue, this.logValue);
}
}
@Override
public LogNumber minus(
final LogNumber other)
{
final LogNumber result = this.clone();
result.minusEquals(other);
return result;
}
@Override
public void minusEquals(
final LogNumber other)
{
if (this.negative != other.negative)
{
this.logValue = LogMath.add(this.logValue, other.logValue);
}
else if (this.logValue < other.logValue)
{
this.negative = !other.negative;
this.logValue = LogMath.subtract(other.logValue, this.logValue);
}
else
{
this.logValue = LogMath.subtract(this.logValue, other.logValue);
}
}
/**
* Multiples this value times another value and returns the result.
*
* @param other
* The other value.
* @return
* The result of this * other.
*/
@Override
public LogNumber times(
final LogNumber other)
{
// Multiplication inside the log becomes addition of the log values.
return new LogNumber(this.negative ^ other.negative,
this.logValue + other.logValue);
}
/**
* Multiplies this value times another value and stores the result in
* this value.
*
* @param other
* The other value.
*/
@Override
public void timesEquals(
final LogNumber other)
{
// Multiplication inside the log becomes addition of the log values.
this.negative ^= other.negative;
this.logValue += other.logValue;
}
/**
* Divides this value by another value and returns the result.
*
* @param other
* The other value.
* @return
* The result of this / other.
*/
@Override
public LogNumber divide(
final LogNumber other)
{
// Division inside the log becomes subtraction of the log values.
return new LogNumber(this.negative ^ other.negative,
this.logValue - other.logValue);
}
/**
* Divides this value by another value and stores the result in
* this value.
*
* @param other
* The other value.
*/
@Override
public void divideEquals(
final LogNumber other)
{
// Division inside the log becomes subtraction of the log values.
this.negative ^= other.negative;
this.logValue -= other.logValue;
}
@Override
public LogNumber dotTimes(
final LogNumber other)
{
return this.times(other);
}
@Override
public void dotTimesEquals(
final LogNumber other)
{
this.timesEquals(other);
}
@Override
public LogNumber scale(
final double scaleFactor)
{
final LogNumber result = this.clone();
result.scaleEquals(scaleFactor);
return result;
}
@Override
public void scaleEquals(
final double scaleFactor)
{
if (scaleFactor == 0.0)
{
this.negative = false;
if ( this.logValue == Double.POSITIVE_INFINITY
|| Double.isNaN(this.logValue))
{
this.logValue = Double.NaN;
}
else
{
this.logValue = LogMath.LOG_0;
}
}
else if (scaleFactor < 0.0)
{
this.negative = !this.negative;
this.logValue += Math.log(-scaleFactor);
}
else // (scaleFactor > 0.0)
{
this.logValue += Math.log(scaleFactor);
}
}
@Override
public LogNumber scaledPlus(
final double scaleFactor,
final LogNumber other)
{
final LogNumber result = this.clone();
result.scaledPlusEquals(scaleFactor, other);
return result;
}
@Override
public void scaledPlusEquals(
final double scaleFactor,
final LogNumber other)
{
this.plusEquals(other.scale(scaleFactor));
}
@Override
public LogNumber scaledMinus(
final double scaleFactor,
final LogNumber other)
{
final LogNumber result = this.clone();
result.scaledMinusEquals(scaleFactor, other);
return result;
}
@Override
public void scaledMinusEquals(
final double scaleFactor,
final LogNumber other)
{
this.minusEquals(other.scale(scaleFactor));
}
@Override
public LogNumber negative()
{
return new LogNumber(!this.negative, this.logValue);
}
@Override
public void negativeEquals()
{
this.negative = !this.negative;
}
@Override
public void inverseEquals()
{
this.logValue = -this.logValue;
}
@Override
public LogNumber inverse()
{
return new LogNumber(this.negative, -this.logValue);
}
@Override
public void zero()
{
this.negative = false;
this.logValue = LogMath.LOG_0;
}
@Override
public boolean isZero()
{
return this.logValue == LogMath.LOG_0;
}
@Override
public boolean isZero(
final double effectiveZero)
{
return this.logValue <= Math.log(effectiveZero);
}
/**
* Returns a new {@code LogNumber} that represents the absolute value
* of this {@code LogNumber}.
*
* @return
* The absolute value of this log number.
*/
public LogNumber absoluteValue()
{
return new LogNumber(false, this.logValue);
}
/**
* Transforms this value to be its absolute value.
*/
public void absoluteValueEquals()
{
this.negative = false;
}
/**
* Returns a new {@code LogNumber} representing this log number taken
* to the given power.
*
* @param power
* The power.
* @return
* The log number to the given power.
*/
public LogNumber power(
final double power)
{
final LogNumber result = this.clone();
result.powerEquals(power);
return result;
}
/**
* Transforms this log number by taking it to the given power.
*
* @param power
* The power.
*/
public void powerEquals(
final double power)
{
if (power == 0.0)
{
this.logValue = 0.0;
this.negative = false;
}
else if (!this.negative)
{
this.logValue *= power;
}
else if (this.negative)
{
final double powerInt = Math.floor(power);
if (powerInt == power)
{
this.negative = ((powerInt % 2) == 1) || powerInt == -1;
this.logValue *= power;
}
else if (this.logValue == Double.POSITIVE_INFINITY)
{
this.negative = false;
this.logValue = Double.NEGATIVE_INFINITY;
}
else if (power == Double.POSITIVE_INFINITY
&& this.logValue != 0.0)
{
this.negative = false;
this.logValue = Double.NEGATIVE_INFINITY;
}
else
{
// Can only take non-integer powers of positive numbers.
this.logValue = Double.NaN;
}
}
}
/**
* A new {@code LogNumber} that is the minimum of this and another.
*
* @param other
* Another value.
* @return
* A new object containing the minimum of this value or the given
* value.
*/
public LogNumber min(
final LogNumber other)
{
final LogNumber result = this.clone();
result.minEquals(other);
return result;
}
/**
* Changes this value to be the minimum of this value or the given value.
*
* @param other
* Another value.
*/
public void minEquals(
final LogNumber other)
{
if (this.compareTo(other) > 0)
{
this.negative = other.negative;
this.logValue = other.logValue;
}
}
/**
* A new {@code LogNumber} that is the maximum of this and another.
*
* @param other
* Another value.
* @return
* A new object containing the maximum of this value or the given
* value.
*/
public LogNumber max(
final LogNumber other)
{
final LogNumber result = this.clone();
result.maxEquals(other);
return result;
}
/**
* Changes this value to be the maximum of this value or the given value.
*
* @param other
* Another value.
*/
public void maxEquals(
final LogNumber other)
{
if (this.compareTo(other) < 0)
{
this.negative = other.negative;
this.logValue = other.logValue;
}
}
/**
* Gets the value represented by the log number. Since the value is
* stored as log(value), this returns exp(log(value)) == value. Thus, it
* is equivalent to Math.exp(getLogValue()).
*
* @return
* The value represented by this object.
*/
public double getValue()
{
if (this.negative)
{
return -Math.exp(this.logValue);
}
else
{
return +Math.exp(this.logValue);
}
}
/**
* Sets the value represented by the log number. Since the value is
* stored as log(value), this sets the logValue to log(value). Thus, it
* is equivalent to doing setLogValue(Math.log(value)).
*
* @param value
* The value for this object to represent.
*/
public void setValue(
final double value)
{
if (value >= 0.0)
{
this.negative = false;
this.logValue = Math.log(value);
}
else
{
this.negative = true;
this.logValue = Math.log(-value);
}
}
/**
* Gets whether or not this value has a negative sign.
*
* @return
* True if this value has a negative sign.
*/
public boolean isNegative()
{
return this.negative;
}
/**
* Sets whether or not this value has a negative sign.
*
* @param negative
* True if this value has a negative sign.
*/
public void setNegative(
final boolean negative)
{
this.negative = negative;
}
/**
* Gets the log of the value represented by this object, which is what is
* stored in the object. That is, log(|value|).
*
* @return
* The log of the value represented by the object (log(|value|)).
*/
public double getLogValue()
{
return this.logValue;
}
/**
* Sets the log of the value represented by this object, which is what is
* stored in the object. That is, log(|value|).
*
* @param logValue
* The log of the value for this object to represent.
*/
public void setLogValue(
final double logValue)
{
this.logValue = logValue;
}
@Override
public int intValue()
{
return (int) this.getValue();
}
@Override
public long longValue()
{
return (long) this.getValue();
}
@Override
public float floatValue()
{
return (float) this.getValue();
}
@Override
public double doubleValue()
{
return this.getValue();
}
}