/* * FlexibleTree.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.MutableTaxonListListener; import dr.evolution.util.Taxon; import dr.util.Attributable; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; /** * data structure for binary rooted trees * * @author Andrew Rambaut * @author Alexei Drummond * @version $Id: FlexibleTree.java,v 1.34 2006/07/02 21:14:52 rambaut Exp $ */ public class FlexibleTree implements MutableTree { /** * Constructor tree with no nodes. Use adoptNodes to add some nodes. */ public FlexibleTree() { root = null; } /** * clone constructor */ public FlexibleTree(Tree tree) { this(tree, false); } /** * clone constructor */ public FlexibleTree(Tree tree, boolean copyAttributes) { setUnits(tree.getUnits()); root = new FlexibleNode(tree, tree.getRoot(), copyAttributes); nodeCount = tree.getNodeCount(); internalNodeCount = tree.getInternalNodeCount(); externalNodeCount = tree.getExternalNodeCount(); nodes = new FlexibleNode[nodeCount]; FlexibleNode node = root; do { node = (FlexibleNode) TreeUtils.postorderSuccessor(this, node); if ((node.getNumber() >= externalNodeCount && node.isExternal()) || (node.getNumber() < externalNodeCount && !node.isExternal())) { throw new RuntimeException("Error cloning tree: node numbers are incompatible"); } nodes[node.getNumber()] = node; } while (node != root); heightsKnown = tree.hasNodeHeights(); lengthsKnown = tree.hasBranchLengths(); } /** * clone constructor */ public FlexibleTree(FlexibleNode root) { this(root, true, true, null); } /** * clone constructor */ public FlexibleTree(FlexibleNode root, boolean heightsKnown, boolean lengthsKnown) { this(root, heightsKnown, lengthsKnown, null); } /** * clone constructor */ public FlexibleTree(FlexibleNode root, Map<Taxon, Integer> taxonNumberMap) { this(root, true, true, taxonNumberMap); } /** * clone constructor */ public FlexibleTree(FlexibleNode root, boolean heightsKnown, boolean lengthsKnown, Map<Taxon, Integer> taxonNumberMap) { adoptNodes(root, taxonNumberMap); this.heightsKnown = heightsKnown; this.lengthsKnown = lengthsKnown; } /** * @return a copy of this tree */ public Tree getCopy() { return new FlexibleTree(this); } public void adoptTreeModelOrdering() { resolveTree(); int i = 0; int j = externalNodeCount; FlexibleNode node = (FlexibleNode) getRoot(); do { node = (FlexibleNode) TreeUtils.postorderSuccessor(this, node); if (node.isExternal()) { node.setNumber(i); nodes[i] = node; i++; } else { node.setNumber(j); nodes[j] = node; j++; } } while (node != root); } /** * Adopt a node hierarchy as its own. Only called by the FlexibleTree(FlexibleNode, TaxonList). * This creates the node list and stores the nodes in post-traversal order. */ protected void adoptNodes(FlexibleNode node, Map<Taxon, Integer> taxonNumberMap) { if (inEdit) throw new RuntimeException("Mustn't be in an edit transaction to call this method!"); internalNodeCount = 0; externalNodeCount = 0; root = node; do { node = (FlexibleNode) TreeUtils.postorderSuccessor(this, node); if (node.isExternal()) { externalNodeCount++; } else internalNodeCount++; } while (node != root); nodeCount = internalNodeCount + externalNodeCount; //System.out.println("internal count = " + internalNodeCount); //System.out.println("external count = " + externalNodeCount); nodes = new FlexibleNode[nodeCount]; node = root; int i = 0; int j = externalNodeCount; do { node = (FlexibleNode) TreeUtils.postorderSuccessor(this, node); //System.out.print("node = " + node.getId() + " "); if (node.isExternal()) { if (taxonNumberMap != null && taxonNumberMap.size() > 0) { i = taxonNumberMap.get(node.getTaxon()); } node.setNumber(i); //System.out.println(" leaf number " + i); nodes[i] = node; if (taxonNumberMap == null || taxonNumberMap.size() == 0) { i++; } } else { node.setNumber(j); //System.out.println(" ancestor number " + j); nodes[j] = node; j++; } } while (node != root); } /** * Return the units that this tree is expressed in. */ public final Type getUnits() { return units; } /** * Sets the units that this tree is expressed in. */ public final void setUnits(Type units) { this.units = units; } /** * @return a count of the number of nodes (internal + external) in this * tree. */ public int getNodeCount() { return nodeCount; } public boolean hasNodeHeights() { return heightsKnown; } public double getNodeHeight(NodeRef node) { if (!heightsKnown) { calculateNodeHeights(); } return ((FlexibleNode) node).getHeight(); } public boolean hasBranchLengths() { return lengthsKnown; } public double getBranchLength(NodeRef node) { if (!lengthsKnown) { calculateBranchLengths(); } return ((FlexibleNode) node).getLength(); } public double getNodeRate(NodeRef node) { Object rateAttr = getNodeAttribute(node, "rate"); if (rateAttr != null) { if (rateAttr instanceof Number) return (Double) rateAttr; if (rateAttr instanceof String) return Double.parseDouble((String) rateAttr); } return ((FlexibleNode) node).getRate(); } public Taxon getNodeTaxon(NodeRef node) { return ((FlexibleNode) node).getTaxon(); } public void setNodeTaxon(NodeRef node, Taxon taxon) { ((FlexibleNode) node).setTaxon(taxon); } public int getChildCount(NodeRef node) { return ((FlexibleNode) node).getChildCount(); } public boolean isExternal(NodeRef node) { return ((FlexibleNode) node).getChildCount() == 0; } public boolean isRoot(NodeRef node) { return (node == root); } public NodeRef getChild(NodeRef node, int i) { return ((FlexibleNode) node).getChild(i); } public NodeRef getParent(NodeRef node) { return ((FlexibleNode) node).getParent(); } public final NodeRef getExternalNode(int i) { return nodes[i]; } public final NodeRef getInternalNode(int i) { return nodes[i + externalNodeCount]; } public final NodeRef getNode(int i) { return nodes[i]; } /** * Returns the number of external nodes. */ public final int getExternalNodeCount() { return externalNodeCount; } /** * Returns the ith internal node. */ public final int getInternalNodeCount() { return internalNodeCount; } /** * Returns the root node of this tree. */ public final NodeRef getRoot() { return root; } /** * Set a new node as root node. */ public final void setRoot(NodeRef r) { if (!inEdit) throw new RuntimeException("Must be in edit transaction to call this method!"); if (!(r instanceof FlexibleNode)) { throw new IllegalArgumentException(); } root = (FlexibleNode) r; } /** * @return the height of the root node. */ public final double getRootHeight() { return getNodeHeight(root); } /** * Set the height of the root node. */ public final void setRootHeight(double height) { setNodeHeight(root, height); fireTreeChanged(); } public void addChild(NodeRef p, NodeRef c) { if (!inEdit) throw new RuntimeException("Must be in edit transaction to call this method!"); FlexibleNode parent = (FlexibleNode) p; FlexibleNode child = (FlexibleNode) c; if (parent.hasChild(child)) throw new IllegalArgumentException("Child already existists in parent"); parent.addChild(child); } public void removeChild(NodeRef p, NodeRef c) { if (!inEdit) throw new RuntimeException("Must be in edit transaction to call this method!"); FlexibleNode parent = (FlexibleNode) p; FlexibleNode child = (FlexibleNode) c; for (int i = 0; i < parent.getChildCount(); i++) { if (parent.getChild(i) == child) { parent.removeChild(i); return; } } } public void replaceChild(NodeRef node, NodeRef child, NodeRef newChild) { throw new RuntimeException("Unimplemented"); } public boolean beginTreeEdit() { boolean r = inEdit; inEdit = true; return r; } public void endTreeEdit() { inEdit = false; fireTreeChanged(); } public void setNodeHeight(NodeRef n, double height) { if (!heightsKnown) { calculateNodeHeights(); } FlexibleNode node = (FlexibleNode) n; node.setHeight(height); lengthsKnown = false; fireTreeChanged(); } public void setBranchLength(NodeRef n, double length) { if (!lengthsKnown) { calculateBranchLengths(); } FlexibleNode node = (FlexibleNode) n; node.setLength(length); heightsKnown = false; fireTreeChanged(); } public void setHeightsKnown(boolean heightsKnown) { this.heightsKnown = heightsKnown; } public void setLengthsKnown(boolean lengthsKnown) { this.lengthsKnown = lengthsKnown; } public void setNodeRate(NodeRef n, double rate) { FlexibleNode node = (FlexibleNode) n; node.setRate(rate); fireTreeChanged(); } /** * Set the node heights from the current branch lengths. */ protected void calculateNodeHeights() { if (!lengthsKnown) { throw new IllegalArgumentException("Branch lengths not known"); } nodeLengthsToHeights((FlexibleNode) getRoot(), 0.0); double maxHeight = 0.0; FlexibleNode node; for (int i = 0; i < getExternalNodeCount(); i++) { node = (FlexibleNode) getExternalNode(i); if (node.getHeight() > maxHeight) { maxHeight = node.getHeight(); } } for (int i = 0; i < getNodeCount(); i++) { node = (FlexibleNode) getNode(i); node.setHeight(maxHeight - node.getHeight()); } heightsKnown = true; } /** * Set the node heights from the current node branch lengths. Actually * sets distance from root so the heights then need to be reversed. */ private void nodeLengthsToHeights(FlexibleNode node, double height) { double newHeight = height; if (node.getLength() > 0.0) { newHeight += node.getLength(); } node.setHeight(newHeight); for (int i = 0; i < node.getChildCount(); i++) { nodeLengthsToHeights(node.getChild(i), newHeight); } } /** * Calculate branch lengths from the current node heights. */ protected void calculateBranchLengths() { nodeHeightsToLengths((FlexibleNode) getRoot(), getRootHeight()); lengthsKnown = true; } /** * Calculate branch lengths from the current node heights. */ private void nodeHeightsToLengths(FlexibleNode node, double height) { node.setLength(height - node.getHeight()); for (int i = 0; i < node.getChildCount(); i++) { nodeHeightsToLengths(node.getChild(i), node.getHeight()); } } /** * Re-root the tree on the branch above the given node at the given height. */ public void changeRoot(NodeRef node, double height) { FlexibleNode node1 = (FlexibleNode) node; FlexibleNode parent = node1.getParent(); double l1 = height - getNodeHeight(node); if (l1 < 0.0) { throw new IllegalArgumentException("New root height less than the node's height"); } double l2 = getNodeHeight(parent) - height; if (l2 < 0.0) { throw new IllegalArgumentException("New root height above the node's parent's height"); } changeRoot(node, l1, l2); } /** * Re-root the tree on the branch above the given node with the given branch lengths. */ public void changeRoot(NodeRef node, double l1, double l2) { FlexibleNode node1 = (FlexibleNode) node; FlexibleNode parent = node1.getParent(); if (parent == null || parent == root) { // the node is already the root so nothing to do... return; } beginTreeEdit(); if (!lengthsKnown) { calculateBranchLengths(); } FlexibleNode parent2 = parent.getParent(); swapParentNode(parent, parent2, null); // the root is now free so use it as the root again parent.removeChild(node1); root.addChild(node1); root.addChild(parent); node1.setLength(l1); parent.setLength(l2); heightsKnown = false; String t = toString(); endTreeEdit(); } /** * Work up through the tree putting the parent into the child. */ private void swapParentNode(FlexibleNode node, FlexibleNode parent, FlexibleNode child) { if (parent != null) { FlexibleNode parent2 = parent.getParent(); swapParentNode(parent, parent2, node); if (child != null) { node.removeChild(child); child.addChild(node); node.setLength(child.getLength()); } } else { // First remove child from the root node.removeChild(child); if (node.getChildCount() > 1) { throw new IllegalArgumentException("Trees must be binary"); } FlexibleNode tmp = node.getChild(0); node.removeChild(tmp); child.addChild(tmp); tmp.setLength(tmp.getLength() + child.getLength()); } } /** * Resolve tree so that it is fully bifurcating. Resolving nodes arbitrarily */ public void resolveTree() { for (int i = 0; i < getInternalNodeCount(); i++) { FlexibleNode node = ((FlexibleNode) getInternalNode(i)); if (node.getChildCount() > 2) { resolveNode(node); } else if (node.getChildCount() == 1) { FlexibleNode parent = node.getParent(); if (parent != null) { // remove the degree 2 node and add its child to its parent FlexibleNode child = node.getChild(0); child.setParent(parent); parent.removeChild(node); parent.addChild(child); } else { // the root is a degree 1 node so make the root the node root = node; } } } adoptNodes(root, null); fireTreeChanged(); } /** * Resolve a node so that it is fully bifurcating. */ private void resolveNode(FlexibleNode node) { while (node.getChildCount() > 2) { FlexibleNode node0 = node.getChild(0); FlexibleNode node1 = node.getChild(1); node.removeChild(node0); node.removeChild(node1); FlexibleNode node2 = node.getShallowCopy(); node2.addChild(node0); node2.addChild(node1); node2.setLength(0.0); node.addChild(node2); } } /** * 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) { ((FlexibleNode) node).setAttribute(name, value); fireTreeChanged(); } /** * @param node the node whose attribute is being fetched. * @param name the name of the attribute of interest. * @return an object representing the named attributed for the given node. */ public Object getNodeAttribute(NodeRef node, String name) { return ((FlexibleNode) node).getAttribute(name); } /** * @return a key set of attribute names available for this node. */ public Iterator getNodeAttributeNames(NodeRef node) { return ((FlexibleNode) node).getAttributeNames(); } // ************************************************************** // TaxonList IMPLEMENTATION // ************************************************************** /** * @return a count of the number of taxa in the list. */ public int getTaxonCount() { return getExternalNodeCount(); } /** * @return the ith taxon in the list. */ public Taxon getTaxon(int taxonIndex) { return ((FlexibleNode) getExternalNode(taxonIndex)).getTaxon(); } /** * @return the ID of the taxon of the ith external node. If it doesn't have * a taxon, returns the ID of the node itself. */ public String getTaxonId(int taxonIndex) { Taxon taxon = getTaxon(taxonIndex); if (taxon != null) return taxon.getId(); else return ((FlexibleNode) getExternalNode(taxonIndex)).getId(); } /** * returns the index of the taxon with the given id. */ public int getTaxonIndex(String id) { for (int i = 0, n = getTaxonCount(); i < n; i++) { if (getTaxonId(i).equals(id)) return i; } return -1; } /** * returns the index of the given taxon. */ public int getTaxonIndex(Taxon taxon) { for (int i = 0, n = getTaxonCount(); i < n; i++) { if (getTaxon(i) == taxon) return i; } return -1; } public List<Taxon> asList() { List<Taxon> taxa = new ArrayList<Taxon>(); for (int i = 0, n = getTaxonCount(); i < n; i++) { taxa.add(getTaxon(i)); } return taxa; } public Iterator<Taxon> iterator() { return new Iterator<Taxon>() { private int index = -1; public boolean hasNext() { return index < getTaxonCount() - 1; } public Taxon next() { index++; return getTaxon(index); } public void remove() { /* do nothing */ } }; } /** * @param taxonIndex the index of the taxon whose attribute is being fetched. * @param name the name of the attribute of interest. * @return an object representing the named attributed for the taxon of the given * external node. If the node doesn't have a taxon then the nodes own attribute * is returned. */ public Object getTaxonAttribute(int taxonIndex, String name) { Taxon taxon = getTaxon(taxonIndex); if (taxon != null) return taxon.getAttribute(name); else return ((FlexibleNode) getExternalNode(taxonIndex)).getAttribute(name); } // ************************************************************** // MutableTaxonList IMPLEMENTATION // ************************************************************** public int addTaxon(Taxon taxon) { throw new IllegalArgumentException("Cannot add taxon to a MutableTree"); } public boolean removeTaxon(Taxon taxon) { throw new IllegalArgumentException("Cannot add taxon to a MutableTree"); } /** * Sets the ID of the taxon of the ith external node. If it doesn't have * a taxon, sets the ID of the node itself. */ public void setTaxonId(int taxonIndex, String id) { Taxon taxon = getTaxon(taxonIndex); if (taxon != null) taxon.setId(id); else ((FlexibleNode) getExternalNode(taxonIndex)).setId(id); fireTreeChanged(); fireTaxaChanged(); } /** * Sets an named attribute for the taxon of a given external node. If the node * doesn't have a taxon then the attribute is added to the node itself. * * @param taxonIndex the index of the taxon whose attribute is being set. * @param name the name of the attribute. * @param value the new value of the attribute. */ public void setTaxonAttribute(int taxonIndex, String name, Object value) { Taxon taxon = getTaxon(taxonIndex); if (taxon != null) taxon.setAttribute(name, value); else ((FlexibleNode) getExternalNode(taxonIndex)).setAttribute(name, value); fireTreeChanged(); fireTaxaChanged(); } // ************************************************************** // Identifiable IMPLEMENTATION // ************************************************************** protected String id = null; /** * @return the id. */ public String getId() { return id; } /** * Sets the id. */ public void setId(String id) { this.id = id; fireTreeChanged(); } // ************************************************************** // Attributable IMPLEMENTATION // ************************************************************** private Attributable.AttributeHelper attributes = null; /** * Sets an named attribute for this object. * * @param name the name of the attribute. * @param value the new value of the attribute. */ public void setAttribute(String name, Object value) { if (attributes == null) attributes = new Attributable.AttributeHelper(); attributes.setAttribute(name, value); fireTreeChanged(); } /** * @param name the name of the attribute of interest. * @return an object representing the named attributed for this object. */ public Object getAttribute(String name) { if (attributes == null) return null; else return attributes.getAttribute(name); } /** * @return an iterator of the attributes that this object has. */ public Iterator<String> getAttributeNames() { if (attributes == null) return null; else return attributes.getAttributeNames(); } public void addMutableTreeListener(MutableTreeListener listener) { mutableTreeListeners.add(listener); } private void fireTreeChanged() { for (MutableTreeListener mutableTreeListener : mutableTreeListeners) { (mutableTreeListener).treeChanged(this); } } private final ArrayList<MutableTreeListener> mutableTreeListeners = new ArrayList<MutableTreeListener>(); public void addMutableTaxonListListener(MutableTaxonListListener listener) { mutableTaxonListListeners.add(listener); } private void fireTaxaChanged() { for (MutableTaxonListListener mutableTaxonListListener : mutableTaxonListListeners) { (mutableTaxonListListener).taxaChanged(this); } } private final ArrayList<MutableTaxonListListener> mutableTaxonListListeners = new ArrayList<MutableTaxonListListener>(); /** * @return a string containing a newick representation of the tree */ public String toString() { return TreeUtils.newick(this); } /** * @return whether two trees have the same topology */ public boolean equals(Object obj) { if (obj == null) { return false; } if (!(obj instanceof Tree)) { throw new IllegalArgumentException("FlexibleTree.equals can only compare instances of Tree"); } return TreeUtils.equal(this, (Tree) obj); } // ************************************************************** // Private Stuff // ************************************************************** /** * root node */ FlexibleNode root; /** * list of internal nodes (including root) */ FlexibleNode[] nodes = null; /** * number of nodes (including root and tips) */ int nodeCount; /** * number of external nodes */ int externalNodeCount; /** * number of internal nodes (including root) */ int internalNodeCount; /** * holds the units of the trees branches. */ private Type units = Type.SUBSTITUTIONS; boolean inEdit = false; boolean heightsKnown = false; boolean lengthsKnown = false; }