Ejemplo n.º 1
0
def populate_with_bp_auid(bp_auid, pars, nil_name, with_opening_par, t,
                          current_node, going_down):
    while len(bp_auid) > 0:
        if with_opening_par and bp_auid[0] == pars[0]:
            going_down = True
            node_source, bp_auid = process_node_source(bp_auid[1:], pars,
                                                       nil_name, '')
            new_node = generate_unique_node()
            t.add_node(new_node, source=node_source)
            t.add_edge(current_node, new_node)
            current_node = new_node
        elif bp_auid[0] == pars[1]:
            if going_down:
                t.add_edge(current_node, NIL)
                going_down = False  # Add NIL only as a leaf
            current_node = next(t.predecessors(
                current_node))  # The only parent, this is a tree
            bp_auid = bp_auid[1:]
        else:
            going_down = True
            node_source = "" if bp_auid[0] == nil_name else bp_auid[0]
            bp_auid = bp_auid[1:]
            new_node = generate_unique_node()
            t.add_node(new_node, source=node_source)
            t.add_edge(current_node, new_node)
            current_node = new_node
Ejemplo n.º 2
0
def policy_buchi_pa(pa, weight_label='weight'):
    '''Computes the policy.'''
    if not pa.final:
        return float('Inf'), None

    vinit = generate_unique_node()
    pa.g.add_node(vinit)
    pa.g.add_edges_from([(vinit, init, {weight_label: 0}) for init in pa.init])

    prefix_costs, prefix_paths = nx.single_source_dijkstra(pa.g,
                                                           source=vinit,
                                                           weight=weight_label)
    pa.g.remove_node(vinit)

    opt_cost, opt_suffix_path = float('Inf'), None
    for final in pa.final:
        if final in prefix_costs:
            suffix_cost, suffix_path = source_to_target_dijkstra(
                pa.g,
                source=final,
                target=final,
                degen_paths=False,
                weight_key=weight_label)
            if prefix_costs[final] + suffix_cost < opt_cost:
                opt_cost = prefix_costs[final] + suffix_cost
                opt_suffix_path = suffix_path

    if opt_suffix_path is None:
        return float('Inf'), None

    opt_final = opt_suffix_path[0]
    return (opt_cost, [u[0] for u in prefix_paths[opt_final][1:]],
            [u[0] for u in opt_suffix_path])
Ejemplo n.º 3
0
def create_prefix_tree(paths):
    '''
    Creates the prefix tree (also known as trie) out of the paths. Implementation copied from package networkx v2.1.
    Implementation is extended by edge weights that correspond to the frequency of the transition in the input paths.
    '''
    def _helper(paths, root, B):
        children = defaultdict(list)
        for path in paths:
            if not path:
                B.add_edge(root, NIL, frequency=0)
                continue
            child, *rest = path
            children[child].append(rest)
        for head, tails in children.items():
            new_head = generate_unique_node()
            B.add_node(new_head, source=head)
            B.add_edge(root, new_head, frequency=len(tails))
            _helper(tails, new_head, B)

    T = nx.DiGraph()
    root = generate_unique_node()
    T.add_node(root, source=None)
    T.add_node(NIL, source=NIL)
    _helper(paths, root, T)
    return T, root
Ejemplo n.º 4
0
def _detect_unboundedness(R):
    """Detect infinite-capacity negative cycles.
    """
    s = generate_unique_node()
    G = nx.DiGraph()
    G.add_nodes_from(R)

    # Value simulating infinity.
    inf = R.graph['inf']
    # True infinity.
    f_inf = float('inf')
    for u in R:
        for v, e in R[u].items():
            # Compute the minimum weight of infinite-capacity (u, v) edges.
            w = f_inf
            for k, e in e.items():
                if e['capacity'] == inf:
                    w = min(w, e['weight'])
            if w != f_inf:
                G.add_edge(u, v, weight=w)

    if nx.negative_edge_cycle(G):
        raise nx.NetworkXUnbounded(
            'Negative cost cycle of infinite capacity found. '
            'Min cost flow may be unbounded below.')
Ejemplo n.º 5
0
def negative_edge_cycle(G, weight='weight'):
    """Return True if there exists a negative edge cycle anywhere in G.

    Parameters
    ----------
    G : NetworkX graph

    weight : string or function
       If this is a string, then edge weights will be accessed via the
       edge attribute with this key (that is, the weight of the edge
       joining `u` to `v` will be ``G.edge[u][v][weight]``). If no
       such edge attribute exists, the weight of the edge is assumed to
       be one.

       If this is a function, the weight of an edge is the value
       returned by the function. The function must accept exactly three
       positional arguments: the two endpoints of an edge and the
       dictionary of edge attributes for that edge. The function must
       return a number.

    Returns
    -------
    negative_cycle : bool
        True if a negative edge cycle exists, otherwise False.

    Examples
    --------
    >>> import networkx as nx
    >>> G = nx.cycle_graph(5, create_using = nx.DiGraph())
    >>> print(nx.negative_edge_cycle(G))
    False
    >>> G[1][2]['weight'] = -7
    >>> print(nx.negative_edge_cycle(G))
    True

    Notes
    -----
    Edge weight attributes must be numerical.
    Distances are calculated as sums of weighted edges traversed.

    This algorithm uses bellman_ford() but finds negative cycles
    on any component by first adding a new node connected to
    every node, and starting bellman_ford on that node.  It then
    removes that extra node.
    """
    newnode = generate_unique_node()
    G.add_edges_from([(newnode, n) for n in G])

    try:
        bellman_ford(G, newnode, weight)
    except nx.NetworkXUnbounded:
        return True
    finally:
        G.remove_node(newnode)
    return False
Ejemplo n.º 6
0
def bp_auid_to_preffix_tree(bp_auid, pars, nil_name, with_opening_par):
    # Init the tree with its root and the NIL "pseudo-leaf"
    t = nx.DiGraph()
    r = generate_unique_node()
    t.add_node(r, source=None)
    t.add_node(NIL, source=NIL)
    # And populate with the contents of lbp
    current_node = r
    populate_with_bp_auid(bp_auid, pars, nil_name, with_opening_par, t, r,
                          True)
    return (t, r)
Ejemplo n.º 7
0
 def _helper(paths, root, B):
     children = defaultdict(list)
     for path in paths:
         if not path:
             B.add_edge(root, NIL, frequency=0)
             continue
         child, *rest = path
         children[child].append(rest)
     for head, tails in children.items():
         new_head = generate_unique_node()
         B.add_node(new_head, source=head)
         B.add_edge(root, new_head, frequency=len(tails))
         _helper(tails, new_head, B)
Ejemplo n.º 8
0
def solve_graph_collision(ref: Graph,
                          other: Graph) -> Dict[Hashable, Hashable]:
    """
    Given two NetworkX graphs, find eventual name collisions between nodes and propose a solution.

    The proposed solution comes in the form of a dictionary, containing remapping rules that could be applied to the
    second graph in order to solve any clash.

    :param ref: a reference graph
    :param other: the other graph, on which renaming has to be performed
    :return: a partial mapping that solves eventual clashes once applied on the second graph
    """

    id_clashes = ref.nbunch_iter(other.nodes)
    return {idc: generate_unique_node() for idc in id_clashes}
Ejemplo n.º 9
0
def negative_edge_cycle(G, weight = 'weight'):
    """Return True if there exists a negative edge cycle anywhere in G.

    Parameters
    ----------
    G : NetworkX graph

    weight: string, optional (default='weight')
       Edge data key corresponding to the edge weight

    Returns
    -------
    negative_cycle : bool
        True if a negative edge cycle exists, otherwise False.

    Examples
    --------
    >>> import networkx as nx
    >>> G = nx.cycle_graph(5, create_using = nx.DiGraph())
    >>> print(nx.negative_edge_cycle(G))
    False
    >>> G[1][2]['weight'] = -7
    >>> print(nx.negative_edge_cycle(G))
    True

    Notes
    -----
    Edge weight attributes must be numerical.
    Distances are calculated as sums of weighted edges traversed.

    This algorithm uses bellman_ford() but finds negative cycles
    on any component by first adding a new node connected to
    every node, and starting bellman_ford on that node.  It then
    removes that extra node.
    """
    newnode = generate_unique_node()
    G.add_edges_from([ (newnode,n) for n in G])

    try:
        bellman_ford(G, newnode, weight)
    except nx.NetworkXUnbounded:
        G.remove_node(newnode)
        return True
    G.remove_node(newnode)
    return False
Ejemplo n.º 10
0
def negative_edge_cycle(G, weight='weight'):
    """Return True if there exists a negative edge cycle anywhere in G.

    Parameters
    ----------
    G : NetworkX graph

    weight: string, optional (default='weight')
       Edge data key corresponding to the edge weight

    Returns
    -------
    negative_cycle : bool
        True if a negative edge cycle exists, otherwise False.

    Examples
    --------
    >>> import networkx as nx
    >>> G = nx.cycle_graph(5, create_using = nx.DiGraph())
    >>> print(nx.negative_edge_cycle(G))
    False
    >>> G[1][2]['weight'] = -7
    >>> print(nx.negative_edge_cycle(G))
    True

    Notes
    -----
    Edge weight attributes must be numerical.
    Distances are calculated as sums of weighted edges traversed.

    This algorithm uses bellman_ford() but finds negative cycles
    on any component by first adding a new node connected to
    every node, and starting bellman_ford on that node.  It then
    removes that extra node.
    """
    newnode = generate_unique_node()
    G.add_edges_from([(newnode, n) for n in G])

    try:
        bellman_ford(G, newnode, weight)
    except nx.NetworkXUnbounded:
        return True
    finally:
        G.remove_node(newnode)
    return False
Ejemplo n.º 11
0
    def _helper(paths, root, B):
        """Recursively create a trie from the given list of paths.

        `paths` is a list of paths, each of which is itself a list of
        nodes, relative to the given `root` (but not including it). This
        list of paths will be interpreted as a tree-like structure, in
        which two paths that share a prefix represent two branches of
        the tree with the same initial segment.

        `root` is the parent of the node at index 0 in each path.

        `B` is the "accumulator", the :class:`networkx.DiGraph`
        representing the branching to which the new nodes and edges will
        be added.

        """
        # Create a mapping from each head node to the list of tail paths
        # remaining beneath that node.
        children = defaultdict(list)
        for path in paths:
            # If the path is the empty list, that represents the empty
            # string, so we add an edge to the NIL node.
            if not path:
                B.add_edge(root, NIL)
                continue
            # TODO In Python 3, this should be `child, *rest = path`.
            child, rest = path[0], path[1:]
            # `child` may exist as the head of more than one path in `paths`.
            children[child].append(rest)
        # Add a node for each child found above and add edges from the
        # root to each child. In this loop, `head` is the child and
        # `tails` is the list of remaining paths under that child.
        for head, tails in children.items():
            # We need to relabel each child with a unique name. To do
            # this we simply change each key in the dictionary to be a
            # (key, uuid) pair.
            new_head = generate_unique_node()
            # Ensure the new child knows the name of the old child so
            # that the user can recover the mapping to the original
            # nodes.
            B.add_node(new_head, source=head)
            B.add_edge(root, new_head)
            _helper(tails, new_head, B)
Ejemplo n.º 12
0
def prefix_tree(paths):
    """Creates a directed prefix tree from the given list of iterables.

    Parameters
    ----------
    paths: iterable of lists
        An iterable over "paths", which are themselves lists of
        nodes. Common prefixes among these paths are converted into
        common initial segments in the generated tree.

        Most commonly, this may be an iterable over lists of integers,
        or an iterable over Python strings.

    Returns
    -------
    T: DiGraph
        A directed graph representing an arborescence consisting of the
        prefix tree generated by `paths`. Nodes are directed "downward",
        from parent to child. A special "synthetic" root node is added
        to be the parent of the first node in each path. A special
        "synthetic" leaf node, the "nil" node, is added to be the child
        of all nodes representing the last element in a path. (The
        addition of this nil node technically makes this not an
        arborescence but a directed acyclic graph; removing the nil node
        makes it an arborescence.)

        Each node has an attribute 'source' whose value is the original
        element of the path to which this node corresponds. The 'source'
        of the root node is None, and the 'source' of the nil node is
        :data:`.NIL`.

        The root node is the only node of in-degree zero in the graph,
        and the nil node is the only node of out-degree zero.  For
        convenience, the nil node can be accessed via the :data:`.NIL`
        attribute; for example::

            >>> from networkx.generators.trees import NIL
            >>> paths = ['ab', 'abs', 'ad']
            >>> T, root = nx.prefix_tree(paths)
            >>> T.predecessors(NIL)  # doctest: +SKIP

    root : string
        The randomly generated uuid of the root node.

    Notes
    -----
    The prefix tree is also known as a *trie*.

    Examples
    --------
    Create a prefix tree from a list of strings with some common
    prefixes::

        >>> strings = ['ab', 'abs', 'ad']
        >>> T, root = nx.prefix_tree(strings)

    Continuing the above example, to recover the original paths that
    generated the prefix tree, traverse up the tree from the
    :data:`.NIL` node to the root::

        >>> from networkx.generators.trees import NIL
        >>>
        >>> strings = ['ab', 'abs', 'ad']
        >>> T, root = nx.prefix_tree(strings)
        >>> recovered = []
        >>> for v in T.predecessors(NIL):
        ...     s = ''
        ...     while v != root:
        ...         # Prepend the character `v` to the accumulator `s`.
        ...         s = str(T.node[v]['source']) + s
        ...         # Each non-nil, non-root node has exactly one parent.
        ...         v = next(T.predecessors(v))
        ...     recovered.append(s)
        >>> sorted(recovered)
        ['ab', 'abs', 'ad']

    """
    def _helper(paths, root, B):
        """Recursively create a trie from the given list of paths.

        `paths` is a list of paths, each of which is itself a list of
        nodes, relative to the given `root` (but not including it). This
        list of paths will be interpreted as a tree-like structure, in
        which two paths that share a prefix represent two branches of
        the tree with the same initial segment.

        `root` is the parent of the node at index 0 in each path.

        `B` is the "accumulator", the :class:`networkx.DiGraph`
        representing the branching to which the new nodes and edges will
        be added.

        """
        # Create a mapping from each head node to the list of tail paths
        # remaining beneath that node.
        children = defaultdict(list)
        for path in paths:
            # If the path is the empty list, that represents the empty
            # string, so we add an edge to the NIL node.
            if not path:
                B.add_edge(root, NIL)
                continue
            # TODO In Python 3, this should be `child, *rest = path`.
            child, rest = path[0], path[1:]
            # `child` may exist as the head of more than one path in `paths`.
            children[child].append(rest)
        # Add a node for each child found above and add edges from the
        # root to each child. In this loop, `head` is the child and
        # `tails` is the list of remaining paths under that child.
        for head, tails in children.items():
            # We need to relabel each child with a unique name. To do
            # this we simply change each key in the dictionary to be a
            # (key, uuid) pair.
            new_head = generate_unique_node()
            # Ensure the new child knows the name of the old child so
            # that the user can recover the mapping to the original
            # nodes.
            B.add_node(new_head, source=head)
            B.add_edge(root, new_head)
            _helper(tails, new_head, B)

    # Initialize the prefix tree with a root node and a nil node.
    T = nx.DiGraph()
    root = generate_unique_node()
    T.add_node(root, source=None)
    T.add_node(NIL, source=NIL)
    # Populate the tree.
    _helper(paths, root, T)
    return T, root
Ejemplo n.º 13
0
def inverse_line_graph(G):
    """ Returns the inverse line graph of graph G.

    If H is a graph, and G is the line graph of H, such that H = L(G).
    Then H is the inverse line graph of G.

    Not all graphs are line graphs and these do not have an inverse line graph.
    In these cases this generator returns a NetworkXError.

    Parameters
    ----------
    G : graph
        A NetworkX Graph

    Returns
    -------
    H : graph
        The inverse line graph of G.

    Raises
    ------
    NetworkXNotImplemented
        If G is directed or a multigraph

    NetworkXError
        If G is not a line graph

    Notes
    -----
    This is an implementation of the Roussopoulos algorithm.

    If G consists of multiple components, then the algorithm doesn't work.
    You should invert every component seperately:

    >>> K5 = nx.complete_graph(5)
    >>> P4 = nx.Graph([('a', 'b'), ('b', 'c'), ('c', 'd')])
    >>> G = nx.union(K5, P4)
    >>> root_graphs = []
    >>> for comp in nx.connected_components(G):
    ...     root_graphs.append(nx.inverse_line_graph(G.subgraph(comp)))
    >>> len(root_graphs)
    2

    References
    ----------
    * Roussopolous, N, "A max {m, n} algorithm for determining the graph H from
      its line graph G", Information Processing Letters 2, (1973), 108--112.

    """
    if G.number_of_nodes() == 0:
        a = generate_unique_node()
        H = nx.Graph()
        H.add_node(a)
        return H
    elif G.number_of_nodes() == 1:
        v = list(G)[0]
        a = (v, 0)
        b = (v, 1)
        H = nx.Graph([(a, b)])
        return H
    elif G.number_of_nodes() > 1 and G.number_of_edges() == 0:
        msg = ("inverse_line_graph() doesn't work on an edgeless graph. "
               "Please use this function on each component seperately.")
        raise nx.NetworkXError(msg)

    starting_cell = _select_starting_cell(G)
    P = _find_partition(G, starting_cell)
    # count how many times each vertex appears in the partition set
    P_count = {u: 0 for u in G.nodes()}
    for p in P:
        for u in p:
            P_count[u] += 1

    if max(P_count.values()) > 2:
        msg = "G is not a line graph (vertex found in more " \
              "than two partition cells)"
        raise nx.NetworkXError(msg)
    W = tuple([(u, ) for u in P_count if P_count[u] == 1])
    H = nx.Graph()
    H.add_nodes_from(P)
    H.add_nodes_from(W)
    for a, b in combinations(H.nodes(), 2):
        if len(set(a).intersection(set(b))) > 0:
            H.add_edge(a, b)
    return H
Ejemplo n.º 14
0
def _initial_tree_solution(G,
                           r,
                           demand='demand',
                           capacity='capacity',
                           weight='weight'):
    """Find a initial tree solution rooted at r.

    The initial tree solution is obtained by considering edges (r, v)
    for all nodes v with non-negative demand and (v, r) for all nodes
    with negative demand. If these edges do not exist, we add them to
    the graph and call them artificial edges.
    """
    H = nx.DiGraph(G)
    T = nx.DiGraph()
    y = {r: 0}
    artificialEdges = []
    flowCost = 0

    n = G.number_of_nodes()
    try:
        maxWeight = max(
            abs(d[weight]) for u, v, d in G.edges(data=True) if weight in d)
    except ValueError:
        maxWeight = 0
    hugeWeight = 1 + n * maxWeight

    for v, d in G.nodes(data=True)[1:]:
        vDemand = d.get(demand, 0)
        if vDemand >= 0:
            if not (r, v) in G.edges():
                H.add_edge(r, v, {weight: hugeWeight, 'flow': vDemand})
                artificialEdges.append((r, v))
                y[v] = H[r][v].get(weight, 0)
                T.add_edge(r, v)
                flowCost += vDemand * H[r][v].get(weight, 0)

            else:  # (r, v) in G.edges()
                if (not capacity in G[r][v] or vDemand <= G[r][v][capacity]):
                    H[r][v]['flow'] = vDemand
                    y[v] = H[r][v].get(weight, 0)
                    T.add_edge(r, v)
                    flowCost += vDemand * H[r][v].get(weight, 0)

                else:  # existing edge does not have enough capacity
                    newLabel = generate_unique_node()
                    H.add_edge(r, newLabel, {
                        weight: hugeWeight,
                        'flow': vDemand
                    })
                    H.add_edge(newLabel, v, {
                        weight: hugeWeight,
                        'flow': vDemand
                    })
                    artificialEdges.append((r, newLabel))
                    artificialEdges.append((newLabel, v))
                    y[v] = 2 * hugeWeight
                    y[newLabel] = hugeWeight
                    T.add_edge(r, newLabel)
                    T.add_edge(newLabel, v)
                    flowCost += 2 * vDemand * hugeWeight

        else:  # vDemand < 0
            if not (v, r) in G.edges():
                H.add_edge(v, r, {weight: hugeWeight, 'flow': -vDemand})
                artificialEdges.append((v, r))
                y[v] = -H[v][r].get(weight, 0)
                T.add_edge(v, r)
                flowCost += -vDemand * H[v][r].get(weight, 0)

            else:
                if (not capacity in G[v][r] or -vDemand <= G[v][r][capacity]):
                    H[v][r]['flow'] = -vDemand
                    y[v] = -H[v][r].get(weight, 0)
                    T.add_edge(v, r)
                    flowCost += -vDemand * H[v][r].get(weight, 0)
                else:  # existing edge does not have enough capacity
                    newLabel = generate_unique_node()
                    H.add_edge(v, newLabel, {
                        weight: hugeWeight,
                        'flow': -vDemand
                    })
                    H.add_edge(newLabel, r, {
                        weight: hugeWeight,
                        'flow': -vDemand
                    })
                    artificialEdges.append((v, newLabel))
                    artificialEdges.append((newLabel, r))
                    y[v] = -2 * hugeWeight
                    y[newLabel] = -hugeWeight
                    T.add_edge(v, newLabel)
                    T.add_edge(newLabel, r)
                    flowCost += 2 * -vDemand * hugeWeight

    return H, T, y, artificialEdges, flowCost
Ejemplo n.º 15
0
def _initial_tree_solution(G, r, demand = 'demand', capacity = 'capacity',
                           weight = 'weight'):
    """Find a initial tree solution rooted at r.

    The initial tree solution is obtained by considering edges (r, v)
    for all nodes v with non-negative demand and (v, r) for all nodes
    with negative demand. If these edges do not exist, we add them to
    the graph and call them artificial edges.
    """
    H = nx.DiGraph(G)
    T = nx.DiGraph()
    y = {r: 0}
    artificialEdges = []
    flowCost = 0

    n = G.number_of_nodes()
    try:
        maxWeight = max(abs(d[weight]) for u, v, d in G.edges(data = True)
                        if weight in d)
    except ValueError:
        maxWeight = 0
    hugeWeight = 1 + n * maxWeight

    for v, d in G.nodes(data = True)[1:]:
        vDemand = d.get(demand, 0)
        if vDemand >= 0:
            if not (r, v) in G.edges():
                H.add_edge(r, v, {weight: hugeWeight, 'flow': vDemand})
                artificialEdges.append((r, v))
                y[v] = H[r][v].get(weight, 0)
                T.add_edge(r, v)
                flowCost += vDemand * H[r][v].get(weight, 0)

            else: # (r, v) in G.edges()
                if (not capacity in G[r][v]
                    or vDemand <= G[r][v][capacity]):
                    H[r][v]['flow'] = vDemand
                    y[v] = H[r][v].get(weight, 0)
                    T.add_edge(r, v)
                    flowCost += vDemand * H[r][v].get(weight, 0)

                else: # existing edge does not have enough capacity
                    newLabel = generate_unique_node()
                    H.add_edge(r, newLabel, {weight: hugeWeight, 'flow': vDemand})
                    H.add_edge(newLabel, v, {weight: hugeWeight, 'flow': vDemand})
                    artificialEdges.append((r, newLabel))
                    artificialEdges.append((newLabel, v))
                    y[v] = 2 * hugeWeight
                    y[newLabel] = hugeWeight
                    T.add_edge(r, newLabel)
                    T.add_edge(newLabel, v)
                    flowCost += 2 * vDemand * hugeWeight

        else: # vDemand < 0
            if not (v, r) in G.edges():
                H.add_edge(v, r, {weight: hugeWeight, 'flow': -vDemand})
                artificialEdges.append((v, r))
                y[v] = -H[v][r].get(weight, 0)
                T.add_edge(v, r)
                flowCost += -vDemand * H[v][r].get(weight, 0)

            else:
                if (not capacity in G[v][r]
                    or -vDemand <= G[v][r][capacity]):
                    H[v][r]['flow'] = -vDemand
                    y[v] = -H[v][r].get(weight, 0)
                    T.add_edge(v, r)
                    flowCost += -vDemand * H[v][r].get(weight, 0)
                else: # existing edge does not have enough capacity
                    newLabel = generate_unique_node()
                    H.add_edge(v, newLabel,
                               {weight: hugeWeight, 'flow': -vDemand})
                    H.add_edge(newLabel, r,
                               {weight: hugeWeight, 'flow': -vDemand})
                    artificialEdges.append((v, newLabel))
                    artificialEdges.append((newLabel, r))
                    y[v] = -2 * hugeWeight
                    y[newLabel] = -hugeWeight
                    T.add_edge(v, newLabel)
                    T.add_edge(newLabel, r)
                    flowCost += 2 * -vDemand * hugeWeight
            
    return H, T, y, artificialEdges, flowCost
Ejemplo n.º 16
0
def min_cost_flow(G, demand='demand', capacity='capacity', weight='weight'):
    """
    Uses successive shortest path algorithm:
    http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=minimumCostFlow2
    """
    if not G.is_directed():
        raise nx.NetworkXError("Undirected graph not supported (yet).")
    if not nx.is_connected(G.to_undirected()):
        raise nx.NetworkXError("Not connected graph not supported (yet).")
    demand_sum = sum(d[demand] for v, d in G.nodes_iter(data=True)
                     if demand in d)
    if demand_sum != 0:
        raise nx.NetworkXUnfeasible("Sum of the demands should be 0.")

    H = nx.MultiDiGraph(G)

    for u, v, key in H.edges_iter(keys=True):
        if not isinstance(key, (int, long)):
            raise nx.NetworkXError("Edge keys must be integers.")
    
    # Add source and sink nodes.
    source = generate_unique_node()
    sink = generate_unique_node()
    for node, data in H.nodes_iter(data=True):
        node_demand = data.get(demand)
        if node_demand:
            if node_demand > 0:
                H.add_edge(node, sink, capacity=node_demand, weight=0)
            else:
                H.add_edge(source, node, capacity=-node_demand, weight=0)

    # TODO: Transform R weights to be nonnegative at each iteration,
    # using bellman-ford shortest-path length source -> n as the potential
    # for each node.  Then can use dijskstra_path instead of bellman_ford.

    flow_cost = 0
    search = source in H.nodes()  # No source => no demand => no flow.
    while search:
        R = _residual_graph(H, capacity=capacity, weight=weight)
        try:
            #path = nx.dijkstra_path(R, source, sink, weight=weight)
            path = _bellman_ford_path(R, source, sink, weight=weight)
        except nx.NetworkXNoPath:
            # Check that demands have been satisfied.
            for node, edge_dict in H[source].items():
                # Only one edge to each supply node from source.
                data = edge_dict[0]
                if data['capacity'] != data.get('flow', 0):
                    raise nx.NetworkXUnfeasible(
                        "No flow satisfying all demands.")
            break

        new_flow, path_edges = _max_path_flow(
            R, path, capacity=capacity, weight=weight)
        new_cost =  _augment_flow(H, path_edges, new_flow, R)
        flow_cost += new_cost

    if search:
        H.remove_node(source)
        H.remove_node(sink)
    flow_dict = _create_flow_dict(H)
    return flow_cost, flow_dict
Ejemplo n.º 17
0
def all_pairs_lowest_common_ancestor(G, pairs=None):
    """Compute the lowest common ancestor for pairs of nodes.

    Parameters
    ----------
    G : NetworkX directed graph

    pairs : iterable of pairs of nodes, optional (default: all pairs)
        The pairs of nodes of interest.
        If None, will find the LCA of all pairs of nodes.

    Returns
    -------
    An iterator over ((node1, node2), lca) where (node1, node2) are
    the pairs specified and lca is a lowest common ancestor of the pair.
    Note that for the default of all pairs in G, we consider
    unordered pairs, e.g. you will not get both (b, a) and (a, b).

    Notes
    -----
    Only defined on non-null directed acyclic graphs.

    Uses the $O(n^3)$ ancestor-list algorithm from:
    M. A. Bender, M. Farach-Colton, G. Pemmasani, S. Skiena, P. Sumazin.
    "Lowest common ancestors in trees and directed acyclic graphs."
    Journal of Algorithms, 57(2): 75-94, 2005.

    See Also
    --------
    tree_all_pairs_lowest_common_ancestor
    lowest_common_ancestor
    """
    if not nx.is_directed_acyclic_graph(G):
        raise nx.NetworkXError("LCA only defined on directed acyclic graphs.")
    elif len(G) == 0:
        raise nx.NetworkXPointlessConcept("LCA meaningless on null graphs.")
    elif None in G:
        raise nx.NetworkXError("None is not a valid node.")

    # The copy isn't ideal, neither is the switch-on-type, but without it users
    # passing an iterable will encounter confusing errors, and itertools.tee
    # does not appear to handle builtin types efficiently (IE, it materializes
    # another buffer rather than just creating listoperators at the same
    # offset). The Python documentation notes use of tee is unadvised when one
    # is consumed before the other.
    #
    # This will always produce correct results and avoid unnecessary
    # copies in many common cases.
    #
    if (not isinstance(pairs, (Mapping, Set)) and pairs is not None):
        pairs = set(pairs)

    # Convert G into a dag with a single root by adding a node with edges to
    # all sources iff necessary.
    sources = [n for n, deg in G.in_degree if deg == 0]
    if len(sources) == 1:
        root = sources[0]
        super_root = None
    else:
        G = G.copy()
        super_root = root = generate_unique_node()
        for source in sources:
            G.add_edge(root, source)

    # Start by computing a spanning tree, and the DAG of all edges not in it.
    # We will then use the tree lca algorithm on the spanning tree, and use
    # the DAG to figure out the set of tree queries necessary.
    spanning_tree = nx.dfs_tree(G, root)
    dag = nx.DiGraph((u, v) for u, v in G.edges
                     if u not in spanning_tree or v not in spanning_tree[u])

    # Ensure that both the dag and the spanning tree contains all nodes in G,
    # even nodes that are disconnected in the dag.
    spanning_tree.add_nodes_from(G)
    dag.add_nodes_from(G)

    counter = count()

    # Necessary to handle graphs consisting of a single node and no edges.
    root_distance = {root: next(counter)}

    for edge in nx.bfs_edges(spanning_tree, root):
        for node in edge:
            if node not in root_distance:
                root_distance[node] = next(counter)

    # Index the position of all nodes in the Euler tour so we can efficiently
    # sort lists and merge in tour order.
    euler_tour_pos = {}
    for node in nx.depth_first_search.dfs_preorder_nodes(G, root):
        if node not in euler_tour_pos:
            euler_tour_pos[node] = next(counter)

    # Generate the set of all nodes of interest in the pairs.
    pairset = set()
    if pairs is not None:
        pairset = set(chain.from_iterable(pairs))

    for n in pairset:
        if n not in G:
            msg = "The node %s is not in the digraph." % str(n)
            raise nx.NodeNotFound(msg)

    # Generate the transitive closure over the dag (not G) of all nodes, and
    # sort each node's closure set by order of first appearance in the Euler
    # tour.
    ancestors = {}
    for v in dag:
        if pairs is None or v in pairset:
            my_ancestors = nx.dag.ancestors(dag, v)
            my_ancestors.add(v)
            ancestors[v] = sorted(my_ancestors, key=euler_tour_pos.get)

    def _compute_dag_lca_from_tree_values(tree_lca, dry_run):
        """Iterate through the in-order merge for each pair of interest.

        We do this to answer the user's query, but it is also used to
        avoid generating unnecessary tree entries when the user only
        needs some pairs.
        """
        for (node1, node2) in pairs if pairs is not None else tree_lca:
            best_root_distance = None
            best = None

            indices = [0, 0]
            ancestors_by_index = [ancestors[node1], ancestors[node2]]

            def get_next_in_merged_lists(indices):
                """Returns index of the list containing the next item

                Next order refers to the merged order.
                Index can be 0 or 1 (or None if exhausted).
                """
                index1, index2 = indices
                if (index1 >= len(ancestors[node1]) and
                        index2 >= len(ancestors[node2])):
                    return None
                elif index1 >= len(ancestors[node1]):
                    return 1
                elif index2 >= len(ancestors[node2]):
                    return 0
                elif (euler_tour_pos[ancestors[node1][index1]] <
                      euler_tour_pos[ancestors[node2][index2]]):
                    return 0
                else:
                    return 1

            # Find the LCA by iterating through the in-order merge of the two
            # nodes of interests' ancestor sets. In principle, we need to
            # consider all pairs in the Cartesian product of the ancestor sets,
            # but by the restricted min range query reduction we are guaranteed
            # that one of the pairs of interest is adjacent in the merged list
            # iff one came from each list.
            i = get_next_in_merged_lists(indices)
            cur = ancestors_by_index[i][indices[i]], i
            while i is not None:
                prev = cur
                indices[i] += 1
                i = get_next_in_merged_lists(indices)
                if i is not None:
                    cur = ancestors_by_index[i][indices[i]], i

                    # Two adjacent entries must not be from the same list
                    # in order for their tree LCA to be considered.
                    if cur[1] != prev[1]:
                        tree_node1, tree_node2 = prev[0], cur[0]
                        if (tree_node1, tree_node2) in tree_lca:
                            ans = tree_lca[tree_node1, tree_node2]
                        else:
                            ans = tree_lca[tree_node2, tree_node1]
                        if not dry_run and (best is None or
                                            root_distance[ans] > best_root_distance):
                            best_root_distance = root_distance[ans]
                            best = ans

            # If the LCA is super_root, there is no LCA in the user's graph.
            if not dry_run and (super_root is None or best != super_root):
                yield (node1, node2), best

    # Generate the spanning tree lca for all pairs. This doesn't make sense to
    # do incrementally since we are using a linear time offline algorithm for
    # tree lca.
    if pairs is None:
        # We want all pairs so we'll need the entire tree.
        tree_lca = dict(tree_all_pairs_lowest_common_ancestor(spanning_tree,
                                                              root))
    else:
        # We only need the merged adjacent pairs by seeing which queries the
        # algorithm needs then generating them in a single pass.
        tree_lca = defaultdict(int)
        for _ in _compute_dag_lca_from_tree_values(tree_lca, True):
            pass

        # Replace the bogus default tree values with the real ones.
        for (pair, lca) in tree_all_pairs_lowest_common_ancestor(spanning_tree,
                                                                 root,
                                                                 tree_lca):
            tree_lca[pair] = lca

    # All precomputations complete. Now we just need to give the user the pairs
    # they asked for, or all pairs if they want them all.
    return _compute_dag_lca_from_tree_values(tree_lca, False)
Ejemplo n.º 18
0
def min_cost_flow(G, demand='demand', capacity='capacity', weight='weight'):
    """
    Uses successive shortest path algorithm:
    http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=minimumCostFlow2
    """
    if not G.is_directed():
        raise nx.NetworkXError("Undirected graph not supported (yet).")
    if not nx.is_connected(G.to_undirected()):
        raise nx.NetworkXError("Not connected graph not supported (yet).")
    demand_sum = sum(d[demand] for v, d in G.nodes_iter(data=True)
                     if demand in d)
    if demand_sum != 0:
        raise nx.NetworkXUnfeasible("Sum of the demands should be 0.")

    H = nx.MultiDiGraph(G)

    for u, v, key in H.edges_iter(keys=True):
        if not isinstance(key, (int, long)):
            raise nx.NetworkXError("Edge keys must be integers.")

    # Add source and sink nodes.
    source = generate_unique_node()
    sink = generate_unique_node()
    for node, data in H.nodes_iter(data=True):
        node_demand = data.get(demand)
        if node_demand:
            if node_demand > 0:
                H.add_edge(node, sink, capacity=node_demand, weight=0)
            else:
                H.add_edge(source, node, capacity=-node_demand, weight=0)

    # TODO: Transform R weights to be nonnegative at each iteration,
    # using bellman-ford shortest-path length source -> n as the potential
    # for each node.  Then can use dijskstra_path instead of bellman_ford.

    flow_cost = 0
    search = source in H.nodes()  # No source => no demand => no flow.
    while search:
        R = _residual_graph(H, capacity=capacity, weight=weight)
        try:
            #path = nx.dijkstra_path(R, source, sink, weight=weight)
            path = _bellman_ford_path(R, source, sink, weight=weight)
        except nx.NetworkXNoPath:
            # Check that demands have been satisfied.
            for node, edge_dict in H[source].items():
                # Only one edge to each supply node from source.
                data = edge_dict[0]
                if data['capacity'] != data.get('flow', 0):
                    raise nx.NetworkXUnfeasible(
                        "No flow satisfying all demands.")
            break

        new_flow, path_edges = _max_path_flow(R,
                                              path,
                                              capacity=capacity,
                                              weight=weight)
        new_cost = _augment_flow(H, path_edges, new_flow, R)
        flow_cost += new_cost

    if search:
        H.remove_node(source)
        H.remove_node(sink)
    flow_dict = _create_flow_dict(H)
    return flow_cost, flow_dict
Ejemplo n.º 19
0
def negative_edge_cycle(G, weight='weight'):
    """
    If there is a negative edge cycle anywhere in G, returns True.
    Also returns the total weight of the cycle and the nodes in the cycle.
    Parameters
    ----------
    G : NetworkX graph
    weight: string, optional (default='weight')
       Edge data key corresponding to the edge weight
    Returns
    -------
    length : numeric
        Length of a negative edge cycle if one exists, otherwise None.
    edges: list
        Edges in a negative edge cycle (in order) if one exists,
        otherwise None.
    negative_cycle : bool
        True if a negative edge cycle exists, otherwise False.
    Examples
    --------
    >>> import networkx as nx
    >>> import bellmanford as bf
    >>> G = nx.cycle_graph(5, create_using = nx.DiGraph())
    >>> print(bf.negative_edge_cycle(G))
    (None, [], False)
    >>> G[1][2]['weight'] = -7
    >>> print(bf.negative_edge_cycle(G))
    (-3, [(1, 2), (2, 3), (3, 4), (4, 0), (0, 1)], True)
    Notes
    -----
    Edge weight attributes must be numerical.
    Distances are calculated as sums of weighted edges traversed.
    This algorithm uses bellman_ford() but finds negative cycles
    on any component by first adding a new node connected to
    every node, and starting bellman_ford on that node.  It then
    removes that extra node.
    """
    newnode = generate_unique_node()
    G.add_edges_from([(newnode, n) for n in G])

    try:
        pred, _, negative_cycle_end = _bellman_ford_relaxation(
            G, newnode, G.number_of_nodes(), weight)

        if negative_cycle_end:
            edges = []
            negative_cycle = True
            v = negative_cycle_end
            while True:
                u = pred[G.number_of_nodes()][v]
                edges.insert(0, (u, v))
                if edges.count((u, v)) > 1:
                    end_index = edges[1:].index((u, v))
                    edges = edges[:end_index + 1]
                    break
                v = u
            length = sum(G[u][v].get(weight, 1) for (u, v) in edges)
        else:
            edges = None
            negative_cycle = False
            length = None

        return length, edges, negative_cycle
    finally:
        G.remove_node(newnode)
Ejemplo n.º 20
0
def basic_blocks(code: CodeFragment) -> List[BasicBlock]:
    """
    Extract the basic blocks from a code fragment.

    The resulting basic blocks contain views on the source fragment, and come in the same order in which they appear in
    the original fragment. Non-code statements are discarded if they reside between BB boundaries and are not
    interleaved with code statements.

    For a correct behaviour, launch this function on a well-delimited code fragment (started by at least one label,
    terminated by a jump).

    Be aware that fancy ways of jumping around based on runtime-loaded addresses are not currently supported by this
    package.

    :param code: the code fragment whose basic blocks will be extracted
    :return: the list of basic blocks contained in the original fragment
    :raise InvalidCodeError: when the provided code fragment has no label or no outgoing jump
    """

    # Identify the block boundaries, that is: those lines marked by a label or containing a control transfer instruction
    block_boundaries = filter(
        lambda asl: isinstance(asl.statement, Instruction) and
        (asl.statement.opcode in jump_ops or len(asl.statement.labels) > 0),
        # Use a line-oriented iterator, so that we can extract the line numbers
        to_line_iterator(iter(code), code.begin))

    # Given the boundaries, calculate the appropriate cutoff points.
    # A dictionary is used as a way of implementing an "ordered set" for easy duplicate removal.
    # TODO find a more elegant way to remove duplicates online
    cutoff_points = dict()
    for boundary in block_boundaries:
        if len(boundary.statement.labels
               ) > 0 and boundary.statement.opcode in jump_ops:
            # For a labeled line that also contains a jump, record two cut-points so that a single-line block can be
            # created.
            cutoff_points[boundary.number] = None
            cutoff_points[boundary.number + 1] = None
        elif len(boundary.statement.labels) > 0:
            # Labeled lines mark cut-points themselves
            cutoff_points[boundary.number] = None
        else:
            # A cut has to be made below any line containing a jump
            cutoff_points[boundary.number + 1] = None

    if len(cutoff_points) < 2:
        raise InvalidCodeError(
            "Code fragment does not start with a label or end with a jump/return."
        )

    # Convert the "ordered set" back into a list
    cutoff_points = list(iter(cutoff_points))

    # Start slicing code into basic blocks
    bb = []
    head = cutoff_points[0]
    for tail in cutoff_points[1:]:
        if any(isinstance(line, Instruction) for line in code[head:tail]):
            # Since these blocks are probably gonna end up inside a graph, use the NetworkX's function for unique IDs
            bb.append(
                BasicBlock(FragmentView(code, head, tail, head),
                           generate_unique_node()))
        head = tail

    return bb
Ejemplo n.º 21
0
def exec_graph(
    cfg: LocalGraph,
    entry_point: Union[str, Hashable],
    ignore_calls: FrozenSet[str] = frozenset()
) -> DiGraph:
    """
    Given a local CFG and an entry-point, return the graph of the node visits performed by the execution flow.

    The procedure consists in a recursive, depth-first visit of sub-graphs, starting from the initial node and repeating
    itself for every `CALL` arc encountered. Given their nasty nature, recursive calls are not expanded; instead, they
    are represented by special nodes with IDs of the form `call{<call destination>, <unique ID>}`.

    The user can specify additional calls that mustn't be expanded.

    Different calls to the same procedure result in differently-labeled sub-graphs being attached, so the resulting
    graph is more a substantiation of the execution paths than a sub-graph of the original CFG. As a consequence, don't
    expect a one-to-one correspondence between the CFG's nodes and the one in the execution graph.

    Terminal nodes reachability is guaranteed only if the graph is well formed and any external call reached by the
    execution flow has been internalized, if not explicitly set as ignored.

    :param cfg: a CFG description of some code
    :param entry_point: an entry-point specification for the CFG, either as a node ID or as a symbolic label
    :param ignore_calls: a set of calls that won't be expanded into sub-graphs
    :return: a directed graph representing the execution starting from the specified entry-point
    """

    # Get the entry-point ID
    source = entry_point if entry_point in cfg.entry_point_ids else cfg.get_symbol_table(
    )[entry_point]
    source_labels = cfg.graph.nodes[source]['labels']

    # If one of the entry-point's labels is in the ignore set, return a node summarizing the call
    if not ignore_calls.isdisjoint(source_labels):
        res = DiGraph()
        # The node will have a synthetic ID 'call{<call destination>, <unique ID>}', and will carry the original labels.
        res.add_node('call{' + str(source) + ', ' + generate_unique_node() +
                     '}',
                     labels=source_labels,
                     external=True)
        return res

    # Traverse the subtree rooted at the entry-point and collect the visited nodes
    visited_nodes = frozenset(dfs_preorder_nodes(cfg.graph, source))
    # Produce a view of the visited component
    visited_component: Graph = subgraph_view(cfg.graph,
                                             lambda n: n in visited_nodes)

    # Initialize the returned graph with the contents of the visited component
    res = DiGraph()
    res.update(visited_component)

    # Iterate over the CALL edges inside the visited component
    for edge in filter(
            lambda e: visited_component.edges[e]['kind'] == Transition.CALL,
            visited_component.edges):
        # Recursively compute the component of the called procedures
        nested_component = exec_graph(cfg,
                                      visited_component.edges[edge]['callee'],
                                      ignore_calls.union(source_labels))
        # Add the nested component to the result, avoiding ID clashes
        relabel_nodes(nested_component,
                      solve_graph_collision(res, nested_component), False)
        res.update(nested_component)

        # Take the root of the sub-component and its terminal nodes
        head = next(
            filter(lambda n: nested_component.in_degree(n) == 0,
                   nested_component.nodes))
        tail = filter(lambda n: nested_component.out_degree(n) == 0,
                      nested_component.nodes)

        # Substitute the original edge with call and return edges toward/from the sub-component
        res.remove_edge(*edge)
        res.add_edge(edge[0], head, kind=Transition.CALL)
        res.add_edges_from(zip(tail, repeat(edge[1])), kind=Transition.RETURN)

    return res
def all_pairs_lowest_common_ancestor(G, pairs=None):
    """Compute the lowest common ancestor for pairs of nodes.

    Parameters
    ----------
    G : NetworkX directed graph

    pairs : iterable of pairs of nodes, optional (default: all pairs)
        The pairs of nodes of interest.
        If None, will find the LCA of all pairs of nodes.

    Returns
    -------
    An iterator over ((node1, node2), lca) where (node1, node2) are
    the pairs specified and lca is a lowest common ancestor of the pair.
    Note that for the default of all pairs in G, we consider
    unordered pairs, e.g. you will not get both (b, a) and (a, b).

    Notes
    -----
    Only defined on non-null directed acyclic graphs.

    Uses the $O(n^3)$ ancestor-list algorithm from:
    M. A. Bender, M. Farach-Colton, G. Pemmasani, S. Skiena, P. Sumazin.
    "Lowest common ancestors in trees and directed acyclic graphs."
    Journal of Algorithms, 57(2): 75-94, 2005.

    See Also
    --------
    tree_all_pairs_lowest_common_ancestor
    lowest_common_ancestor
    """
    if not nx.is_directed_acyclic_graph(G):
        raise nx.NetworkXError("LCA only defined on directed acyclic graphs.")
    elif len(G) == 0:
        raise nx.NetworkXPointlessConcept("LCA meaningless on null graphs.")
    elif None in G:
        raise nx.NetworkXError("None is not a valid node.")

    # The copy isn't ideal, neither is the switch-on-type, but without it users
    # passing an iterable will encounter confusing errors, and itertools.tee
    # does not appear to handle builtin types efficiently (IE, it materializes
    # another buffer rather than just creating listoperators at the same
    # offset). The Python documentation notes use of tee is unadvised when one
    # is consumed before the other.
    #
    # This will always produce correct results and avoid unnecessary
    # copies in many common cases.
    #
    if (not isinstance(pairs, (Mapping, Set)) and pairs is not None):
        pairs = set(pairs)

    # Convert G into a dag with a single root by adding a node with edges to
    # all sources iff necessary.
    sources = [n for n, deg in G.in_degree if deg == 0]
    if len(sources) == 1:
        root = sources[0]
        super_root = None
    else:
        G = G.copy()
        super_root = root = generate_unique_node()
        for source in sources:
            G.add_edge(root, source)

    # Start by computing a spanning tree, and the DAG of all edges not in it.
    # We will then use the tree lca algorithm on the spanning tree, and use
    # the DAG to figure out the set of tree queries necessary.
    spanning_tree = nx.dfs_tree(G, root)
    dag = nx.DiGraph((u, v) for u, v in G.edges
                     if u not in spanning_tree or v not in spanning_tree[u])

    # Ensure that both the dag and the spanning tree contains all nodes in G,
    # even nodes that are disconnected in the dag.
    spanning_tree.add_nodes_from(G)
    dag.add_nodes_from(G)

    counter = count()

    # Necessary to handle graphs consisting of a single node and no edges.
    root_distance = {root: next(counter)}

    for edge in nx.bfs_edges(spanning_tree, root):
        for node in edge:
            if node not in root_distance:
                root_distance[node] = next(counter)

    # Index the position of all nodes in the Euler tour so we can efficiently
    # sort lists and merge in tour order.
    euler_tour_pos = {}
    for node in nx.depth_first_search.dfs_preorder_nodes(G, root):
        if node not in euler_tour_pos:
            euler_tour_pos[node] = next(counter)

    # Generate the set of all nodes of interest in the pairs.
    pairset = set()
    if pairs is not None:
        pairset = set(chain.from_iterable(pairs))

    for n in pairset:
        if n not in G:
            msg = f"The node {str(n)} is not in the digraph."
            raise nx.NodeNotFound(msg)

    # Generate the transitive closure over the dag (not G) of all nodes, and
    # sort each node's closure set by order of first appearance in the Euler
    # tour.
    ancestors = {}
    for v in dag:
        if pairs is None or v in pairset:
            my_ancestors = nx.dag.ancestors(dag, v)
            my_ancestors.add(v)
            ancestors[v] = sorted(my_ancestors, key=euler_tour_pos.get)

    def _compute_dag_lca_from_tree_values(tree_lca, dry_run):
        """Iterate through the in-order merge for each pair of interest.

        We do this to answer the user's query, but it is also used to
        avoid generating unnecessary tree entries when the user only
        needs some pairs.
        """
        for (node1, node2) in pairs if pairs is not None else tree_lca:
            best_root_distance = None
            best = None

            indices = [0, 0]
            ancestors_by_index = [ancestors[node1], ancestors[node2]]

            def get_next_in_merged_lists(indices):
                """Returns index of the list containing the next item

                Next order refers to the merged order.
                Index can be 0 or 1 (or None if exhausted).
                """
                index1, index2 = indices
                if (index1 >= len(ancestors[node1])
                        and index2 >= len(ancestors[node2])):
                    return None
                elif index1 >= len(ancestors[node1]):
                    return 1
                elif index2 >= len(ancestors[node2]):
                    return 0
                elif (euler_tour_pos[ancestors[node1][index1]] <
                      euler_tour_pos[ancestors[node2][index2]]):
                    return 0
                else:
                    return 1

            # Find the LCA by iterating through the in-order merge of the two
            # nodes of interests' ancestor sets. In principle, we need to
            # consider all pairs in the Cartesian product of the ancestor sets,
            # but by the restricted min range query reduction we are guaranteed
            # that one of the pairs of interest is adjacent in the merged list
            # iff one came from each list.
            i = get_next_in_merged_lists(indices)
            cur = ancestors_by_index[i][indices[i]], i
            while i is not None:
                prev = cur
                indices[i] += 1
                i = get_next_in_merged_lists(indices)
                if i is not None:
                    cur = ancestors_by_index[i][indices[i]], i

                    # Two adjacent entries must not be from the same list
                    # in order for their tree LCA to be considered.
                    if cur[1] != prev[1]:
                        tree_node1, tree_node2 = prev[0], cur[0]
                        if (tree_node1, tree_node2) in tree_lca:
                            ans = tree_lca[tree_node1, tree_node2]
                        else:
                            ans = tree_lca[tree_node2, tree_node1]
                        if not dry_run and (best is None or root_distance[ans]
                                            > best_root_distance):
                            best_root_distance = root_distance[ans]
                            best = ans

            # If the LCA is super_root, there is no LCA in the user's graph.
            if not dry_run and (super_root is None or best != super_root):
                yield (node1, node2), best

    # Generate the spanning tree lca for all pairs. This doesn't make sense to
    # do incrementally since we are using a linear time offline algorithm for
    # tree lca.
    if pairs is None:
        # We want all pairs so we'll need the entire tree.
        tree_lca = dict(
            tree_all_pairs_lowest_common_ancestor(spanning_tree, root))
    else:
        # We only need the merged adjacent pairs by seeing which queries the
        # algorithm needs then generating them in a single pass.
        tree_lca = defaultdict(int)
        for _ in _compute_dag_lca_from_tree_values(tree_lca, True):
            pass

        # Replace the bogus default tree values with the real ones.
        for (pair, lca) in tree_all_pairs_lowest_common_ancestor(
                spanning_tree, root, tree_lca):
            tree_lca[pair] = lca

    # All precomputations complete. Now we just need to give the user the pairs
    # they asked for, or all pairs if they want them all.
    return _compute_dag_lca_from_tree_values(tree_lca, False)
Ejemplo n.º 23
0
def allpairslca(G, pairs=None):
    """
    Compute the lowest common ancestor for pairs of nodes.
    """
    if not nx.is_directed_acyclic_graph(G):
        raise nx.NetworkXError("LCA only defined on directed acyclic graphs.")
    elif len(G) == 0:
        raise nx.NetworkXPointlessConcept("LCA meaningless on null graphs.")
    elif None in G:
        raise nx.NetworkXError("None is not a valid node.")
    if (not isinstance(pairs, (Mapping, Set)) and pairs is not None):
        pairs = set(pairs)
    sources = [n for n, deg in G.in_degree if deg == 0]
    if len(sources) == 1:
        root = sources[0]
        super_root = None
    else:
        G = G.copy()
        super_root = root = generate_unique_node()
        for source in sources:
            G.add_edge(root, source)
    spanning_tree = nx.dfs_tree(G, root)
    dag = nx.DiGraph((u, v) for u, v in G.edges
                     if u not in spanning_tree or v not in spanning_tree[u])
    spanning_tree.add_nodes_from(G)
    dag.add_nodes_from(G)
    counter = count()
    root_distance = {root: next(counter)}
    for edge in nx.bfs_edges(spanning_tree, root):
        for node in edge:
            if node not in root_distance:
                root_distance[node] = next(counter)
    euler_tour_pos = {}
    for node in nx.depth_first_search.dfs_preorder_nodes(G, root):
        if node not in euler_tour_pos:
            euler_tour_pos[node] = next(counter)
    pairset = set()
    if pairs is not None:
        pairset = set(chain.from_iterable(pairs))
    for n in pairset:
        if n not in G:
            msg = "The node %s is not in the digraph." % str(n)
            raise nx.NodeNotFound(msg)
    ancestors = {}
    for v in dag:
        if pairs is None or v in pairset:
            my_ancestors = nx.dag.ancestors(dag, v)
            my_ancestors.add(v)
            ancestors[v] = sorted(my_ancestors, key=euler_tour_pos.get)
    def computedag(tree_lca, dry_run):
        """
        Iterate through the in-order merge for each pair of interest.
        We do this to answer the user's query, but it is also used to
        avoid generating unnecessary tree entries when the user only
        needs some pairs.
        """
        for (node1, node2) in pairs if pairs is not None else tree_lca:
            best_root_distance = None
            best = None
            indices = [0, 0]
            ancestors_by_index = [ancestors[node1], ancestors[node2]]
            def getnext(indices):
                """
                Returns index of the list containing the next item
                Next order refers to the merged order.
                Index can be 0 or 1 (or None if exhausted).
                """
                index1, index2 = indices
                if (index1 >= len(ancestors[node1]) and
                        index2 >= len(ancestors[node2])):
                    return None
                elif index1 >= len(ancestors[node1]):
                    return 1
                elif index2 >= len(ancestors[node2]):
                    return 0
                elif (euler_tour_pos[ancestors[node1][index1]] <
                      euler_tour_pos[ancestors[node2][index2]]):
                    return 0
                else:
                    return 1
            i = get_next_in_merged_lists(indices)
            cur = ancestors_by_index[i][indices[i]], i
            while i is not None:
                prev = cur
                indices[i] += 1
                i = get_next_in_merged_lists(indices)
                if i is not None:
                    cur = ancestors_by_index[i][indices[i]], i
                    # Two adjacent entries must not be from the same list
                    # in order for their tree LCA to be considered.
                    if cur[1] != prev[1]:
                        tree_node1, tree_node2 = prev[0], cur[0]
                        if (tree_node1, tree_node2) in tree_lca:
                            ans = tree_lca[tree_node1, tree_node2]
                        else:
                            ans = tree_lca[tree_node2, tree_node1]
                        if not dry_run and (best is None or root_distance[ans] > best_root_distance):
                            best_root_distance = root_distance[ans]
                            best = ans
            # If the LCA is super_root, there is no LCA in the user's graph.
            if not dry_run and (super_root is None or best != super_root):
                yield (node1, node2), best
    if pairs is None:
        # We want all pairs so we'll need the entire tree.
        tree_lca = dict(tarjan(spanning_tree, root))
    else:
        # We only need the merged adjacent pairs by seeing which queries the
        # algorithm needs then generating them in a single pass.
        tree_lca = defaultdict(int)
        for _ in computedag(tree_lca, True):
            pass
        # Replace the bogus default tree values with the real ones.
        for (pair, lca) in tarjan(spanning_tree, root, tree_lca):
            tree_lca[pair] = lca
    return computedag(tree_lca, False)