/*
* MutableTree.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.evolution.tree;
import dr.evolution.util.MutableTaxonList;
import dr.math.MathUtils;
/**
* Interface for a phylogenetic or genealogical tree.
*
* @version $Id: MutableTree.java,v 1.22 2006/07/28 11:27:32 rambaut Exp $
*
* @author Andrew Rambaut
* @author Alexei Drummond
*/
public interface MutableTree extends Tree, MutableTaxonList {
public class InvalidTreeException extends Exception {
/**
*
*/
private static final long serialVersionUID = 1955744780140327882L;
public InvalidTreeException(String message) { super(message); }
}
// return true if tree already in edit mode
boolean beginTreeEdit();
void endTreeEdit();
/**
* Add child to the children of parent.
* @throws IllegalArgumentException If child is already a child of parent
*/
void addChild(NodeRef parent, NodeRef child);
/**
* Removes child from the children of parent.
* @throws IllegalArgumentException If child is not a child of parent
*/
void removeChild(NodeRef parent, NodeRef child);
/**
* Replace child with another
* @param node
* @param child of node to replace
* @param newChild replacment child
*/
void replaceChild(NodeRef node, NodeRef child, NodeRef newChild);
/**
* Will throw an exception if any nodes have this node as their children.
*/
void setRoot(NodeRef root);
/**
* set the height of the ith node in the tree (where the first n are internal).
*/
void setNodeHeight(NodeRef node, double height);
/**
* set the rate of the ith node in the tree (where the first n are internal).
*/
void setNodeRate(NodeRef node, double height);
/**
* set the length of the branch above the ith node in the tree (where the first n are internal).
*/
void setBranchLength(NodeRef node, double length);
/**
* Sets an named attribute for a given node.
* @param node the node whose attribute is being set.
* @param name the name of the attribute.
* @param value the new value of the attribute.
*/
public void setNodeAttribute(NodeRef node, String name, Object value);
/**
* Adds a listener to this tree.
*/
void addMutableTreeListener(MutableTreeListener listener);
public class Utils {
public static int order(MutableTree tree, NodeRef node) {
if (tree.isExternal(node)) {
return node.getNumber();
} else {
NodeRef child1 = tree.getChild(node, 0);
NodeRef child2 = tree.getChild(node, 1);
int num1 = order(tree, child1);
int num2 = order(tree, child2);
if (num1 > num2) {
// swap child order
tree.removeChild(node, child1);
tree.removeChild(node, child2);
tree.addChild(node, child2);
tree.addChild(node, child1);
}
return Math.min(num1, num2);
}
}
/**
* Multiples all node heights by the given scale.
*/
public static void scaleNodeHeights(MutableTree tree, double scale) {
for (int i = 0; i < tree.getExternalNodeCount(); i++) {
NodeRef node = tree.getExternalNode(i);
tree.setNodeHeight(node, tree.getNodeHeight(node)*scale);
}
for (int i = 0; i < tree.getInternalNodeCount(); i++) {
NodeRef node = tree.getInternalNode(i);
tree.setNodeHeight(node, tree.getNodeHeight(node)*scale);
}
}
/**
* This method makes sure all internal nodes are at least as tall as their children.
* Following this method call there will be no negative branches, but some node heights may
* have changed
*/
public static void correctHeightsForTips(MutableTree tree) {
correctHeightsForTips(tree, tree.getRoot());
}
/**
* This method makes sure all internal nodes from the given node down are at least as tall
* as their children.
* Following this method call there will be no negative branches, but some node heights may
* have changed
*/
private static void correctHeightsForTips(MutableTree tree, NodeRef node) {
if( !tree.isExternal(node) ) {
// pre-order recursion
for(int i = 0; i < tree.getChildCount(node); i++) {
correctHeightsForTips(tree, tree.getChild(node, i));
}
}
if( !tree.isRoot(node) ) {
final double parentHeight = tree.getNodeHeight(tree.getParent(node));
if( parentHeight <= tree.getNodeHeight(node) ) {
// set the parent height to be slightly above this node's height
// picks
double height = tree.getNodeHeight(node);
height += tree.getNodeHeight(tree.getRoot()) * (MathUtils.nextDouble() * 0.001);
tree.setNodeHeight(tree.getParent(node), height);
}
}
}
}
}