view toposort.py @ 93:312700177979

modified topological sort to be non-recursive
author Scott Chacon <schacon@gmail.com>
date Sat, 09 May 2009 22:52:30 -0700
parents 71b07e16004f
children
line wrap: on
line source

''
"""
   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)