def _render_parasite_branches(fig: plot_tools.FigureWrapper, node: tree.Node, recon_obj: recon.Reconciliation, host_lookup: dict, parasite_lookup: dict): """ Very basic branch drawing :param fig: Figure object that visualizes trees using MatplotLib :param node: Node object representing the parasite event being rendered :param recon_obj: Reconciliation object that represents an edge-to-edge mapping from a parasite tree to a host tree :param host_lookup: Dictionary with host node names as the key and host node objects as the values :param parasite_lookup: Dictionary with parasite node names as the key and parasite node objects as the values """ node_pos = plot_tools.Position(node.layout.x, node.layout.y) left_node, right_node = _get_children(node, recon_obj, parasite_lookup) right_pos = plot_tools.Position(right_node.layout.x, right_node.layout.y) mapping_node = recon_obj.mapping_of(node.name) event = recon_obj.event_of(mapping_node) if event.event_type is recon.EventType.COSPECIATION: _render_cospeciation_branch(node, host_lookup, parasite_lookup, recon_obj, fig) elif event.event_type is recon.EventType.DUPLICATION: _connect_children(node, host_lookup, parasite_lookup, recon_obj, fig) elif event.event_type is recon.EventType.TRANSFER: _render_transfer_branch(node_pos, right_pos, fig, node, host_lookup, recon_obj, right_node) _connect_child_to_parent(node, left_node, host_lookup, recon_obj, fig) else: raise ValueError('%s is not an known event type' % event.event_type)
def _is_sharing_track(node: tree.Node, host_name: str, recon_obj: recon.Reconciliation): """ Determines if a node is sharing it's horizontal track with its children :param node: Node object representing a parasite event :param host_name: Name of host node :param recon_obj: Reconciliation Object """ left_host_name = recon_obj.mapping_of(node.left_node.name).host right_host_name = recon_obj.mapping_of(node.right_node.name).host return host_name == left_host_name or host_name == right_host_name
def dict_to_reconciliation(old_recon: Dict[Tuple, List], event_scores: Dict[tuple, float] = None): """ Convert the old reconciliation graph format to Reconciliation. Example of old format: old_recon = { ('n0', 'm2'): [('S', ('n2', 'm3'), ('n1', 'm4'))], ('n1', 'm4'): [('C', (None, None), (None, None))], ('n2', 'm3'): [('T', ('n3', 'm3'), ('n4', 'm1'))], ('n3', 'm3'): [('C', (None, None), (None, None))], ('n4', 'm1'): [('C', (None, None), (None, None))], } """ roots = _find_roots(old_recon) if len(roots) > 1: raise ValueError("old_recon has many roots") root = roots[0] recon = Reconciliation(root) event = None for mapping in old_recon: host, parasite = mapping if len(old_recon[mapping]) != 1: raise ValueError( 'old_recon mapping node has no or multiple events') event_tuple = old_recon[mapping][0] etype, left, right = event_tuple mapping_node = MappingNode(host, parasite) if etype in 'SDT': left_parasite, left_host = left right_parasite, right_host = right left_mapping = MappingNode(left_parasite, left_host) right_mapping = MappingNode(right_parasite, right_host) if etype == 'S': event = Cospeciation(left_mapping, right_mapping) if etype == 'D': event = Duplication(left_mapping, right_mapping) if etype == 'T': event = Transfer(left_mapping, right_mapping) elif etype == 'L': child_parasite, child_host = left child_mapping = MappingNode(child_parasite, child_host) event = Loss(child_mapping) elif etype == 'C': event = TipTip() else: raise ValueError('%s not in "SDTLC"' % etype) if event_scores is not None: event._freq = event_scores[event_tuple] recon.set_event(mapping_node, event) return recon
def _get_children(node: tree.Node, recon_obj: recon.Reconciliation, parasite_lookup: dict): """ Gets the children of a node in the order they appear in the mapping node. :param node: Node object representing a parasite event :param recon_obj: Reconciliation Object :param parasite_lookup: Dictionary with parasite node names as the key and parasite node objects as the values :return A tuple consisting of the left node and right node """ mapping_node = recon_obj.mapping_of(node.name) event = recon_obj.event_of(mapping_node) left_mapping_node = event.left right_mapping_node = event.right left_node_name = left_mapping_node.parasite right_node_name = right_mapping_node.parasite left_node = parasite_lookup[left_node_name] right_node = parasite_lookup[right_node_name] return left_node, right_node
def _populate_host_tracks(node: tree.Node, recon_obj: recon.Reconciliation, host_lookup: dict): """ :param node: Node object :param recon_obj: Reconciliation object that represents an edge-to-edge mapping from a parasite tree to a host tree :param host_lookup: Dictionary with host node names as the key and host node objects as the values """ mapping_node = recon_obj.mapping_of(node.name) event = recon_obj.event_of(mapping_node) host_name = mapping_node.host host_node = host_lookup[host_name] if not (event.event_type is recon.EventType.DUPLICATION or event.event_type is recon.EventType.TRANSFER): host_node.update_count() else: if not (_is_sharing_track(node, host_name, recon_obj)): host_node.update_count() if not (node.is_leaf()): _populate_host_tracks(node.left_node, recon_obj, host_lookup) _populate_host_tracks(node.right_node, recon_obj, host_lookup)
def _connect_child_to_parent(node: tree.Node, child_node: tree.Node, host_lookup: dict, recon_obj: recon.Reconciliation, fig: plot_tools.FigureWrapper, stop_row: float = None): """ Connects a child node to its parent node :param node: Node object representing a parasite event :param child_node: The child node object of a given node :param host_lookup: Dictionary with host node names as the key and host node objects as the values :param recon_obj: Reconciliation object that represents an edge-to-edge mapping from a parasite tree to a host tree :param fig: Figure object that visualizes trees using MatplotLib :param stop_row: row number to stop line drawing on """ mapping_node = recon_obj.mapping_of(child_node.name) host_node = host_lookup[mapping_node.host] if stop_row == None: stop_row = node.layout.row current_pos = plot_tools.Position(child_node.layout.x, child_node.layout.y) while host_node.layout.row != stop_row and host_node.parent_node: parent_node = host_node.parent_node if parent_node.layout.row < host_node.layout.row: v_track = parent_node.get_and_update_track( tree.Track.UPPER_VERTICAL) else: v_track = parent_node.get_and_update_track( tree.Track.LOWER_VERTICAL) while v_track < parent_node.layout.upper_v_track: v_track = parent_node.get_and_update_track( tree.Track.LOWER_VERTICAL) h_track = parent_node.get_and_update_track(tree.Track.HORIZONTAL) offset = parent_node.layout.offset sub_parent_pos = plot_tools.Position(parent_node.layout.x - (offset * v_track), \ parent_node.layout.y + (offset * h_track)) _render_loss_branch(sub_parent_pos, current_pos, fig) host_node = parent_node current_pos = sub_parent_pos node_pos = plot_tools.Position(node.layout.x, node.layout.y) mid_pos = plot_tools.Position(node_pos.x, current_pos.y) fig.line(node_pos, mid_pos, render_settings.PARASITE_EDGE_COLOR) fig.line(mid_pos, current_pos, render_settings.PARASITE_EDGE_COLOR)
def _fix_transfer(node: tree.Node, left_node, right_node: tree.Node, host_node: tree.Node, host_lookup: dict, parasite_lookup: dict, recon_obj: recon.Reconciliation, node_col: float = None, offset_number: int = 1): """ Checks to see in tranfer node is inconsistent and the tries to fix node if it can be slid down the host edge The tries to push a given node forward if possible to correct the assumed inconsistency :param node: Node object representing the parasite event being rendered :param node: Right node of the node object :param left_node: Left node of the node object :param right_node: Right node of the node object :param host_node: Node object represeting a host that the parasite node is mapped to :param host_lookup: Dictionary with host node names as the key and host node objects as the values :param parasite_lookup: Dictionary with parasite node names as the key and parasite node objects as the values :param recon_obj: Reconciliation object that represents an edge-to-edge mapping from a parasite tree to a host tree :param node_col: Column of the node, used when function is called recursively to check the next available transfer spot :param offset_number: Used to push node to an available transfer spot """ min_col = host_lookup[recon_obj.mapping_of( right_node.name).host].parent_node.layout.col max_col = host_node.layout.col node_col = node.layout.col max_col = min(host_node.layout.col, left_node.layout.col) if not (node_col): node_col = node.layout.col # Checks to see if transfer is inconsistent and if the inconsistency can be fixed by sliding the transfer node down the host edge if min_col >= node_col and min_col < max_col and not (_is_sharing_track( node, host_node.name, recon_obj)): node.set_layout(col=min_col + 0.5, x=min_col + 0.5) if min_col < max_col: new_value = min_col + render_settings.PUSHED_NODE_OFFSET * offset_number if _is_col_taken(new_value, host_lookup, parasite_lookup): _fix_transfer(node, left_node, right_node, host_node, host_lookup, parasite_lookup, recon_obj, node_col=new_value, offset_number=offset_number + 1) else: node.set_layout(col=new_value, x=new_value)
def _render_transfer_branch(node_pos: plot_tools.Position, right_pos: plot_tools.Position, fig: plot_tools.FigureWrapper, node: tree.Node, host_lookup: dict, recon_obj: recon.Reconciliation, right_node: tree.Node): """ Renders a transfer branch :param node_xy: x and y position of a node :param right_pos: x and y position of the right child of a node :param fig: Figure object that visualizes trees using MatplotLib :param node: Node object representing the parasite event being rendered :param host_lookup: Dictionary with host node names as the key and host node objects as the values :param recon_obj: Reconciliation object that represents an edge-to-edge mapping from a parasite tree to a host tree :param right_node: The right node object of node """ child_mapping_node = recon_obj.mapping_of(right_node.name) child_host_node = host_lookup[child_mapping_node.host] # Check temporal consistency of transfer event if child_host_node.parent_node.layout.col < node.layout.col: # Draw right node, which is transfered mid_pos = plot_tools.Position(node_pos.x, right_pos.y) # xy coords of midpoint y_midpoint = abs( mid_pos.y + node_pos.y) / 2 # value of midpoint between mid_xy and parent node # Determine if transfer is upwards or downwards, and draw triangle accordingly is_upwards = True if y_midpoint < mid_pos.y else False arrow_pos = plot_tools.Position(node_pos.x, y_midpoint) if is_upwards: fig.triangle(arrow_pos, render_settings.PARASITE_EDGE_COLOR) else: fig.triangle(arrow_pos, render_settings.PARASITE_EDGE_COLOR, rotation=render_settings.DOWN_ARROW_ROTATION) # Draw branch to midpoint, then draw branch to child fig.line(node_pos, mid_pos, render_settings.PARASITE_EDGE_COLOR) fig.line(mid_pos, right_pos, render_settings.PARASITE_EDGE_COLOR) else: transfer_edge_color = plot_tools.transparent_color( render_settings.PARASITE_EDGE_COLOR, render_settings.TRANSFER_TRANSPARENCY) fig.arrow_segment(node_pos, right_pos, transfer_edge_color) fig.line(node_pos, right_pos, transfer_edge_color)
def _render_cospeciation_branch(node: tree.Node, host_lookup: dict, parasite_lookup: dict, recon_obj: recon.Reconciliation, fig: plot_tools.FigureWrapper): """ Renders the cospeciation branch. :param node: Node object representing the parasite event being rendered :param host_lookup: Dictionary with host node names as the key and host node objects as the values :param parasite_lookup: Dictionary with parasite node names as the key and parasite node objects as the values :param recon_obj: Reconciliation object that represents an edge-to-edge mapping from a parasite tree to a host tree :param fig: Figure object that visualizes trees using MatplotLib """ left_node, right_node = _get_children(node, recon_obj, parasite_lookup) node_pos = plot_tools.Position(node.layout.x, node.layout.y) left_pos = plot_tools.Position(left_node.layout.x, left_node.layout.y) right_pos = plot_tools.Position(right_node.layout.x, right_node.layout.y) mapping_node = recon_obj.mapping_of(node.name) host_node = host_lookup[mapping_node.host] # Update h_track host_node.get_and_update_track(tree.Track.HORIZONTAL) left_mapping_node = recon_obj.mapping_of(left_node.name) left_host_node = host_lookup[left_mapping_node.host] right_mapping_node = recon_obj.mapping_of(right_node.name) right_host_node = host_lookup[right_mapping_node.host] # Draw left node offset = host_node.layout.offset if host_node.left_node.name == left_host_node.name: _render_curved_line_to(node_pos, left_pos, fig) if host_node.layout.lower_v_track < (host_node.layout.x - node_pos.x) / offset: host_node.layout.lower_v_track += (host_node.layout.x - node_pos.x) / offset + offset else: stop_row = host_node.left_node.layout.row _connect_child_to_parent(node, left_node, host_lookup, recon_obj, fig, stop_row=stop_row) # Draw Right node if host_node.right_node.name == right_host_node.name: _render_curved_line_to(node_pos, right_pos, fig) if host_node.layout.upper_v_track < (host_node.layout.x - node_pos.x) / offset: host_node.layout.upper_v_track += (host_node.layout.x - node_pos.x) / offset + offset else: stop_row = host_node.right_node.layout.row _connect_child_to_parent(node, right_node, host_lookup, recon_obj, fig, stop_row=stop_row)
def _render_parasite_helper(fig: plot_tools.FigureWrapper, node: tree.Node, recon_obj: recon.Reconciliation, host_lookup: dict, parasite_lookup: dict, show_internal_labels: bool, show_freq: bool, font_size: float, longest_host_name: int): """ Helper function for rendering the parasite tree. :param fig: Figure object that visualizes trees using MatplotLib :param node: Node object representing the parasite event being rendered :param recon_obj: Reconciliation object that represents an edge-to-edge mapping from a parasite tree to a host tree :param host_lookup: Dictionary with host node names as the key and host node objects as the values :param parasite_lookup: Dictionary with parasite node names as the key and parasite node objects as the values :param show_internal_labels: Boolean that determines whether or not the internal labels are shown :param show_freq: Boolean that determines wheter or not the frequencies are shown :param font_size: Font size for the text of the tips and internal nodes of the tree :param longest_host_name: The number of symbols in the longest host tree tip name """ # mapping_node is of type MappingNode which associates # a parasite to a host in a reconciliation mapping_node = recon_obj.mapping_of(node.name) # A reconciliation has an event_of method which is an object of # type Event. event = recon_obj.event_of(mapping_node) host_name = mapping_node.host # host_lookup is a dictionary computed in the Tree class # that associates a host name (a string) with the correspond node # object for that host. The node object contains layout information # which we need here. host_node = host_lookup[host_name] # Set parasite node layout host_row = host_node.layout.row # host_col = host_node.layout.col # host_x = host_node.layout.x host_y = host_node.layout.y node.set_layout(row=host_row, x=node.layout.col, y=host_y) # Render parasite node and recurse if not a leaf if node.is_leaf(): node.layout.y += host_node.get_and_update_track( tree.Track.HORIZONTAL) * host_node.layout.offset _render_parasite_node(fig, node, event, font_size, longest_host_name) return # If the Node is in their own track, change their position if not (_is_sharing_track(node, host_name, recon_obj)): node.layout.y += host_node.layout.h_track * host_node.layout.offset left_node, right_node = _get_children(node, recon_obj, parasite_lookup) _render_parasite_helper(fig, left_node, recon_obj, host_lookup, parasite_lookup, show_internal_labels, show_freq, font_size, longest_host_name) _render_parasite_helper(fig, right_node, recon_obj, host_lookup, parasite_lookup, show_internal_labels, show_freq, font_size, longest_host_name) # Checking to see if left node is mapped to the same host node as parent if node.layout.row == left_node.layout.row: node.set_layout(y=left_node.layout.y) elif node.layout.row == right_node.layout.row: node.set_layout(y=right_node.layout.y) elif event.event_type is recon.EventType.TRANSFER: node.layout.y = host_node.layout.y + host_node.layout.h_track * host_node.layout.offset #Checks to see if transfer node is inconsistent and if it can be fixed if event.event_type is recon.EventType.TRANSFER: min_col = host_lookup[recon_obj.mapping_of( right_node.name).host].parent_node.layout.col if min_col >= node.layout.col: _fix_transfer(node, left_node, right_node, host_node, host_lookup, parasite_lookup, recon_obj) _render_parasite_branches(fig, node, recon_obj, host_lookup, parasite_lookup) _render_parasite_node(fig, node, event, font_size, longest_host_name, show_internal_labels, show_freq)