/*
* ParameterPriorGenerator.java
*
* Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard
*
* This file is part of BEAST.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership and licensing.
*
* BEAST is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* BEAST is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with BEAST; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
package dr.app.beauti.generator;
import dr.app.beauti.components.ComponentFactory;
import dr.app.beauti.options.*;
import dr.app.beauti.types.PriorType;
import dr.app.beauti.util.XMLWriter;
import dr.evolution.util.Taxa;
import dr.evomodel.tree.TreeModel;
import dr.evomodelxml.tree.CTMCScalePriorParser;
import dr.evomodelxml.tree.MonophylyStatisticParser;
import dr.inference.model.ParameterParser;
import dr.inferencexml.distribution.*;
import dr.inferencexml.model.BooleanLikelihoodParser;
import dr.inferencexml.model.OneOnXPriorParser;
import dr.util.Attribute;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author Alexei Drummond
* @author Andrew Rambaut
* @author Walter Xie
*/
public class ParameterPriorGenerator extends Generator {
public ParameterPriorGenerator(BeautiOptions options, ComponentFactory[] components) {
super(options, components);
}
/**
* Write the priors for each parameter
*
* @param useStarBEAST
* @param writer the writer
*/
public void writeParameterPriors(XMLWriter writer, boolean useStarBEAST) {
boolean first = true;
Map<Taxa, Boolean> taxonSetsMono = useStarBEAST ? options.speciesSetsMono : options.taxonSetsMono;
for (Map.Entry<Taxa, Boolean> taxaBooleanEntry : taxonSetsMono.entrySet()) {
if (taxaBooleanEntry.getValue()) {
if (first) {
writer.writeOpenTag(BooleanLikelihoodParser.BOOLEAN_LIKELIHOOD);
first = false;
}
final String taxaRef = "monophyly(" + taxaBooleanEntry.getKey().getId() + ")";
writer.writeIDref(MonophylyStatisticParser.MONOPHYLY_STATISTIC, taxaRef);
}
}
if (!first) {
writer.writeCloseTag(BooleanLikelihoodParser.BOOLEAN_LIKELIHOOD);
}
List<Parameter> parameters = options.selectParameters();
if (useStarBEAST) {
for (Parameter parameter : parameters) {
if (!(parameter.priorType == PriorType.NONE_TREE_PRIOR || parameter.priorType == PriorType.NONE_STATISTIC)) {
if (parameter.isCached) {
writeCachedParameterPrior(parameter, writer);
//if (parameter.priorType != PriorType.UNIFORM_PRIOR || parameter.isNodeHeight) {
} else if (!(options.treeModelOptions.isNodeCalibrated(parameter) && parameter.isCalibratedYule)) {
writeParameterPrior(parameter, writer);
}
}
}
} else {
for (Parameter parameter : parameters) {
if (!(parameter.priorType == PriorType.NONE_TREE_PRIOR ||
parameter.priorType == PriorType.NONE_FIXED ||
parameter.priorType == PriorType.NONE_STATISTIC)) {
if (parameter.isCached) {
writeCachedParameterPrior(parameter, writer);
//if (parameter.priorType != PriorType.UNIFORM_PRIOR || parameter.isNodeHeight) {
} else if (!(options.treeModelOptions.isNodeCalibrated(parameter) && parameter.isCalibratedYule)) {
writeParameterPrior(parameter, writer);
}
}
}
}
}
private void writeCachedParameterPrior(Parameter parameter, XMLWriter writer) {
writer.writeOpenTag(CachedDistributionLikelihoodParser.CACHED_PRIOR);
writeParameterPrior(parameter, writer);
writeParameterIdref(writer, parameter);
writer.writeCloseTag(CachedDistributionLikelihoodParser.CACHED_PRIOR);
}
/**
* Write the priors for each parameter
*
* @param parameter the parameter
* @param writer the writer
*/
public void writeParameterPrior(Parameter parameter, XMLWriter writer) {
if (parameter.priorType != PriorType.NONE_FIXED && parameter.isTruncated) {
// if there is a truncation then put it at the top so it short-circuits any other prior
// calculations
// todo: We should switch this to truncatedDistribution so that the density is normalized correctly
writer.writeOpenTag(PriorParsers.UNIFORM_PRIOR,
new Attribute[]{
new Attribute.Default<String>(PriorParsers.LOWER, "" + parameter.getLowerBound()),
new Attribute.Default<String>(PriorParsers.UPPER, "" + parameter.getUpperBound())
});
writeParameterIdref(writer, parameter);
writer.writeCloseTag(PriorParsers.UNIFORM_PRIOR);
}
switch (parameter.priorType) {
case NONE_FIXED:
break;
case NONE_IMPROPER:
writer.writeComment("Improper uniform prior: " + parameter.getName());
break;
case UNIFORM_PRIOR:
if (parameter.isPriorImproper()) {
throw new IllegalArgumentException("Uniform priors cannot have infinite bounds (use 'NONE_IMPROPER')");
// writer.writeComment("Improper uniform prior: " + parameter.getName());
} else {
writer.writeOpenTag(PriorParsers.UNIFORM_PRIOR,
new Attribute[]{
new Attribute.Default<String>(PriorParsers.LOWER, "" + parameter.getLowerBound()),
new Attribute.Default<String>(PriorParsers.UPPER, "" + parameter.getUpperBound())
});
writeParameterIdref(writer, parameter);
writer.writeCloseTag(PriorParsers.UNIFORM_PRIOR);
}
break;
case EXPONENTIAL_PRIOR:
writer.writeOpenTag(PriorParsers.EXPONENTIAL_PRIOR,
new Attribute[]{
new Attribute.Default<String>(PriorParsers.MEAN, "" + parameter.mean),
new Attribute.Default<String>(PriorParsers.OFFSET, "" + parameter.offset)
});
writeParameterIdref(writer, parameter);
writer.writeCloseTag(PriorParsers.EXPONENTIAL_PRIOR);
break;
case LAPLACE_PRIOR:
writer.writeOpenTag(PriorParsers.LAPLACE_PRIOR,
new Attribute[]{
new Attribute.Default<String>(PriorParsers.MEAN, "" + parameter.mean),
new Attribute.Default<String>(PriorParsers.SCALE, "" + parameter.scale)
});
writeParameterIdref(writer, parameter);
writer.writeCloseTag(PriorParsers.LAPLACE_PRIOR);
break;
case NORMAL_PRIOR:
writer.writeOpenTag(PriorParsers.NORMAL_PRIOR,
new Attribute[]{
new Attribute.Default<String>(PriorParsers.MEAN, "" + parameter.mean),
new Attribute.Default<String>(PriorParsers.STDEV, "" + parameter.stdev)
});
writeParameterIdref(writer, parameter);
writer.writeCloseTag(PriorParsers.NORMAL_PRIOR);
break;
case LOGNORMAL_PRIOR:
writer.writeOpenTag(PriorParsers.LOG_NORMAL_PRIOR,
new Attribute[]{
new Attribute.Default<String>(PriorParsers.MEAN, "" + parameter.mean),
new Attribute.Default<String>(PriorParsers.STDEV, "" + parameter.stdev),
new Attribute.Default<String>(PriorParsers.OFFSET, "" + parameter.offset),
new Attribute.Default<Boolean>(PriorParsers.MEAN_IN_REAL_SPACE, parameter.isMeanInRealSpace())
});
writeParameterIdref(writer, parameter);
writer.writeCloseTag(PriorParsers.LOG_NORMAL_PRIOR);
break;
case GAMMA_PRIOR:
writer.writeOpenTag(PriorParsers.GAMMA_PRIOR,
new Attribute[]{
new Attribute.Default<String>(PriorParsers.SHAPE, "" + parameter.shape),
new Attribute.Default<String>(PriorParsers.SCALE, "" + parameter.scale),
new Attribute.Default<String>(PriorParsers.OFFSET, "" + parameter.offset)
});
writeParameterIdref(writer, parameter);
writer.writeCloseTag(PriorParsers.GAMMA_PRIOR);
break;
case INVERSE_GAMMA_PRIOR:
writer.writeOpenTag(PriorParsers.INVGAMMA_PRIOR,
new Attribute[]{
new Attribute.Default<String>(PriorParsers.SHAPE, "" + parameter.shape),
new Attribute.Default<String>(PriorParsers.SCALE, "" + parameter.scale),
new Attribute.Default<String>(PriorParsers.OFFSET, "" + parameter.offset)
});
writeParameterIdref(writer, parameter);
writer.writeCloseTag(PriorParsers.INVGAMMA_PRIOR);
break;
case ONE_OVER_X_PRIOR:
writer.writeOpenTag(OneOnXPriorParser.ONE_ONE_X_PRIOR);
writeParameterIdref(writer, parameter);
writer.writeCloseTag(OneOnXPriorParser.ONE_ONE_X_PRIOR);
break;
case POISSON_PRIOR:
writer.writeOpenTag(PriorParsers.POISSON_PRIOR,
new Attribute[]{
new Attribute.Default<String>(PriorParsers.MEAN, "" + parameter.mean),
new Attribute.Default<String>(PriorParsers.OFFSET, "" + parameter.offset)
});
writeParameterIdref(writer, parameter);
writer.writeCloseTag(PriorParsers.POISSON_PRIOR);
break;
case BETA_PRIOR:
writer.writeOpenTag(PriorParsers.BETA_PRIOR,
new Attribute[]{
new Attribute.Default<String>(PriorParsers.SHAPE, "" + parameter.shape),
new Attribute.Default<String>(PriorParsers.SHAPEB, "" + parameter.shapeB),
new Attribute.Default<String>(PriorParsers.OFFSET, "" + parameter.offset)
});
writeParameterIdref(writer, parameter);
writer.writeCloseTag(PriorParsers.BETA_PRIOR);
break;
case CTMC_RATE_REFERENCE_PRIOR:
writer.writeOpenTag(CTMCScalePriorParser.MODEL_NAME);
writer.writeOpenTag(CTMCScalePriorParser.SCALEPARAMETER);
writeParameterIdref(writer, parameter);
writer.writeCloseTag(CTMCScalePriorParser.SCALEPARAMETER);
// Find correct tree for this rate parameter
PartitionTreeModel treeModel = null;
for (PartitionClockModel pcm : options.getPartitionClockModels()) {
if (pcm.getClockRateParameter() == parameter) {
for (AbstractPartitionData pd : options.getDataPartitions(pcm)) {
treeModel = pd.getPartitionTreeModel();
break;
}
}
}
if (treeModel == null) {
throw new IllegalArgumentException("No tree model found for clock model");
}
writer.writeIDref(TreeModel.TREE_MODEL, treeModel.getPrefix() + TreeModel.TREE_MODEL);
writer.writeCloseTag(CTMCScalePriorParser.MODEL_NAME);
break;
case NORMAL_HPM_PRIOR:
case LOGNORMAL_HPM_PRIOR:
// Do nothing, densities are already in a distributionLikelihood
break;
case DIRICHLET_PRIOR:
// at the moment I don't think we want anything other than Dirichlet(1, ..., 1)
// int dimensions = parameter.getParameterDimensionWeights().length;
// String counts = "1.0";
// for (int i = 1; i < dimensions; i++) {
// counts += " 1.0";
// }
writer.writeOpenTag(PriorParsers.DIRICHLET_PRIOR,
new Attribute[]{
new Attribute.Default<String>(PriorParsers.ALPHA, "1.0"),
new Attribute.Default<Double>(PriorParsers.SUMS_TO, parameter.maintainedSum)
}
);
writeParameterIdref(writer, parameter);
writer.writeCloseTag(PriorParsers.DIRICHLET_PRIOR);
break;
default:
throw new IllegalArgumentException("Unknown priorType");
}
}
private void writeParameterIdref(XMLWriter writer, Parameter parameter) {
if (parameter.isStatistic) {
writer.writeIDref("statistic", parameter.getName());
} else {
writer.writeIDref(ParameterParser.PARAMETER, parameter.getName());
}
}
}