def collapse_conflicting(subtree_root, split, split_bitmask): """ Takes a node that is the root of a subtree. Collapses every edge in the subtree that conflicts with split. This can include the edge subtending subtree_root. """ # we flip splits so that both the split and each edges split have the # lowest bit of the clade mask set to one lb = treesplit.lowest_bit_only(split_bitmask) if lb & split: cropped_split = split & split_bitmask else: cropped_split = (~split) & split_bitmask to_collapse_head_nodes = [] for nd in subtree_root.postorder_iter(): if not nd.is_leaf(): ncm = nd.edge.split_bitmask if lb & ncm: nd_split = ncm & split_bitmask else: nd_split = (~ncm) & split_bitmask cm_union = nd_split | cropped_split if (cm_union != nd_split) and (cm_union != cropped_split): to_collapse_head_nodes.append(nd) for nd in to_collapse_head_nodes: e = nd.edge e.collapse()
def reroot_on_lowest_common_index_path(t, common_mask): """This operation is only for unrooted trees that are being merged using SCM. The path the separates the lowest index taxon in the leaf set intersection is placed as the first descendant path of the "seed_node" for the tree. This assures that all split representations are oriented in the same way for subsequent operations. The mask most contain more that 2 bits (there must be an internal node in the tree that is has degree > 2 wrt the common leafset). """ l = lowest_bit_only(common_mask) assert (l > 0) assert (count_bits(common_mask) > 2) # start at the lowest leaf in common. curr_n = t.split_edges[l].head_node # walk back toward the root until we find a node that has another bit p = curr_n.parent_node while p: if (p.edge.split_bitmask & common_mask) != l: break curr_n = p p = curr_n.parent_node without_lowest = common_mask ^ l taxa_mask = t.seed_node.edge.split_bitmask if (curr_n.edge.split_bitmask & common_mask) == l: # we did not make it to the root. Make curr_n, the first_child of the root t.to_outgroup_position(curr_n, update_splits=True, delete_outdegree_one=True) avoid = curr_n nd_source = iter(t.seed_node.child_nodes()) try: while True: curr_n = nd_source.next() if curr_n is not avoid: cm = (curr_n.edge.split_bitmask & without_lowest) if cm: if cm == without_lowest: r = t.seed_node assert curr_n.parent_node is r t.reseed_at(curr_n, update_splits=True, delete_outdegree_one=True) t.to_outgroup_position(r, update_splits=True, delete_outdegree_one=True) nd_source = iter(curr_n.child_nodes()) avoid = r else: return except StopIteration: assert False return # we hit the root, now we walk up the tree, to find the a relevant internal lowest_on_path_to_l = curr_n comp_mask = (~common_mask) & taxa_mask children = curr_n.child_nodes() assert (len(children) > 1) nd_source = iter(children) try: while True: c = nd_source.next() cm = c.edge.split_bitmask masked_cm = cm & common_mask if masked_cm: if masked_cm == without_lowest: curr_n = c children = curr_n.child_nodes() assert (len(children) > 1) nd_source = iter(children) else: break except StopIteration: raise AssertionError("Reaching supposedly unreachable code") if curr_n is not t.seed_node: # We have found the first relevant internal node, we want to make it # the root. We can do this by putting one of its children into the # "outgroup position" and then putting the path to lowest commond # leaf in the outgroup position (this last operation is just a # rearrangement of the order of children in the root. children = curr_n.child_nodes() assert (len(children) > 1) p = curr_n.parent t.to_outgroup_position(children[0], update_splits=True, delete_outdegree_one=True) t.to_outgroup_position(p, update_splits=True, delete_outdegree_one=True) else: # if the root first relevant, node then we just make the path leading # to the lowest index node the first child of the root t.to_outgroup_position(lowest_on_path_to_l, update_splits=True, delete_outdegree_one=True)
def reroot_on_lowest_common_index_path(t, common_mask): """This operation is only for unrooted trees that are being merged using SCM. The path the separates the lowest index taxon in the leaf set intersection is placed as the first descendant path of the "seed_node" for the tree. This assures that all split representations are oriented in the same way for subsequent operations. The mask most contain more that 2 bits (there must be an internal node in the tree that is has degree > 2 wrt the common leafset). """ l = lowest_bit_only(common_mask) assert(l > 0) assert(count_bits(common_mask) > 2) # start at the lowest leaf in common. curr_n = t.split_edges[l].head_node # walk back toward the root until we find a node that has another bit p = curr_n.parent_node while p: if (p.edge.split_bitmask & common_mask) != l: break curr_n = p p = curr_n.parent_node without_lowest = common_mask^l taxa_mask = t.seed_node.edge.split_bitmask if (curr_n.edge.split_bitmask & common_mask) == l: # we did not make it to the root. Make curr_n, the first_child of the root t.to_outgroup_position(curr_n, update_splits=True, delete_outdegree_one=True) avoid = curr_n nd_source = iter(t.seed_node.child_nodes()) try: while True: curr_n = nd_source.next() if curr_n is not avoid: cm = (curr_n.edge.split_bitmask & without_lowest) if cm: if cm == without_lowest: r = t.seed_node assert curr_n.parent_node is r t.reseed_at(curr_n, update_splits=True, delete_outdegree_one=True) t.to_outgroup_position(r, update_splits=True, delete_outdegree_one=True) nd_source = iter(curr_n.child_nodes()) avoid = r else: return except StopIteration: assert False return # we hit the root, now we walk up the tree, to find the a relevant internal lowest_on_path_to_l = curr_n comp_mask = (~common_mask) & taxa_mask children = curr_n.child_nodes() assert(len(children) > 1) nd_source = iter(children) try: while True: c = nd_source.next() cm = c.edge.split_bitmask masked_cm = cm & common_mask if masked_cm: if masked_cm == without_lowest: curr_n = c children = curr_n.child_nodes() assert(len(children) > 1) nd_source = iter(children) else: break except StopIteration: raise AssertionError("Reaching supposedly unreachable code") if curr_n is not t.seed_node: # We have found the first relevant internal node, we want to make it # the root. We can do this by putting one of its children into the # "outgroup position" and then putting the path to lowest commond # leaf in the outgroup position (this last operation is just a # rearrangement of the order of children in the root. children = curr_n.child_nodes() assert(len(children) > 1) p = curr_n.parent t.to_outgroup_position(children[0], update_splits=True, delete_outdegree_one=True) t.to_outgroup_position(p, update_splits=True, delete_outdegree_one=True) else: # if the root first relevant, node then we just make the path leading # to the lowest index node the first child of the root t.to_outgroup_position(lowest_on_path_to_l, update_splits=True, delete_outdegree_one=True)
def runTest(self): for n, expected in enumerate( [0, 1, 2, 1, 4, 1, 2, 1, 8, 1, 2, 1, 4, 1, 2, 1, 16]): self.assertEqual(treesplit.lowest_bit_only(n), expected)
def runTest(self): for n, expected in enumerate([0, 1, 2, 1, 4, 1, 2, 1, 8, 1, 2, 1, 4, 1, 2, 1, 16]): self.assertEqual(treesplit.lowest_bit_only(n), expected)