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
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])
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
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.')
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
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)
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)
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}
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
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
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)
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
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
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
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
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
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)
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
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)
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
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)
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)