def newick_node_to_shape(node): """ Create a `Shape` object from a `Node` object. :param node: a `Node`. :return: `Shape` instance. """ if not bool(node.descendants): return Shape(None) return Shape(sorted([newick_node_to_shape(x) for x in node.descendants]))
def shape(self): """ Returns the `Shape` associated to self. Namely, it "forgets" the names of the leaves. :return: `Shape` instance. """ if self.is_leaf(): return Shape(None) else: return Shape([x.shape() for x in self.children])
def test_shape(self): self.assertEqual(Shape.LEAF, Shape()) self.assertEqual(Shape.CHERRY, Shape([Shape.LEAF, Shape.LEAF])) self.assertEqual(Shape.LEAF.shape(), Shape.LEAF) self.assertEqual(Shape.CHERRY.shape(), Shape.CHERRY) self.assertEqual(Shape([Shape.LEAF, Shape.CHERRY]), Shape([Shape.LEAF, Shape.CHERRY]))
def go(t, dis): if dis: if dis[0] != 0: return Shape( [go(t.children[0], [di - 1 for di in dis]), t.children[1]]) else: return Shape([ Shape.LEAF, go(t.children[1], [di - 1 for di in dis[1:]]) ]) else: return t
def star(n): """ Returns a star `Shape` with n leaves. :param n: `int` instance. :return: `Shape` instance. """ return Shape([Shape.LEAF for _ in range(n)])
def alphagamma_from_t(t, prob): """ Returns a list of tuples containing each shape obtained from `Shape` sh (assuming sh has probability prob) with their associate probability under the Alpha-Gamma model. :param sh: `Shape` instance. :param prob: `function` instance. :return: `list` instance. """ n = count_leaves(t) if t.is_leaf(): return [(add_leaf_to_edge(t), lambda a, c: simplify(prob(a, c) * (1 - a) / (n - a)))] else: for i in range(len(t.children)): k = count_leaves(t.children[i]) if n > 1: p_times_m = lambda a, c, k=k: simplify( prob(a, c) * (k - a) / (n - a)) else: p_times_m = prob for ti, q in alphagamma_from_t(t.children[i], p_times_m): ts2 = list(iter_replace_tree_at(t.children, i, ti)) yield (Shape(ts2), q) yield (add_leaf_to_edge(t), lambda a, c: simplify(prob(a, c) * c / (n - a))) k = len(t.children) if k > 1: prob2 = lambda a, c, k=k: simplify( prob(a, c) * ((k - 1) * a - c) / (n - a)) else: prob2 = prob yield (add_leaf_to_node(t), prob2)
def add_leaf_to_edge(t): """ Returns a `Shape` instance with a new root; both a new leaf and the input `Shape` pend from it. :param t: `Shape` instance. :return: `Shape` instance. """ return Shape([Shape.LEAF, t])
def quartet_index(tree, vs=range(5)): Q = get_quartets() t0 = Shape([Shape.LEAF, Shape.LEAF, Shape.LEAF]) def go(t): if t.is_leaf(): return 0, 0, 1 ts = t.children quartets, triples, kappas = zip(*map(go, ts)) kappa = sum(kappas) if kappa < 3: triple = 0 elif isomorphic(t, t0): triple = 1 else: t_s0 = sum(triples) t_s1 = sum(kappas[i1] * kappas[i2] * kappas[i3] for i1 in range(len(ts)) for i2 in range(i1 + 1, len(ts)) for i3 in range(i2 + 1, len(ts))) triple = t_s0 + t_s1 if kappa < 4: return 0, triple, kappa for q, v in zip(Q, vs): if isomorphic(t, q): return v, triple, kappa s0 = sum(quartets) s1 = sum( binom2(kappas[i1]) * kappas[i2] * kappas[i3] + binom2(kappas[i2]) * kappas[i1] * kappas[i3] + binom2(kappas[i3]) * kappas[i1] * kappas[i2] for i1 in range(len(ts)) for i2 in range(i1 + 1, len(ts)) for i3 in range(i2 + 1, len(ts))) s2 = sum(kappas[i1] * triples[i2] + kappas[i2] * triples[i1] for i1 in range(len(ts)) for i2 in range(i1 + 1, len(ts))) s3 = sum( binom2(kappas[i1]) * binom2(kappas[i2]) for i1 in range(len(ts)) for i2 in range(i1 + 1, len(ts))) s4 = sum(kappas[i1] * kappas[i2] * kappas[i3] * kappas[i4] for i1 in range(len(ts)) for i2 in range(i1 + 1, len(ts)) for i3 in range(i2 + 1, len(ts)) for i4 in range(i3 + 1, len(ts))) return s0 + vs[1] * s1 + vs[2] * s2 + vs[3] * s3 + vs[ 4] * s4, triple, kappa return go(tree)[0]
def add_leaf_to_node(t): """ Returns a `Shape` instance with the same root; we have added a pending leaf to it. :param t: `Shape` instance. :return: `Shape` instance. """ if t.is_leaf(): return add_leaf_to_edge(t) else: return Shape([Shape.LEAF] + t.children)
def test_deg(self): self.assertEqual(rooted_deg(Shape.LEAF), 0) self.assertEqual(rooted_deg(Shape.CHERRY), 2) self.assertEqual(unrooted_deg(Shape.LEAF), 1) self.assertEqual(unrooted_deg(Shape.CHERRY), 3) t = Shape([Shape.LEAF, Shape.CHERRY]) self.assertEqual(rooted_deg(t), 2) self.assertEqual(unrooted_deg(t), 3) self.assertEqual(unrooted_deg(t, root=True), 2)
def comb(n): """ Returns a comb `Shape` with n leaves. :param n: `int` instance. :return: `Shape` instance. """ if n == 1: return Shape.LEAF elif n == 2: return Shape.CHERRY else: return Shape([Shape.LEAF, comb(n - 1)])
def all_binary_trees_from_t(t): """ Returns a list with all the binary `Shape` instances that can be obtained from the input tree. :param t: `Shape` instance. :return: `list` instance. """ if not t.is_leaf(): for i in range(len(t.children)): for ti in all_binary_trees_from_t(t.children[i]): ts2 = list(iter_replace_tree_at(t.children, i, ti)) yield Shape(ts2) yield add_leaf_to_edge(t)
def delete_nodes_with_out_degree_one(t): """ Deletes all nodes in the input `Shape` instance with only one child, for they are redundant :return: `Shape` instance """ if t.is_leaf(): return t else: if len(t.children) == 1: return delete_nodes_with_out_degree_one(t.children[0]) else: return Shape( [delete_nodes_with_out_degree_one(ch) for ch in t.children])
def all_binary_trees_with_n_leaves(n): """ Returns a list with all the binary `Shape` instances that have n leaves. :param n: `int` instance. :return: `list` instance. """ if n <= 0: pass elif n == 1: yield Shape.LEAF elif n == 2: yield Shape([Shape.LEAF, Shape.LEAF]) else: for t in all_binary_trees_with_n_leaves(n - 1): yield from all_binary_trees_from_t(t)
def binary_max_balanced(n): """ Returns a binary maximum balanced `Shape` with n leaves. :param n: `int` instance. :return: `Shape` instance. """ if n == 1: return Shape.LEAF elif n == 2: return Shape.CHERRY else: s = n % 2 return Shape([ binary_max_balanced((n - s) // 2), binary_max_balanced((n - s) // 2 + s) ])
def alphagamma(n): """ Returns a list of tuples containing all the shapes of n leaves that can be obtained under the Alpha-Gamma model and their corresponding probability. :param n: `int` instance. :return: `list` instance. """ if n <= 0: pass elif n == 1: yield (Shape.LEAF, lambda a, c: 1) elif n == 2: yield (Shape([Shape.LEAF, Shape.LEAF]), lambda a, c: 1) else: for t, prob in alphagamma(n - 1): yield from alphagamma_from_t(t, prob)
def test_shape(self): self.assertEqual( L1.shape(), L2.shape()) self.assertEqual( C12.shape(), C31.shape()) self.assertEqual( L1.shape(), Shape.LEAF) self.assertEqual( C23.shape(), Shape.CHERRY) self.assertEqual( PhyloTree(None, [L1, C31]).shape(), Shape([Shape.LEAF, Shape.CHERRY]))
def min_colless(n): """ Returns all the `Shape` instances that attain the minimum Colless index with `int` n leaves. :param n: `int` instance. :return: `list` instance. """ if n == 0: return [] elif n == 1: return [Shape.LEAF] elif n == 2: return [Shape.CHERRY] else: tss = [] for n1, n2 in min_colless_root(n): ts = unique([ Shape(sorted([t1, t2])) for t1 in min_colless(n1) for t2 in min_colless(n2) ]) tss.extend(ts) return tss
def get_quartets(): q0 = Shape( [Shape.LEAF, Shape([Shape.LEAF, Shape([Shape.LEAF, Shape.LEAF])])]) q1 = Shape([Shape.LEAF, Shape.LEAF, Shape([Shape.LEAF, Shape.LEAF])]) q2 = Shape([Shape.LEAF, Shape([Shape.LEAF, Shape.LEAF, Shape.LEAF])]) q3 = Shape( [Shape([Shape.LEAF, Shape.LEAF]), Shape([Shape.LEAF, Shape.LEAF])]) q4 = Shape([Shape.LEAF, Shape.LEAF, Shape.LEAF, Shape.LEAF]) return [q0, q1, q2, q3, q4]