[OpenLayers-Commits] r10979 - sandbox/tschaub/uncircular/tools
commits-20090109 at openlayers.org
commits-20090109 at openlayers.org
Sat Jan 1 15:28:19 EST 2011
Author: tschaub
Date: 2011-01-01 12:28:19 -0800 (Sat, 01 Jan 2011)
New Revision: 10979
Modified:
sandbox/tschaub/uncircular/tools/toposort.py
Log:
Simpler toposort.
Modified: sandbox/tschaub/uncircular/tools/toposort.py
===================================================================
--- sandbox/tschaub/uncircular/tools/toposort.py 2011-01-01 19:07:43 UTC (rev 10978)
+++ sandbox/tschaub/uncircular/tools/toposort.py 2011-01-01 20:28:19 UTC (rev 10979)
@@ -1,260 +1,35 @@
-#
-# According to <http://www.vrplumber.com/programming/> this file
-# is licensed under a BSD-style license. We only use the section
-# originally by Tim Peters.
-#
-# TODO: The use of this code needs to be okayed by someone.
-#
+"""
+toposort.py
+Sorts dictionary keys based on lists of dependencies.
+"""
-class RecursionError( OverflowError, ValueError ):
- '''Unable to calculate result because of recursive structure'''
-
+class MissingDependency(Exception):
+ """Exception raised when a listed dependency is not in the dictionary."""
-def sort(nodes, routes, noRecursion=1):
- '''Passed a list of node IDs and a list of source,dest ID routes
- attempt to create a list of stages where each sub list
- is one stage in a process.
- '''
- children, parents = _buildChildrenLists(routes)
- # first stage is those nodes
- # having no incoming routes...
- stage = []
- stages = [stage]
- taken = []
- for node in nodes:
- if (not parents.get(node)):
- stage.append (node)
- if nodes and not stage:
- # there is no element which does not depend on
- # some other element!!!
- stage.append( nodes[0])
- taken.extend( stage )
- nodes = filter ( lambda x, l=stage: x not in l, nodes )
- while nodes:
- previousStageChildren = []
- nodelen = len(nodes)
- # second stage are those nodes
- # which are direct children of the first stage
- for node in stage:
- for child in children.get (node, []):
- if child not in previousStageChildren and child not in taken:
- previousStageChildren.append(child)
- elif child in taken and noRecursion:
- raise RecursionError( (child, node) )
- # unless they are children of other direct children...
- # TODO, actually do that...
- stage = previousStageChildren
- removes = []
- for current in stage:
- currentParents = parents.get( current, [] )
- for parent in currentParents:
- if parent in stage and parent != current:
- # might wind up removing current...
- if not current in parents.get(parent, []):
- # is not mutually dependent...
- removes.append( current )
- for remove in removes:
- while remove in stage:
- stage.remove( remove )
- stages.append( stage)
- taken.extend( stage )
- nodes = filter ( lambda x, l=stage: x not in l, nodes )
- if nodelen == len(nodes):
- if noRecursion:
- raise RecursionError( nodes )
- else:
- stages.append( nodes[:] )
- nodes = []
- return stages
-
-def _buildChildrenLists (routes):
- childrenTable = {}
- parentTable = {}
- for sourceID,destinationID in routes:
- currentChildren = childrenTable.get( sourceID, [])
- currentParents = parentTable.get( destinationID, [])
- if not destinationID in currentChildren:
- currentChildren.append ( destinationID)
- if not sourceID in currentParents:
- currentParents.append ( sourceID)
- childrenTable[sourceID] = currentChildren
- parentTable[destinationID] = currentParents
- return childrenTable, parentTable
-
-
-def toposort (nodes, routes, noRecursion=1):
- '''Topological sort from Tim Peters, fairly efficient
- in comparison (it seems).'''
- #first calculate the recursion depth
+class Sorter(object):
+ def __init__(self, dependencies):
+ self.dependencies = dependencies
+ self.visited = set()
+ self.sorted = ()
- dependencies = {}
- inversedependencies = {}
- if not nodes:
- return []
- if not routes:
- return [nodes]
- for node in nodes:
- dependencies[ node ] = (0, node)
- inversedependencies[ node ] = []
+ def sort(self):
+ for key in self.dependencies:
+ self._visit(key)
+ return self.sorted
-
- for depended, depends in routes:
- # is it a null rule
- try:
- newdependencylevel, object = dependencies.get ( depends, (0, depends))
- except TypeError:
- print depends
- raise
- dependencies[ depends ] = (newdependencylevel + 1, depends)
- # "dependency (existence) of depended-on"
- newdependencylevel,object = dependencies.get ( depended, (0, depended) )
- dependencies[ depended ] = (newdependencylevel, depended)
- # Inverse dependency set up
- dependencieslist = inversedependencies.get ( depended, [])
- dependencieslist.append (depends)
- inversedependencies[depended] = dependencieslist
- ### Now we do the actual sorting
- # The first task is to create the sortable
- # list of dependency-levels
- sortinglist = dependencies.values()
- sortinglist.sort ()
- output = []
- while sortinglist:
- deletelist = []
- generation = []
- output.append( generation)
- while sortinglist and sortinglist[0][0] == 0:
- number, object = sortinglist[0]
- generation.append ( object )
- deletelist.append( object )
- for inverse in inversedependencies.get(object, () ):
- try:
- oldcount, inverse = dependencies [ inverse]
- if oldcount > 0:
- # will be dealt with on later pass
- dependencies [ inverse] = (oldcount-1, inverse)
- else:
- # will be dealt with on this pass,
- # so needs not to be in the sorting list next time
- deletelist.append( inverse )
- # just in case a loop comes through
- inversedependencies[object] = []
- except KeyError:
- # dealing with a recursion-breaking run...
- pass
- del sortinglist [0]
- # if no elements could be deleted, then
- # there is something which depends upon itself
- if not deletelist:
- if noRecursion:
- raise RecursionError( sortinglist )
- else:
- # hack so that something gets deleted...
-## import pdb
-## pdb.set_trace()
- dependencies[sortinglist[0][1]] = (0,sortinglist[0][1])
- # delete the items that were dealt with
- for item in deletelist:
- try:
- del dependencies [ item ]
- except KeyError:
- pass
- # need to recreate the sortinglist
- sortinglist = dependencies.values()
- if not generation:
- output.remove( generation )
- sortinglist.sort ()
- return output
+ def _visit(self, key):
+ if key not in self.visited:
+ self.visited.add(key)
+ if not self.dependencies.has_key(key):
+ raise MissingDependency(key)
+ for depends in self.dependencies[key]:
+ self._visit(depends)
+ self.sorted += (key,)
-
-
-
-
-if __name__ == "__main__":
-
- nodes = ['a', 'b', 'c', 'd', 'e', 'f']
- route = [('a', 'b'), ('b', 'c'), ('b', 'd'), ('e','f')]
-
- for x in toposort( nodes, route):
- for a in x:
- print a
-
- raise SystemExit
-
-
-
- import pprint, traceback
- nodes= [ 0,1,2,3,4,5 ]
- testingValues = [
- [ (0,1),(1,2),(2,3),(3,4),(4,5)],
- [ (0,1),(0,2),(1,2),(3,4),(4,5)],
- [
- (0,1),
- (0,2),
- (0,2),
- (2,4),
- (2,5),
- (3,2),
- (0,3)],
- [
- (0,1), # 3-element cycle test, no orphan nodes
- (1,2),
- (2,0),
- (2,4),
- (2,5),
- (3,2),
- (0,3)],
- [
- (0,1),
- (1,1),
- (1,1),
- (1,4),
- (1,5),
- (1,2),
- (3,1),
- (2,1),
- (2,0)],
- [
- (0,1),
- (1,0),
- (0,2),
- (0,3),
- ],
- [
- (0,1),
- (1,0),
- (0,2),
- (3,1),
- ],
- ]
- print 'sort, no recursion allowed'
- for index in range(len(testingValues)):
-## print ' %s -- %s'%( index, testingValues[index])
- try:
- print ' ', sort( nodes, testingValues[index] )
- except:
- print 'exception raised'
- print 'toposort, no recursion allowed'
- for index in range(len(testingValues)):
-## print ' %s -- %s'%( index, testingValues[index])
- try:
- print ' ', toposort( nodes, testingValues[index] )
- except:
- print 'exception raised'
- print 'sort, recursion allowed'
- for index in range(len(testingValues)):
-## print ' %s -- %s'%( index, testingValues[index])
- try:
- print ' ', sort( nodes, testingValues[index],0 )
- except:
- print 'exception raised'
- print 'toposort, recursion allowed'
- for index in range(len(testingValues)):
-## print ' %s -- %s'%( index, testingValues[index])
- try:
- print ' ', toposort( nodes, testingValues[index],0 )
- except:
- print 'exception raised'
-
-
-
+def toposort(dependencies):
+ """Returns a tuple of the dependencies dictionary keys sorted by entries
+ in the dependency lists. Given circular dependencies, sort will impose
+ an order. Raises MissingDependency if a key is not found.
+ """
+ s = Sorter(dependencies)
+ return s.sort()
More information about the Commits
mailing list