/*
* TreeModel.java
*
* Copyright (c) 2002-2017 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.tree;
import dr.evolution.tree.*;
import dr.evolution.util.MutableTaxonListListener;
import dr.evolution.util.Taxon;
import dr.inference.model.*;
import dr.util.Attributable;
import dr.util.Author;
import dr.util.Citable;
import dr.util.Citation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import java.util.*;
/**
* A model component for trees.
*
* @author Andrew Rambaut
* @author Alexei Drummond
* @version $Id: TreeModel.java,v 1.129 2006/01/05 17:55:47 rambaut Exp $
*/
public class TreeModel extends AbstractModel implements MultivariateTraitTree, Citable {
//
// Public stuff
//
public static final String TREE_MODEL = "treeModel";
private static final boolean TEST_NODE_BOUNDS = false;
public TreeModel(String name) {
super(name);
nodeCount = 0;
externalNodeCount = 0;
internalNodeCount = 0;
isTreeRandom = true;
}
public TreeModel(Tree tree) {
this(TREE_MODEL, tree, false, false, false);
}
public TreeModel(String id, Tree tree) { this(id, tree, false, false); }
public TreeModel(String id, Tree tree, boolean fixHeights, boolean fixTree) {
this(id, tree, false, fixHeights, fixTree);
setId(id);
}
/* New constructor that copies the attributes of Tree tree into the new TreeModel
* Useful for constructing a TreeModel from a NEXUS file entry
*/
public TreeModel(String name, Tree tree, boolean copyAttributes, boolean fixHeights, boolean fixTree) {
super(name);
// get a rooted version of the tree to clone
FlexibleTree binaryTree = new FlexibleTree(tree, copyAttributes);
binaryTree.resolveTree();
// adjust the heights to be compatible with the tip dates and perturb
// any zero branches.
if (!fixHeights) {
MutableTree.Utils.correctHeightsForTips(binaryTree);
}
this.isTreeRandom = !fixTree;
// clone the node structure (this will create the individual parameters)
Node node = new Node(binaryTree, binaryTree.getRoot());
internalNodeCount = binaryTree.getInternalNodeCount();
externalNodeCount = binaryTree.getExternalNodeCount();
nodeCount = internalNodeCount + externalNodeCount;
nodes = new Node[nodeCount];
storedNodes = new Node[nodeCount];
int i = 0;
int j = externalNodeCount;
root = node;
do {
node = (Node) TreeUtils.postorderSuccessor(this, node);
if (node.isExternal()) {
node.number = i;
nodes[i] = node;
storedNodes[i] = new Node();
storedNodes[i].taxon = node.taxon;
storedNodes[i].number = i;
i++;
} else {
node.number = j;
nodes[j] = node;
storedNodes[j] = new Node();
storedNodes[j].number = j;
j++;
}
} while (node != root);
// must be done here to allow programmatic running of BEAST
setupHeightBounds();
}
boolean heightBoundsSetup = false;
public void setupHeightBounds() {
if (heightBoundsSetup) {
throw new IllegalArgumentException("Node height bounds set up twice");
}
for (int i = 0; i < nodeCount; i++) {
nodes[i].setupHeightBounds();
}
heightBoundsSetup = true;
}
/**
* Push a tree changed event into the event stack.
*/
public void pushTreeChangedEvent() {
pushTreeChangedEvent(new TreeChangedEvent());
}
/**
* Push a tree changed event into the event stack.
*/
public void pushTreeChangedEvent(NodeRef nodeRef) {
pushTreeChangedEvent(new TreeChangedEvent((Node) nodeRef));
}
/**
* Push a tree changed event into the event stack.
*/
public void pushTreeChangedEvent(Node node, Parameter parameter, int index) {
pushTreeChangedEvent(new TreeChangedEvent(node, parameter, index));
}
/**
* Push a tree changed event into the event stack.
*/
public void pushTreeChangedEvent(TreeChangedEvent event) {
if (!isTreeRandom) throw new IllegalStateException("Attempting state change in fixed tree");
if (inEdit) {
treeChangedEvents.add(event);
} else {
listenerHelper.fireModelChanged(this, event);
}
}
protected void handleModelChangedEvent(Model model, Object object, int index) {
// no submodels so nothing to do
}
/**
* Called when a parameter changes.
*/
public void handleVariableChangedEvent(Variable variable, int index, Parameter.ChangeType type) {
final Node node = getNodeOfParameter((Parameter) variable);
if (type == Parameter.ChangeType.ALL_VALUES_CHANGED) {
//this signals events where values in all dimensions of a parameter is changed.
pushTreeChangedEvent(new TreeChangedEvent(node, (Parameter) variable, TreeChangedEvent.CHANGE_IN_ALL_INTERNAL_NODES));
} else {
pushTreeChangedEvent(node, (Parameter) variable, index);
}
}
private final List<TreeChangedEvent> treeChangedEvents = new ArrayList<TreeChangedEvent>();
public boolean hasRates() {
return hasRates;
}
public boolean inTreeEdit() {
return inEdit;
}
public class TreeChangedEvent {
static final int CHANGE_IN_ALL_INTERNAL_NODES = -2;
final Node node;
final Parameter parameter;
final int index;
public TreeChangedEvent() {
this(null, null, -1);
}
public TreeChangedEvent(Node node) {
this(node, null, -1);
}
public TreeChangedEvent(Node node, Parameter parameter, int index) {
this.node = node;
this.parameter = parameter;
this.index = index;
}
public int getIndex() {
return index;
}
public Node getNode() {
return node;
}
public Parameter getParameter() {
return parameter;
}
public boolean isTreeChanged() {
return parameter == null;
}
public boolean isNodeChanged() {
return node != null;
}
public boolean isNodeParameterChanged() {
return parameter != null;
}
public boolean isHeightChanged() {
return parameter == node.heightParameter;
}
public boolean isRateChanged() {
return parameter == node.rateParameter;
}
public boolean isTraitChanged(String name) {
return parameter == node.traitParameters.get(name);
}
public boolean areAllInternalHeightsChanged() {
if (parameter != null) {
return parameter == node.heightParameter && index == CHANGE_IN_ALL_INTERNAL_NODES;
}
return false;
}
}
// *****************************************************************
// Interface Tree
// *****************************************************************
/**
* Return the units that this tree is expressed in.
*/
public Type getUnits() {
return units;
}
/**
* Sets the units that this tree is expressed in.
*/
public 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 true;
}
public double getNodeHeight(NodeRef node) {
return ((Node) node).getHeight();
}
public final double getNodeHeightUpper(NodeRef node) {
return ((Node) node).heightParameter.getBounds().getUpperLimit(0);
}
public final double getNodeHeightLower(NodeRef node) {
return ((Node) node).heightParameter.getBounds().getLowerLimit(0);
}
/**
* @param node
* @return the rate parameter associated with this node.
*/
public double getNodeRate(NodeRef node) {
if (!hasRates) {
return 1.0;
}
return ((Node) node).getRate();
}
public Object getNodeAttribute(NodeRef node, String name) {
if (name.equals("rate")) {
return getNodeRate(node);
}
return null;
}
public Iterator getNodeAttributeNames(NodeRef node) {
return new Iterator() {
int i = 0;
String[] attributes = {"rate"};
public boolean hasNext() {
return i < attributes.length;
}
public Object next() {
return attributes[i++];
}
public void remove() {
throw new UnsupportedOperationException("can't remove from this iterator!");
}
};
}
public boolean hasNodeTraits() {
return hasTraits;
}
public Map<String, Parameter> getTraitMap(NodeRef node) {
if (!hasTraits) throw new IllegalArgumentException("Trait parameters have not been created");
return ((Node) node).getTraitMap();
}
public double getNodeTrait(NodeRef node, String name) {
if (!hasTraits) throw new IllegalArgumentException("Trait parameters have not been created");
return ((Node) node).getTrait(name);
}
public Parameter getNodeTraitParameter(NodeRef node, String name) {
if (!hasTraits) throw new IllegalArgumentException("Trait parameters have not been created");
return ((Node) node).getTraitParameter(name);
}
public double[] getMultivariateNodeTrait(NodeRef node, String name) {
if (!hasTraits) throw new IllegalArgumentException("Trait parameters have not been created");
return ((Node) node).getMultivariateTrait(name);
}
public final void swapAllTraits(NodeRef node1, NodeRef node2) {
if (!hasTraits) throw new IllegalArgumentException("Trait parameters have not been created");
swapAllTraits((Node) node1, (Node) node2);
}
public Taxon getNodeTaxon(NodeRef node) {
return ((Node) node).taxon;
}
public void setNodeTaxon(NodeRef node, Taxon taxon) {
((Node) node).taxon = taxon;
}
public boolean isExternal(NodeRef node) {
return ((Node) node).isExternal();
}
public boolean isRoot(NodeRef node) {
return (node == root);
}
public int getChildCount(NodeRef node) {
return ((Node) node).getChildCount();
}
public NodeRef getChild(NodeRef node, int i) {
return ((Node) node).getChild(i);
}
public NodeRef getParent(NodeRef node) {
return ((Node) node).parent;
}
public boolean hasBranchLengths() {
return true;
}
public double getBranchLength(NodeRef node) {
NodeRef parent = getParent(node);
if (parent == null) {
return 0.0;
}
return getNodeHeight(parent) - getNodeHeight(node);
}
public NodeRef getExternalNode(int i) {
return nodes[i];
}
public NodeRef getInternalNode(int i) {
return nodes[i + externalNodeCount];
}
public NodeRef getNode(int i) {
return nodes[i];
}
public NodeRef[] getNodes() {
return nodes;
}
/**
* Returns the number of external nodes.
*/
public int getExternalNodeCount() {
return externalNodeCount;
}
/**
* Returns the ith internal node.
*/
public int getInternalNodeCount() {
return internalNodeCount;
}
/**
* Returns the root node of this tree.
*/
public NodeRef getRoot() {
return root;
}
// *****************************************************************
// Interface MutableTree
// *****************************************************************
/**
* Set a new node as root node.
*/
public final void setRoot(NodeRef newRoot) {
if (!inEdit) throw new RuntimeException("Must be in edit transaction to call this method!");
root = (Node) newRoot;
// We shouldn't need this because the addChild will already have fired appropriate events.
pushTreeChangedEvent(root);
}
public void addChild(NodeRef p, NodeRef c) {
if (!inEdit) throw new RuntimeException("Must be in edit transaction to call this method!");
Node parent = (Node) p;
Node child = (Node) c;
if (parent.hasChild(child)) throw new IllegalArgumentException("Child already exists in parent");
parent.addChild(child);
pushTreeChangedEvent(parent);
}
public void removeChild(NodeRef p, NodeRef c) {
if (!inEdit) throw new RuntimeException("Must be in edit transaction to call this method!");
Node parent = (Node) p;
Node child = (Node) c;
parent.removeChild(child);
}
public void replaceChild(NodeRef node, NodeRef child, NodeRef newChild) {
throw new RuntimeException("Unimplemented");
}
private Node oldRoot;
public boolean beginTreeEdit() {
if (inEdit) throw new RuntimeException("Alreading in edit transaction mode!");
oldRoot = root;
inEdit = true;
return false;
}
public void endTreeEdit() {
if (!inEdit) throw new RuntimeException("Not in edit transaction mode!");
inEdit = false;
if (root != oldRoot) {
swapParameterObjects(oldRoot, root);
}
if (TEST_NODE_BOUNDS) {
try {
checkTreeIsValid();
} catch (InvalidTreeException ite) {
throw new RuntimeException(ite.getMessage());
}
}
for (TreeChangedEvent treeChangedEvent : treeChangedEvents) {
listenerHelper.fireModelChanged(this, treeChangedEvent);
}
treeChangedEvents.clear();
}
public void checkTreeIsValid() throws MutableTree.InvalidTreeException {
for (Node node : nodes) {
if (!node.heightParameter.isWithinBounds()) {
throw new InvalidTreeException("height parameter out of bounds");
}
}
}
public void setNodeHeight(NodeRef n, double height) {
((Node) n).setHeight(height);
}
public void setNodeRate(NodeRef n, double rate) {
if (!hasRates) throw new IllegalArgumentException("Rate parameters have not been created");
((Node) n).setRate(rate);
}
public void setNodeTrait(NodeRef n, String name, double value) {
if (!hasTraits) throw new IllegalArgumentException("Trait parameters have not been created");
((Node) n).setTrait(name, value);
}
public void setMultivariateTrait(NodeRef n, String name, double[] value) {
if (!hasTraits) throw new IllegalArgumentException("Trait parameters have not been created");
((Node) n).setMultivariateTrait(name, value);
}
public void setBranchLength(NodeRef node, double length) {
throw new UnsupportedOperationException("TreeModel cannot have branch lengths set");
}
public void setNodeAttribute(NodeRef node, String name, Object value) {
throw new UnsupportedOperationException("TreeModel does not use NodeAttributes");
}
// *****************************************************************
// Interface ModelComponent
// *****************************************************************
/**
* Store current state
*/
protected void storeState() {
copyNodeStructure(storedNodes);
storedRootNumber = root.getNumber();
}
/**
* Restore the stored state
*/
protected void restoreState() {
Node[] tmp = storedNodes;
storedNodes = nodes;
nodes = tmp;
root = nodes[storedRootNumber];
}
/**
* accept the stored state
*/
protected void acceptState() {
} // nothing to do
/**
* Copies the node connections from this TreeModel's nodes array to the
* destination array. Basically it connects up the nodes in destination
* in the same way as this TreeModel is set up. This method is package
* private.
*/
private void copyNodeStructure(Node[] destination) {
if (nodes.length != destination.length) {
throw new IllegalArgumentException("Node arrays are of different lengths");
}
for (int i = 0, n = nodes.length; i < n; i++) {
Node node0 = nodes[i];
Node node1 = destination[i];
// the parameter values are automatically stored and restored
// just need to keep the links
node1.heightParameter = node0.heightParameter;
node1.rateParameter = node0.rateParameter;
node1.traitParameters = node0.traitParameters;
if (node0.parent != null) {
node1.parent = storedNodes[node0.parent.getNumber()];
} else {
node1.parent = null;
}
if (node0.leftChild != null) {
node1.leftChild = storedNodes[node0.leftChild.getNumber()];
} else {
node1.leftChild = null;
}
if (node0.rightChild != null) {
node1.rightChild = storedNodes[node0.rightChild.getNumber()];
} else {
node1.rightChild = null;
}
}
}
/**
* Copies a different tree into the current treeModel. Needs to reconnect
* the existing internal and external nodes, taking into account that the
* node numbers of the external nodes may differ between the two trees.
*/
public void adoptTreeStructure(Tree donor) {
/*System.err.println("internalNodeCount: " + this.internalNodeCount);
System.err.println("externalNodeCount: " + this.externalNodeCount);
for (int i = 0; i < this.nodeCount; i++) {
System.err.println(nodes[i]);
}*/
//first remove all the child nodes of the internal nodes
for (int i = this.externalNodeCount; i < this.nodeCount; i++) {
int childCount = nodes[i].getChildCount();
for (int j = 0; j < childCount; j++) {
nodes[i].removeChild(j);
}
}
// set-up nodes in this.nodes[] to mirror connectedness in donor via a simple recursion on donor.getRoot()
addNodeStructure(donor, donor.getRoot());
//Tree donor has no rates nor traits, only heights
}
/**
* Modifies the current tree by adopting the provided collection of edges
* @param edges Edges are provided as index: child number; parent: array entry
* @param nodeHeights Also sets the node heights to the provided values
* @param childOrder Array that contains whether a child node is left or right child
*/
public void adoptTreeStructure(int[] edges, double[] nodeHeights, int[] childOrder) {
if (this.nodeCount != edges.length) {
throw new RuntimeException("Incorrect number of edges provided: " + edges.length + " versus " + this.nodeCount + " nodes.");
}
//first remove all the child nodes of the internal nodes
for (int i = this.externalNodeCount; i < this.nodeCount; i++) {
int childCount = nodes[i].getChildCount();
for (int j = 0; j < childCount; j++) {
nodes[i].removeChild(j);
}
}
//set the node heights
for (int i = 0; i < nodeHeights.length; i++) {
setNodeHeight(nodes[i], nodeHeights[i]);
}
int newRootIndex = -1;
//now add the parent-child links again to ALL the nodes
for (int i = 0; i < edges.length; i++) {
if (edges[i] != -1) {
nodes[edges[i]].addChild(nodes[i]);
} else {
newRootIndex = i;
}
}
//not possible to determine correct ordering of child nodes in the loop where they're being assigned
//hence perform possible swaps in a separate loop
for (int i = 0; i < edges.length; i++) {
if (edges[i] != -1) {
if (childOrder[i] == 0 && nodes[edges[i]].getChild(0) != nodes[i]) {
//swap child nodes
Node childOne = nodes[edges[i]].removeChild(0);
Node childTwo = nodes[edges[i]].removeChild(1);
nodes[edges[i]].addChild(childTwo);
nodes[edges[i]].addChild(childOne);
}
}
}
this.setRoot(nodes[newRootIndex]);
}
/**
* Recursive algorithm to copy a proposed tree structure into the current treeModel.
*/
private void addNodeStructure(Tree donorTree, NodeRef donorNode) {
NodeRef acceptorNode = null;
if (donorTree.isExternal(donorNode)) {
//external nodes can have different numbers between both trees
acceptorNode = this.nodes[this.getTaxonIndex(donorTree.getTaxonId(donorNode.getNumber()))];
} else {
//not really important for internal nodes
acceptorNode = this.nodes[donorNode.getNumber()];
}
setNodeHeight(acceptorNode, donorTree.getNodeHeight(donorNode));
//removing all child nodes up front currently works
//((Node)acceptorNode).leftChild = null;
//((Node)acceptorNode).rightChild = null;
/*int nrChildren = getChildCount(acceptorNode);
for (int i = 0; i < nrChildren; i++) {
this.removeChild(acceptorNode, this.getChild(acceptorNode, i));
}*/
for (int i = 0; i < donorTree.getChildCount(donorNode); i++) {
//add a check when the added child is an external node
if (donorTree.isExternal(donorTree.getChild(donorNode, i))) {
addChild(acceptorNode, this.nodes[this.getTaxonIndex(donorTree.getTaxonId(donorTree.getChild(donorNode, i).getNumber()))]);
} else {
addChild(acceptorNode, this.nodes[donorTree.getChild(donorNode, i).getNumber()]);
}
}
pushTreeChangedEvent(acceptorNode);
if (!donorTree.isExternal(donorNode)) {
for (int i = 0; i < donorTree.getChildCount(donorNode); i++) {
addNodeStructure(donorTree, donorTree.getChild(donorNode, i));
}
}
}
/**
* @return the number of statistics of this component.
*/
public int getStatisticCount() {
return super.getStatisticCount() + 1;
}
/**
* @return the ith statistic of the component
*/
public Statistic getStatistic(int i) {
if (i == super.getStatisticCount()) return root.heightParameter;
return super.getStatistic(i);
}
// public String getModelComponentName() {
// return TREE_MODEL;
// }
// **************************************************************
// 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 ((Node) getExternalNode(taxonIndex)).taxon;
}
/**
* @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 null;
}
}
/**
* 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);
}
return null;
}
// **************************************************************
// MutableTaxonList IMPLEMENTATION
// **************************************************************
public int addTaxon(Taxon taxon) {
throw new IllegalArgumentException("Cannot add taxon to a TreeModel");
}
public boolean removeTaxon(Taxon taxon) {
throw new IllegalArgumentException("Cannot add taxon to a TreeModel");
}
public void setTaxonId(int taxonIndex, String id) {
throw new IllegalArgumentException("Cannot set taxon id in a TreeModel");
}
public void setTaxonAttribute(int taxonIndex, String name, Object value) {
throw new IllegalArgumentException("Cannot set taxon attribute in a TreeModel");
}
public void addMutableTreeListener(MutableTreeListener listener) {
} // Do nothing at the moment
public void addMutableTaxonListListener(MutableTaxonListListener listener) {
} // Do nothing at the moment
// **************************************************************
// Identifiable IMPLEMENTATION
// **************************************************************
private String id = null;
/**
* @return the id.
*/
public String getId() {
return id;
}
/**
* Sets the id.
*/
public void setId(String id) {
this.id = id;
}
// **************************************************************
// Attributable IMPLEMENTATION
// **************************************************************
private Attributable.AttributeHelper treeAttributes = 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 (treeAttributes == null)
treeAttributes = new Attributable.AttributeHelper();
treeAttributes.setAttribute(name, value);
}
/**
* @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 (treeAttributes == null)
return null;
else
return treeAttributes.getAttribute(name);
}
/**
* @return an iterator of the attributes that this object has.
*/
public Iterator<String> getAttributeNames() {
if (treeAttributes == null)
return null;
else
return treeAttributes.getAttributeNames();
}
/**
* @return a string containing a newick representation of the tree
*/
public final String getNewick() {
return TreeUtils.newick(this);
}
/**
* @return a string containing a newick representation of the tree
*/
public String toString() {
return getNewick();
}
public Tree getCopy() {
throw new UnsupportedOperationException("please don't call this function");
}
// **************************************************************
// XMLElement IMPLEMENTATION
// **************************************************************
public Element createElement(Document document) {
throw new RuntimeException("Not implemented yet");
}
// ***********************************************************************
// Private methods
// ***********************************************************************
/**
* @return the node that this parameter is a member of
*/
public Node getNodeOfParameter(Parameter parameter) {
if (parameter == null) throw new IllegalArgumentException("Parameter is null!");
for (Node node : nodes) {
if (node.heightParameter == parameter) {
return node;
}
}
if (hasRates) {
for (Node node : nodes) {
if (node.rateParameter == parameter) {
return node;
}
}
}
if (hasTraits) {
for (Node node : nodes) {
if (node.traitParameters.containsValue(parameter)) {
return node;
}
}
}
throw new RuntimeException("Parameter not found in any nodes:" + parameter.getId() + " " + parameter.hashCode());
// assume it is a trait parameter and return null
// return null;
}
/**
* Get the root height parameter. Is private because it can only be called by the XMLParser
*/
public Parameter getRootHeightParameter() {
return root.heightParameter;
}
/**
* @return the relevant node height parameter. Is private because it can only be called by the XMLParser
*/
public Parameter createNodeHeightsParameter(boolean rootNode, boolean internalNodes, boolean leafNodes) {
if (!rootNode && !internalNodes && !leafNodes) {
throw new IllegalArgumentException("At least one of rootNode, internalNodes or leafNodes must be true");
}
CompoundParameter parameter = new CompoundParameter("nodeHeights(" + getId() + ")");
for (int i = externalNodeCount; i < nodeCount; i++) {
if ((rootNode && nodes[i] == root) || (internalNodes && nodes[i] != root)) {
parameter.addParameter(nodes[i].heightParameter);
}
}
if (leafNodes) {
for (int i = 0; i < externalNodeCount; i++) {
parameter.addParameter(nodes[i].heightParameter);
}
}
return parameter;
}
public Parameter getLeafHeightParameter(NodeRef node) {
if (!isExternal(node)) {
throw new RuntimeException("only leaves can be used with getLeafHeightParameter");
}
isTipDateSampled = true;
return nodes[node.getNumber()].heightParameter;
}
/**
* @return the relevant node rate parameter. Is private because it can only be called by the XMLParser
*/
public Parameter createNodeRatesParameter(double[] initialValues, boolean rootNode, boolean internalNodes, boolean leafNodes) {
if (!rootNode && !internalNodes && !leafNodes) {
throw new IllegalArgumentException("At least one of rootNode, internalNodes or leafNodes must be true");
}
CompoundParameter parameter = new CompoundParameter("nodeRates(" + getId() + ")");
hasRates = true;
for (int i = externalNodeCount; i < nodeCount; i++) {
nodes[i].createRateParameter(initialValues);
if ((rootNode && nodes[i] == root) || (internalNodes && nodes[i] != root)) {
parameter.addParameter(nodes[i].rateParameter);
}
}
for (int i = 0; i < externalNodeCount; i++) {
nodes[i].createRateParameter(initialValues);
if (leafNodes) {
parameter.addParameter(nodes[i].rateParameter);
}
}
return parameter;
}
public Parameter createNodeTraitsParameter(String name, double[] initialValues) {
return createNodeTraitsParameter(name, initialValues.length,
initialValues, true, true, true, true);
}
/**
* Create a node traits parameter. Is private because it can only be called by the XMLParser
*/
public Parameter createNodeTraitsParameter(String name, int dim, double[] initialValues,
boolean rootNode, boolean internalNodes,
boolean leafNodes, boolean firesTreeEvents) {
checkValidFlags(rootNode, internalNodes, leafNodes);
CompoundParameter parameter = new CompoundParameter(name);
hasTraits = true;
for (int i = externalNodeCount; i < nodeCount; i++) {
nodes[i].createTraitParameter(name, dim, initialValues, firesTreeEvents);
if ((rootNode && nodes[i] == root) || (internalNodes && nodes[i] != root)) {
parameter.addParameter(nodes[i].getTraitParameter(name));
}
}
for (int i = 0; i < externalNodeCount; i++) {
nodes[i].createTraitParameter(name, dim, initialValues, firesTreeEvents);
if (leafNodes) {
parameter.addParameter(nodes[i].getTraitParameter(name));
}
}
return parameter;
}
public Parameter createNodeTraitsParameterAsMatrix(String name, int dim, double[] initialValues,
boolean rootNode, boolean internalNodes,
boolean leafNodes, boolean firesTreeEvents) {
checkValidFlags(rootNode, internalNodes, leafNodes);
final int rowDim = dim;
final int colDim = (rootNode ? 1 : 0)
+ (internalNodes ? internalNodeCount - 1 : 0)
+ (leafNodes ? externalNodeCount : 0);
FastMatrixParameter parameter = new FastMatrixParameter(name, rowDim, colDim, 0.0);
parameter.addBounds(new Parameter.DefaultBounds(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY,
rowDim * colDim));
hasTraits = true;
int parameterIndex = 0;
for (int i = externalNodeCount; i < nodeCount; i++) {
if ((rootNode && nodes[i] == root) || (internalNodes && nodes[i] != root)) {
nodes[i].addTraitParameter(name, parameter.getParameter(parameterIndex), initialValues, firesTreeEvents);
++parameterIndex;
}
}
for (int i = 0; i < externalNodeCount; i++) {
if (leafNodes) {
nodes[i].addTraitParameter(name, parameter.getParameter(parameterIndex), initialValues, firesTreeEvents);
++parameterIndex;
}
}
return parameter;
}
private void checkValidFlags(boolean rootNode, boolean internalNodes, boolean leafNodes) {
if (!rootNode && !internalNodes && !leafNodes) {
throw new IllegalArgumentException("At least one of rootNode, internalNodes or leafNodes must be true");
}
}
private void swapAllTraits(Node n1, Node n2) {
for (Map.Entry<String, Parameter> entry : n1.traitParameters.entrySet()) {
Parameter p1 = n1.traitParameters.get(entry.getKey());
Parameter p2 = n2.traitParameters.get(entry.getKey());
final int dim = p1.getDimension();
for (int i = 0; i < dim; i++) {
double transfer = p1.getParameterValue(i);
p1.setParameterValue(i, p2.getParameterValue(i));
p2.setParameterValue(i, transfer);
}
}
}
/**
* This method swaps the parameter objects of the two nodes
* but maintains the values in each node.
* This method is used to ensure that root node of the tree
* always has the same parameter object.
*/
private void swapParameterObjects(Node n1, Node n2) {
double height1 = n1.getHeight();
double height2 = n2.getHeight();
double rate1 = 1.0, rate2 = 1.0;
if (hasRates) {
rate1 = n1.getRate();
rate2 = n2.getRate();
}
// swap all trait parameters
if (hasTraits) {
Map<String, Parameter> traits1 = new HashMap<String, Parameter>();
Map<String, Parameter> traits2 = new HashMap<String, Parameter>();
traits1.putAll(n1.traitParameters);
traits2.putAll(n2.traitParameters);
Map<String, Parameter> temp = n1.traitParameters;
n1.traitParameters = n2.traitParameters;
n2.traitParameters = temp;
for (Map.Entry<String, Parameter> entry : traits1.entrySet()) {
n1.traitParameters.get(entry.getKey()).setParameterValueQuietly(0, entry.getValue().getParameterValue(0));
}
for (Map.Entry<String, Parameter> entry : traits2.entrySet()) {
n2.traitParameters.get(entry.getKey()).setParameterValueQuietly(0, entry.getValue().getParameterValue(0));
}
}
Parameter temp = n1.heightParameter;
n1.heightParameter = n2.heightParameter;
n2.heightParameter = temp;
if (hasRates) {
temp = n1.rateParameter;
n1.rateParameter = n2.rateParameter;
n2.rateParameter = temp;
}
n1.heightParameter.setParameterValueQuietly(0, height1);
n2.heightParameter.setParameterValueQuietly(0, height2);
if (hasRates) {
n1.rateParameter.setParameterValueQuietly(0, rate1);
n2.rateParameter.setParameterValueQuietly(0, rate2);
}
}
// **************************************************************
// Private inner classes
// **************************************************************
public class Node implements NodeRef {
public Node parent;
public Node leftChild, rightChild;
private int number;
public Parameter heightParameter;
public Parameter rateParameter = null;
//public Parameter traitParameter = null;
public Taxon taxon = null;
Map<String, Parameter> traitParameters = new HashMap<String, Parameter>();
public Node() {
parent = null;
leftChild = rightChild = null;
heightParameter = null;
number = 0;
taxon = null;
}
/**
* constructor used to clone a node and all children
*/
public Node(Tree tree, NodeRef node) {
parent = null;
leftChild = rightChild = null;
heightParameter = new Parameter.Default(tree.getNodeHeight(node));
addVariable(heightParameter);
number = node.getNumber();
taxon = tree.getNodeTaxon(node);
heightParameter.setId("" + number);
for (int i = 0; i < tree.getChildCount(node); i++) {
addChild(new Node(tree, tree.getChild(node, i)));
}
}
public final void setupHeightBounds() {
heightParameter.addBounds(new NodeHeightBounds(heightParameter));
}
public final void createRateParameter(double[] initialValues) {
if (rateParameter == null) {
if (initialValues != null) {
rateParameter = new Parameter.Default(initialValues[0]);
} else {
rateParameter = new Parameter.Default(1.0);
}
setParameterId("rate", rateParameter);
rateParameter.addBounds(new Parameter.DefaultBounds(Double.POSITIVE_INFINITY, 0.0, 1));
addVariable(rateParameter);
}
}
public final void createTraitParameter(String name, double[] initialValues, boolean firesTreeEvents) {
createTraitParameter(name, initialValues.length, initialValues, firesTreeEvents);
}
public final void addTraitParameter(String name, Parameter trait, double[] initialValues, boolean firesTreeEvents) {
if (!traitParameters.containsKey(name)) {
setParameterId(name, trait);
setParameterValues(trait, trait.getDimension(), initialValues);
traitParameters.put(name, trait);
if (firesTreeEvents) {
addVariable(trait);
}
}
}
private void setParameterValues(Parameter parameter, int dim, double[] initialValues) {
if (initialValues != null && initialValues.length > 0) {
for (int i = 0; i < dim; i++) {
if (initialValues.length == dim) {
parameter.setParameterValue(i, initialValues[i]);
} else {
parameter.setParameterValue(i, initialValues[0]);
}
}
}
}
public final void createTraitParameter(String name, int dim, double[] initialValues, boolean firesTreeEvents) {
if (!traitParameters.containsKey(name)) {
Parameter trait = new Parameter.Default(dim);
setParameterId(name, trait);
trait.addBounds(new Parameter.DefaultBounds(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, dim));
setParameterValues(trait, dim, initialValues);
traitParameters.put(name, trait);
if (firesTreeEvents) {
addVariable(trait);
}
}
}
private void setParameterId(String name, Parameter trait) {
if (isRoot()) {
trait.setId("root." + name);
} else if (isExternal()) {
trait.setId(getTaxonId(getNumber()) + "." + name);
} else {
trait.setId("node" + getNumber() + "." + name);
}
}
public final double getHeight() {
return heightParameter.getParameterValue(0);
}
public final double getRate() {
return rateParameter.getParameterValue(0);
}
public final double getTrait(String name) {
return traitParameters.get(name).getParameterValue(0);
}
public final double[] getMultivariateTrait(String name) {
return traitParameters.get(name).getParameterValues();
}
public final Map<String, Parameter> getTraitMap() {
return traitParameters;
}
public final void setHeight(double height) {
heightParameter.setParameterValue(0, height);
}
public final void setRate(double rate) {
//System.out.println("Rate set for parameter " + rateParameter.getParameterName());
rateParameter.setParameterValue(0, rate);
}
public final void setTrait(String name, double trait) {
//System.out.println("Trait set for parameter " + traitParameter.getParameterName());
traitParameters.get(name).setParameterValue(0, trait);
}
public final void setMultivariateTrait(String name, double[] trait) {
int dim = trait.length;
for (int i = 0; i < dim; i++)
traitParameters.get(name).setParameterValue(i, trait[i]);
}
public int getNumber() {
return number;
}
public void setNumber(int n) {
number = n;
}
/**
* Returns the number of children this node has.
*/
public final int getChildCount() {
int n = 0;
if (leftChild != null) n++;
if (rightChild != null) n++;
return n;
}
public Node getChild(int n) {
if (n == 0) return leftChild;
if (n == 1) return rightChild;
throw new IllegalArgumentException("TreeModel.Nodes can only have 2 children");
}
public boolean hasChild(Node node) {
return (leftChild == node || rightChild == node);
}
/**
* add new child node
*
* @param node new child node
*/
public void addChild(Node node) {
if (leftChild == null) {
leftChild = node;
} else if (rightChild == null) {
rightChild = node;
} else {
throw new IllegalArgumentException("TreeModel.Nodes can only have 2 children");
}
node.parent = this;
}
/**
* remove child
*
* @param node child to be removed
*/
public Node removeChild(Node node) {
if (leftChild == node) {
leftChild = null;
} else if (rightChild == node) {
rightChild = null;
} else {
throw new IllegalArgumentException("Unknown child node");
}
node.parent = null;
return node;
}
/**
* remove child
*
* @param n number of child to be removed
*/
public Node removeChild(int n) {
Node node;
if (n == 0) {
node = leftChild;
leftChild = null;
} else if (n == 1) {
node = rightChild;
rightChild = null;
} else {
throw new IllegalArgumentException("TreeModel.Nodes can only have 2 children");
}
node.parent = null;
return node;
}
public boolean hasNoChildren() {
return (leftChild == null && rightChild == null);
}
public boolean isExternal() {
return hasNoChildren();
}
public boolean isRoot() {
return (parent == null);
}
public String toString() {
return "node " + number + ", height=" + getHeight() + (taxon != null ? ": " + taxon.getId() : "");
}
public Parameter getTraitParameter(String name) {
return traitParameters.get(name);
}
}
/**
* This class provides bounds for parameters that represent a node height
* in this tree model.
*/
private class NodeHeightBounds implements Bounds<Double> {
public NodeHeightBounds(Parameter parameter) {
nodeHeightParameter = parameter;
}
public Double getUpperLimit(int i) {
Node node = getNodeOfParameter(nodeHeightParameter);
if (node.isRoot()) {
return Double.POSITIVE_INFINITY;
} else {
return node.parent.getHeight();
}
}
public Double getLowerLimit(int i) {
Node node = getNodeOfParameter(nodeHeightParameter);
if (node.isExternal()) {
return 0.0;
} else {
return Math.max(node.leftChild.getHeight(), node.rightChild.getHeight());
}
}
public int getBoundsDimension() {
return 1;
}
private Parameter nodeHeightParameter = null;
}
// ***********************************************************************
// Private members
// ***********************************************************************
/**
* root node
*/
private Node root = null;
private int storedRootNumber;
/**
* list of internal nodes (including root)
*/
private Node[] nodes = null;
private Node[] storedNodes = null;
/**
* number of nodes (including root and tips)
*/
private final int nodeCount;
/**
* number of external nodes
*/
private final int externalNodeCount;
/**
* number of internal nodes (including root)
*/
private final int internalNodeCount;
/**
* holds the units of the trees branches.
*/
private Type units = Type.SUBSTITUTIONS;
private boolean inEdit = false;
private boolean hasRates = false;
private boolean hasTraits = false;
private boolean isTipDateSampled = false;
private final boolean isTreeRandom;
public boolean isTipDateSampled() {
return isTipDateSampled;
}
public boolean isTreeRandom() {
return isTreeRandom;
}
@Override
public Citation.Category getCategory() {
return Citation.Category.TREE_PRIORS;
}
@Override
public String getDescription() {
return "Sampling tip dates model";
}
@Override
public List<Citation> getCitations() {
if (isTipDateSampled()) {
return Arrays.asList(new Citation(
new Author[]{
new Author("B", "Shapiro"),
new Author("SYW", "Ho"),
new Author("AJ", "Drummond"),
new Author("MA", "Suchard"),
new Author("OG", "Pybus"),
new Author("A", "Rambaut"),
},
"A Bayesian phylogenetic method to estimate unknown sequence ages",
2010,
"Mol Biol Evol",
28,
879, 887,
"10.1093/molbev/msq262"
),
new Citation(
new Author[]{
new Author("AJ", "Drummond"),
},
"PhD Thesis",
2002,
"University of Auckland",
""
));
} else {
return Collections.EMPTY_LIST;
}
}
}