mirror of
https://github.com/facebook/sapling.git
synced 2024-10-10 16:57:49 +03:00
160 lines
5.0 KiB
Python
160 lines
5.0 KiB
Python
''
|
|
"""
|
|
Tarjan's algorithm and topological sorting implementation in Python
|
|
by Paul Harrison
|
|
Public domain, do with it as you will
|
|
"""
|
|
class TopoSort(object):
|
|
|
|
def __init__(self, commitdict):
|
|
self._sorted = self.robust_topological_sort(commitdict)
|
|
self._shas = []
|
|
for level in self._sorted:
|
|
for sha in level:
|
|
self._shas.append(sha)
|
|
|
|
def items(self):
|
|
self._shas.reverse()
|
|
return self._shas
|
|
|
|
def strongly_connected_components(self, graph):
|
|
""" Find the strongly connected components in a graph using
|
|
Tarjan's algorithm.
|
|
|
|
graph should be a dictionary mapping node names to
|
|
lists of successor nodes.
|
|
"""
|
|
|
|
result = [ ]
|
|
stack = [ ]
|
|
low = { }
|
|
|
|
def visit(node):
|
|
if node in low: return
|
|
|
|
num = len(low)
|
|
low[node] = num
|
|
stack_pos = len(stack)
|
|
stack.append(node)
|
|
|
|
for successor in graph[node].parents:
|
|
visit(successor)
|
|
low[node] = min(low[node], low[successor])
|
|
|
|
if num == low[node]:
|
|
component = tuple(stack[stack_pos:])
|
|
del stack[stack_pos:]
|
|
result.append(component)
|
|
for item in component:
|
|
low[item] = len(graph)
|
|
|
|
for node in graph:
|
|
visit(node)
|
|
|
|
return result
|
|
|
|
def strongly_connected_components_non(self, G):
|
|
"""Returns a list of strongly connected components in G.
|
|
|
|
Uses Tarjan's algorithm with Nuutila's modifications.
|
|
Nonrecursive version of algorithm.
|
|
|
|
References:
|
|
|
|
R. Tarjan (1972). Depth-first search and linear graph algorithms.
|
|
SIAM Journal of Computing 1(2):146-160.
|
|
|
|
E. Nuutila and E. Soisalon-Soinen (1994).
|
|
On finding the strongly connected components in a directed graph.
|
|
Information Processing Letters 49(1): 9-14.
|
|
|
|
"""
|
|
preorder={}
|
|
lowlink={}
|
|
scc_found={}
|
|
scc_queue = []
|
|
scc_list=[]
|
|
i=0 # Preorder counter
|
|
for source in G:
|
|
if source not in scc_found:
|
|
queue=[source]
|
|
while queue:
|
|
v=queue[-1]
|
|
if v not in preorder:
|
|
i=i+1
|
|
preorder[v]=i
|
|
done=1
|
|
v_nbrs=G[v]
|
|
for w in v_nbrs.parents:
|
|
if w not in preorder:
|
|
queue.append(w)
|
|
done=0
|
|
break
|
|
if done==1:
|
|
lowlink[v]=preorder[v]
|
|
for w in v_nbrs.parents:
|
|
if w not in scc_found:
|
|
if preorder[w]>preorder[v]:
|
|
lowlink[v]=min([lowlink[v],lowlink[w]])
|
|
else:
|
|
lowlink[v]=min([lowlink[v],preorder[w]])
|
|
queue.pop()
|
|
if lowlink[v]==preorder[v]:
|
|
scc_found[v]=True
|
|
scc=(v,)
|
|
while scc_queue and preorder[scc_queue[-1]]>preorder[v]:
|
|
k=scc_queue.pop()
|
|
scc_found[k]=True
|
|
scc.append(k)
|
|
scc_list.append(scc)
|
|
else:
|
|
scc_queue.append(v)
|
|
scc_list.sort(lambda x, y: cmp(len(y),len(x)))
|
|
return scc_list
|
|
|
|
def topological_sort(self, graph):
|
|
count = { }
|
|
for node in graph:
|
|
count[node] = 0
|
|
for node in graph:
|
|
for successor in graph[node]:
|
|
count[successor] += 1
|
|
|
|
ready = [ node for node in graph if count[node] == 0 ]
|
|
|
|
result = [ ]
|
|
while ready:
|
|
node = ready.pop(-1)
|
|
result.append(node)
|
|
|
|
for successor in graph[node]:
|
|
count[successor] -= 1
|
|
if count[successor] == 0:
|
|
ready.append(successor)
|
|
|
|
return result
|
|
|
|
def robust_topological_sort(self, graph):
|
|
""" First identify strongly connected components,
|
|
then perform a topological sort on these components. """
|
|
|
|
components = self.strongly_connected_components_non(graph)
|
|
|
|
node_component = { }
|
|
for component in components:
|
|
for node in component:
|
|
node_component[node] = component
|
|
|
|
component_graph = { }
|
|
for component in components:
|
|
component_graph[component] = [ ]
|
|
|
|
for node in graph:
|
|
node_c = node_component[node]
|
|
for successor in graph[node].parents:
|
|
successor_c = node_component[successor]
|
|
if node_c != successor_c:
|
|
component_graph[node_c].append(successor_c)
|
|
|
|
return self.topological_sort(component_graph)
|