phylotree
Version:
A JavaScript library for developing applications and interactive visualizations involving [phylogenetic trees](https://en.wikipedia.org/wiki/Phylogenetic_tree), written as an extension of the [D3](http://d3js.org) [hierarchy layout](https://github.com/d3/
120 lines (102 loc) • 3.76 kB
JavaScript
import * as _ from "underscore";
/*
* given a tree, this function will compute quantities required to work with
* all v all pairwise distances (like in COT)
*
* @param tree the tree object
* @returns leaf count
*
*/
function computePairwiseDistances(tree) {
/*
* traverse the tree and populate the following values in each node
*
* .cot_computed_length -> for each node (except the root), the current branch length
* (so as to not compute them each time via a callback)
* .cot_leaf_index -> post_order traversal order of a leaf (0 to number of leaves - 1)
*
* for each node
*
* .cot_path_to_leaves_below
* -> a dictionary that maps a leaf index to the total path length from this node
* to each of the descendant leaves, EXCLUDING the length of this branch
*
* .cot_path_to_leaves_above
* -> a dictionary that maps a leaf index to the total path length from this node
* to each of the leaves outside the split defined by this node, EXCLUDING
* the length of this branch
*/
var bl = tree.branch_length_accessor;
if (!bl) {
throw "A non-null branch lengths accessor function is required for this operation";
}
var leaf_count = 0;
tree.traverse_and_compute(function(n) {
n.cot_computed_length = bl(n);
if (n.parent && _.isUndefined(n.cot_computed_length)) {
throw "Non-null branch lengths are required for this operation: missing branch length at node " + n.data.name;
}
if (tree.isLeafNode(n)) {
n.cot_leaf_index = leaf_count++;
n.cot_path_to_leaves_below = {};
n.cot_path_to_leaves_below[n.cot_leaf_index] = 0;
n.cot_path_to_leaves_above = {};
} else {
n.cot_path_to_leaves_below = {};
n.cot_path_to_leaves_above = {};
}
});
// populate all cot_path_to_leaves_below
tree.traverse_and_compute(function(n) {
if (n.parent) {
_.each(n.cot_path_to_leaves_below, function(length_so_far, leaf_index) {
n.parent.cot_path_to_leaves_below[leaf_index] =
length_so_far + n.cot_computed_length;
});
}
});
// populate all cot_path_to_leaves_above; this is done via a 'pre-order' traversal
// handle root node first
var root_node = tree.getRootNode();
function CopyFromSiblings(a_node) {
for (var this_node = 0; this_node < a_node.children.length; this_node++) {
for (
var other_node = 0;
other_node < a_node.children.length;
other_node++
) {
if (this_node != other_node) {
_.each(a_node.children[other_node].cot_path_to_leaves_below, function(
length,
index
) {
if (a_node.children[this_node].cot_path_to_leaves_above) {
a_node.children[this_node].cot_path_to_leaves_above[index] =
length + a_node.children[other_node].cot_computed_length;
}
});
}
}
}
}
CopyFromSiblings(root_node);
// takes two passes
tree.traverse_and_compute(function(n) {
if (n.parent) {
// copy parent's 'above' nodes
_.each(n.parent.cot_path_to_leaves_above, function(
length_so_far,
leaf_index
) {
n.cot_path_to_leaves_above[leaf_index] =
length_so_far + n.parent.cot_computed_length;
});
if (!tree.isLeafNode(n)) {
CopyFromSiblings(n);
}
// copy sibling's 'below' nodes
}
}, "pre-order");
return leaf_count;
}
export default computePairwiseDistances;