/*
* CoalescentSimulator.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.evomodel.coalescent;
import dr.evolution.tree.*;
import dr.evolution.util.Date;
import dr.evolution.util.Taxon;
import dr.evolution.util.TaxonList;
import dr.evolution.util.TimeScale;
import dr.inference.distribution.ParametricDistributionModel;
import dr.math.UnivariateFunction;
/**
* Simulates a set of coalescent intervals given a demographic model.
*
* @author Alexei Drummond
* @version $Id: CoalescentSimulator.java,v 1.43 2005/10/27 10:40:48 rambaut Exp $
*/
public class CoalescentSimulator {
dr.evolution.coalescent.CoalescentSimulator simulator = new dr.evolution.coalescent.CoalescentSimulator();
/**
* Simulates a coalescent tree from a set of subtrees.
*/
public CoalescentSimulator() {
}
/**
* Simulates a coalescent tree from a set of subtrees.
*
* @param subtrees an array of tree to be used as subtrees
* @param model the demographic model to use
* @param rootHeight an optional root height with which to scale the whole tree
* @param preserveSubtreesHeights true if heights of subtrees should be preserved
* @return a simulated coalescent tree
*/
public SimpleTree simulateTree(Tree[] subtrees, DemographicModel model, double rootHeight,
boolean preserveSubtreesHeights) {
SimpleNode[] roots = new SimpleNode[subtrees.length];
SimpleTree tree;
for (int i = 0; i < roots.length; i++) {
roots[i] = new SimpleNode(subtrees[i], subtrees[i].getRoot());
}
// if just one taxonList then finished
if (roots.length == 1) {
tree = new SimpleTree(roots[0]);
} else {
tree = new SimpleTree(simulator.simulateCoalescent(roots, model.getDemographicFunction()));
}
if (!Double.isNaN(rootHeight) && rootHeight > 0.0) {
if (preserveSubtreesHeights) {
limitNodes(tree, rootHeight - 1e-12);
tree.setRootHeight(rootHeight);
} else {
attemptToScaleTree(tree, rootHeight);
}
}
return tree;
}
/**
* Simulates a coalescent tree, given a taxon list.
*
* @param taxa the set of taxa to simulate a coalescent tree between
* @param model the demographic model to use
* @return a simulated coalescent tree
*/
public SimpleTree simulateTree(TaxonList taxa, DemographicModel model) {
return simulator.simulateTree(taxa, model.getDemographicFunction());
}
public void attemptToScaleTree(MutableTree tree, double rootHeight) {
// avoid empty tree
if (tree.getRoot() == null) return;
double scale = rootHeight / tree.getNodeHeight(tree.getRoot());
for (int i = 0; i < tree.getInternalNodeCount(); i++) {
NodeRef n = tree.getInternalNode(i);
tree.setNodeHeight(n, tree.getNodeHeight(n) * scale);
}
MutableTree.Utils.correctHeightsForTips(tree);
}
public int sizeOfIntersection(TaxonList tl1, TaxonList tl2) {
int nIn = 0;
for (int j = 0; j < tl1.getTaxonCount(); ++j) {
if (tl2.getTaxonIndex(tl1.getTaxon(j)) >= 0) {
++nIn;
}
}
return nIn;
}
public boolean contained(TaxonList taxons, TaxonList taxons1) {
return sizeOfIntersection(taxons, taxons1) == taxons.getTaxonCount();
}
/**
* Clip nodes height above limit.
*
* @param tree to clip
* @param limit height limit
*/
private void limitNodes(MutableTree tree, double limit) {
for (int i = 0; i < tree.getInternalNodeCount(); i++) {
final NodeRef n = tree.getInternalNode(i);
if (tree.getNodeHeight(n) > limit) {
tree.setNodeHeight(n, limit);
}
}
MutableTree.Utils.correctHeightsForTips(tree);
}
public static class TaxaConstraint {
public final TaxonList taxons;
public final double lower;
public final boolean isMonophyletic;
public double upper;
public TaxaConstraint(TaxonList taxons, ParametricDistributionModel p, boolean isMono) {
this.taxons = taxons;
this.isMonophyletic = isMono;
if (p != null) {
final UnivariateFunction univariateFunction = p.getProbabilityDensityFunction();
lower = univariateFunction.getLowerBound();
upper = univariateFunction.getUpperBound();
} else {
lower = 0;
upper = Double.POSITIVE_INFINITY;
}
}
public TaxaConstraint(TaxonList taxons, double low, double high, boolean isMono) {
this.taxons = taxons;
this.isMonophyletic = isMono;
upper = high;
lower = low;
}
public boolean realLimits() {
return lower != 0 || upper != Double.POSITIVE_INFINITY;
}
}
}