/*
* Generator.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.util.XMLWriter;
import dr.evoxml.MergePatternsParser;
import dr.evoxml.SitePatternsParser;
import dr.inference.distribution.ExponentialDistributionModel;
import dr.inference.distribution.GammaDistributionModel;
import dr.inference.model.ParameterParser;
import dr.inferencexml.distribution.DistributionModelParser;
import dr.inferencexml.distribution.LogNormalDistributionModelParser;
import dr.inferencexml.distribution.NormalDistributionModelParser;
import dr.inferencexml.distribution.UniformDistributionModelParser;
import dr.util.Attribute;
import dr.xml.XMLParser;
import java.util.ArrayList;
import java.util.List;
/**
* @author Alexei Drummond
* @author Andrew Rambaut
* @author Walter Xie
*/
public abstract class Generator {
protected static final String COALESCENT = "coalescent";
public static final String SP_TREE = "sptree";
protected static final String SP_START_TREE = "spStartingTree";
protected static final String SPECIATION_LIKE = "speciation.likelihood";
public static final String SPLIT_POPS = "splitPopSize";
protected static final String PDIST = "pdist";
// protected static final String STP = "stp";
protected static final String SPOPS = TraitData.TRAIT_SPECIES + "." + "popSizesLikelihood";
protected final BeautiOptions options;
// protected PartitionSubstitutionModel model;
protected String modelPrefix = ""; // model prefix, could be PSM, PCM, PTM, PTP
protected Generator(BeautiOptions options) {
this.options = options;
}
public Generator(BeautiOptions options, ComponentFactory[] components) {
this.options = options;
if (components != null) {
for (ComponentFactory component : components) {
this.components.add(component.createGenerator(options));
}
}
}
public final void checkComponentOptions() throws GeneratorException {
for (ComponentGenerator component : components) {
component.checkOptions();
}
}
public String getModelPrefix() {
return modelPrefix;
}
public void setModelPrefix(String modelPrefix) {
this.modelPrefix = modelPrefix;
}
/**
* fix a parameter
*
* @param id the id
* @param value the value
*/
// public void fixParameter(Parameter parameter, double value) {
//// dr.app.beauti.options.Parameter parameter = options.getParameter(id);
// if (parameter == null) {
// throw new IllegalArgumentException("parameter with name, " + parameter.getName() + ", is unknown");
// }
// parameter.isFixed = true;
// parameter.initial = value;
// }
/**
* write a parameter
*
* @param wrapperName wrapperName
* @param id the id
* @param writer the writer
*/
public void writeParameterRef(String wrapperName, String id, XMLWriter writer) {
writer.writeOpenTag(wrapperName);
writer.writeIDref(ParameterParser.PARAMETER, id);
writer.writeCloseTag(wrapperName);
}
public void writeParameterRef(String id, XMLWriter writer) {
writer.writeIDref(ParameterParser.PARAMETER, id);
}
/**
* write a parameter
*
* @param id the id
* @param options PartitionOptions
* @param writer the writer
*/
public void writeParameter(String id, PartitionOptions options, XMLWriter writer) {
Parameter parameter = options.getParameter(id);
String prefix = options.getPrefix();
if (parameter == null) {
throw new IllegalArgumentException("parameter with name, " + id + ", is unknown; and its prefix is " + options.getPrefix());
}
writeParameter(prefix + id, parameter, writer);
}
public void writeParameter(int num, String id, PartitionSubstitutionModel model, XMLWriter writer) {
Parameter parameter = model.getParameter(model.getPrefixCodon(num) + id);
String prefix = model.getPrefix(num);
if (parameter == null) {
throw new IllegalArgumentException("parameter with name, " + id + ", is unknown; and its prefix is " + model.getPrefix());
}
writeParameter(prefix + id, parameter, writer);
}
/**
* write a parameter
*
* @param wrapperName wrapperName
* @param id the id
* @param dimension dimension
* @param writer the writer
*/
public void writeParameter(String wrapperName, String id, int dimension, XMLWriter writer) {
Parameter parameter = options.getParameter(id);
writer.writeOpenTag(wrapperName);
writeParameter(parameter, dimension, writer);
writer.writeCloseTag(wrapperName);
}
/**
* write a parameter
*
* @param num num
* @param wrapperName wrapperName
* @param id the id
* @param model PartitionSubstitutionModel
* @param writer the writer
*/
public void writeParameter(int num, String wrapperName, String id, PartitionSubstitutionModel model, XMLWriter writer) {
writer.writeOpenTag(wrapperName);
writeParameter(num, id, model, writer);
writer.writeCloseTag(wrapperName);
}
public void writeParameter(String wrapperName, String id, PartitionOptions options, XMLWriter writer) {
writer.writeOpenTag(wrapperName);
writeParameter(id, options, writer);
writer.writeCloseTag(wrapperName);
}
/**
* write a parameter
*
* @param parameter the parameter
* @param dimension the dimension
* @param writer the writer
*/
public void writeParameter(Parameter parameter, int dimension, XMLWriter writer) {
// Parameter parameter = options.getParameter(id);
if (parameter == null) {
throw new IllegalArgumentException("parameter (== null) is unknown");
}
if (parameter.isFixed()) { // with prefix
writeParameter(parameter.getName(), dimension, parameter.getInitial(), Double.NaN, Double.NaN, writer);
} else {
double lower = Double.NaN;
double upper = Double.NaN;
if (parameter.isNonNegative) {
lower = 0.0;
}
if (parameter.isZeroOne) {
lower = 0.0;
upper = 1.0;
}
writeParameter(parameter.getName(), dimension, parameter.getInitial(), lower, upper, writer);
}
}
/**
* write a parameter
*
* @param parameterId the parameter name/id
* @param parameterColumn the parameter column from which the samples are taken
* @param fileName the file from which the samples are taken
* @param burnin the number of samples to be discarded
* @param writer the writer
*/
public void writeParameter(String parameterId, String parameterColumn, String fileName, int burnin, XMLWriter writer) {
ArrayList<Attribute.Default> attributes = new ArrayList<Attribute.Default>();
attributes.add(new Attribute.Default<String>(XMLParser.ID, parameterId));
attributes.add(new Attribute.Default<String>("parameterColumn", parameterColumn));
attributes.add(new Attribute.Default<String>("fileName", fileName));
attributes.add(new Attribute.Default<String>("burnin", "" + burnin));
Attribute[] attrArray = new Attribute[attributes.size()];
for (int i = 0; i < attrArray.length; i++) {
attrArray[i] = attributes.get(i);
}
writer.writeTag(ParameterParser.PARAMETER, attrArray, true);
}
public void writeParameter(String id, Parameter parameter, XMLWriter writer) {
if (parameter.isFixed()) {
writeParameter(id, 1, parameter.getInitial(), Double.NaN, Double.NaN, writer);
} else {
double lower = Double.NaN;
double upper = Double.NaN;
if (parameter.isNonNegative) {
lower = 0.0;
}
if (parameter.isZeroOne) {
lower = 0.0;
upper = 1.0;
}
writeParameter(id, 1, parameter.getInitial(), lower, upper, writer);
}
}
/**
* write a parameter
*
* @param id the id
* @param dimension the dimension
* @param value the value
* @param lower the lower bound
* @param upper the upper bound
* @param writer the writer
*/
public void writeParameter(String id, int dimension, double value, double lower, double upper, XMLWriter writer) {
ArrayList<Attribute.Default> attributes = new ArrayList<Attribute.Default>();
if (id != null && id.length() > 0) {
attributes.add(new Attribute.Default<String>(XMLParser.ID, id));
}
if (dimension > 1) {
attributes.add(new Attribute.Default<String>(ParameterParser.DIMENSION, dimension + ""));
}
if (!Double.isNaN(value)) {
attributes.add(new Attribute.Default<String>(ParameterParser.VALUE, multiDimensionValue(dimension, value)));
}
if (!Double.isNaN(lower)) {
attributes.add(new Attribute.Default<String>(ParameterParser.LOWER, multiDimensionValue(dimension, lower)));
}
if (!Double.isNaN(upper)) {
attributes.add(new Attribute.Default<String>(ParameterParser.UPPER, multiDimensionValue(dimension, upper)));
}
Attribute[] attrArray = new Attribute[attributes.size()];
for (int i = 0; i < attrArray.length; i++) {
attrArray[i] = attributes.get(i);
}
writer.writeTag(ParameterParser.PARAMETER, attrArray, true);
}
/**
* write a parameter
*
* @param wrapperName wrapperName
* @param id the id
* @param dimension the dimension
* @param value the value
* @param lower the lower bound
* @param upper the upper bound
* @param writer the writer
*/
public void writeParameter(String wrapperName, String id, int dimension, double value, double lower, double upper, XMLWriter writer) {
writer.writeOpenTag(wrapperName);
writeParameter(id, dimension, value, lower, upper, writer);
writer.writeCloseTag(wrapperName);
}
protected void writeCodonPatternsRef(String prefix, int num, int CodonPartitionCount, XMLWriter writer) {
if (CodonPartitionCount == 2 && num == 1) { // "11" of "112", num start from 1
writer.writeIDref(MergePatternsParser.MERGE_PATTERNS, prefix + SitePatternsParser.PATTERNS);
} else { // "2" of "112" and "123"
writer.writeIDref(SitePatternsParser.PATTERNS, prefix + SitePatternsParser.PATTERNS);
}
}
private String multiDimensionValue(int dimension, double value) {
String multi = "";
multi += value + "";
// AR: A multidimensional parameter only needs to give initial values for every dimension
// if they are actually different. A single value will automatically be expanded to every
// dimension and make for a cleaner looking XML (and more robust to changes in the number
// of groups/taxa etc.
// for (int i = 2; i <= dimension; i++)
// multi += " " + value;
return multi;
}
/**
* Write the distribution for *DistributionModel
*
* @param parameter the parameter
* @param isRef only work for uniform dist
* @param writer the writer
*/
protected void writeDistribution(Parameter parameter, boolean isRef, XMLWriter writer) {
switch (parameter.priorType) {
case UNIFORM_PRIOR:
String id = parameter.taxaId + "-uniformDist";
if (isRef) {
writer.writeIDref(UniformDistributionModelParser.UNIFORM_DISTRIBUTION_MODEL, id);
} else {
writer.writeOpenTag(UniformDistributionModelParser.UNIFORM_DISTRIBUTION_MODEL,
new Attribute[]{
new Attribute.Default<String>(XMLParser.ID, id)
});
writer.writeOpenTag(UniformDistributionModelParser.LOWER);
writer.writeText(Double.toString(parameter.uniformLower));
writer.writeCloseTag(UniformDistributionModelParser.LOWER);
writer.writeOpenTag(UniformDistributionModelParser.UPPER);
writer.writeText(Double.toString(parameter.uniformUpper));
writer.writeCloseTag(UniformDistributionModelParser.UPPER);
writer.writeCloseTag(UniformDistributionModelParser.UNIFORM_DISTRIBUTION_MODEL);
}
break;
case EXPONENTIAL_PRIOR:
writer.writeOpenTag(ExponentialDistributionModel.EXPONENTIAL_DISTRIBUTION_MODEL);
writer.writeOpenTag(DistributionModelParser.MEAN);
writer.writeText(Double.toString(parameter.mean));
writer.writeCloseTag(DistributionModelParser.MEAN);
writer.writeOpenTag(DistributionModelParser.OFFSET);
writer.writeText(Double.toString(parameter.offset));
writer.writeCloseTag(DistributionModelParser.OFFSET);
writer.writeCloseTag(ExponentialDistributionModel.EXPONENTIAL_DISTRIBUTION_MODEL);
break;
case NORMAL_PRIOR:
writer.writeOpenTag(NormalDistributionModelParser.NORMAL_DISTRIBUTION_MODEL);
writer.writeOpenTag(NormalDistributionModelParser.MEAN);
writer.writeText(Double.toString(parameter.mean));
writer.writeCloseTag(NormalDistributionModelParser.MEAN);
writer.writeOpenTag(NormalDistributionModelParser.STDEV);
writer.writeText(Double.toString(parameter.stdev));
writer.writeCloseTag(NormalDistributionModelParser.STDEV);
writer.writeCloseTag(NormalDistributionModelParser.NORMAL_DISTRIBUTION_MODEL);
break;
case LOGNORMAL_PRIOR:
writer.writeOpenTag(LogNormalDistributionModelParser.LOGNORMAL_DISTRIBUTION_MODEL,
new Attribute[]{
new Attribute.Default<Boolean>(LogNormalDistributionModelParser.MEAN_IN_REAL_SPACE, parameter.isMeanInRealSpace()),
new Attribute.Default<Boolean>(LogNormalDistributionModelParser.STDEV_IN_REAL_SPACE, parameter.isMeanInRealSpace())
});
writer.writeOpenTag(LogNormalDistributionModelParser.MEAN);
writer.writeText(Double.toString(parameter.mean));
writer.writeCloseTag(LogNormalDistributionModelParser.MEAN);
writer.writeOpenTag(LogNormalDistributionModelParser.STDEV);
writer.writeText(Double.toString(parameter.stdev));
writer.writeCloseTag(LogNormalDistributionModelParser.STDEV);
writer.writeOpenTag(LogNormalDistributionModelParser.OFFSET);
writer.writeText(Double.toString(parameter.offset));
writer.writeCloseTag(LogNormalDistributionModelParser.OFFSET);
writer.writeCloseTag(LogNormalDistributionModelParser.LOGNORMAL_DISTRIBUTION_MODEL);
break;
case GAMMA_PRIOR:
writer.writeOpenTag(GammaDistributionModel.GAMMA_DISTRIBUTION_MODEL);
writer.writeOpenTag(DistributionModelParser.SHAPE);
writer.writeText(Double.toString(parameter.shape));
writer.writeCloseTag(DistributionModelParser.SHAPE);
writer.writeOpenTag(DistributionModelParser.SCALE);
writer.writeText(Double.toString(parameter.scale));
writer.writeCloseTag(DistributionModelParser.SCALE);
writer.writeOpenTag(DistributionModelParser.OFFSET);
writer.writeText(Double.toString(parameter.offset));
writer.writeCloseTag(DistributionModelParser.OFFSET);
writer.writeCloseTag(GammaDistributionModel.GAMMA_DISTRIBUTION_MODEL);
break;
default:
throw new IllegalArgumentException("Unknown Distribution Model for " + parameter.getName());
}
}
public void writeReferenceComment(String[] lines, XMLWriter writer) {
for (String line : lines)
writer.writeComment(line);
}
public void generateInsertionPoint(final ComponentGenerator.InsertionPoint ip, final XMLWriter writer) {
generateInsertionPoint(ip, null, writer);
}
public void generateInsertionPoint(final ComponentGenerator.InsertionPoint ip, final Object item, final XMLWriter writer) {
for (ComponentGenerator component : components) {
if (component.usesInsertionPoint(ip)) {
component.generateAtInsertionPoint(this, ip, item, writer);
}
}
}
public void generateInsertionPoint(final ComponentGenerator.InsertionPoint ip, final Object item, final String prefix, final XMLWriter writer) {
for (ComponentGenerator component : components) {
if (component.usesInsertionPoint(ip)) {
component.generateAtInsertionPoint(this, ip, item, prefix, writer);
}
}
}
private final List<ComponentGenerator> components = new ArrayList<ComponentGenerator>();
public class GeneratorException extends Exception {
public GeneratorException(String message) {
super(message);
switchToPanel = null;
}
public GeneratorException(String message, String switchToPanel) {
super(message);
this.switchToPanel = switchToPanel;
}
public String getSwitchToPanel() {
return switchToPanel;
}
private final String switchToPanel;
}
}