/*
* RescaledTreeParser.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.evoxml;
import dr.evolution.tree.*;
import dr.evolution.util.Taxon;
import dr.evolution.util.TaxonList;
import dr.xml.*;
import java.util.HashSet;
import java.util.Set;
/**
* Parser takes a tree and rescales the node heights to match a set of clade heights defined by taxon sets.
* @author Andrew Rambaut
*
* @version $Id$
*/
public class RescaledTreeParser extends AbstractXMLObjectParser {
//
// Public stuff
//
public final static String RESCALED_TREE = "rescaledTree";
public final static String CLADE = "clade";
public final static String HEIGHT = "height";
public String getParserName() { return RESCALED_TREE; }
public Object parseXMLObject(XMLObject xo) throws XMLParseException {
Tree tree = (Tree)xo.getChild(Tree.class);
// make a mutable copy
SimpleTree rescaledTree = new SimpleTree(tree);
// First flag all internal nodes as unset....
for (int i = 0; i < rescaledTree.getInternalNodeCount(); i++) {
rescaledTree.setNodeHeight(rescaledTree.getInternalNode(i), Double.NEGATIVE_INFINITY);
}
for (int i = 0; i < xo.getChildCount(); i++) {
if (xo.getChild(i) instanceof XMLObject) {
XMLObject cxo = (XMLObject)xo.getChild(i);
if (cxo.getName().equals(CLADE)) {
TaxonList taxa = (TaxonList)cxo.getChild(TaxonList.class);
Set<String> leafSet = new HashSet<String>();
for (Taxon taxon : taxa) {
leafSet.add(taxon.getId());
}
NodeRef mrca = TreeUtils.getCommonAncestorNode(rescaledTree, leafSet);
if (mrca == null || TreeUtils.getLeafCount(rescaledTree, mrca) != leafSet.size()) {
throw new XMLParseException("Clade defined by taxon Set, " + taxa.getId() + ", is not found in the guide tree");
}
if (cxo.hasAttribute(HEIGHT)) {
double height = cxo.getDoubleAttribute(HEIGHT);
rescaledTree.setNodeHeight(mrca, height);
}
}
}
}
if (xo.hasAttribute(HEIGHT)) {
rescaledTree.setNodeHeight(rescaledTree.getRoot(), xo.getDoubleAttribute(HEIGHT));
}
interpolateHeights(rescaledTree, rescaledTree.getRoot());
return rescaledTree;
}
private double interpolateHeights(MutableTree tree, NodeRef node) {
if (!tree.isExternal(node)) {
double maxHeight = Double.NEGATIVE_INFINITY;
for (int i = 0; i < tree.getChildCount(node); i++) {
NodeRef child = tree.getChild(node, i);
double h = interpolateHeights(tree, child);
if (h > maxHeight) {
maxHeight = h;
}
}
double height = tree.getNodeHeight(node);
if (Double.isInfinite(height)) {
int count = 1;
NodeRef parent = tree.getParent(node);
while (parent != null && Double.isInfinite(tree.getNodeHeight(parent))) {
parent = tree.getParent(parent);
count ++;
}
if (parent == null) {
height = maxHeight + count;
} else {
height = (tree.getNodeHeight(parent) + maxHeight) / 2.0;
}
}
tree.setNodeHeight(node, height);
return height;
} else {
return tree.getNodeHeight(node);
}
}
public String getParserDescription() {
return "This element rescales a given tree with a set of clade heights.";
}
public Class getReturnType() { return Tree.class; }
public XMLSyntaxRule[] getSyntaxRules() { return rules; }
private XMLSyntaxRule[] rules = new XMLSyntaxRule[] {
AttributeRule.newDoubleRule(HEIGHT, true),
new ElementRule(Tree.class),
new ElementRule(CLADE, new XMLSyntaxRule[] {
AttributeRule.newDoubleRule(HEIGHT, true),
new ElementRule(TaxonList.class)
}, 0, Integer.MAX_VALUE)
};
}