package beast.math.distributions; import java.util.List; import java.util.Random; import org.apache.commons.math.distribution.GammaDistribution; import org.apache.commons.math.distribution.GammaDistributionImpl; import beast.core.Description; import beast.core.Distribution; import beast.core.Input; import beast.core.Input.Validate; import beast.core.State; import beast.core.parameter.RealParameter; import beast.math.distributions.LogNormalDistributionModel.LogNormalImpl; /** * Initial version Ported from Beast 1.7 ExponentialMarkovModel */ @Description("A class that produces a distribution chaining values in a parameter through the Gamma distribution. " + "The value of a parameter is assumed to be Gamma distributed with mean as the previous value in the parameter. " + "If useLogNormal is set, a log normal distribution is used instead of a Gamma. " + "If a Jeffrey's prior is used, the first value is assumed to be distributed as 1/x, otherwise it is assumed to be uniform. " + "Handy for population parameters. ") public class MarkovChainDistribution extends Distribution { final public Input<Boolean> isJeffreysInput = new Input<>("jeffreys", "use Jeffrey's prior (default false)", false); final public Input<Boolean> isReverseInput = new Input<>("reverse", "parameter in reverse (default false)", false); final public Input<Boolean> useLogInput = new Input<>("uselog", "use logarithm of parameter values (default false)", false); final public Input<Double> shapeInput = new Input<>("shape", "shape parameter of the Gamma distribution (default 1.0 = exponential distribution) " + " or precision parameter if the log normal is used.", 1.0); final public Input<RealParameter> parameterInput = new Input<>("parameter", "chain parameter to calculate distribution over", Validate.REQUIRED); final public Input<Boolean> useLogNormalInput = new Input<>("useLogNormal", "use Log Normal distribution instead of Gamma (default false)", false); // ************************************************************** // Private instance variables // ************************************************************** private RealParameter chainParameter = null; private boolean jeffreys = false; private boolean reverse = false; private boolean uselog = false; private double shape = 1.0; GammaDistribution gamma; LogNormalImpl logNormal; boolean useLogNormal; @Override public void initAndValidate() { reverse = isReverseInput.get(); jeffreys = isJeffreysInput.get(); uselog = useLogInput.get(); shape = shapeInput.get(); chainParameter = parameterInput.get(); useLogNormal = useLogNormalInput.get(); gamma = new GammaDistributionImpl(shape, 1); logNormal = new LogNormalDistributionModel().new LogNormalImpl(1, 1); } /** * Get the log likelihood. * * @return the log likelihood. */ @SuppressWarnings("deprecation") @Override public double calculateLogP() { logP = 0.0; // jeffreys Prior! if (jeffreys) { logP += -Math.log(getChainValue(0)); } for (int i = 1; i < chainParameter.getDimension(); i++) { final double mean = getChainValue(i - 1); final double x = getChainValue(i); if (useLogNormal) { final double sigma = 1.0 / shape; // shape = precision // convert mean to log space final double M = Math.log(mean) - (0.5 * sigma * sigma); logNormal.setMeanAndStdDev(M, sigma); logP += logNormal.logDensity(x); } else { final double scale = mean / shape; gamma.setBeta(scale); logP += gamma.logDensity(x); } } return logP; } private double getChainValue(int i) { if (uselog) { return Math.log(chainParameter.getValue(index(i))); } else { return chainParameter.getValue(index(i)); } } private int index(int i) { if (reverse) return chainParameter.getDimension() - i - 1; else return i; } @Override public List<String> getArguments() { return null; } @Override public List<String> getConditions() { return null; } @Override public void sample(State state, Random random) { } }