/******************************************************************************* * 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.util.ArrayList; import java.util.Comparator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Stack; import org.jgrapht.Graphs; import org.jgrapht.graph.DefaultWeightedEdge; public class CachedRootedTree<V extends CachedVertex,E extends DefaultWeightedEdge> extends RootedTree<V,E> { private static final long serialVersionUID = 1L; protected boolean inSync; public Comparator enclSorter = new CachedEnclosedLeavesComparator(-1); public CachedRootedTree(Class<? extends E> edgeClass) { super(edgeClass); } public V createVertex() { CachedVertex cv = new CachedVertex(); return (V) cv; } private boolean inSync() { return inSync; } /** * Synchronizes each vertex's cached values with the current structure of * the tree. If the tree is already updated (i.e. modID == calcID), then * nothing happens. */ public void sync() { if (holdCalculations) { inSync = false; return; } if (inSync() || root == null) return; calculateStuff(); inSync = true; } boolean holdCalculations; public void setHoldCalculations(boolean holdMe) { holdCalculations = holdMe; } private synchronized List<V> getChildrenOfNoSort(V vertex) { List<V> l; if (useNeighborIndex) { l = new ArrayList<V>(); l.addAll(neighbors.successorsOf(vertex)); } else l = Graphs.successorListOf(this, vertex); return l; } protected void calculateStuff() { if (holdCalculations) return; /* * Everything should be able to be cached by first sweeping from root to * leaves, then from leaves to root. */ /* * STEP 1: ROOT TO LEAVES. */ // Roll our own breadth-first iteration. Stack<V> s = new Stack<V>(); s.add(root); while (!s.isEmpty()) { V v = s.pop(); if (getParentOf(v) == null) { // Set the root-level cached values. v.setDepthToRoot(0); v.setHeightToRoot(0); v.setParent(null); } else { // Normal iteration. V p = getParentOf(v); v.setDepthToRoot(p.getDepthToRoot() + 1); double ew = getEdgeWeight(getEdge(p, v)); v.setBranchLength(ew); v.setHeightToRoot(p.getHeightToRoot() + ew); v.setParent(p); } // Add this vertex's children to the stack. s.addAll(getChildrenOfNoSort(v)); } /* * STEP 2: LEAVES TO ROOT. */ // Load the vertices breadth-first into a linked list. LinkedList<V> traversal = new LinkedList<V>(); /* * Destination linkedlist for the vertices. Root is first, leaves are * last. */ LinkedList<V> dest = new LinkedList<V>(); traversal.add(getRoot()); while (!traversal.isEmpty()) { V o = traversal.removeFirst(); dest.addLast(o); List<V> children = getChildrenOfNoSort(o); for (int i = 0; i < children.size(); i++) { traversal.addLast(children.get(i)); } } while (!dest.isEmpty()) { V cv = dest.removeLast(); if (super.isLeaf(cv)) { // If this vertex is a leaf, set the base cached values. cv.setNumEnclosed(0); cv.setNumLeaves(1); cv.setMaxDepthToLeaf(0); cv.setMaxHeightToLeaf(0); } else { // Regular iteration, building up from the children's cached // values. int numEnc = 0; int numLeaves = 0; int maxDepth = 0; int minChildEnc = Integer.MAX_VALUE; int maxChildEnc = 0; double maxHeight = 0; List<V> children = getChildrenOfNoSort(cv); sortChildrenList(cv, children, enclSorter); for (int i = 0; i < children.size(); i++) { V child = children.get(i); if (child.getNumEnclosed() >= maxChildEnc) { maxChildEnc = child.getNumEnclosed(); // lastChild = child; } if (child.getNumEnclosed() <= minChildEnc) { minChildEnc = child.getNumEnclosed(); // firstChild = child; } numEnc += child.getNumEnclosed() + 1; numLeaves += child.getNumLeaves(); double ew = getEdgeWeight(getEdge(cv, child)); if (child.getMaxHeightToLeaf() + ew > maxHeight) maxHeight = ew + child.getMaxHeightToLeaf(); if (child.getMaxDepthToLeaf() + 1 > maxDepth) maxDepth = child.getMaxDepthToLeaf() + 1; } cv.setMaxChildEnclosed(maxChildEnc); cv.setNumEnclosed(numEnc); cv.setNumLeaves(numLeaves); cv.setMaxDepthToLeaf(maxDepth); cv.setMaxHeightToLeaf(maxHeight); /* * Cache the first and last child. */ cv.setFirstChild(children.get(0)); cv.setLastChild(children.get(children.size() - 1)); } } } @Override public int getMaxChildEnclosed(V vertex) { CachedVertex c = (CachedVertex) vertex; return c.getMaxChildEnclosed(); } @Override public V getFirstChild(V vertex) { sync(); V c = vertex; return (V)c.getFirstChild(); } @Override public V getLastChild(V vertex) { sync(); CachedVertex c = (CachedVertex) vertex; return (V)c.getLastChild(); } @Override public int getDepthToRoot(V vertex) { sync(); if (inSync()) { CachedVertex cv = (CachedVertex) vertex; return cv.getDepthToRoot(); } else return super.getDepthToRoot(vertex); } public double getHeightToRoot(V vertex) { sync(); if (inSync()) { CachedVertex cv = (CachedVertex) vertex; return cv.getHeightToRoot(); } else return super.getHeightToRoot(vertex); } public int getMaxDepthToLeaf(V vertex) { sync(); if (inSync()) { CachedVertex cv = (CachedVertex) vertex; return cv.getMaxDepthToLeaf(); } else return super.getMaxDepthToLeaf(vertex); } public double getMaxHeightToLeaf(V vertex) { sync(); if (inSync()) { CachedVertex cv = (CachedVertex) vertex; return cv.getMaxHeightToLeaf(); } else return super.getMaxHeightToLeaf(vertex); } public synchronized int getNumEnclosedLeaves(V vertex) { sync(); if (inSync()) { CachedVertex cv = (CachedVertex) vertex; return cv.getNumLeaves(); } else return super.getNumEnclosedLeaves(vertex); } @Override public synchronized boolean isLeaf(V vertex) { // sync(); // if (inSync()) // { // CachedVertex cv = (CachedVertex) vertex; // return cv.getNumEnclosed() == 0; // } else return super.isLeaf(vertex); } protected void fireEdgeAdded(E arg0) { modPlus(); super.fireEdgeAdded(arg0); } protected void fireEdgeRemoved(E arg0) { modPlus(); super.fireEdgeRemoved(arg0); } protected void fireVertexAdded(V arg0) { modPlus(); super.fireVertexAdded(arg0); } protected void fireVertexRemoved(V arg0) { modPlus(); super.fireVertexRemoved(arg0); } public void setBranchLength(V vertex, double weight) { modPlus(); super.setBranchLength(vertex, weight); } // @Override // public void reverseSubtree(V vertex) // { //// setHoldCalculations(true); // super.reverseSubtree(vertex); //// setHoldCalculations(false); // } public void modPlus() { super.modPlus(); inSync = false; } @Override public void alignLeaves() { super.alignLeaves(); } @Override public void setBranchLengths(Map<V, Double> branchLengths) { setHoldCalculations(true); super.setBranchLengths(branchLengths); setHoldCalculations(false); } class CachedEnclosedLeavesComparator implements Comparator { int dir; public CachedEnclosedLeavesComparator(int dir) { this.dir = dir; } public int compare(Object o1, Object o2) { V ca = (V) o1; V cb = (V) o2; int a = ca.getNumEnclosed(); int b = cb.getNumEnclosed(); 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); } } }