Source code for dendropy.datamodel.treemodel._edge

#! /usr/bin/env python
# -*- coding: utf-8 -*-

from io import StringIO
from dendropy.datamodel import basemodel
from dendropy.datamodel.treemodel import _bipartition

[docs] class Edge(basemodel.DataObject, basemodel.Annotable): """ An :term:``edge`` on a :term:``tree``. """ def __init__(self, **kwargs): """ Keyword Arguments ----------------- head_node : |Node|, optional Node from to which this edge links, i.e., the child node of this node ``tail_node``. length : numerical, optional A value representing the weight of the edge. rootedge : boolean, optional Is the child node of this edge the root or seed node of the tree? label : string, optional Label for this edge. """ basemodel.DataObject.__init__(self, label=kwargs.pop("label", None)) self._head_node = kwargs.pop("head_node", None) if "tail_node" in kwargs: raise TypeError( "Setting the tail node directly is no longer supported: instead, set" " the parent node of the head node" ) self.rootedge = kwargs.pop("rootedge", None) self.length = kwargs.pop("length", None) if kwargs: raise TypeError("Unsupported keyword arguments: {}".format(kwargs)) self._bipartition = None self.comments = [] def __copy__(self, memo=None): raise TypeError("Cannot directly copy Edge")
[docs] def taxon_namespace_scoped_copy(self, memo=None): raise TypeError("Cannot directly copy Edge")
def __deepcopy__(self, memo=None): # call Annotable.__deepcopy__() return basemodel.Annotable.__deepcopy__(self, memo=memo) # return super(Edge, self).__deepcopy__(memo=memo) def __hash__(self): return id(self) def __eq__(self, other): return self is other def __lt__(self, other): return id(self) < id(other) def _get_tail_node(self): if self._head_node is None: return None return self._head_node._parent_node def _set_tail_node(self, node): if self._head_node is None: raise ValueError("'_head_node' is 'None': cannot assign 'tail_node'") # Go through managed property instead of # setting attribute to ensure book-keeping self._head_node.parent_node = node tail_node = property(_get_tail_node, _set_tail_node) def _get_head_node(self): return self._head_node def _set_head_node(self, node): # Go through managed property instead of setting attribute to ensure # book-keeping; following should also set ``_head_node`` of ``self`` node.edge = self head_node = property(_get_head_node, _set_head_node)
[docs] def is_leaf(self): "Returns True if the head node has no children" return self.head_node and self.head_node.is_leaf()
def is_terminal(self): return self.is_leaf()
[docs] def is_internal(self): "Returns True if the head node has children" return self.head_node and not self.head_node.is_leaf()
[docs] def get_adjacent_edges(self): """ Returns a list of all edges that "share" a node with ``self``. """ he = [i for i in self.head_node.incident_edges() if i is not self] te = [i for i in self.tail_node.incident_edges() if i is not self] he.extend(te) return he
adjacent_edges = property(get_adjacent_edges)
[docs] def collapse(self, adjust_collapsed_head_children_edge_lengths=False): """ Inserts all children of the head_node of self as children of the tail_node of self in the same place in the child_node list that head_node had occupied. The edge length and head_node will no longer be part of the tree unless ``adjust_collapsed_head_children_edge_lengths``. is True. """ to_del = self.head_node parent = self.tail_node if not parent: return children = to_del.child_nodes() if not children: raise ValueError("collapse_self called with a terminal.") pos = parent.child_nodes().index(to_del) parent.remove_child(to_del) for child in children: parent.insert_child(pos, child) pos += 1 if adjust_collapsed_head_children_edge_lengths and self.length is not None: # print id(child), child.edge.length, self.length if child.edge.length is None: child.edge.length = self.length else: child.edge.length += self.length
[docs] def invert(self, update_bipartitions=False): """ Changes polarity of edge. """ # self.head_node, self.tail_node = self.tail_node, self.head_node if not self.head_node: raise ValueError("Cannot invert edge with 'None' for head node") if not self.tail_node: raise ValueError("Cannot invert edge with 'None' for tail node") old_head_node = self.head_node new_tail_node = old_head_node old_tail_node = self.tail_node new_head_node = old_tail_node grandparent = old_tail_node._parent_node if grandparent is not None: for idx, ch in enumerate(grandparent._child_nodes): if ch is old_tail_node: grandparent._child_nodes[idx] = old_head_node break else: # we did not break loop: force insertion of old_head_node if # not already there if old_head_node not in grandparent._child_nodes: grandparent._child_nodes.append(old_head_node) assert old_head_node in old_tail_node._child_nodes old_tail_node.remove_child(old_head_node) assert old_head_node not in old_tail_node._child_nodes old_head_node.add_child(old_tail_node) old_tail_node.edge.length, old_head_node.edge.length = ( old_head_node.edge.length, old_tail_node.edge_length, )
def _get_bipartition(self): if self._bipartition is None: self._bipartition = _bipartition.Bipartition( edge=self, is_mutable=True, ) return self._bipartition def _set_bipartition(self, v=None): self._bipartition = v bipartition = property(_get_bipartition, _set_bipartition) def _get_split_bitmask(self): return self.bipartition._split_bitmask def _set_split_bitmask(self, h): self.bipartition._split_bitmask = h split_bitmask = property(_get_split_bitmask, _set_split_bitmask) def _get_leafset_bitmask(self): return self.bipartition._leafset_bitmask def _set_leafset_bitmask(self, h): self.bipartition._leafset_bitmask = h leafset_bitmask = property(_get_leafset_bitmask, _set_leafset_bitmask) def _get_tree_leafset_bitmask(self): return self.bipartition._tree_leafset_bitmask def _set_tree_leafset_bitmask(self, h): self.bipartition._tree_leafset_bitmask = h tree_leafset_bitmask = property( _get_tree_leafset_bitmask, _set_tree_leafset_bitmask ) def split_as_bitstring(self): return self.bipartition.split_as_bitstring() def leafset_as_bitstring(self): return self.bipartition.leafset_as_bitstring()
[docs] def description( self, depth=1, indent=0, itemize="", output=None, taxon_namespace=None ): """ Returns description of object, up to level ``depth``. """ if depth is None or depth < 0: return output_strio = StringIO() if self.label is None: label = " (%s, Length=%s)" % (id(self), str(self.length)) else: label = " (%s: '%s', Length=%s)" % (id(self), self.label, str(self.length)) output_strio.write( "%s%sEdge object at %s%s" % (indent * " ", itemize, hex(id(self)), label) ) if depth >= 1: leader1 = " " * (indent + 4) leader2 = " " * (indent + 8) output_strio.write("\n%s[Length]" % leader1) if self.length is not None: length = self.length else: length = "None" output_strio.write("\n%s%s" % (leader2, length)) output_strio.write("\n%s[Tail Node]" % leader1) if self.tail_node is not None: tn = self.tail_node.description(0) else: tn = "None" output_strio.write("\n%s%s" % (leader2, tn)) output_strio.write("\n%s[Head Node]" % leader1) if self.head_node is not None: hn = self.head_node.description(0) else: hn = "None" output_strio.write("\n%s%s" % (leader2, hn)) s = output_strio.getvalue() if output is not None: output.write(s) return s
def _format_edge(self, **kwargs): ef = kwargs.get('edge_formatter', None) if ef: return ef(self) return str(self)