package beast.math.distributions;
import org.apache.commons.math.MathException;
import org.apache.commons.math.distribution.ContinuousDistribution;
import org.apache.commons.math.distribution.Distribution;
import beast.core.Description;
import beast.core.Input;
@Description("Uniform distribution over a given interval (including lower and upper values)")
public class Uniform extends ParametricDistribution {
final public Input<Double> lowerInput = new Input<>("lower", "lower bound on the interval, default 0", 0.0);
final public Input<Double> upperInput = new Input<>("upper", "lower bound on the interval, default 1", 1.0);
UniformImpl distr = new UniformImpl();
double _lower, _upper, density;
private boolean infiniteSupport;
@Override
public void initAndValidate() {
_lower = lowerInput.get();
_upper = upperInput.get();
if (_lower >= _upper) {
throw new IllegalArgumentException("Upper value should be higher than lower value");
}
distr.setBounds(_lower, _upper);
infiniteSupport = Double.isInfinite(_lower) || Double.isInfinite(_upper);
if (infiniteSupport) {
density = 1.0;
} else {
density = 1.0 / (_upper - _lower);
}
}
class UniformImpl implements ContinuousDistribution {
private double lower;
private double upper;
public void setBounds(final double lower, final double upper) {
this.lower = lower;
this.upper = upper;
}
@Override
public double cumulativeProbability(double x) throws MathException {
x = Math.max(x, lower);
return (x - lower) / (upper - lower);
}
@Override
public double cumulativeProbability(double x0, double x1) throws MathException {
x0 = Math.max(x0, lower);
x1 = Math.min(x1, upper);
if (x1 < lower || x1 > upper) {
throw new RuntimeException("Value x (" + x1 + ") out of bounds (" + lower + "," + upper + ").");
}
return (x1 - x0) / (upper - lower);
}
@Override
public double inverseCumulativeProbability(final double p) throws MathException {
if (p < 0.0 || p > 1.0) {
throw new RuntimeException("inverseCumulativeProbability::argument out of range [0...1]");
}
if( p == 0 ) {
// works even when one bound is infinite
return _lower;
}
if( p == 1 ) {
// works even when one bound is infinite
return _upper;
}
if( infiniteSupport ) {
throw new RuntimeException("Inverse Cumulative Probability for 0 < p < 1 and infinite support") ;
}
return (upper - lower) * p + lower;
}
@Override
public double density(final double x) {
if (x >= lower && x <= upper) {
return density;
} else {
return 0;
}
}
@Override
public double logDensity(final double x) {
return Math.log(density(x));
}
} // class UniformImpl
@Override
public Distribution getDistribution() {
return distr;
}
@Override
public double density(final double x) {
if (x >= _lower && x <= _upper) {
// (BUG)?? why does this not return this.density??? (JH)
return 1;
} else {
return 0;
}
}
@Override
public double getMean() {
if (Double.isInfinite(_lower) || Double.isInfinite(_upper)) {
return Double.NaN;
}
return offsetInput.get() + (_upper + _lower)/2;
}
}