comparison hggit/toposort.py @ 253:505d7cdca198 0.1.0

package with distutils (patch tweaked slightly by Augie Fackler)
author Kevin Bullock <kbullock@ringworld.org>
date Wed, 30 Sep 2009 14:39:49 -0500
parents toposort.py@312700177979
children
comparison
equal deleted inserted replaced
252:3e0eb85a83a7 253:505d7cdca198
1 ''
2 """
3 Tarjan's algorithm and topological sorting implementation in Python
4 by Paul Harrison
5 Public domain, do with it as you will
6 """
7 class TopoSort(object):
8
9 def __init__(self, commitdict):
10 self._sorted = self.robust_topological_sort(commitdict)
11 self._shas = []
12 for level in self._sorted:
13 for sha in level:
14 self._shas.append(sha)
15
16 def items(self):
17 self._shas.reverse()
18 return self._shas
19
20 def strongly_connected_components(self, graph):
21 """ Find the strongly connected components in a graph using
22 Tarjan's algorithm.
23
24 graph should be a dictionary mapping node names to
25 lists of successor nodes.
26 """
27
28 result = [ ]
29 stack = [ ]
30 low = { }
31
32 def visit(node):
33 if node in low: return
34
35 num = len(low)
36 low[node] = num
37 stack_pos = len(stack)
38 stack.append(node)
39
40 for successor in graph[node].parents:
41 visit(successor)
42 low[node] = min(low[node], low[successor])
43
44 if num == low[node]:
45 component = tuple(stack[stack_pos:])
46 del stack[stack_pos:]
47 result.append(component)
48 for item in component:
49 low[item] = len(graph)
50
51 for node in graph:
52 visit(node)
53
54 return result
55
56 def strongly_connected_components_non(self, G):
57 """Returns a list of strongly connected components in G.
58
59 Uses Tarjan's algorithm with Nuutila's modifications.
60 Nonrecursive version of algorithm.
61
62 References:
63
64 R. Tarjan (1972). Depth-first search and linear graph algorithms.
65 SIAM Journal of Computing 1(2):146-160.
66
67 E. Nuutila and E. Soisalon-Soinen (1994).
68 On finding the strongly connected components in a directed graph.
69 Information Processing Letters 49(1): 9-14.
70
71 """
72 preorder={}
73 lowlink={}
74 scc_found={}
75 scc_queue = []
76 scc_list=[]
77 i=0 # Preorder counter
78 for source in G:
79 if source not in scc_found:
80 queue=[source]
81 while queue:
82 v=queue[-1]
83 if v not in preorder:
84 i=i+1
85 preorder[v]=i
86 done=1
87 v_nbrs=G[v]
88 for w in v_nbrs.parents:
89 if w not in preorder:
90 queue.append(w)
91 done=0
92 break
93 if done==1:
94 lowlink[v]=preorder[v]
95 for w in v_nbrs.parents:
96 if w not in scc_found:
97 if preorder[w]>preorder[v]:
98 lowlink[v]=min([lowlink[v],lowlink[w]])
99 else:
100 lowlink[v]=min([lowlink[v],preorder[w]])
101 queue.pop()
102 if lowlink[v]==preorder[v]:
103 scc_found[v]=True
104 scc=(v,)
105 while scc_queue and preorder[scc_queue[-1]]>preorder[v]:
106 k=scc_queue.pop()
107 scc_found[k]=True
108 scc.append(k)
109 scc_list.append(scc)
110 else:
111 scc_queue.append(v)
112 scc_list.sort(lambda x, y: cmp(len(y),len(x)))
113 return scc_list
114
115 def topological_sort(self, graph):
116 count = { }
117 for node in graph:
118 count[node] = 0
119 for node in graph:
120 for successor in graph[node]:
121 count[successor] += 1
122
123 ready = [ node for node in graph if count[node] == 0 ]
124
125 result = [ ]
126 while ready:
127 node = ready.pop(-1)
128 result.append(node)
129
130 for successor in graph[node]:
131 count[successor] -= 1
132 if count[successor] == 0:
133 ready.append(successor)
134
135 return result
136
137 def robust_topological_sort(self, graph):
138 """ First identify strongly connected components,
139 then perform a topological sort on these components. """
140
141 components = self.strongly_connected_components_non(graph)
142
143 node_component = { }
144 for component in components:
145 for node in component:
146 node_component[node] = component
147
148 component_graph = { }
149 for component in components:
150 component_graph[component] = [ ]
151
152 for node in graph:
153 node_c = node_component[node]
154 for successor in graph[node].parents:
155 successor_c = node_component[successor]
156 if node_c != successor_c:
157 component_graph[node_c].append(successor_c)
158
159 return self.topological_sort(component_graph)