/*******************************************************************************
* Copyright (c) 2007, 2008 Gregory Jordan
*
* This file is part of PhyloWidget.
*
* PhyloWidget is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 2 of the License, or (at your option) any later
* version.
*
* PhyloWidget 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* PhyloWidget. If not, see <http://www.gnu.org/licenses/>.
*/
package org.phylowidget.tree;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.WeakHashMap;
import org.jgrapht.Graphs;
import org.jgrapht.alg.DirectedNeighborIndex;
import org.jgrapht.graph.DefaultWeightedEdge;
import org.jgrapht.graph.ListenableDirectedWeightedGraph;
import org.jgrapht.traverse.BreadthFirstIterator;
import org.jgrapht.traverse.DepthFirstIterator;
import org.phylowidget.UsefulConstants;
public class RootedTree<V extends DefaultVertex, E extends DefaultWeightedEdge> extends
ListenableDirectedWeightedGraph<V, E>
{
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* This object helps cache the sets of predecessor and successor neighbors
* by listening to changes on this JGraphT object and updating as necessary.
* If you expect to be changing your graph lots and not calling the
* childrenOf() method very often, then you're best setting useNeighborIndex
* to "false" by overriding the setOptions() method and setting it false in
* there.
*/
DirectedNeighborIndex<V, E> neighbors;
/**
* The current root of this rooted tree.
*/
V root;
/**
* This Hashtable keeps track of the sort orders for each node, if they have
* been set to be different from the defalut sort order.
*/
public WeakHashMap<V, Integer> sorting;
public static final Integer REVERSE = new Integer(1);
public static final Integer FORWARD = new Integer(-1);
public static final int REVERSE_I = 1;
public static final int FORWARD_I = -1;
public Comparator<V> enclosedSorter = new EnclosedLeavesComparator(-1);
public Comparator<V> sorter = new Comparator<V>()
{
public int compare(V o1, V o2)
{
if (o1.getClass() == PhyloNode.class && o2.getClass() == PhyloNode.class) {
PhyloNode n1 = (PhyloNode) o1;
PhyloNode n2 = (PhyloNode) o2;
String s1 = n1.getAnnotation(UsefulConstants.CHILD_ORDER);
String s2 = n2.getAnnotation(UsefulConstants.CHILD_ORDER);
if (s1 != null && s2 != null) {
return Integer.parseInt(s1) < Integer.parseInt(s2) ? -1 : 1;
}
}
return o1.getLabel().compareTo(o2.getLabel());
}
};
public Comparator<V> leafSorter = new DepthToRootComparator(1);
private HashSet<V> collapsedNodes = new HashSet<V>();
/*
* ****** OPTIONS ******
*/
/**
* (only relevant for subclassers) If true, then this tree will keep an
* indexed structure of the neighbors of each node's neighbors.
*/
protected boolean useNeighborIndex;
/**
* If true, then this RootedTree will ensure that no two vertices in the
* tree have the same string representation (i.e. the same String resulting
* from calling toString()).
*/
protected boolean enforceUniqueLabels;
/**
* An object which will handle keeping our labels unique.
*/
private UniqueLabeler uniqueLabeler;
private boolean isValid = true;
public boolean isValid()
{
return isValid;
}
Class<? extends E> edgeClass;
public RootedTree(Class<? extends E> edgeClass)
{
// super(new SimpleDirectedWeightedGraph(DefaultWeightedEdge.class));
super(edgeClass);
this.edgeClass = edgeClass;
setOptions();
if (useNeighborIndex)
createNeighborIndex();
sorting = new WeakHashMap<V, Integer>();
isValid = true;
}
void createNeighborIndex()
{
if (neighbors != null)
removeGraphListener(neighbors);
neighbors = new DirectedNeighborIndex<V, E>(this);
addGraphListener(neighbors);
}
/**
* Subclasses should override this method to set any of the "option-like"
* boolean variables provided, such as "useNeighborIndex".
*/
protected void setOptions()
{
useNeighborIndex = true;
setEnforceUniqueLabels(true);
}
/**
* Returns whether this RootedTree enforces unique node labels or not.
*/
public boolean getEnforceUniqueLabels()
{
return enforceUniqueLabels;
}
/**
*
* Set this RootedTree to either enforce or not enforce unique node labels.
*
* @param enforceUniqueLabels
* If true, then this RootedTree will ensure that node labels are
* unique.
*/
public void setEnforceUniqueLabels(boolean enforceUniqueLabels)
{
this.enforceUniqueLabels = enforceUniqueLabels;
if (enforceUniqueLabels)
{
uniqueLabeler = new UniqueLabeler();
uniqueLabeler.resetVertexLabels(this);
} else
{
uniqueLabeler.removeDuplicateTags(this);
}
}
public boolean isLabelSignificant(String s)
{
if (enforceUniqueLabels)
{
return uniqueLabeler.isLabelSignificant(s);
} else
return s.length() > 0;
}
/**
* Checks the current Rooted Tree for existence of the label attached to the
* vertex, and if it already exists in the tree, returns a new unique label.
*/
public String getLabel(V vertex)
{
if (isCollapsed(vertex))
{
List<V> kids = getChildrenOf(vertex);
int numLeaves = 0;
for (V n : kids)
{
numLeaves += getNumEnclosedLeaves(n);
}
return vertex.getLabel() + " (" + numLeaves + " leaves)";
}
return vertex.getLabel();
}
public void setLabel(Object vertex, String label)
{
if (enforceUniqueLabels)
{
uniqueLabeler.changeLabel(vertex, label);
} else if (vertex instanceof Labelable)
{
Labelable v = (Labelable) vertex;
v.setLabel(label);
}
}
public RootedTree<V, E> extractSubtree(V... vertices)
{
// TODO: Implement me!
return null;
}
public int getNumLineagesAtHeight(double height)
{
List<V> nodes = getAllNodes(getRoot());
int numLineages = 0;
for (V v : nodes)
{
// Look for nodes whose parental branch covers the depth we're looking for.
double curHeight = getHeightToRoot(v);
double heightToParent = getBranchLength(v);
if (curHeight > height && heightToParent < height)
numLineages++;
}
return numLineages;
}
public List<V> getNodesAtHeight(double height)
{
List<V> nodes = getAllNodes(getRoot());
ArrayList<V> keepers = new ArrayList<V>(nodes.size());
for (V v : nodes)
{
// Look for nodes whose parental branch covers the depth we're looking for.
double curHeight = getHeightToRoot(v);
double heightToParent = getBranchLength(v);
if (curHeight > height && heightToParent < height)
keepers.add(v);
}
return keepers;
}
public List<V> getVerticesForLabels(Collection<String> labels)
{
ArrayList<V> verts = new ArrayList<V>(labels.size());
for (String label : labels)
{
V v = getVertexForLabel(label);
if (v != null)
verts.add(v);
}
return verts;
}
public List<V> getAllLeaves()
{
return getAllLeaves(getRoot());
}
public List<V> getAllNodes()
{
return getAllNodes(getRoot());
}
public List<V> getAllLeaves(V vertex)
{
List<V> leaves = new ArrayList<V>();
getAll(vertex, leaves, null);
return leaves;
}
public List<V> getAllNodes(V vertex)
{
List<V> nodes = new ArrayList<V>();
getAll(vertex, null, nodes);
return nodes;
}
public List<String> getLabelsForVertices(Collection<V> verts)
{
ArrayList<String> labels = new ArrayList<String>(verts.size());
for (V vert : verts)
{
labels.add(getLabel(vert));
}
return labels;
}
public V getVertexForLabel(String label)
{
if (enforceUniqueLabels)
{
V o = (V) uniqueLabeler.getNodeForLabel(label);
return o;
} else
{
DepthFirstIterator<V, E> it = new DepthFirstIterator<V, E>(this, getRoot());
while (it.hasNext())
{
V vertex = it.next();
if (getLabel(vertex).equals(label))
{
return vertex;
}
}
return null;
}
}
public double getBranchLength(V vertex)
{
V parent = getParentOf(vertex);
return getEdgeWeight(getEdge(parent, vertex));
}
public void setBranchLength(V vertex, double length)
{
V parent = getParentOf(vertex);
if (parent == null)
return;
E edge = getEdge(parent, vertex);
setEdgeWeight(edge, length);
}
public void resetVertexLabels()
{
uniqueLabeler.resetVertexLabels(this);
}
/**
* A "factory" method for creating node objects. Currently it just returns
* the string given as input, but it could be extended by a subclass to
* create a node object that holds more detailed information. These objects
* will be the "vertex" objects inserted into the JGraphT structure.
* <p>
* It's probably useful to note that inserting larger objects into the
* JGraphT won't slow it down or make it take anymore memory; it simply
* stores a hashtable of references back to these vertex objects, and does
* all the internal stuff using its own internal data structures.
*
* @param label
* @return
*/
public V createVertex()
{
DefaultVertex v = new DefaultVertex();
return (V) v;
}
public boolean addVertex(V o)
{
if (super.addVertex(o))
{
if (enforceUniqueLabels)
uniqueLabeler.addLabel(o);
return true;
}
return false;
}
public boolean removeVertex(V o)
{
if (super.removeVertex(o))
{
if (enforceUniqueLabels)
uniqueLabeler.removeLabel(o);
return true;
}
return false;
}
public V createAndAddVertex()
{
V newV = createVertex();
addVertex(newV);
return newV;
}
public boolean isLeaf(V vertex)
{
if (isCollapsed(vertex))
return true;
return (outDegreeOf(vertex) == 0);
}
public List<V> getChildrenOf(V vertex)
{
List<V> l;
if (useNeighborIndex)
{
l = new ArrayList<V>();
l.addAll(neighbors.successorsOf(vertex));
} else
l = Graphs.successorListOf(this, vertex);
return sortChildrenList(vertex, l, sorter);
}
public void modPlus()
{
}
public int getModCount()
{
return 0;
}
List<V> sortChildrenList(V vertex, List<V> l, Comparator<V> sorter)
{
// Sort the resulting list.
Collections.sort(l, sorter);
// if (sorting.containsKey(vertex))
// {
// if (sorting.get(vertex) == REVERSE)
// Collections.reverse(l);
// }
if (getSorting(vertex) == REVERSE_I)
{
Collections.reverse(l);
}
return l;
}
public DepthFirstIterator<V, E> getDepthFirstIterator()
{
return getDepthFirstIterator(getRoot());
}
public DepthFirstIterator<V, E> getDepthFirstIterator(V v)
{
return new DepthFirstIterator<V, E>(this,v);
}
public V getFirstLeaf(V vertex)
{
V cur = vertex;
while (!isLeaf(cur))
{
cur = getFirstChild(cur);
}
return cur;
}
public V getFirstChild(V vertex)
{
return getChildrenOf(vertex).get(0);
}
public V getLastChild(V vertex)
{
List<V> l = getChildrenOf(vertex);
return l.get(l.size());
}
public V getLastLeaf(V vertex)
{
V cur = vertex;
while (!isLeaf(cur))
{
cur = getLastChild(cur);
}
return cur;
}
public V getParentOf(V child)
{
// Special case: this vertex has no parents, i.e. it is the root.
if (inDegreeOf(child) == 0)
return null;
if (!useNeighborIndex)
{
Set<E> edgeSet = incomingEdgesOf(child);
Iterator<E> i = edgeSet.iterator();
E e = i.next();
return getEdgeSource(e);
} else
{
Set<V> parentSet = neighbors.predecessorsOf(child);
return parentSet.iterator().next();
}
}
public boolean isRoot(V vertex)
{
return getParentOf(vertex) == null;
}
public boolean isParentChild(V parent, V child)
{
V v = child;
while (v != null)
{
if (v == parent)
return true;
v = getParentOf(v);
}
return false;
}
public String getNewick()
{
return TreeIO.createNewickString(this);
}
public String getNeXML()
{
return TreeIO.createNeXMLString(this);
}
public String getNHX()
{
return TreeIO.createNHXString(this);
}
public int getMaxDepthToLeaf(V vertex)
{
int maxDepth = 0;
BreadthFirstIterator<V, E> bfi = new BreadthFirstIterator<V, E>(this, vertex);
while (bfi.hasNext())
{
V o = bfi.next();
if (isLeaf(o))
{
int curDepth = getDepthToVertex(o, vertex);
if (curDepth > maxDepth)
maxDepth = curDepth;
}
}
return maxDepth;
}
public int getDepthToRoot(V vertex)
{
if (vertex == getRoot())
return 0;
return getDepthToVertex(vertex, getRoot());
}
int getDepthToVertex(V vertex, V target)
{
return Math.abs(getDepthToRoot(vertex) - getDepthToRoot(target));
// int depth = 0;
// while (vertex != target)
// {
// depth += 1;
// vertex = getParentOf(vertex);
// if (vertex == getRoot() || vertex == null)
// return depth;
// }
// if (depth > 0)
// return depth;
// return -depth;
}
// Note that this method doesn't check whether target is a descendant of vertex.
public double getHeightToVertex(V vertex, V target)
{
double vHeight = getHeightToRoot(vertex);
double tHeight = getHeightToRoot(target);
return tHeight - vHeight;
}
public V getFurthestLeafFromVertex(V vertex)
{
double maxHeight = 0;
V furthestVertex = vertex;
BreadthFirstIterator<V, E> bfi = new BreadthFirstIterator<V, E>(this, vertex);
while (bfi.hasNext())
{
V o = bfi.next();
if (isLeaf(o))
{
double curHeight = getHeightToRoot(o);
if (curHeight >= maxHeight)
{
maxHeight = curHeight;
furthestVertex = o;
}
}
}
return furthestVertex;
}
public double getMaxTreeLength()
{
return getMaxHeightToLeaf(getRoot());
}
public double getTotalTreeLength()
{
List<V> nodes = getAllNodes();
double totalLength = 0;
for (V node : nodes)
{
totalLength += getBranchLength(node);
}
return totalLength;
}
public double getMaxHeightToLeaf(V vertex)
{
double vertexHeight = getHeightToRoot(vertex);
double maxHeight = 0;
BreadthFirstIterator<V, E> bfi = new BreadthFirstIterator<V, E>(this, vertex);
while (bfi.hasNext())
{
V o = bfi.next();
if (isLeaf(o))
{
double curHeight = getHeightToRoot(o);
if (curHeight >= maxHeight)
maxHeight = curHeight;
}
}
// System.out.println("mh:"+maxHeight);
return maxHeight - vertexHeight;
}
public double getHeightToRoot(V vertex)
{
double height = 0;
while (vertex != root)
{
V parent = getParentOf(vertex);
E edge = getEdge(parent, vertex);
height += getEdgeWeight(edge);
// System.out.println("v:"+vertex+" p:"+parent+"
// w:"+getEdgeWeight(edge));
vertex = parent;
}
return height;
}
public double getMya(V vertex)
{
double maxHeight = this.getMaxHeightToLeaf(this.getRoot());
double heightToRoot = this.getHeightToRoot(vertex);
if (Math.abs(heightToRoot-maxHeight) < maxHeight/100000)
return 0;
return maxHeight - heightToRoot;
}
public synchronized int getLeafCount()
{
return getNumEnclosedLeaves(getRoot());
}
public synchronized int getNodeCount()
{
return getAllNodes().size();
}
public synchronized int getNumEnclosedLeaves(V vertex)
{
return getEnclosedLeaves(vertex).size();
}
public int getMaxChildEnclosed(V vertex)
{
if (isLeaf(vertex))
return 0;
List<V> children = getChildrenOf(vertex);
int max = 0;
for (int i = 0; i < children.size(); i++)
{
int cur = getNumEnclosedLeaves(children.get(i));
if (cur > max)
{
max = cur;
}
}
return max;
}
/**
* A method for retrieving all nodes below a given vertex in a tree. The
* "leaves" and "nodes" List objects (which must have already been created
* by the caller) will be filled with all the appropriate nodes.
*
* @param vertex
* @param leaves
* @param nodes
*/
public synchronized void getAll(V vertex, List<V> leaves, List<V> nodes)
{
if (vertex == null)
return;
Stack<V> s = new Stack();
s.push(vertex);
while (!s.isEmpty())
{
V v = s.pop();
// Special case: collapsed non-leaf node.
if (isLeaf(v))
{
if (leaves != null)
leaves.add(v);
} else
{
List<V> children = getChildrenOf(v);
for (int i = children.size() - 1; i >= 0; i--)
{
s.add(children.get(i));
}
}
if (nodes != null)
nodes.add(v);
}
}
private List<V> getEnclosedVertices(V vertex)
{
ArrayList<V> l = new ArrayList<V>();
BreadthFirstIterator<V, E> bfi = new BreadthFirstIterator<V, E>(this, vertex);
while (bfi.hasNext())
{
l.add(bfi.next());
}
return l;
}
private List<V> getEnclosedLeaves(V vertex)
{
ArrayList<V> l = new ArrayList<V>();
BreadthFirstIterator<V, E> bfi = new BreadthFirstIterator<V, E>(this, vertex);
while (bfi.hasNext())
{
V o = bfi.next();
if (isLeaf(o))
l.add(o);
}
return l;
}
public synchronized void deleteSubtree(V vertex)
{
if (vertex == getRoot())
{
V newRoot = createAndAddVertex();
setRoot(newRoot);
}
if (isLeaf(vertex))
{
removeVertex(vertex);
} else
{
List<V> nodes = getEnclosedVertices(vertex);
synchronized (this)
{
for (int i = 0; i < nodes.size(); i++)
{
removeVertex(nodes.get(i));
}
}
}
}
public void deleteLeafLineage(V vertex)
{
V parent = getParentOf(vertex);
removeVertex(vertex);
if (parent != null)
{
if (isLeaf(parent))
{
deleteLeafLineage(parent);
}
}
}
public synchronized void deleteNode(V vertex)
{
if (getParentOf(vertex) != null)
{
if (!isLeaf(vertex))
{
V parent = getParentOf(vertex);
double weightToParent = getEdgeWeight(getEdge(parent, vertex));
List<V> children = getChildrenOf(vertex);
for (int i = 0; i < children.size(); i++)
{
V child = children.get(i);
double weight = getEdgeWeight(getEdge(vertex, child));
E edge = addEdge(parent, child);
setEdgeWeight(edge, weightToParent + weight);
}
}
removeVertex(vertex);
} else
{
V newRoot = createAndAddVertex();
setRoot(newRoot);
removeVertex(vertex);
}
}
public RootedTree<V, E> cloneSubtree(V vertex)
{
RootedTree<V, E> newTree;
try
{
Constructor<? extends RootedTree> c = this.getClass().getConstructor();
newTree = c.newInstance();
} catch (Exception e)
{
e.printStackTrace();
return null;
}
/*
* The basic idea here is to go through the current subtree, and
* basically mirror the same actions in the new tree, creating vertices
* and edges as needed.
*/
Stack<V> myStack = new Stack(); // One for me...
Stack<V> newStack = new Stack(); // One for you.
// Initialize the traversal with the designated start vertex.
myStack.push(vertex);
V newRoot = newTree.createAndAddVertex();
newRoot.setLabel(vertex.getLabel());
newTree.setRoot(newRoot);
newStack.push(newRoot);
while (!myStack.isEmpty())
{
V parent = myStack.pop();
V newParent = newStack.pop();
List<V> list = getChildrenOf(parent);
for (int i = 0; i < list.size(); i++)
{
V thisChild = list.get(i);
myStack.push(thisChild);
double thisWeight = getEdgeWeight(getEdge(parent, thisChild));
// Now do the same (while creating stuff) for the cloned tree.
// V newChild = newTree.createAndAddVertex();
V newChild = (V) thisChild.clone();
newTree.addVertex(newChild);
newStack.push(newChild);
E e = newTree.addEdge(newParent, newChild);
newTree.setEdgeWeight(e, thisWeight);
}
}
return newTree;
}
/**
* Adds a new sister node to this vertex. In order to do this and maintain a
* relatively bifurcated tree, this method creates a new node <em>above</em>
* the given vertex, and adds a new sister node to the newly created
* "parent" node.
*
* @param v
*/
public void addSisterNode(V v, V newSister)
{
V curParent = getParentOf(v);
V newParent = createAndAddVertex();
if (v == getRoot())
{
setRoot(newParent);
addEdge(newParent, newSister);
addEdge(newParent, v);
} else
{
insertNodeBetween(curParent, v, newParent);
}
addEdge(newParent, newSister);
double ew = getEdgeWeight(getEdge(newParent, v));
setEdgeWeight(getEdge(newParent, newSister), ew);
modPlus();
}
/**
* Adds an "anonymous" child node below the given vertex.
*
* @param v
*/
public void addChildNode(V v)
{
V newNode = createAndAddVertex();
addEdge(v, newNode);
}
/**
* Inserts vertex insertMe at the midpoint of the edge between a and b. Note
* that insertMe must have already been created using the
* createVertex(Object) method of this RootedTree.
*
* @param a
* the parent node (edge source)
* @param b
* the child node (edge target)
* @param insertMe
*/
public void insertNodeBetween(V a, V b, V insertMe)
{
E e = getEdge(a, b);
double weight = getEdgeWeight(e);
E aToNew = addEdge(a, insertMe); //
setEdgeWeight(aToNew, weight / 2);
E newToB = addEdge(insertMe, b);
setEdgeWeight(newToB, weight / 2);
removeEdge(a, b);
modPlus();
}
/**
* Reroots this RootedTree using the midpoint method. The pivot edge is
* taken to be the edge between the given vertex (called "pivot") and its
* parent. See the inline comments for more information on the algorithm and
* steps involved.
*
* @param pivot
*/
public void reroot(V pivot)
{
isValid = false;
// System.out.println("Rerooting tree...");
// System.out.println("Step 1...");
// System.out.println(this);
if (pivot == root || getParentOf(pivot) == root)
return;
/**
* Ok, let's do this thang.
*
* Here's the plan:
* <p>
* 1. Create the new root using the midpoint method. Take the edge
* between pivot and parentOf(pivot) and create a new vertex in its
* midpoint.
* <p>
* 2. Get rid of the current root, by removing it and re-attaching its
* children to the child in the direction of the new root. Sum the edge
* lengths to make it all stay correct.
* <p>
* 3. Re-orient all the edges in the tree to point away from the root.
*/
E e = null;
V v = null;
// STEP 1: Remove the current root and reconnect the top-level vertices.
//
// Find the child that "points" to the new root from the current root.
v = pivot;
while (getParentOf(v) != null)
{
if (getParentOf(v) == root)
break;
v = getParentOf(v);
}
// V should now be the child that leads from root to newRoot.
double lengthB = getEdgeWeight(getEdge(root, v));
List<V> l = getChildrenOf(root);
int origRootChildCount = l.size();
// if (l.size() == 2) {
// for (int i = 0; i < l.size(); i++)
// {
// V child = l.get(i);
// if (child == v) // If this is the child that "points", continue.
// continue;
// // Get the edge length between root and this child.
// e = getEdge(root, child);
// double lengthA = getEdgeWeight(e);
// // Create the new edge between v and child.
// removeEdge(v, child);
// e = addEdge(v, child);
// setEdgeWeight(e, lengthA + lengthB);
// }
// removeVertex(root);
// }
// Remove the root vertex and all its touching edges.
// System.out.println("Step 2...");
// System.out.println(this);
// STEP 2: Create the new root.
//
// Add the new root vertex.
V newRoot = createAndAddVertex();
// Capture the length of the edge above the pivot vertex.
insertNodeBetween(getParentOf(pivot), pivot, newRoot);
root = newRoot;
// System.out.println("Step 3...");
// System.out.println(this);
// STEP 3: Re-orient all edges, branching out from the root edge.
//
// Iterate over an undirected version of this graph, so we can "go
// against the grain" when we need to.
BreadthFirstIterator<V, E> bfi = new BreadthFirstIterator<V, E>(Graphs.undirectedGraph(this), root);
// Toss all the nodes into a linked list.
LinkedList<V> linked = new LinkedList();
while (bfi.hasNext())
{
linked.addLast(bfi.next());
}
// Now, go through the list of nodes, re-orienting edges as needed.
// Set the root node as the first parent in our algorithm.
HashMap<V, Integer> seen = new HashMap();
Integer stupidInt = new Integer(1);
while (!linked.isEmpty())
{
V curNode = linked.removeFirst();
/*
* Here we grab all the edges touching curNode and iterate through
* them. We can't just use the Set's iterator because we'll be
* modifying the graph structure as we go -- this causes bad stuff
* to happen in the graph-backed iterator.
*/
List<V> list = Graphs.neighborListOf(this, curNode);
for (int i = 0; i < list.size(); i++)
{
V child = list.get(i);
if (seen.containsKey(child)) // Skip if node already seen.
continue;
// Capture the edge length.
double edgeWeight = 1;
if (containsEdge(curNode, child))
edgeWeight = getEdgeWeight(getEdge(curNode, child));
else if (containsEdge(child, curNode))
edgeWeight = getEdgeWeight(getEdge(child, curNode));
// Delete the edges between these nodes.
removeEdge(curNode, child);
removeEdge(child, curNode);
// Insert a correctly-oriented edge.
e = addEdge(curNode, child);
setEdgeWeight(e, edgeWeight);
}
seen.put(curNode, stupidInt);
}
if (origRootChildCount == 2) {
removeElbowsBelow(getRoot());
}
isValid = true;
}
/**
* Flips the children directly below the given vertex.
*
* A note on implementation: the RootedTree object contains a HashMap that
* keeps track of a sort value for each node. This can be either FORWARD or
* REVERSE, where FORWARD is the default sort. Note that this means we
* cannot arbitrarily shuffle the children of a vertex if there are more
* than 3. However, for most phylogenetic purposes, this is a rare
* occurrence and is not worth the extra effort.
*
* @param parent
* the vertex whose direct children to flip.
*/
public void flipChildren(V parent)
{
// If we've already stored a sorting value, then toggle it.
Integer o = sorting.get(parent);
if (o == null)
setSorting(parent, REVERSE);
else if (o == REVERSE)
setSorting(parent, FORWARD);
else
setSorting(parent, REVERSE);
}
/**
* Reverses the entire subtree below a given vertex.
*
* @param vertex
*/
public void reverseSubtree(V vertex)
{
ArrayList<V> nodes = new ArrayList<V>();
getAll(vertex, null, nodes);
for (V node : nodes)
{
flipChildren(node);
}
modPlus();
}
/**
* Ladderizes the tree below the given vertex. Ladderizing is equivalent to
* "re-sorting" the tree, where the vertices below a given vertex are sorted
* by the their number of total enclosed leaf vertices.
*
* @param vertex
*/
public void ladderizeSubtree(V vertex)
{
BreadthFirstIterator<V, E> bfi = new BreadthFirstIterator<V, E>(this, vertex);
while (bfi.hasNext())
{
V o = bfi.next();
List<V> children = getChildrenOf(o); // Uses character sorting.
if (children.size() > 0)
{
V first = children.get(0);
sortChildrenList(o, children, enclosedSorter);
// Collections.sort(children, enclosedSorter); // Uses enclosed leaves sorting.
V first2 = children.get(0);
if (first != first2)
setSorting(o, REVERSE);
else
setSorting(o, FORWARD);
} else
{
setSorting(o,FORWARD);
}
}
}
int fInt = FORWARD.intValue();
public void setSorting(V vertex, int direction)
{
if (fInt == direction)
sorting.put(vertex, FORWARD);
else
sorting.put(vertex, REVERSE);
}
public int getSorting(V v)
{
Integer i = sorting.get(v);
if (i == null || i == FORWARD)
return FORWARD_I;
else
return REVERSE_I;
}
/*
* Removes "elbowed" nodes below the given vertex, but does NOT remove nodes
* that are contained in the doNotRemove list.
*/
public void removeElbowsBelow(V vertex,List<V> doNotRemove)
{
HashSet<V> keeperSet = new HashSet<V>();
if (doNotRemove != null)
keeperSet.addAll(doNotRemove);
// Continue if this node was flagged as a keeper.
for (V v : keeperSet)
{
if (!this.containsVertex(v)) // Because we're removing things from the tree as we go through the keepers, we need to check for existence.
continue;
// GJ 2009-03-03 : Delete other nodes until this guy isn't an elbow anymore.
while (isElbow(v))
{
V child = getFirstChild(v);
if (keeperSet.contains(child) && !isLeaf(child)) // Keep the child if it is ALSO a keeper (strange, but should work.)
{
continue;
} else if (isLeaf(child))
{
break;
} else
{
deleteNode(child);
}
}
}
List<V> list = getAllNodes(vertex);
for (int i = 0; i < list.size(); i++)
{
V o = list.get(i);
if (getParentOf(o) != null && getChildrenOf(o).size() == 1)
{
V parent = getParentOf(o);
V child = getChildrenOf(o).get(0);
double edgeWeight = getEdgeWeight(getEdge(parent, o));
edgeWeight += getEdgeWeight(getEdge(o, child));
removeVertex(o);
addEdge(parent, child);
setEdgeWeight(getEdge(parent, child), edgeWeight);
} else if (getParentOf(o) == null && getChildrenOf(o).size() == 1)
{
V child = getChildrenOf(o).get(0);
setBranchLength(child, 0);
setRoot(child);
removeVertex(o);
}
}
}
public boolean isElbow(V vertex)
{
return getParentOf(vertex) != null && getChildrenOf(vertex).size() == 1;
}
/**
* Removes "elbowed" nodes from the subtree below the given vertex. An
* elbowed node is defined as a node that has a single parent and a single
* child.
*
* @param vertex
* the node at which to begin culling
*/
public void removeElbowsBelow(V vertex)
{
removeElbowsBelow(vertex, null);
}
/**
* @return the current root of this RootedTree.
*/
public V getRoot()
{
return root;
}
/**
* Sets the root vertex of this tree. Note that this DOES NOT reroot the
* tree using the midpoint algorithm. For that operation, see
* reroot(object).
*
* @param newRoot
*/
public void setRoot(V newRoot)
{
// if (!containsVertex(newRoot))
// {
// addVertex(newRoot);
// }
root = newRoot;
}
/**
* Aligns the leaves of the tree, adjusting branch lengths accordingly.
*
* General algorithm is this: - Start at root. - For each node: - Find the
* mean height-to-leaf among children nodes. - Scale each child node's
* branch length accordingly, then iterate.
*/
public void alignLeaves()
{
DepthFirstIterator<V, E> it = new DepthFirstIterator<V, E>(this, getRoot());
LinkedList<V> list = new LinkedList<V>();
while (it.hasNext())
{
list.add(it.next());
}
while (!list.isEmpty())
{
V v = list.removeLast();
if (isLeaf(v))
continue;
List<V> children = getChildrenOf(v);
double totalHeight = 0;
for (V child : children)
{
double below = getMaxHeightToLeaf(child);
double above = getBranchLength(child);
totalHeight += below + above;
}
totalHeight /= children.size();
for (V child : children)
{
double below = getMaxHeightToLeaf(child);
double above = getBranchLength(child);
if (below + above == 0)
below = 0.00001;
double childScale = totalHeight / (below + above);
scaleSubtree(child, childScale);
}
}
}
public void resolvePolytomy(V v, double maxDistanceToSpread)
{
List<V> children = getChildrenOf(v);
if (children.size() <= 2)
{
return; // Dummy check -- exit if we have a bifurcation.
}
// Find the smallest branch length of either myself or all my children.
double minBranchLength = getBranchLength(v);
for (V child : children)
{
double bl = getBranchLength(child);
if (bl < minBranchLength)
minBranchLength = bl;
}
double resolutionWindow = minBranchLength / 3;
resolutionWindow = Math.min(resolutionWindow, maxDistanceToSpread);
// Choose the "first" child.
V child = children.get(0);
// Create a new node to go below V.
V newV = createAndAddVertex();
addEdge(v, newV);
setBranchLength(newV, resolutionWindow);
setBranchLength(v, getBranchLength(v) - resolutionWindow / 2);
for (V child2 : children)
{
if (child2 == child)
{
double bl = getBranchLength(child);
setBranchLength(child,bl+resolutionWindow/2);
continue;
}
double bl = getBranchLength(child2);
removeEdge(v, child2);
addEdge(newV, child2);
setBranchLength(child2, bl - resolutionWindow / 2);
}
modPlus();
resolvePolytomy(newV);
}
public void resolvePolytomy(V v)
{
double maxResolutionDistance = Double.MAX_VALUE;
resolvePolytomy(v,maxResolutionDistance);
}
public V getCommonAncestorOf(String... nodeLabels)
{
ArrayList<String> labels = new ArrayList<String>();
Collections.addAll(labels, nodeLabels);
List<V> nodes = getVerticesForLabels(labels);
if (nodes.size() == 0)
return null;
return getCommonAncestorOf(nodes.toArray(emptyArray(nodes.get(0))));
}
private V[] emptyArray(V... array)
{
V[] a = (V[]) java.lang.reflect.Array.newInstance(array.getClass().getComponentType(), 0);
return a;
}
public V getCommonAncestorOf(V... nodes)
{
V a = nodes[0];
V b = null;
for (int i = 1; i < nodes.length; i++)
{
b = nodes[i];
a = getCommonAncestorOf(a, b);
}
return a;
}
public V getCommonAncestorOf(String labelA, String labelB)
{
return getCommonAncestorOf(getVertexForLabel(labelA), getVertexForLabel(labelB));
}
public V getCommonAncestorOf(V a, V b)
{
HashSet<V> seenAncestors = new HashSet<V>();
V ancA = a;
V ancB = b;
V root = getRoot();
while (true) // Maybe we should have some fail-safe cutoff here.
{
if (ancA != root && ancA != null)
{
if (seenAncestors.contains(ancA))
return ancA;
seenAncestors.add(ancA);
ancA = getParentOf(ancA);
}
if (ancB != root && ancB != null)
{
if (seenAncestors.contains(ancB))
return ancB;
seenAncestors.add(ancB);
ancB = getParentOf(ancB);
}
if (ancA == root && ancB == root)
return ancA;
if (ancA == null)
return null;
}
}
public void collapseNode(V v)
{
collapsedNodes.add(v);
modPlus();
}
public void uncollapseNode(V v)
{
collapsedNodes.remove(v);
modPlus();
}
public boolean isCollapsed(V v)
{
return collapsedNodes.contains(v);
}
public List<V> getAllCollapsedNodes()
{
ArrayList<V> list = new ArrayList<V>();
List<V> allN = getAllNodes(getRoot());
for (V v : allN)
{
if (isCollapsed(v))
list.add(v);
}
return list;
}
public void uncollapseAllNodes()
{
DepthFirstIterator<V, E> it = new DepthFirstIterator<V, E>(this, getRoot());
it.next(); // Should I be doing this?
while (it.hasNext())
{
V curV = it.next();
if (isCollapsed(curV))
{
uncollapseNode(curV);
}
}
}
public void scaleSubtree(V v, double scale)
{
DepthFirstIterator<V, E> it = new DepthFirstIterator<V, E>(this, v);
it.next(); // Should I be doing this?
while (it.hasNext())
{
V curV = it.next();
setBranchLength(curV, getBranchLength(curV) * scale);
}
}
public void evenlySpaceLineage(V start, V end)
{
double totalHeight = getHeightToVertex(start, end);
double depth = getDepthToVertex(start, end);
double evenStep = totalHeight / depth;
V v = end;
while (v != start)
{
setBranchLength(v, evenStep);
v = getParentOf(v);
}
}
public void logTransform(V v, double factor)
{
double totalLength = getTotalTreeLength();
// First, store a HashMap of each node and its log-transformed distance-to-root.
List<V> nodes = getAllNodes();
double maxMya = 0;
HashMap<V,Double> distances = new HashMap<V,Double>();
for (V node : nodes)
{
double mya = getMya(node);
if (mya > maxMya)
maxMya = mya;
}
for (V node : nodes)
{
double mya = getMya(node);
distances.put(node, Math.log(mya+factor));
}
this.setBranchLengthsFromMyaMatrix(distances);
// Re-scale the tree so it has the same total branch length.
double newTotalLength = getTotalTreeLength();
this.scaleSubtree(getRoot(), totalLength/newTotalLength);
}
public void makeSubtreeUltrametric(V v)
{
makeSubtreeUltrametric(v, 1, false);
}
public void makeSubtreeUltrametric(V v, double totalHeight, boolean changeCurrentLength)
{
// double subtreeHeight = getMaxHeightToLeaf(v);
double subtreeDepth = getMaxDepthToLeaf(v);
if (changeCurrentLength)
subtreeDepth = getMaxDepthToLeaf(getParentOf(v));
double step = totalHeight / (subtreeDepth);
// System.out.println("sHeight: "+subtreeHeight+" sDepth:"+subtreeDepth+" step:"+step);
HashMap<V, Double> branchLengths = new HashMap<V, Double>();
DepthFirstIterator<V, E> it = new DepthFirstIterator<V, E>(this, v);
while (it.hasNext())
{
V curV = it.next();
if (curV == v && !changeCurrentLength)
continue;
if (isLeaf(curV))
{
double curDepth = getDepthToVertex(curV, v);
double curHeight = curDepth * step;
double desiredHeight = totalHeight - curHeight;
branchLengths.put(curV, desiredHeight);
} else
{
branchLengths.put(curV, step);
}
}
setBranchLengths(branchLengths);
}
public void setBranchLengths(Map<V, Double> branchLengths)
{
Set<V> set = branchLengths.keySet();
for (V v : set)
{
setBranchLength(v, branchLengths.get(v));
}
}
public void pruneNodes(List<V> vertices)
{
int i = 0;
for (V v : vertices)
{
i++;
if (isLeaf(v))
deleteLeafLineage(v);
else
deleteNode(v);
}
}
public void setBranchLengthsFromMyaMatrix(Map<V,Double> nodeMyas)
{
Collection<Double> ages = nodeMyas.values();
double maxMya = Collections.max(ages);
DepthFirstIterator<V, E> it = new DepthFirstIterator<V, E>(this, getRoot());
while (it.hasNext())
{
V vertex = it.next();
double nodeAge = nodeMyas.get(vertex);
setBranchLength(vertex,0);
double currentHeightToRoot = getHeightToRoot(vertex);
double desiredHeightToRoot = maxMya - nodeAge;
double change = desiredHeightToRoot - currentHeightToRoot;
setBranchLength(vertex,change);
}
}
public void translateLabels(V v, Map<String, String> oldToNew)
{
DepthFirstIterator<V, E> it = new DepthFirstIterator<V, E>(this, v);
while (it.hasNext())
{
V vertex = it.next();
String oldS = getLabel(vertex);
String newS = oldToNew.get(oldS);
if (newS == null)
{
continue;
}
setLabel(vertex, newS);
}
}
public void pruneNodesByLabel(List<String> vertexLabels)
{
List<V> verts = getVerticesForLabels(vertexLabels);
for (V v : verts)
{
deleteSubtree(v);
}
}
public String getAnnotation(V vertex, String key)
{
return null;
}
public void clearAnnotation(V vertex, String key)
{
// Do nothing.
}
public void fixSortingByAnnotation(String annotation)
{
List<V> nodes = getAllNodes();
for (V v : nodes)
{
V lastChild = getLastChild(v);
String s = getAnnotation(lastChild,annotation);
if (s != null)
{
setSorting(v,getSorting(v)*-1);
}
}
}
public void dispose()
{
sorting = null;
neighbors = null;
root = null;
uniqueLabeler = null;
}
class VertexAndDouble
{
public V v;
public double d;
public VertexAndDouble(V v, double d)
{
this.v = v;
this.d = d;
}
}
public class DepthToRootComparator implements Comparator
{
int dir;
public DepthToRootComparator(int dir)
{
this.dir = dir;
}
public int compare(Object o1, Object o2)
{
if (o1.getClass() == PhyloNode.class)
{
String z1 = ((PhyloNode) o1).getAnnotation(UsefulConstants.Z_ORDER);
String z2 = ((PhyloNode) o2).getAnnotation(UsefulConstants.Z_ORDER);
if (z1 != null && z2 == null)
return 1 * -dir;
else if (z2 != null && z1 == null)
return -1 * -dir;
else if (z1 != null && z2 != null)
{
int z1i = Integer.parseInt(z1);
int z2i = Integer.parseInt(z2);
if (z1i == z2i)
return 0;
else if (z1i > z2i)
return 1 * -dir;
else
return -1 * -dir;
}
}
int a = getDepthToRoot((V) o1);
int b = getDepthToRoot((V) o2);
if (dir == 1)
{
if (a > b)
return 1;
else if (a < b)
return -1;
else
return 0;
} else
{
if (a > b)
return -1;
else if (a < b)
return 1;
else
return 0;
}
}
}
public class EnclosedLeavesComparator implements Comparator
{
int dir;
public EnclosedLeavesComparator(int dir)
{
this.dir = dir;
}
public int compare(Object o1, Object o2)
{
int a = getNumEnclosedLeaves((V) o1);
int b = getNumEnclosedLeaves((V) o2);
if (dir == 1)
{
if (a > b)
return 1;
else if (a < b)
return -1;
else
return 0;
} else
{
if (a > b)
return -1;
else if (a < b)
return 1;
else
return 0;
}
}
public boolean equals(Object o1, Object o2)
{
return (compare(o1, o1) == 0);
}
}
}