/*
* BeautiTemplate.java
*
* Copyright (C) 2002-2006 Alexei Drummond and Andrew Rambaut
*
* 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.util;
import dr.app.beast.BeastVersion;
import dr.app.beauti.types.PriorType;
import dr.app.beauti.options.BeautiOptions;
import dr.app.beauti.options.ModelOptions;
import dr.app.beauti.options.Operator;
import dr.app.beauti.options.Parameter;
import dr.evolution.util.Date;
import dr.evolution.util.Taxa;
import dr.evolution.util.Units;
import dr.evoxml.TaxaParser;
import dr.evoxml.TaxonParser;
import dr.xml.XMLParser;
import org.jdom.Document;
import org.jdom.Element;
/**
* @author Andrew Rambaut
* @author Alexei Drummond
* @author Walter Xie
* @version $Id: BeautiTemplate.java, rambaut Exp $
*/
public class BeautiTemplate extends ModelOptions {
private final BeautiOptions options;
public BeautiTemplate(BeautiOptions options) {
this.options = options;
}
/**
* Write options from a file
*
* @param guessDates guess dates?
* @return the Document
*/
public Document create(boolean guessDates) {
final BeastVersion version = new BeastVersion();
Element root = new Element("beauti");
root.setAttribute("version", version.getVersion());
Element dataElement = new Element("data");
// dataElement.addContent(createChild("fileNameStem", fileNameStem));
// dataElement.addContent(createChild("datesUnits", options.datesUnits));
// dataElement.addContent(createChild("datesDirection", options.datesDirection));
dataElement.addContent(createChild("translation", options.translation));
//TODO:
// dataElement.addContent(createChild("startingTreeType", startingTreeType.name()));
dataElement.addContent(createChild("guessDates", guessDates));
dataElement.addContent(createChild("guessType", options.dateGuesser.guessType.name()));
dataElement.addContent(createChild("fromLast", options.dateGuesser.fromLast));
dataElement.addContent(createChild("order", options.dateGuesser.order));
dataElement.addContent(createChild("prefix", options.dateGuesser.prefix));
dataElement.addContent(createChild("offset", options.dateGuesser.offset));
dataElement.addContent(createChild("unlessLessThan", options.dateGuesser.unlessLessThan));
dataElement.addContent(createChild("offset2", options.dateGuesser.offset2));
root.addContent(dataElement);
Element taxaElement = new Element(TaxaParser.TAXA);
for (Taxa taxonSet : options.taxonSets) {
Element taxonSetElement = new Element("taxonSet");
taxonSetElement.addContent(createChild(XMLParser.ID, taxonSet.getId()));
taxonSetElement.addContent(createChild("enforceMonophyly",
options.taxonSetsMono.get(taxonSet) ? "true" : "false"));
for (int j = 0; j < taxonSet.getTaxonCount(); j++) {
Element taxonElement = new Element(TaxonParser.TAXON);
taxonElement.addContent(createChild(XMLParser.ID, taxonSet.getTaxon(j).getId()));
taxonSetElement.addContent(taxonElement);
}
taxaElement.addContent(taxonSetElement);
}
root.addContent(taxaElement);
// for (PartitionSubstitutionModel model : partitionModels) {
//
// Element modelElement = new Element("model");
//
// /*modelElement.addContent(createChild("nucSubstitutionModel", nucSubstitutionModel));
// modelElement.addContent(createChild("aaSubstitutionModel", aaSubstitutionModel));
// modelElement.addContent(createChild("binarySubstitutionModel", binarySubstitutionModel));
// modelElement.addContent(createChild("frequencyPolicy", frequencyPolicy));
// modelElement.addContent(createChild("gammaHetero", gammaHetero));
// modelElement.addContent(createChild("gammaCategories", gammaCategories));
// modelElement.addContent(createChild("invarHetero", invarHetero));
// modelElement.addContent(createChild("codonHeteroPattern", codonHeteroPattern));
// modelElement.addContent(createChild("maximumTipHeight", maximumTipHeight));
// modelElement.addContent(createChild("hasSetFixedSubstitutionRate", hasSetFixedSubstitutionRate));
// modelElement.addContent(createChild("meanSubstitutionRate", meanSubstitutionRate));
// modelElement.addContent(createChild("fixedSubstitutionRate", fixedSubstitutionRate));
// modelElement.addContent(createChild("unlinkedSubstitutionModel", unlinkedSubstitutionModel));
// modelElement.addContent(createChild("unlinkedHeterogeneityModel", unlinkedHeterogeneityModel));
// modelElement.addContent(createChild("unlinkedFrequencyModel", unlinkedFrequencyModel));
// modelElement.addContent(createChild("clockModel", clockModel));
// modelElement.addContent(createChild("nodeHeightPrior", nodeHeightPrior));
// modelElement.addContent(createChild("parameterization", parameterization));
// modelElement.addContent(createChild("skylineGroupCount", skylineGroupCount));
// modelElement.addContent(createChild("skylineModel", skylineModel));
// modelElement.addContent(createChild("fixedTree", fixedTree)); */
//
// root.addContent(modelElement);
// }
Element priorsElement = new Element("priors");
for (String name : getParameters().keySet()) {
Parameter parameter = getParameters().get(name);
Element e = new Element(name);
e.addContent(createChild("initial", parameter.initial));
e.addContent(createChild("priorType", parameter.priorType));
e.addContent(createChild("priorEdited", parameter.isPriorEdited()));
// e.addContent(createChild("uniformLower", parameter.uniformLower));
// e.addContent(createChild("uniformUpper", parameter.uniformUpper));
// e.addContent(createChild("exponentialMean", parameter.exponentialMean));
// e.addContent(createChild("exponentialOffset", parameter.exponentialOffset));
// e.addContent(createChild("normalMean", parameter.normalMean));
// e.addContent(createChild("normalStdev", parameter.normalStdev));
// e.addContent(createChild("logNormalMean", parameter.logNormalMean));
// e.addContent(createChild("logNormalStdev", parameter.logNormalStdev));
// e.addContent(createChild("logNormalOffset", parameter.logNormalOffset));
// e.addContent(createChild("gammaAlpha", parameter.gammaAlpha));
// e.addContent(createChild("gammaBeta", parameter.gammaBeta));
// e.addContent(createChild("gammaOffset", parameter.gammaOffset));
priorsElement.addContent(e);
}
for (Taxa taxonSet : options.taxonSets) {
Parameter statistic = getStatistics().get(taxonSet);
Element e = new Element(statistic.getXMLName());
e.addContent(createChild("initial", statistic.initial));
e.addContent(createChild("priorType", statistic.priorType));
e.addContent(createChild("priorEdited", statistic.isPriorEdited()));
// e.addContent(createChild("uniformLower", statistic.uniformLower));
// e.addContent(createChild("uniformUpper", statistic.uniformUpper));
// e.addContent(createChild("exponentialMean", statistic.exponentialMean));
// e.addContent(createChild("exponentialOffset", statistic.exponentialOffset));
// e.addContent(createChild("normalMean", statistic.normalMean));
// e.addContent(createChild("normalStdev", statistic.normalStdev));
// e.addContent(createChild("logNormalMean", statistic.logNormalMean));
// e.addContent(createChild("logNormalStdev", statistic.logNormalStdev));
// e.addContent(createChild("logNormalOffset", statistic.logNormalOffset));
// e.addContent(createChild("gammaAlpha", statistic.gammaAlpha));
// e.addContent(createChild("gammaBeta", statistic.gammaBeta));
// e.addContent(createChild("gammaOffset", statistic.gammaOffset));
priorsElement.addContent(e);
}
root.addContent(priorsElement);
Element operatorsElement = new Element("operators");
operatorsElement.addContent(createChild("autoOptimize", options.autoOptimize));
for (String name : getOperators().keySet()) {
Operator operator = getOperators().get(name);
Element e = new Element(name);
e.addContent(createChild("tuning", operator.tuning));
e.addContent(createChild("tuningEdited", operator.tuningEdited));
e.addContent(createChild("weight", operator.weight));
e.addContent(createChild("inUse", operator.inUse));
operatorsElement.addContent(e);
}
root.addContent(operatorsElement);
Element mcmcElement = new Element("mcmc");
mcmcElement.addContent(createChild("chainLength", options.chainLength));
mcmcElement.addContent(createChild("logEvery", options.logEvery));
mcmcElement.addContent(createChild("echoEvery", options.echoEvery));
//if (logFileName != null) mcmcElement.addContent(createChild("logFileName", logFileName));
//if (treeFileName != null) mcmcElement.addContent(createChild("treeFileName", treeFileName));
//mcmcElement.addContent(createChild("mapTreeLog", mapTreeLog));
//if (mapTreeFileName != null) mcmcElement.addContent(createChild("mapTreeFileName", mapTreeFileName));
mcmcElement.addContent(createChild("substTreeLog", options.substTreeLog));
//if (substTreeFileName != null) mcmcElement.addContent(createChild("substTreeFileName", substTreeFileName));
root.addContent(mcmcElement);
return new Document(root);
}
private Element createChild(String name, String value) {
Element e = new Element(name);
if (value != null) {
e.setText(value);
}
return e;
}
private Element createChild(String name, int value) {
Element e = new Element(name);
e.setText(Integer.toString(value));
return e;
}
private Element createChild(String name, PriorType value) {
Element e = new Element(name);
e.setText(value.name());
return e;
}
private Element createChild(String name, double value) {
Element e = new Element(name);
e.setText(Double.toString(value));
return e;
}
private Element createChild(String name, boolean value) {
Element e = new Element(name);
e.setText(value ? "true" : "false");
return e;
}
/**
* Read options from a file
*
* @param document the Document
* @throws dr.xml.XMLParseException if there is a problem with XML parsing
*/
public void parse(Document document) throws dr.xml.XMLParseException {
Element root = document.getRootElement();
if (!root.getName().equals("beauti")) {
throw new dr.xml.XMLParseException("This document does not appear to be a BEAUti file");
}
Element taxaElement = root.getChild(TaxaParser.TAXA);
Element modelElement = root.getChild("model");
Element priorsElement = root.getChild("priors");
Element operatorsElement = root.getChild("operators");
Element mcmcElement = root.getChild("mcmc");
/*
if (taxaElement != null) {
for (Object ts : taxaElement.getChildren("taxonSet")) {
Element taxonSetElement = (Element) ts;
String id = getStringChild(taxonSetElement, XMLParser.ID, "");
final Taxa taxonSet = new Taxa(id);
Boolean enforceMonophyly = Boolean.valueOf(getStringChild(taxonSetElement, "enforceMonophyly", "false"));
for (Object o : taxonSetElement.getChildren("taxon")) {
Element taxonElement = (Element) o;
String taxonId = getStringChild(taxonElement, XMLParser.ID, "");
int index = taxonList.getTaxonIndex(taxonId);
if (index != -1) {
taxonSet.addTaxon(taxonList.getTaxon(index));
}
}
taxonSets.add(taxonSet);
taxonSetsMono.put(taxonSet, enforceMonophyly);
}
}
if (modelElement != null) {
nucSubstitutionModel = getIntegerChild(modelElement, "nucSubstitutionModel", HKY);
aaSubstitutionModel = getIntegerChild(modelElement, "aaSubstitutionModel", BLOSUM_62);
binarySubstitutionModel = getIntegerChild(modelElement, "binarySubstitutionModel", BIN_SIMPLE);
frequencyPolicy = getIntegerChild(modelElement, "frequencyPolicy", ESTIMATED);
gammaHetero = getBooleanChild(modelElement, "gammaHetero", false);
gammaCategories = getIntegerChild(modelElement, "gammaCategories", 5);
invarHetero = getBooleanChild(modelElement, "invarHetero", false);
codonHeteroPattern = (getBooleanChild(modelElement, "codonHetero", false) ? "123" : null);
codonHeteroPattern = getStringChild(modelElement, "codonHeteroPattern", null);
maximumTipHeight = getDoubleChild(modelElement, "maximumTipHeight", 0.0);
fixedSubstitutionRate = getBooleanChild(modelElement, "fixedSubstitutionRate", false);
hasSetFixedSubstitutionRate = getBooleanChild(modelElement, "hasSetFixedSubstitutionRate", false);
meanSubstitutionRate = getDoubleChild(modelElement, "meanSubstitutionRate", 1.0);
unlinkedSubstitutionModel = getBooleanChild(modelElement, "unlinkedSubstitutionModel", false);
unlinkedHeterogeneityModel = getBooleanChild(modelElement, "unlinkedHeterogeneityModel", false);
unlinkedFrequencyModel = getBooleanChild(modelElement, "unlinkedFrequencyModel", false);
clockModel = getIntegerChild(modelElement, "clockModel", clockModel);
// the old name was "coalescentModel" so try to read this first
nodeHeightPrior = getIntegerChild(modelElement, "coalescentModel", CONSTANT);
nodeHeightPrior = getIntegerChild(modelElement, "nodeHeightPrior", nodeHeightPrior);
// we don't allow no nodeHeightPrior in BEAUti so switch it to Yule:
if (nodeHeightPrior == NONE_TREE_PRIOR) nodeHeightPrior = YULE;
parameterization = getIntegerChild(modelElement, "parameterization", GROWTH_RATE);
skylineGroupCount = getIntegerChild(modelElement, "skylineGroupCount", 10);
skylineModel = getIntegerChild(modelElement, "skylineModel", CONSTANT_SKYLINE);
fixedTree = getBooleanChild(modelElement, "fixedTree", false);
}
if (operatorsElement != null) {
autoOptimize = getBooleanChild(operatorsElement, "autoOptimize", true);
for (String name : operators.keySet()) {
Operator operator = operators.get(name);
Element e = operatorsElement.getChild(name);
if (e == null) {
throw new XMLParseException("Operators element, " + name + " missing");
}
operator.tuning = getDoubleChild(e, "tuning", 1.0);
operator.tuningEdited = getBooleanChild(e, "tuningEdited", false);
operator.weight = getDoubleChild(e, "weight", 1);
operator.inUse = getBooleanChild(e, "inUse", true);
}
}
if (priorsElement != null) {
for (String name : parameters.keySet()) {
Parameter parameter = parameters.get(name);
Element e = priorsElement.getChild(name);
if (e == null) {
throw new XMLParseException("Priors element, " + name + " missing");
}
parameter.initial = getDoubleChild(e, "initial", 1.0);
parameter.priorType = PriorType.valueOf(getStringChild(e, "priorType", PriorType.UNIFORM_PRIOR.name()));
parameter.priorEdited = getBooleanChild(e, "priorEdited", false);
parameter.uniformLower = Math.max(getDoubleChild(e, "uniformLower", parameter.uniformLower), parameter.lower);
parameter.uniformUpper = Math.min(getDoubleChild(e, "uniformUpper", parameter.uniformUpper), parameter.upper);
parameter.exponentialMean = getDoubleChild(e, "exponentialMean", parameter.exponentialMean);
parameter.exponentialOffset = getDoubleChild(e, "exponentialOffset", parameter.exponentialOffset);
parameter.normalMean = getDoubleChild(e, "normalMean", parameter.normalMean);
parameter.normalStdev = getDoubleChild(e, "normalStdev", parameter.normalStdev);
parameter.logNormalMean = getDoubleChild(e, "logNormalMean", parameter.logNormalMean);
parameter.logNormalStdev = getDoubleChild(e, "logNormalStdev", parameter.logNormalStdev);
parameter.logNormalOffset = getDoubleChild(e, "logNormalOffset", parameter.logNormalOffset);
parameter.gammaAlpha = getDoubleChild(e, "gammaAlpha", parameter.gammaAlpha);
parameter.gammaBeta = getDoubleChild(e, "gammaBeta", parameter.gammaBeta);
parameter.gammaOffset = getDoubleChild(e, "gammaOffset", parameter.gammaOffset);
}
for (Taxa taxonSet : taxonSets) {
Parameter statistic = statistics.get(taxonSet);
if (statistic == null) {
statistic = new Parameter(this, taxonSet, "tMRCA for taxon set ");
statistics.put(taxonSet, statistic);
}
Element e = priorsElement.getChild(statistic.getXMLName());
statistic.initial = getDoubleChild(e, "initial", 1.0);
statistic.priorType = PriorType.valueOf(getStringChild(e, "priorType", PriorType.UNIFORM_PRIOR.name()));
statistic.priorEdited = getBooleanChild(e, "priorEdited", false);
statistic.uniformLower = getDoubleChild(e, "uniformLower", statistic.uniformLower);
statistic.uniformUpper = getDoubleChild(e, "uniformUpper", statistic.uniformUpper);
statistic.exponentialMean = getDoubleChild(e, "exponentialMean", statistic.exponentialMean);
statistic.exponentialOffset = getDoubleChild(e, "exponentialOffset", statistic.exponentialOffset);
statistic.normalMean = getDoubleChild(e, "normalMean", statistic.normalMean);
statistic.normalStdev = getDoubleChild(e, "normalStdev", statistic.normalStdev);
statistic.logNormalMean = getDoubleChild(e, "logNormalMean", statistic.logNormalMean);
statistic.logNormalStdev = getDoubleChild(e, "logNormalStdev", statistic.logNormalStdev);
statistic.logNormalOffset = getDoubleChild(e, "logNormalOffset", statistic.logNormalOffset);
statistic.gammaAlpha = getDoubleChild(e, "gammaAlpha", statistic.gammaAlpha);
statistic.gammaBeta = getDoubleChild(e, "gammaBeta", statistic.gammaBeta);
statistic.gammaOffset = getDoubleChild(e, "gammaOffset", statistic.gammaOffset);
}
}
if (mcmcElement != null) {
upgmaStartingTree = getBooleanChild(mcmcElement, "upgmaStartingTree", true);
chainLength = getIntegerChild(mcmcElement, "chainLength", 100000000);
logEvery = getIntegerChild(mcmcElement, "logEvery", 1000);
echoEvery = getIntegerChild(mcmcElement, "echoEvery", 1000);
logFileName = getStringChild(mcmcElement, "logFileName", null);
treeFileName = getStringChild(mcmcElement, "treeFileName", null);
mapTreeLog = getBooleanChild(mcmcElement, "mapTreeLog", false);
mapTreeFileName = getStringChild(mcmcElement, "mapTreeFileName", null);
substTreeLog = getBooleanChild(mcmcElement, "substTreeLog", false);
substTreeFileName = getStringChild(mcmcElement, "substTreeFileName", null);
} */
}
private String getStringChild(Element element, String childName, String defaultValue) {
String value = element.getChildTextTrim(childName);
if (value == null || value.length() == 0) return defaultValue;
return value;
}
private int getIntegerChild(Element element, String childName, int defaultValue) {
String value = element.getChildTextTrim(childName);
if (value == null) return defaultValue;
return Integer.parseInt(value);
}
private double getDoubleChild(Element element, String childName, double defaultValue) {
String value = element.getChildTextTrim(childName);
if (value == null) return defaultValue;
return Double.parseDouble(value);
}
private boolean getBooleanChild(Element element, String childName, boolean defaultValue) {
String value = element.getChildTextTrim(childName);
if (value == null) return defaultValue;
return value.equals("true");
}
private Date createDate(double timeValue, Units.Type units, boolean backwards, double origin) {
if (backwards) {
return Date.createTimeAgoFromOrigin(timeValue, units, origin);
} else {
return Date.createTimeSinceOrigin(timeValue, units, origin);
}
}
}