1 from collections
import defaultdict, namedtuple
4 """Implementation of directed graph"""
18 for src, dst
in self.
_edges:
19 out.append(
"%s -> %s" % (src, dst))
36 """Delete the @node of the graph; Also delete every edge to/from this
40 self._nodes.remove(node)
51 self._edges.append((src, dst))
56 """Add an edge from @src to @dst if it doesn't already exist"""
62 self._edges.remove((src, dst))
100 def find_path(self, src, dst, cycles_count=0, done=None):
103 if dst
in done
and done[dst] > cycles_count:
110 done_n[dst] = done_n.get(dst, 0) + 1
111 for path
in self.
find_path(src, node, cycles_count, done_n):
112 if path
and path[0] == src:
113 out.append(path + [dst])
135 for node
in self.
nodes():
136 out +=
'%s [label="%s"];\n' % (
137 hash(node) & 0xFFFFFFFFFFFFFFFF, self.
node2str(node))
139 for src, dst
in self.
edges():
140 out +=
'%s -> %s [label="%s"]\n' % (hash(src) & 0xFFFFFFFFFFFFFFFF,
141 hash(dst) & 0xFFFFFFFFFFFFFFFF,
148 """Generic algorithm to compute all nodes reachable from/to node
155 if node
in reachable:
159 for next_node
in next_cb(node):
163 """Compute all nodes reachable from node @head. Each son is an
164 immediate successor of an arbitrary, already yielded son of @head"""
168 """Compute all parents of node @leaf. Each parent is an immediate
169 predecessor of an arbitrary, already yielded parent of @leaf"""
174 """Generic algorithm to compute either the dominators or postdominators
176 @head: the head/leaf of the graph
177 @reachable_cb: sons/parents of the head/leaf
178 @prev_cb: return predecessors/succesors of a node
179 @next_cb: return succesors/predecessors of a node
182 nodes = set(reachable_cb(head))
185 dominators[node] = set(nodes)
187 dominators[head] = set([head])
200 for pred
in prev_cb(node):
201 if not pred
in nodes:
204 new_dom = set(dominators[pred])
205 new_dom.intersection_update(dominators[pred])
208 assert(new_dom
is not None)
210 new_dom.update(set([node]))
213 if new_dom == dominators[node]:
216 dominators[node] = new_dom
217 for succ
in next_cb(node):
222 """Compute the dominators of the graph"""
229 """Compute the postdominators of the graph"""
237 """Generic algorithm to return an iterator of the ordered list of
238 @node's dominators/post_dominator.
240 The function doesn't return the self reference in dominators.
241 @node: The start node
242 @gen_dominators: The dictionnary containing at least node's
243 dominators/post_dominators
244 @succ_cb: return predecessors/succesors of a node
249 if node
not in gen_dominators:
252 node_gen_dominators = set(gen_dominators[node])
256 node_gen_dominators.remove(node)
259 while node_gen_dominators:
267 if node
in node_gen_dominators:
275 for pred
in succ_cb(node):
279 assert(new_node
is not None)
281 node_gen_dominators.remove(new_node)
282 todo = set([new_node])
285 """Return an iterator of the ordered list of @node's dominators
286 The function doesn't return the self reference in dominators.
287 @node: The start node
288 @dominators: The dictionnary containing at least node's dominators
295 """Return an iterator of the ordered list of @node's postdominators
296 The function doesn't return the self reference in postdominators.
297 @node: The start node
298 @postdominators: The dictionnary containing at least node's
307 """Compute the immediate dominators of the graph"""
311 for node
in dominators:
313 if predecessor
in dominators[node]
and node != predecessor:
314 idoms[node] = predecessor
320 Compute the dominance frontier of the graph
322 Source: Cooper, Keith D., Timothy J. Harvey, and Ken Kennedy.
323 "A simple, fast dominance algorithm."
324 Software Practice & Experience 4 (2001), p. 9
333 if runner
not in idoms:
335 while runner != idoms[node]:
336 if runner
not in frontier:
337 frontier[runner] = set()
339 frontier[runner].
add(node)
340 runner = idoms[runner]
345 Generic algorithm to compute breadth or depth first search
347 @head: the head of the graph
348 @flag: denotes if @todo is used as queue or stack
349 @succ_cb: returns a node's predecessors/successors
356 node = todo.pop(flag)
361 for succ
in succ_cb(node):
367 """Performs a breadth first search on the graph from @head"""
371 """Performs a depth first search on the graph from @head"""
375 """Performs a breadth first search on the reversed graph from @head"""
379 """Performs a depth first search on the reversed graph from @head"""
384 Computes all natural loops in the graph.
386 Source: Aho, Alfred V., Lam, Monica S., Sethi, R. and Jeffrey Ullman.
387 "Compilers: Principles, Techniques, & Tools, Second Edition"
388 Pearson/Addison Wesley (2007), Chapter 9.6.6
389 :param head: head of the graph
390 :return: yield a tuple of the form (back edge, loop body)
398 Computes all back edges from a node to a
399 dominator in the graph.
400 :param head: head of graph
401 :return: yield a back edge
409 if successor
in dominators[node]:
410 edge = (node, successor)
415 Computes the body of a natural loop by a depth-first
416 search on the reversed control flow graph.
417 :param head: leaf of the loop
418 :param leaf: header of the loop
419 :return: set containing loop body
431 todo.append(predecessor)
436 Partitions the graph into strongly connected components.
438 Iterative implementation of Gabow's path-based SCC algorithm.
439 Source: Gabow, Harold N.
440 "Path-based depth-first search for strong and biconnected components."
441 Information Processing Letters 74.3 (2000), pp. 109--110
443 The iterative implementation is inspired by Mark Dickinson's
445 http://code.activestate.com/recipes/
446 578507-strongly-connected-components-of-a-directed-graph/
447 :return: yield a strongly connected component
451 counter = len(self.
nodes())
454 index = {v: 0
for v
in self.
nodes()}
457 VISIT, HANDLE_RECURSION, MERGE = 0, 1, 2
458 NodeState = namedtuple(
'NodeState', [
'state',
'node'])
460 for node
in self.
nodes():
465 todo = [NodeState(VISIT, node)]
471 if current.node
in done:
475 if current.state == VISIT:
476 stack.append(current.node)
477 index[current.node] = len(stack)
478 boundaries.append(index[current.node])
480 todo.append(NodeState(MERGE, current.node))
483 todo.append(NodeState(HANDLE_RECURSION, successor))
486 elif current.state == HANDLE_RECURSION:
488 if index[current.node] == 0:
489 todo.append(NodeState(VISIT, current.node))
492 while index[current.node] < boundaries[-1]:
497 if index[current.node] == boundaries[-1]:
502 while index[current.node] <= len(stack):
504 index[popped] = counter
507 done.add(current.node)
def walk_depth_first_forward
def compute_immediate_dominators
def walk_depth_first_backward
def walk_breadth_first_backward
def _compute_generic_dominators
def compute_strongly_connected_components
def compute_natural_loops
def compute_dominance_frontier
def compute_postdominators
def _walk_generic_dominator
def walk_breadth_first_forward
def _compute_natural_loop_body