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 org.apache.commons.math.distribution.NormalDistributionImpl;
import beast.core.Description;
import beast.core.Input;
import beast.core.parameter.RealParameter;
/**
* @author Alexei Drummond
*/
@Description("A log-normal distribution with mean and variance parameters.")
public class LogNormalDistributionModel extends ParametricDistribution {
final public Input<RealParameter> MParameterInput = new Input<>("M", "M parameter of lognormal distribution. Equal to the mean of the log-transformed distribution.");
final public Input<RealParameter> SParameterInput = new Input<>("S", "S parameter of lognormal distribution. Equal to the standard deviation of the log-transformed distribution.");
final public Input<Boolean> hasMeanInRealSpaceInput = new Input<>("meanInRealSpace", "Whether the M parameter is in real space, or in log-transformed space. Default false = log-transformed.", false);
boolean hasMeanInRealSpace;
LogNormalImpl dist = new LogNormalImpl(0, 1);
@Override
public void initAndValidate() {
hasMeanInRealSpace = hasMeanInRealSpaceInput.get();
if (MParameterInput.get() != null) {
if (MParameterInput.get().getLower() == null) {
MParameterInput.get().setLower(Double.NEGATIVE_INFINITY);
}
if (MParameterInput.get().getUpper() == null) {
MParameterInput.get().setUpper(Double.POSITIVE_INFINITY);
}
}
if (SParameterInput.get() != null) {
if (SParameterInput.get().getLower() == null) {
SParameterInput.get().setLower(0.0);
}
if (SParameterInput.get().getUpper() == null) {
SParameterInput.get().setUpper(Double.POSITIVE_INFINITY);
}
}
refresh();
}
/**
* make sure internal state is up to date *
*/
void refresh() {
double mean;
double sigma;
if (SParameterInput.get() == null) {
sigma = 1;
} else {
sigma = SParameterInput.get().getValue();
}
if (MParameterInput.get() == null) {
mean = 0;
} else {
mean = MParameterInput.get().getValue();
}
if (hasMeanInRealSpace) {
mean = Math.log(mean) - (0.5 * sigma * sigma);
}
dist.setMeanAndStdDev(mean, sigma);
}
@Override
public Distribution getDistribution() {
refresh();
return dist;
}
public class LogNormalImpl implements ContinuousDistribution {
double m_fMean;
double m_fStdDev;
NormalDistributionImpl m_normal = new NormalDistributionImpl(0, 1);
public LogNormalImpl(double mean, double stdDev) {
setMeanAndStdDev(mean, stdDev);
}
@SuppressWarnings("deprecation")
void setMeanAndStdDev(double mean, double stdDev) {
m_fMean = mean;
m_fStdDev = stdDev;
m_normal.setMean(mean);
m_normal.setStandardDeviation(stdDev);
}
@Override
public double cumulativeProbability(double x) throws MathException {
return m_normal.cumulativeProbability(Math.log(x));
}
@Override
public double cumulativeProbability(double x0, double x1) throws MathException {
return cumulativeProbability(x1) - cumulativeProbability(x0);
}
@Override
public double inverseCumulativeProbability(double p) throws MathException {
return Math.exp(m_normal.inverseCumulativeProbability(p));
}
@Override
public double density(double x) {
if( x <= 0 ) {
return 0;
}
return m_normal.density(Math.log(x)) / x;
}
@Override
public double logDensity(double x) {
if( x <= 0 ) {
return Double.NEGATIVE_INFINITY;
}
return m_normal.logDensity(Math.log(x)) - Math.log(x);
}
} // class LogNormalImpl
@Override
public double getMean() {
if (hasMeanInRealSpace) {
if (MParameterInput.get() != null) {
return offsetInput.get() + MParameterInput.get().getValue();
} else {
return offsetInput.get();
}
} else {
double s = SParameterInput.get().getValue();
double m = MParameterInput.get().getValue();
return Math.exp(m + s * s/2.0);
//throw new RuntimeException("Not implemented yet");
}
}
}