2019-02-24 20:08:59 +08:00
|
|
|
class CyclicDependencyError(ValueError):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2014-11-16 03:25:43 +08:00
|
|
|
def topological_sort_as_sets(dependency_graph):
|
2017-01-25 07:04:12 +08:00
|
|
|
"""
|
|
|
|
Variation of Kahn's algorithm (1962) that returns sets.
|
2014-11-16 03:25:43 +08:00
|
|
|
|
2017-01-25 07:04:12 +08:00
|
|
|
Take a dependency graph as a dictionary of node => dependencies.
|
2014-11-16 03:25:43 +08:00
|
|
|
|
2017-01-25 07:04:12 +08:00
|
|
|
Yield sets of items in topological order, where the first set contains
|
2014-11-16 03:25:43 +08:00
|
|
|
all nodes without dependencies, and each following set contains all
|
2016-08-30 01:58:39 +08:00
|
|
|
nodes that may depend on the nodes only in the previously yielded sets.
|
2014-11-16 03:25:43 +08:00
|
|
|
"""
|
|
|
|
todo = dependency_graph.copy()
|
|
|
|
while todo:
|
2017-11-30 00:54:34 +08:00
|
|
|
current = {node for node, deps in todo.items() if not deps}
|
2014-11-16 03:25:43 +08:00
|
|
|
|
|
|
|
if not current:
|
2019-02-24 20:08:59 +08:00
|
|
|
raise CyclicDependencyError(
|
|
|
|
"Cyclic dependency in graph: {}".format(
|
2014-11-16 03:25:43 +08:00
|
|
|
", ".join(repr(x) for x in todo.items())
|
2022-02-04 03:24:19 +08:00
|
|
|
)
|
|
|
|
)
|
2014-11-16 03:25:43 +08:00
|
|
|
|
|
|
|
yield current
|
|
|
|
|
|
|
|
# remove current from todo's nodes & dependencies
|
|
|
|
todo = {
|
|
|
|
node: (dependencies - current)
|
|
|
|
for node, dependencies in todo.items()
|
|
|
|
if node not in current
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-05-12 14:52:23 +08:00
|
|
|
def stable_topological_sort(nodes, dependency_graph):
|
2014-11-16 03:25:43 +08:00
|
|
|
result = []
|
|
|
|
for layer in topological_sort_as_sets(dependency_graph):
|
2020-05-12 14:52:23 +08:00
|
|
|
for node in nodes:
|
2014-11-16 03:25:43 +08:00
|
|
|
if node in layer:
|
|
|
|
result.append(node)
|
|
|
|
return result
|