[med-svn] [Git][med-team/jebl2][master] 8 commits: New upstream version 0.1+git20230701.b3c0f25
Pierre Gruet (@pgt)
gitlab at salsa.debian.org
Sat Oct 7 22:24:26 BST 2023
Pierre Gruet pushed to branch master at Debian Med / jebl2
Commits:
17eeb316 by Pierre Gruet at 2023-10-07T20:58:22+02:00
New upstream version 0.1+git20230701.b3c0f25
- - - - -
f98314fb by Pierre Gruet at 2023-10-07T20:58:24+02:00
Update upstream source from tag 'upstream/0.1+git20230701.b3c0f25'
Update to upstream version '0.1+git20230701.b3c0f25'
with Debian dir 9b5effa2f8e529c5e44ab7b28d2d5342ed845bae
- - - - -
0aae360c by Pierre Gruet at 2023-10-07T20:59:29+02:00
Raising Standards version to 4.6.2 (no change)
- - - - -
85b82455 by Pierre Gruet at 2023-10-07T21:00:07+02:00
Using my debian.org email address
- - - - -
683a466d by Pierre Gruet at 2023-10-07T21:02:46+02:00
Updating changelog
- - - - -
c42539a5 by Pierre Gruet at 2023-10-07T22:07:36+02:00
Correcting mismatched Lintian override
- - - - -
04dbbac6 by Pierre Gruet at 2023-10-07T22:09:15+02:00
Providing Repository entries in d/u/metadata
- - - - -
a223ac0a by Pierre Gruet at 2023-10-07T22:51:43+02:00
Upload to unstable
- - - - -
18 changed files:
- debian/changelog
- debian/control
- debian/libjebl2-java-doc.lintian-overrides
- debian/upstream/metadata
- src/jebl/evolution/io/FastaExporter.java
- src/jebl/evolution/io/NexusExporter.java
- + src/jebl/evolution/trees/AbstractRootedTree.java
- src/jebl/evolution/trees/CompactRootedTree.java
- src/jebl/evolution/trees/FilteredRootedTree.java
- src/jebl/evolution/trees/MutableRootedTree.java
- src/jebl/evolution/trees/ReRootedTree.java
- src/jebl/evolution/trees/RootedFromUnrooted.java
- src/jebl/evolution/trees/RootedSubtree.java
- src/jebl/evolution/trees/RootedTree.java
- src/jebl/evolution/trees/RootedTreeUtils.java
- src/jebl/evolution/trees/SimpleRootedTree.java
- src/jebl/evolution/trees/TransformedRootedTree.java
- src/jebl/evolution/trees/Utils.java
Changes:
=====================================
debian/changelog
=====================================
@@ -1,10 +1,11 @@
-jebl2 (0.1+git20201011.969bd4b-2) UNRELEASED; urgency=medium
+jebl2 (0.1+git20230701.b3c0f25-1) unstable; urgency=medium
- * Warning: avoid upgrading to new upstream version unless a version of
- figtree >> 1.4.4 is released. figtree is a reverse dependency of jebl2,
- relying on the old ABI of jebl2 at the moment.
+ * New upstream version 0.1+git20230701.b3c0f25
+ * Raising Standards version to 4.6.2 (no change)
+ * Correcting mismatched Lintian override
+ * Providing Repository* entries in d/u/metadata
- -- Pierre Gruet <pgt at debian.org> Fri, 06 May 2022 21:31:43 +0200
+ -- Pierre Gruet <pgt at debian.org> Sat, 07 Oct 2023 22:51:37 +0200
jebl2 (0.1+git20201011.969bd4b-1) unstable; urgency=medium
=====================================
debian/control
=====================================
@@ -1,7 +1,7 @@
Source: jebl2
Maintainer: Debian Med Packaging Team <debian-med-packaging at lists.alioth.debian.org>
Uploaders: Andreas Tille <tille at debian.org>,
- Pierre Gruet <pgtdebian at free.fr>
+ Pierre Gruet <pgt at debian.org>
Section: java
Priority: optional
Build-Depends: debhelper-compat (= 13),
@@ -11,7 +11,7 @@ Build-Depends-Indep: default-jdk,
libtrove-java,
libtrove-java-doc,
ant
-Standards-Version: 4.5.0
+Standards-Version: 4.6.2
Rules-Requires-Root: no
Vcs-Browser: https://salsa.debian.org/med-team/jebl2
Vcs-Git: https://salsa.debian.org/med-team/jebl2.git
=====================================
debian/libjebl2-java-doc.lintian-overrides
=====================================
@@ -1,4 +1,4 @@
# It would be too complicated to remove the javascript that is included in
# javadocs, see the discussion on Debian Java mailing list starting at
# https://lists.debian.org/debian-java/2018/06/msg00020.html.
-embedded-javascript-library usr/share/doc/libjebl2-java/api/*/jquery*.*s *
+embedded-javascript-library please use libjs-* [usr/share/doc/libjebl2-java/api/*]
=====================================
debian/upstream/metadata
=====================================
@@ -1,3 +1,5 @@
---
Bug-Database: https://github.com/rambaut/jebl2/issues
Bug-Submit: https://github.com/rambaut/jebl2/issues/new
+Repository: https://github.com/rambaut/jebl2.git
+Repository-Browse: https://github.com/rambaut/jebl2
=====================================
src/jebl/evolution/io/FastaExporter.java
=====================================
@@ -34,13 +34,22 @@ public class FastaExporter implements SequenceExporter {
*/
public void exportSequences(Collection<? extends Sequence> sequences) throws IOException {
for (Sequence sequence : sequences) {
- final Taxon taxon = sequence.getTaxon();
- String desc = (String) sequence.getAttribute(FastaImporter.descriptionPropertyName);
- if(desc== null) desc = (String) taxon.getAttribute(FastaImporter.descriptionPropertyName);
- writer.println(">" + taxon.getName().replace(' ','_') + ((desc != null) ? (" " + desc) : ""));
- writer.println(sequence.getString());
+ exportSequence(sequence);
}
}
+ /**
+ * export a sequence.
+ */
+ public void exportSequence(Sequence sequence) throws IOException {
+ final Taxon taxon = sequence.getTaxon();
+ String desc = (String) sequence.getAttribute(FastaImporter.descriptionPropertyName);
+ if (desc== null) {
+ desc = (String) taxon.getAttribute(FastaImporter.descriptionPropertyName);
+ }
+ writer.println(">" + taxon.getName().replace(' ','_') + ((desc != null) ? (" " + desc) : ""));
+ writer.println(sequence.getString());
+ }
+
private final PrintWriter writer;
}
=====================================
src/jebl/evolution/io/NexusExporter.java
=====================================
@@ -30,6 +30,8 @@ import java.util.List;
public class NexusExporter implements AlignmentExporter, SequenceExporter, TreeExporter {
+ private static final String BRANCH_LENGTH_FORMAT = "%.6g";
+
public NexusExporter(Writer writer) {
this(writer, true);
}
@@ -65,9 +67,7 @@ public class NexusExporter implements AlignmentExporter, SequenceExporter, TreeE
*/
public void exportSequences(Collection<? extends Sequence> sequences) throws IllegalArgumentException {
- if (treesBlockOpen) {
- endWriteTrees();
- }
+ closeBlock();
establishSequenceTaxa(sequences);
@@ -117,6 +117,16 @@ public class NexusExporter implements AlignmentExporter, SequenceExporter, TreeE
}
}
+ /**
+ * close an existing open trees block
+ */
+ public void closeBlock() {
+ if (treesBlockOpen) {
+ endWriteTrees();
+ }
+ }
+
+
private boolean treesBlockOpen = false;
/**
@@ -159,9 +169,7 @@ public class NexusExporter implements AlignmentExporter, SequenceExporter, TreeE
}
public void close() {
- if (treesBlockOpen) {
- endWriteTrees();
- }
+ closeBlock();
writer.close();
}
@@ -223,9 +231,7 @@ public class NexusExporter implements AlignmentExporter, SequenceExporter, TreeE
}
public void exportMatrix(final DistanceMatrix distanceMatrix) {
- if (treesBlockOpen) {
- endWriteTrees();
- }
+ closeBlock();
final List<Taxon> taxa = distanceMatrix.getTaxa();
establishTaxa(taxa);
@@ -365,8 +371,7 @@ public class NexusExporter implements AlignmentExporter, SequenceExporter, TreeE
appendAttributes(node, null, builder);
if( tree.hasLengths() ) {
- builder.append(':');
- builder.append(roundDouble(tree.getLength(node), 6));
+ builder.append(":").append(String.format(BRANCH_LENGTH_FORMAT, tree.getLength(node)));
}
} else {
builder.append('(');
@@ -384,19 +389,12 @@ public class NexusExporter implements AlignmentExporter, SequenceExporter, TreeE
// whet it is present.
if (parent != null) {
if (tree.hasLengths()) {
- builder.append(":").append(roundDouble(tree.getLength(node), 6));
+ builder.append(":").append(String.format(BRANCH_LENGTH_FORMAT, tree.getLength(node)));
}
}
}
}
- public static double roundDouble(double value, int decimalPlace) {
- double power_of_ten = 1;
- while (decimalPlace-- > 0)
- power_of_ten *= 10.0;
- return Math.round(value * power_of_ten) / power_of_ten;
- }
-
private StringBuilder appendAttributes(Attributable item, String[] excludeKeys, StringBuilder builder) {
if (!writeMetaComments) {
return builder;
=====================================
src/jebl/evolution/trees/AbstractRootedTree.java
=====================================
@@ -0,0 +1,42 @@
+package jebl.evolution.trees;
+
+import jebl.evolution.graphs.Node;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author Andrew Rambaut
+ * @version $
+ */
+public abstract class AbstractRootedTree implements RootedTree {
+ @Override
+ public int getExternalNodeCount(Node node) {
+ if (isExternal(node)) return 1;
+
+ int externalNodeCount = 0;
+ for (Node child : getChildren(node)) {
+ externalNodeCount += getExternalNodeCount(child);
+ }
+
+ return externalNodeCount;
+ }
+
+ /**
+ * @param node the node whose external nodes are being requested.
+ * @return the list of external nodes descendent of the given node.
+ * The set may be empty for a terminal node (a tip).
+ */
+ @Override
+ public List<Node> getExternalNodes(Node node) {
+ if (isExternal(node)) return Collections.singletonList(node);
+
+ List<Node> tips = new ArrayList<Node>();
+ for (Node child : getChildren(node)) {
+ tips.addAll(getExternalNodes(child));
+ }
+
+ return tips;
+ }
+}
=====================================
src/jebl/evolution/trees/CompactRootedTree.java
=====================================
@@ -296,6 +296,35 @@ public class CompactRootedTree extends AttributableImp implements RootedTree {
return clist;
}
+ @Override
+ public int getExternalNodeCount(Node node) {
+ if (isExternal(node)) return 1;
+
+ int externalNodeCount = 0;
+ for (Node child : getChildren(node)) {
+ externalNodeCount += getExternalNodeCount(child);
+ }
+
+ return externalNodeCount;
+ }
+
+ /**
+ * @param node the node whose external nodes are being requested.
+ * @return the list of external nodes descendent of the given node.
+ * The set may be empty for a terminal node (a tip).
+ */
+ @Override
+ public List<Node> getExternalNodes(Node node) {
+ if (isExternal(node)) return Collections.singletonList(node);
+
+ List<Node> tips = new ArrayList<Node>();
+ for (Node child : getChildren(node)) {
+ tips.addAll(getExternalNodes(child));
+ }
+
+ return tips;
+ }
+
public boolean hasHeights() {
return hasHeights;
}
@@ -321,6 +350,20 @@ public class CompactRootedTree extends AttributableImp implements RootedTree {
return heights[index];
}
+ /**
+ * @return Whether the node heights are known or need to be recalculated from the lengths
+ */
+ public boolean isHeightsKnown() {
+ return true;
+ }
+
+ /**
+ * @return Whether the branch lengths are known or need to be recalculated from the heights
+ */
+ public boolean isLengthsKnown() {
+ return true;
+ }
+
public Node getParent(Node node) {
final int index = ((SimpleRootedNode) node).index;
return index == 0 ? null : nodes[parent[index]];
=====================================
src/jebl/evolution/trees/FilteredRootedTree.java
=====================================
@@ -4,16 +4,14 @@ import jebl.evolution.graphs.Edge;
import jebl.evolution.graphs.Node;
import jebl.evolution.taxa.Taxon;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
/**
* @author Andrew Rambaut
* @author Alexei Drummond
* @version $Id: FilteredRootedTree.java 936 2008-08-06 14:12:07Z rambaut $
*/
-public abstract class FilteredRootedTree implements RootedTree {
+public abstract class FilteredRootedTree extends AbstractRootedTree {
public FilteredRootedTree(final RootedTree source) {
this.source = source;
@@ -47,6 +45,14 @@ public abstract class FilteredRootedTree implements RootedTree {
return source.getLength(node);
}
+ public boolean isHeightsKnown() {
+ return source.isHeightsKnown();
+ }
+
+ public boolean isLengthsKnown() {
+ return source.isLengthsKnown();
+ }
+
public Node getParent(Node node) {
return source.getParent(node);
}
=====================================
src/jebl/evolution/trees/MutableRootedTree.java
=====================================
@@ -20,7 +20,7 @@ import java.util.*;
*
*/
-public class MutableRootedTree implements RootedTree {
+public class MutableRootedTree extends AbstractRootedTree {
MutableRootedTree() { super(); }
/**
@@ -349,6 +349,20 @@ public class MutableRootedTree implements RootedTree {
return hasLengths;
}
+ /**
+ * @return Whether the node heights are known or need to be recalculated from the lengths
+ */
+ public boolean isHeightsKnown() {
+ return heightsKnown;
+ }
+
+ /**
+ * @return Whether the branch lengths are known or need to be recalculated from the heights
+ */
+ public boolean isLengthsKnown() {
+ return lengthsKnown;
+ }
+
/**
* @param node the node whose branch length (to its parent) is being requested.
* @return the length of the branch to the parent node (0.0 if the node is the root).
=====================================
src/jebl/evolution/trees/ReRootedTree.java
=====================================
@@ -1,46 +1,835 @@
package jebl.evolution.trees;
-import jebl.evolution.graphs.Node;
+import jebl.evolution.graphs.*;
+import jebl.evolution.taxa.Taxon;
+import jebl.evolution.trees.*;
+import jebl.util.AttributableHelper;
+import jebl.util.HashPair;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
+import java.util.*;
/**
+ * A rooted tree concrete class that wraps another tree and provides a differently
+ * rooted view of that tree.
+ *
* @author Andrew Rambaut
- * @author Alexei Drummond
- * @version $Id: ReRootedTree.java 776 2007-09-05 11:17:12Z rambaut $
*/
-public class ReRootedTree extends FilteredRootedTree {
+final public class ReRootedTree extends AbstractRootedTree {
- public enum RootingType {
- MID_POINT("midpoint"),
- LEAST_SQUARES("least squares");
+ /**
+ * Make a copy of the given unrooted tree
+ * @param source an unrooted source tree
+ * @param ingroupNode the node on one side of the root
+ * @param outgroupNode the node on the other side of the root
+ * @param ingroupBranchLength the branch length from the root to the ingroup node
+ * @throws jebl.evolution.graphs.Graph.NoEdgeException
+ */
+ public ReRootedTree(RootedTree source, Node ingroupNode, Node outgroupNode, double ingroupBranchLength) throws NoEdgeException {
- RootingType(String name) {
- this.name = name;
+ this.source = source;
+ List<Node> children = new ArrayList<Node>();
+
+ Node node1 = createNodes(source, outgroupNode, ingroupNode);
+ setLength(node1, ingroupBranchLength);
+ children.add(node1);
+
+ Node node2 = createNodes(source, ingroupNode, outgroupNode);
+ double l = source.getEdgeLength(ingroupNode, outgroupNode);
+ if (outgroupNode == source.getRootNode()) {
+ // the tree is already rooted at the required location
+ for (Node adj : source.getAdjacencies(outgroupNode)) {
+ if (adj != ingroupNode) {
+ l += source.getEdgeLength(outgroupNode, adj);
+ }
+ }
+ }
+ setLength(node2, Math.max(l - ingroupBranchLength, 0.0));
+ children.add(node2);
+
+ createInternalNode(null, children);
+ }
+
+ /**
+ * Clones the entire tree structure from the given (unrooted) Tree.
+ * @param tree the unrooted tree
+ * @param parent the parent node
+ * @param child the child node
+ */
+ public Node createNodes(RootedTree tree, Node parent, Node child) throws NoEdgeException {
+
+ Node newNode = null;
+ double length;
+
+ if (tree.isExternal(child)) {
+ newNode = createExternalNode(child, tree.getTaxon(child));
+ length = tree.getEdgeLength(parent, child);
+ } else {
+ List<Node> adjacencies = tree.getAdjacencies(child);
+
+ if (adjacencies.size() == 2) {
+ // this is the root node so skip over it...
+ if (adjacencies.get(0) == parent) {
+ newNode = createNodes(tree, child, adjacencies.get(1));
+ } else {
+ newNode = createNodes(tree, child, adjacencies.get(0));
+ }
+ length = tree.getEdgeLength(adjacencies.get(0), child) +
+ tree.getEdgeLength(adjacencies.get(1), child);
+
+ } else {
+ List<Node> children = new ArrayList<Node>();
+
+ for (Node child2 : adjacencies) {
+ if (child2 != parent) {
+ children.add(createNodes(tree, child, child2));
+ }
+ }
+
+ if (tree.getParent(parent) == child) {
+ newNode = createInternalNode(parent, children);
+ } else {
+ newNode = createInternalNode(child, children);
+ }
+ length = tree.getEdgeLength(parent, child);
+ }
+ }
+
+ setLength(newNode, length);
+
+ return newNode;
+ }
+
+ /**
+ * Creates a new external node with the given taxon. See createInternalNode
+ * for a description of how to use these methods.
+ * @param source the source node
+ * @return the created node reference
+ */
+ private Node createExternalNode(Node source, Taxon taxon) {
+ ReRootedNode node = new ReRootedNode(source, taxon);
+ externalNodes.put(taxon, node);
+ return node;
+ }
+
+ /**
+ * Once a SimpleRootedTree has been created, the node stucture can be created by
+ * calling createExternalNode and createInternalNode. First of all createExternalNode
+ * is called giving Taxon objects for the external nodes. Then these are put into
+ * sets and passed to createInternalNode to create a parent of these nodes. The
+ * last node created using createInternalNode is automatically the root so when
+ * all the nodes are created, the tree is complete.
+ *
+ * @param children the child nodes of this nodes
+ * @return the created node reference
+ */
+ private ReRootedNode createInternalNode(Node source, List<? extends Node> children) {
+ ReRootedNode node = new ReRootedNode(source, children);
+
+ for (Node child : children) {
+ ((ReRootedNode)child).setParent(node);
+ }
+
+ internalNodes.add(node);
+
+ rootNode = node;
+ return node;
+ }
+
+ public Node getSourceNode(Node node) {
+ return ((ReRootedNode)node).source;
+ }
+
+ /**
+ * @param node the node whose height is being set
+ * @param height the height
+ */
+ public void setHeight(Node node, double height) {
+ lengthsKnown = false;
+ heightsKnown = true;
+
+ // If a single height of a single node is set then
+ // assume that all nodes have heights and by extension,
+ // branch lengths as well as these will be calculated
+ // from the heights
+ hasLengths = true;
+ hasHeights = true;
+
+ ((ReRootedNode)node).setHeight(height);
+ }
+
+ /**
+ * @param node the node whose branch length (to its parent) is being set
+ * @param length the length
+ */
+ public void setLength(Node node, double length) {
+ heightsKnown = false;
+ lengthsKnown = true;
+
+ // If a single length of a single branch is set then
+ // assume that all branch have lengths and by extension,
+ // node heights as well as these will be calculated
+ // from the lengths
+ hasLengths = true;
+ hasHeights = true;
+
+ ((ReRootedNode)node).setLength(length);
+ }
+
+ /**
+ * @param node the node whose children are being requested.
+ * @return the list of nodes that are the children of the given node.
+ * The list may be empty for a terminal node (a tip).
+ */
+ public List<Node> getChildren(Node node) {
+ return new ArrayList<Node>(((ReRootedNode)node).getChildren());
+ }
+
+ /**
+ * @return Whether this tree has node heights available
+ */
+ public boolean hasHeights() {
+ return hasHeights;
+ }
+
+ /**
+ * @return Whether the node heights are known or need to be recalculated from the lengths
+ */
+ @Override
+ public boolean isHeightsKnown() {
+ return heightsKnown;
+ }
+
+ /**
+ * @param node the node whose height is being requested.
+ * @return the height of the given node. The height will be
+ * less than the parent's height and greater than it children's heights.
+ */
+ public double getHeight(Node node) {
+ if (!hasHeights) throw new IllegalArgumentException("This tree has no node heights");
+ if (!heightsKnown) calculateNodeHeights();
+ return ((ReRootedNode)node).getHeight();
+ }
+
+ /**
+ * @return Whether this tree has branch lengths available
+ */
+ public boolean hasLengths() {
+ return hasLengths;
+ }
+
+ /**
+ * @return Whether the branch lengths are known or need to be recalculated from the heights
+ */
+ @Override
+ public boolean isLengthsKnown() {
+ return lengthsKnown;
+ }
+
+ /**
+ * @param node the node whose branch length (to its parent) is being requested.
+ * @return the length of the branch to the parent node (0.0 if the node is the root).
+ */
+ public double getLength(Node node) {
+ if (!hasLengths) throw new IllegalArgumentException("This tree has no branch lengths");
+ if (!lengthsKnown) calculateBranchLengths();
+ return ((ReRootedNode)node).getLength();
+ }
+
+ /**
+ * @param node the node whose parent is requested
+ * @return the parent node of the given node, or null
+ * if the node is the root node.
+ */
+ public Node getParent(Node node) {
+ if (!(node instanceof ReRootedNode)) {
+ throw new IllegalArgumentException("Node, " + node.toString() + " is not an instance of SimpleRootedNode");
+ }
+ return ((ReRootedNode)node).getParent();
+ }
+
+ public Edge getParentEdge(Node node) {
+ if (!(node instanceof ReRootedNode)) {
+ throw new IllegalArgumentException("Node, " + node.toString() + " is not an instance of SimpleRootedNode");
+ }
+ return ((ReRootedNode)node).getEdge();
+ }
+
+ /**
+ * The root of the tree has the largest node height of
+ * all nodes in the tree.
+ *
+ * @return the root of the tree.
+ */
+ public Node getRootNode() {
+ return rootNode;
+ }
+
+
+ /**
+ * @return a set of all nodes that have degree 1.
+ * These nodes are often refered to as 'tips'.
+ */
+ public Set<Node> getExternalNodes() {
+ return new LinkedHashSet<Node>(externalNodes.values());
+ }
+
+ /**
+ * @return a set of all nodes that have degree 2 or more.
+ * These nodes are often refered to as internal nodes.
+ */
+ public Set<Node> getInternalNodes() {
+ return new LinkedHashSet<Node>(internalNodes);
+ }
+
+ /**
+ * @return the set of taxa associated with the external
+ * nodes of this tree. The size of this set should be the
+ * same as the size of the external nodes set.
+ */
+ public Set<Taxon> getTaxa() {
+ return new LinkedHashSet<Taxon>(externalNodes.keySet());
+ }
+
+ /**
+ * @param node the node whose associated taxon is being requested.
+ * @return the taxon object associated with the given node, or null
+ * if the node is an internal node.
+ */
+ public Taxon getTaxon(Node node) {
+ if (!(node instanceof ReRootedNode)) {
+ throw new IllegalArgumentException("Node, " + node.toString() + " is not an instance of SimpleRootedNode. It is an instance of "+node.getClass().getName());
}
+ return ((ReRootedNode)node).getTaxon();
+ }
- public String toString() { return name; }
+ /**
+ * @param node the node
+ * @return true if the node is of degree 1.
+ */
+ public boolean isExternal(Node node) {
+ if (!(node instanceof ReRootedNode)) {
+ throw new IllegalArgumentException("Node, " + node.toString() + " is not an instance of SimpleRootedNode. It is an instance of "+node.getClass().getName());
+ }
+ return ((ReRootedNode)node).getChildren().size() == 0;
+ }
- private String name;
+ /**
+ * @param taxon the taxon
+ * @return the external node associated with the given taxon, or null
+ * if the taxon is not a member of the taxa set associated with this tree.
+ */
+ public Node getNode(Taxon taxon) {
+ return externalNodes.get(taxon);
}
- public ReRootedTree(final RootedTree source, RootingType rootingType) {
- super(source);
- switch (rootingType) {
- case MID_POINT:
- break;
- case LEAST_SQUARES:
- break;
- default:
- throw new IllegalArgumentException("Unknown enum value");
- }
- }
+ public void renameTaxa(Taxon from, Taxon to) {
+ ReRootedNode node = (ReRootedNode)externalNodes.get(from);
+
+ // TT: The javadoc doesn't specify whether renameTaxa() should fail or silently do nothing
+ // if Taxon from doesn't exist. But the code already threw a NullPointerException before (bug 4824),
+ // so it's probably ok to throw a more informative IllegalArgumentException instead.
+ if (node == null) {
+ throw new IllegalArgumentException("Unknown taxon " + from + "; can't rename to " + to);
+ }
+
+ node.setTaxon(to);
+
+ externalNodes.remove(from);
+ externalNodes.put(to, node);
+ }
+
+ /**
+ * Returns a list of edges connected to this node
+ *
+ * @param node
+ * @return the set of nodes that are attached by edges to the given node.
+ */
+ public List<Edge> getEdges(Node node) {
+ List<Edge> edges = new ArrayList<Edge>();
+ for (Node adjNode : getAdjacencies(node)) {
+ edges.add(((ReRootedNode)adjNode).getEdge());
+
+ }
+ return edges;
+ }
+
+ /**
+ * @param node
+ * @return the set of nodes that are attached by edges to the given node.
+ */
+ public List<Node> getAdjacencies(Node node) {
+ return ((ReRootedNode)node).getAdjacencies();
+ }
+
+ /**
+ * Returns the Edge that connects these two nodes
+ *
+ * @param node1
+ * @param node2
+ * @return the edge object.
+ * @throws jebl.evolution.graphs.Graph.NoEdgeException
+ * if the nodes are not directly connected by an edge.
+ */
+ public Edge getEdge(Node node1, Node node2) throws NoEdgeException {
+ if (((ReRootedNode)node1).getParent() == node2) {
+ return ((ReRootedNode)node1).getEdge();
+ } else if (((ReRootedNode)node2).getParent() == node1) {
+ return ((ReRootedNode)node2).getEdge();
+ } else {
+ throw new NoEdgeException();
+ }
+ }
+
+ /**
+ * @param node1
+ * @param node2
+ * @return the length of the edge connecting node1 and node2.
+ * @throws jebl.evolution.graphs.Graph.NoEdgeException
+ * if the nodes are not directly connected by an edge.
+ */
+ public double getEdgeLength(Node node1, Node node2) throws NoEdgeException {
+ if (((ReRootedNode)node1).getParent() == node2) {
+ if (heightsKnown) {
+ return ((ReRootedNode)node2).getHeight() - ((ReRootedNode)node1).getHeight();
+ } else {
+ return ((ReRootedNode)node1).getLength();
+ }
+ } else if (((ReRootedNode)node2).getParent() == node1) {
+ if (heightsKnown) {
+ return ((ReRootedNode)node1).getHeight() - ((ReRootedNode)node2).getHeight();
+ } else {
+ return ((ReRootedNode)node2).getLength();
+ }
+ } else {
+ throw new NoEdgeException();
+ }
+ }
+
+ /**
+ * Returns an array of 2 nodes which are the nodes at either end of the edge.
+ *
+ * @param edge
+ * @return an array of 2 edges
+ */
+ public Node[] getNodes(Edge edge) {
+ for (Node node : getNodes()) {
+ if (((ReRootedNode)node).getEdge() == edge) {
+ return new Node[] { node, ((ReRootedNode)node).getParent() };
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return the set of all nodes in this graph.
+ */
+ public Set<Node> getNodes() {
+ Set<Node> nodes = new LinkedHashSet<Node>(internalNodes);
+ nodes.addAll(externalNodes.values());
+ return nodes;
+ }
+
+ /**
+ * @return the set of all edges in this graph.
+ */
+ public Set<Edge> getEdges() {
+ Set<Edge> edges = new LinkedHashSet<Edge>();
+ for (Node node : getNodes()) {
+ if (node != getRootNode()) {
+ edges.add(((ReRootedNode)node).getEdge());
+ }
+
+ }
+ return edges;
+ }
+
+ /**
+ * The set of external edges. This is a pretty inefficient implementation because
+ * a new set is constructed each time this is called.
+ * @return the set of external edges.
+ */
+ public Set<Edge> getExternalEdges() {
+ Set<Edge> edges = new LinkedHashSet<Edge>();
+ for (Node node : getExternalNodes()) {
+ edges.add(((ReRootedNode)node).getEdge());
+ }
+ return edges;
+ }
+
+ /**
+ * The set of internal edges. This is a pretty inefficient implementation because
+ * a new set is constructed each time this is called.
+ * @return the set of internal edges.
+ */
+ public Set<Edge> getInternalEdges() {
+ Set<Edge> edges = new LinkedHashSet<Edge>();
+ for (Node node : getInternalNodes()) {
+ if (node != getRootNode()) {
+ edges.add(((ReRootedNode)node).getEdge());
+ }
+ }
+ return edges;
+ }
+
+ /**
+ * @param degree the number of edges connected to a node
+ * @return a set containing all nodes in this graph of the given degree.
+ */
+ public Set<Node> getNodes(int degree) {
+ Set<Node> nodes = new LinkedHashSet<Node>();
+ for (Node node : getNodes()) {
+ // Account for no anncesstor of root, assumed by default in getDegree
+ final int deg = node.getDegree() ;
+ if (deg == degree) nodes.add(node);
+ }
+ return nodes;
+ }
+
+ /**
+ * Set the node heights from the current branch lengths.
+ */
+ private void calculateNodeHeights() {
+
+ if (!lengthsKnown) {
+ throw new IllegalArgumentException("Can't calculate node heights because branch lengths not known");
+ }
+
+ nodeLengthsToHeights(rootNode, 0.0);
+
+ double maxHeight = 0.0;
+ for (Node externalNode : getExternalNodes()) {
+ if (((ReRootedNode)externalNode).getHeight() > maxHeight) {
+ maxHeight = ((ReRootedNode)externalNode).getHeight();
+ }
+ }
+
+ for (Node node : getNodes()) {
+ ((ReRootedNode)node).setHeight(maxHeight - ((ReRootedNode)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(ReRootedNode node, double height) {
+
+ double newHeight = height;
+
+ if (node.getLength() > 0.0) {
+ newHeight += node.getLength();
+ }
+
+ node.setHeight(newHeight);
+
+ for (Node child : node.getChildren()) {
+ nodeLengthsToHeights((ReRootedNode)child, newHeight);
+ }
+ }
+
+ /**
+ * Calculate branch lengths from the current node heights.
+ */
+ protected void calculateBranchLengths() {
+
+ if (!hasLengths) {
+ throw new IllegalArgumentException("Can't calculate branch lengths because node heights not known");
+ }
+
+ nodeHeightsToLengths(rootNode, getHeight(rootNode));
+
+ lengthsKnown = true;
+ }
+
+ /**
+ * Calculate branch lengths from the current node heights.
+ */
+ private void nodeHeightsToLengths(ReRootedNode node, double height) {
+ final double h = node.getHeight();
+ node.setLength(h >= 0 ? height - h : 1);
+
+ for (Node child : node.getChildren()) {
+ nodeHeightsToLengths((ReRootedNode)child, node.getHeight());
+ }
+
+ }
+
+ public boolean conceptuallyUnrooted() {
+ return false;
+ }
+
+ public boolean isRoot(Node node) {
+ return node == rootNode;
+ }
+
+ // Attributable IMPLEMENTATION
+
+ public void setAttribute(String name, Object value) {
+ source.setAttribute(name, value);
+ }
+
+ public Object getAttribute(String name) {
+ return source.getAttribute(name);
+ }
+
+ public void removeAttribute(String name) {
+ source.removeAttribute(name);
+ }
+
+ public Set<String> getAttributeNames() {
+ return source.getAttributeNames();
+ }
+
+ public Map<String, Object> getAttributeMap() {
+ return source.getAttributeMap();
+ }
+
+ /**
+ * Root any tree by locating the "center" of tree and adding a new root node at that point
+ * <p/>
+ * for any point on the tree x let D(x) = Max{distance between x and t : for all tips t}
+ * The "center" c is the point with the smallest distance, i.e. D(c) = min{ D(x) : x in tree }
+ *
+ * @param tree to root
+ * @return rooted tree
+ */
+ public static RootedTree rootTreeAtCenter(RootedTree tree) {
+ // Method - find the pair of tips with the longest distance. It is easy to see that the center
+ // is at the midpoint of the path between them.
+
+ HashMap<HashPair<Node>, Double> dists = new LinkedHashMap<HashPair<Node>, Double>();
+ try {
+ double maxDistance = -Double.MAX_VALUE;
+ // node on maximal path
+ Node current = null;
+ // next node on maximal path
+ Node direction = null;
+
+ // locate one terminal node of longest path
+ for (Node e : tree.getExternalNodes()) {
+ for (Node n : tree.getAdjacencies(e)) {
+ final double d = dist(tree, e, n, dists);
+ if (d > maxDistance) {
+ maxDistance = d;
+ current = e;
+ direction = n;
+ }
+ }
+ }
+
+ // traverse along maximal path to it's middle
+ double distanceLeft = maxDistance / 2.0;
+
+ while (true) {
+ final double len = tree.getEdgeLength(current, direction);
+ if (distanceLeft <= len) {
+ //System.out.println(toNewick(rtree));
+ return new ReRootedTree(tree, current, direction, distanceLeft);
+ }
+ distanceLeft -= len;
+
+ maxDistance = -Double.MAX_VALUE;
+ Node next = null;
+ for (Node n : tree.getAdjacencies(direction)) {
+ if (n == current) continue;
+ final double d = dist(tree, direction, n, dists);
+ if (d > maxDistance) {
+ maxDistance = d;
+ next = n;
+ }
+ }
+ current = direction;
+ direction = next;
+ }
+ } catch (Graph.NoEdgeException e1) {
+ return null; // serious bug, should not happen
+ }
+ }
+
+ private static double dist(Tree tree, Node root, Node node, Map<HashPair<Node>, Double> dists) throws Graph.NoEdgeException {
+ HashPair<Node> p = new HashPair<Node>(root, node);
+ if (dists.containsKey(p)) {
+ return dists.get(p);
+ }
+
+ // assume positive branches
+ double maxDist = 0;
+ for (Node n : tree.getAdjacencies(node)) {
+ if (n != root) {
+ double d = dist(tree, node, n, dists);
+ maxDist = Math.max(maxDist, d);
+ }
+ }
+ double dist = tree.getEdgeLength(node, root) + maxDist;
+
+ dists.put(p, dist);
+ return dist;
+ }
// PRIVATE members
-// private final Node rootChild1;
-// private final Node rootChild2;
-// private final double rootLength1;
-// private final double rootLength2;
+
+ private RootedTree source = null;
+
+ private ReRootedNode rootNode = null;
+ private final Set<Node> internalNodes = new LinkedHashSet<Node>();
+ private final Map<Taxon, Node> externalNodes = new LinkedHashMap<Taxon, Node>();
+
+ private boolean heightsKnown = false;
+ private boolean lengthsKnown = false;
+
+ private boolean hasHeights = false;
+ private boolean hasLengths = false;
+
+ private class ReRootedNode implements Node {
+ public ReRootedNode(Node source, Taxon taxon) {
+ this.source = source;
+ this.children = Collections.unmodifiableList(new ArrayList<Node>());
+ this.taxon = taxon;
+ }
+
+ public ReRootedNode(Node source, List<? extends Node> children) {
+ this.source = source;
+ this.children = Collections.unmodifiableList(new ArrayList<Node>(children));
+ this.taxon = null;
+ }
+
+ public Node getParent() {
+ return parent;
+ }
+
+ public void setParent(Node parent) {
+ this.parent = parent;
+ }
+
+ public List<Node> getChildren() {
+ return children;
+ }
+
+ public double getHeight() {
+ return height;
+ }
+
+ // height above latest tip
+ public void setHeight(double height) {
+ this.height = height;
+ }
+
+ // length of branch to parent
+ public double getLength() {
+ return length;
+ }
+
+ public void setLength(double length) {
+ this.length = length;
+ }
+
+ public int getDegree() {
+ return children.size() +(this==rootNode?0:1);
+ }
+
+ public void setTaxon(Taxon to) {
+ taxon = to;
+ }
+
+ /**
+ * returns the edge connecting this node to the parent node
+ * @return the edge
+ */
+ public Edge getEdge() {
+ if (edge == null) {
+ edge = new BaseEdge() {
+ public double getLength() {
+ return length;
+ }
+ };
+ }
+
+ return edge;
+ }
+
+ /**
+ * For a rooted tree, getting the adjacencies is not the most efficient
+ * operation as it makes a new set containing the children and the parent.
+ * @return the adjacaencies
+ */
+ public List<Node> getAdjacencies() {
+ List<Node> adjacencies = new ArrayList<Node>();
+ if (children != null) adjacencies.addAll(children);
+ if (parent != null) adjacencies.add(parent);
+ return adjacencies;
+ }
+
+ public Taxon getTaxon() {
+ return taxon;
+ }
+
+ // Attributable IMPLEMENTATION
+
+ public void setAttribute(String name, Object value) {
+ if (source == null) {
+ if (helper == null) {
+ helper = new AttributableHelper();
+ }
+ helper.setAttribute(name, value);
+ } else {
+ source.setAttribute(name, value);
+ }
+ }
+
+ public Object getAttribute(String name) {
+ if (source == null) {
+ if (helper == null) {
+ return null;
+ }
+ return helper.getAttribute(name);
+ }
+ return source.getAttribute(name);
+ }
+
+ public void removeAttribute(String name) {
+ if (source == null) {
+ if( helper != null ) {
+ helper.removeAttribute(name);
+ }
+ } else {
+ source.removeAttribute(name);
+ }
+ }
+
+ public Set<String> getAttributeNames() {
+ if (source == null) {
+ if (helper == null) {
+ return Collections.emptySet();
+ }
+ return helper.getAttributeNames();
+ }
+ return source.getAttributeNames();
+ }
+
+ public Map<String, Object> getAttributeMap() {
+ if (source == null) {
+ if (helper == null) {
+ return Collections.emptyMap();
+ }
+ return helper.getAttributeMap();
+ }
+ return source.getAttributeMap();
+ }
+
+ private final Node source;
+
+ private List<Node> children;
+ private Taxon taxon;
+
+ private Node parent;
+ private double height;
+ private double length;
+
+ private Edge edge = null;
+
+ private AttributableHelper helper = null;
+
+ }
}
\ No newline at end of file
=====================================
src/jebl/evolution/trees/RootedFromUnrooted.java
=====================================
@@ -17,7 +17,7 @@ import java.util.*;
*
*/
-public class RootedFromUnrooted implements RootedTree {
+public class RootedFromUnrooted extends AbstractRootedTree {
/**
* The unrooted tree
*/
@@ -157,6 +157,20 @@ public class RootedFromUnrooted implements RootedTree {
return l;
}
+ /**
+ * @return Whether the node heights are known or need to be recalculated from the lengths
+ */
+ public boolean isHeightsKnown() {
+ return false;
+ }
+
+ /**
+ * @return Whether the branch lengths are known or need to be recalculated from the heights
+ */
+ public boolean isLengthsKnown() {
+ return true;
+ }
+
public Node getParent(Node node) {
return parents.get(node);
}
=====================================
src/jebl/evolution/trees/RootedSubtree.java
=====================================
@@ -18,7 +18,7 @@ import java.util.*;
* @author Alexei Drummond
* @version $Id: SimpleRootedTree.java 935 2008-07-22 16:52:04Z rambaut $
*/
-final public class RootedSubtree implements RootedTree {
+final public class RootedSubtree extends AbstractRootedTree {
/**
* Make a copy of the given rooted tree
@@ -199,6 +199,20 @@ final public class RootedSubtree implements RootedTree {
return ((RootedSubtreeNode)node).getLength();
}
+ /**
+ * @return Whether the node heights are known or need to be recalculated from the lengths
+ */
+ public boolean isHeightsKnown() {
+ return heightsKnown;
+ }
+
+ /**
+ * @return Whether the branch lengths are known or need to be recalculated from the heights
+ */
+ public boolean isLengthsKnown() {
+ return lengthsKnown;
+ }
+
/**
* @param node the node whose parent is requested
* @return the parent node of the given node, or null
=====================================
src/jebl/evolution/trees/RootedTree.java
=====================================
@@ -33,11 +33,25 @@ public interface RootedTree extends Tree {
*/
List<Node> getChildren(Node node);
+ int getExternalNodeCount(Node node);
+
+ /**
+ * @param node the node whose external nodes are being requested.
+ * @return the list of external nodes descendent of the given node.
+ * The set may be empty for a terminal node (a tip).
+ */
+ List<Node> getExternalNodes(Node node);
+
/**
* @return Whether this tree has node heights available
*/
boolean hasHeights();
+ /**
+ * @return Whether the node heights are known or need to be recalculated from the lengths
+ */
+ boolean isHeightsKnown();
+
/**
* @param node the node whose height is being requested.
* @return the height of the given node. The height will be
@@ -50,6 +64,11 @@ public interface RootedTree extends Tree {
*/
boolean hasLengths();
+ /**
+ * @return Whether the branch lengths are known or need to be recalculated from the heights
+ */
+ boolean isLengthsKnown();
+
/**
* @param node the node whose branch length (to its parent) is being requested.
* @return the length of the branch to the parent node (0.0 if the node is the root).
=====================================
src/jebl/evolution/trees/RootedTreeUtils.java
=====================================
@@ -25,15 +25,7 @@ public class RootedTreeUtils {
* @return the number of leaves under this node.
*/
public static final int getTipCount(RootedTree tree, Node node) {
- int tipCount = 0;
- for (Node child : tree.getChildren(node)) {
- tipCount += getTipCount(tree, child);
- }
-
- // is external
- if (tipCount == 0) return 1;
-
- return tipCount;
+ return tree.getExternalNodeCount(node);
}
public static double getMinTipHeight(RootedTree tree, Node node) {
@@ -68,7 +60,28 @@ public class RootedTreeUtils {
return maxTipHeight;
}
- /**
+ /**
+ * returns the average distance from the given node to all the tips below it
+ * @param tree
+ * @param node
+ * @return
+ */
+ public static double getAverageTipDistance(RootedTree tree, Node node) {
+ if (tree.isExternal(node)) {
+ return 0.0;
+ }
+
+ double nodeHeight = tree.getHeight(node);
+ double sumTipDistance = 0.0;
+ for (Node tip : tree.getExternalNodes(node)) {
+ sumTipDistance += nodeHeight - tree.getHeight(tip);
+ }
+
+ return sumTipDistance / tree.getExternalNodeCount(node);
+ }
+
+
+ /**
* @return true only if all tips have height 0.0
*/
public static boolean isUltrametric(RootedTree tree, double tolerance) {
=====================================
src/jebl/evolution/trees/SimpleRootedTree.java
=====================================
@@ -1,6 +1,7 @@
package jebl.evolution.trees;
import jebl.evolution.graphs.Edge;
+import jebl.evolution.graphs.Graph.NoEdgeException;
import jebl.evolution.graphs.Node;
import jebl.evolution.taxa.Taxon;
import jebl.util.AttributableHelper;
@@ -68,6 +69,10 @@ final public class SimpleRootedTree implements RootedTree {
* @return created node
*/
public Node createNodes(RootedTree tree, Node node) {
+ hasHeights = tree.hasHeights();
+ heightsKnown = tree.isHeightsKnown();
+ hasLengths = tree.hasLengths();
+ lengthsKnown = tree.isLengthsKnown();
return createNodes(tree, node, (Map<Node, Node>)null);
}
@@ -100,8 +105,12 @@ final public class SimpleRootedTree implements RootedTree {
newNode.setAttribute(e.getKey(), e.getValue());
}
// }
- setHeight(newNode, tree.getHeight(node));
-
+ if (tree.hasHeights() && tree.isHeightsKnown()) {
+ setHeight(newNode, tree.getHeight(node));
+ }
+ if (tree.hasLengths() && tree.isLengthsKnown()) {
+ setLength(newNode, tree.getLength(node));
+ }
return newNode;
}
@@ -304,6 +313,38 @@ final public class SimpleRootedTree implements RootedTree {
return new ArrayList<Node>(((SimpleRootedNode)node).getChildren());
}
+ @Override
+ public int getExternalNodeCount(Node node) {
+ if (((SimpleRootedNode)node).externalNodeCount < 0) {
+ if (isExternal(node)) {
+ ((SimpleRootedNode)node).externalNodeCount = 1;
+ } else {
+ ((SimpleRootedNode)node).externalNodeCount = 0;
+ for (Node child : getChildren(node)) {
+ ((SimpleRootedNode)node).externalNodeCount += getExternalNodeCount(child);
+ }
+ }
+ }
+ return ((SimpleRootedNode)node).externalNodeCount;
+ }
+
+ /**
+ * @param node the node whose external nodes are being requested.
+ * @return the list of external nodes descendent of the given node.
+ * The set may be empty for a terminal node (a tip).
+ */
+ @Override
+ public List<Node> getExternalNodes(Node node) {
+ if (isExternal(node)) return Collections.singletonList(node);
+
+ List<Node> tips = new ArrayList<Node>();
+ for (Node child : getChildren(node)) {
+ tips.addAll(getExternalNodes(child));
+ }
+
+ return tips;
+ }
+
/**
* @return Whether this tree has node heights available
*/
@@ -311,6 +352,13 @@ final public class SimpleRootedTree implements RootedTree {
return hasHeights;
}
+ /**
+ * @return Whether the node heights are known or need to be recalculated from the lengths
+ */
+ public boolean isHeightsKnown() {
+ return heightsKnown;
+ }
+
/**
* @param node the node whose height is being requested.
* @return the height of the given node. The height will be
@@ -329,6 +377,13 @@ final public class SimpleRootedTree implements RootedTree {
return hasLengths;
}
+ /**
+ * @return Whether the branch lengths are known or need to be recalculated from the heights
+ */
+ public boolean isLengthsKnown() {
+ return lengthsKnown;
+ }
+
/**
* @param node the node whose branch length (to its parent) is being requested.
* @return the length of the branch to the parent node (0.0 if the node is the root).
@@ -351,12 +406,12 @@ final public class SimpleRootedTree implements RootedTree {
return ((SimpleRootedNode)node).getParent();
}
- public Edge getParentEdge(Node node) {
- if (!(node instanceof SimpleRootedNode)) {
- throw new IllegalArgumentException("Node, " + node.toString() + " is not an instance of SimpleRootedNode");
- }
- return ((SimpleRootedNode)node).getEdge();
- }
+ public Edge getParentEdge(Node node) {
+ if (!(node instanceof SimpleRootedNode)) {
+ throw new IllegalArgumentException("Node, " + node.toString() + " is not an instance of SimpleRootedNode");
+ }
+ return ((SimpleRootedNode)node).getEdge();
+ }
/**
* The root of the tree has the largest node height of
@@ -517,29 +572,29 @@ final public class SimpleRootedTree implements RootedTree {
}
}
- /**
- * Returns an array of 2 nodes which are the nodes at either end of the edge.
- *
- * @param edge
- * @return an array of 2 edges
- */
- public Node[] getNodes(Edge edge) {
- for (Node node : getNodes()) {
- if (((SimpleRootedNode)node).getEdge() == edge) {
- return new Node[] { node, ((SimpleRootedNode)node).getParent() };
- }
- }
- return null;
- }
-
- /**
- * @return the set of all nodes in this graph.
- */
- public Set<Node> getNodes() {
- Set<Node> nodes = new LinkedHashSet<Node>(internalNodes);
- nodes.addAll(getExternalNodes());
- return nodes;
- }
+ /**
+ * Returns an array of 2 nodes which are the nodes at either end of the edge.
+ *
+ * @param edge
+ * @return an array of 2 edges
+ */
+ public Node[] getNodes(Edge edge) {
+ for (Node node : getNodes()) {
+ if (((SimpleRootedNode)node).getEdge() == edge) {
+ return new Node[] { node, ((SimpleRootedNode)node).getParent() };
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return the set of all nodes in this graph.
+ */
+ public Set<Node> getNodes() {
+ Set<Node> nodes = new LinkedHashSet<Node>(internalNodes);
+ nodes.addAll(getExternalNodes());
+ return nodes;
+ }
/**
* @return the set of all edges in this graph.
@@ -555,33 +610,33 @@ final public class SimpleRootedTree implements RootedTree {
return edges;
}
- /**
- * The set of external edges. This is a pretty inefficient implementation because
- * a new set is constructed each time this is called.
- * @return the set of external edges.
- */
- public Set<Edge> getExternalEdges() {
- Set<Edge> edges = new LinkedHashSet<Edge>();
- for (Node node : getExternalNodes()) {
- edges.add(((SimpleRootedNode)node).getEdge());
- }
- return edges;
- }
-
- /**
- * The set of internal edges. This is a pretty inefficient implementation because
- * a new set is constructed each time this is called.
- * @return the set of internal edges.
- */
- public Set<Edge> getInternalEdges() {
- Set<Edge> edges = new LinkedHashSet<Edge>();
- for (Node node : getInternalNodes()) {
- if (node != getRootNode()) {
- edges.add(((SimpleRootedNode)node).getEdge());
- }
- }
- return edges;
- }
+ /**
+ * The set of external edges. This is a pretty inefficient implementation because
+ * a new set is constructed each time this is called.
+ * @return the set of external edges.
+ */
+ public Set<Edge> getExternalEdges() {
+ Set<Edge> edges = new LinkedHashSet<Edge>();
+ for (Node node : getExternalNodes()) {
+ edges.add(((SimpleRootedNode)node).getEdge());
+ }
+ return edges;
+ }
+
+ /**
+ * The set of internal edges. This is a pretty inefficient implementation because
+ * a new set is constructed each time this is called.
+ * @return the set of internal edges.
+ */
+ public Set<Edge> getInternalEdges() {
+ Set<Edge> edges = new LinkedHashSet<Edge>();
+ for (Node node : getInternalNodes()) {
+ if (node != getRootNode()) {
+ edges.add(((SimpleRootedNode)node).getEdge());
+ }
+ }
+ return edges;
+ }
/**
* @param degree the number of edges connected to a node
@@ -676,11 +731,11 @@ final public class SimpleRootedTree implements RootedTree {
return conceptuallyUnrooted;
}
- public boolean isRoot(Node node) {
- return node == rootNode;
- }
+ public boolean isRoot(Node node) {
+ return node == rootNode;
+ }
- // Attributable IMPLEMENTATION
+ // Attributable IMPLEMENTATION
public void setAttribute(String name, Object value) {
if (helper == null) {
@@ -882,5 +937,7 @@ final public class SimpleRootedTree implements RootedTree {
private double length;
private Edge edge = null;
+
+ private int externalNodeCount = -1;
}
}
\ No newline at end of file
=====================================
src/jebl/evolution/trees/TransformedRootedTree.java
=====================================
@@ -71,6 +71,20 @@ public class TransformedRootedTree extends FilteredRootedTree {
}
}
+ /**
+ * @return Whether the node heights are known or need to be recalculated from the lengths
+ */
+ public boolean isHeightsKnown() {
+ return true;
+ }
+
+ /**
+ * @return Whether the branch lengths are known or need to be recalculated from the heights
+ */
+ public boolean isLengthsKnown() {
+ return true;
+ }
+
private int getCladeSize(Node node) {
if (isExternal(node)) {
return 1;
=====================================
src/jebl/evolution/trees/Utils.java
=====================================
@@ -8,6 +8,7 @@
*/
package jebl.evolution.trees;
+import jebl.evolution.graphs.Edge;
import jebl.evolution.graphs.Graph;
import jebl.evolution.graphs.Node;
import jebl.evolution.taxa.Taxon;
@@ -23,6 +24,8 @@ import java.util.*;
* @version $Id: Utils.java 979 2009-02-24 11:15:18Z rambaut $
*/
public final class Utils {
+ private static final String BRANCH_LENGTH_FORMAT = "%.6g";
+
private Utils() { } // make class uninstantiable
/**
@@ -89,8 +92,7 @@ public final class Utils {
}
buffer.append(name);
if (tree.hasLengths()) {
- buffer.append(':');
- buffer.append(tree.getLength(node));
+ buffer.append(":").append(String.format(BRANCH_LENGTH_FORMAT, tree.getLength(node)));
}
} else {
buffer.append('(');
@@ -105,7 +107,7 @@ public final class Utils {
// Don't write root length. This is ignored elsewhere and the nexus importer fails
// whet it is present.
if (parent != null && tree.hasLengths()) {
- buffer.append(":").append(tree.getLength(node));
+ buffer.append(":").append(String.format(BRANCH_LENGTH_FORMAT, tree.getLength(node)));
}
}
}
@@ -421,6 +423,33 @@ public final class Utils {
return true;
}
+ /**
+ * @param tree the tree
+ * @return the sum of all the branch lengths
+ */
+ public static double getLength(Tree tree) {
+ double length = 0.0;
+ for (Edge edge : tree.getEdges()) {
+ length += edge.getLength();
+ }
+ return length;
+ }
+
+ /**
+ * @param tree the tree
+ * @return the sum of all the branch lengths
+ */
+ public static double getLength(RootedTree tree) {
+ double length = 0.0;
+ for (Node node : tree.getNodes()) {
+ if (!tree.isRoot(node)) {
+ length += tree.getLength(node);
+ }
+ }
+ return length;
+ }
+
+
/**
* Return the number of external nodes under this node.
*
@@ -429,16 +458,7 @@ public final class Utils {
* @return the number of external nodes under this node.
*/
public static int getExternalNodeCount(RootedTree tree, Node node) {
-
- final List<Node> children = tree.getChildren(node);
- if (children.size() == 0) return 1;
-
- int externalNodeCount = 0;
- for (Node child : children) {
- externalNodeCount += getExternalNodeCount(tree, child);
- }
-
- return externalNodeCount;
+ return tree.getExternalNodeCount(node);
}
/**
View it on GitLab: https://salsa.debian.org/med-team/jebl2/-/compare/83b6598cad3faa721f3b416fd34633166884ad2e...a223ac0a4555d63ef68eb1b3474464f7516f0ffb
--
View it on GitLab: https://salsa.debian.org/med-team/jebl2/-/compare/83b6598cad3faa721f3b416fd34633166884ad2e...a223ac0a4555d63ef68eb1b3474464f7516f0ffb
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20231007/42182404/attachment-0001.htm>
More information about the debian-med-commit
mailing list