Todd Wang | c2cba88 | 2014-10-14 10:59:56 -0700 | [diff] [blame] | 1 | // Package toposort implements topological sort. For details see: |
| 2 | // http://en.wikipedia.org/wiki/Topological_sorting |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 3 | package toposort |
| 4 | |
Todd Wang | c2cba88 | 2014-10-14 10:59:56 -0700 | [diff] [blame] | 5 | // Sorter implements a topological sorter. Add nodes and edges to the sorter to |
| 6 | // describe the graph, and call Sort to retrieve topologically-sorted nodes. |
| 7 | // The zero Sorter describes an empty graph. |
| 8 | type Sorter struct { |
| 9 | values map[interface{}]int // maps from user-provided value to index in nodes |
| 10 | nodes []*node // the graph to sort |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 11 | } |
| 12 | |
Todd Wang | c2cba88 | 2014-10-14 10:59:56 -0700 | [diff] [blame] | 13 | // node represents a node in the graph. |
| 14 | type node struct { |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 15 | value interface{} |
Todd Wang | c2cba88 | 2014-10-14 10:59:56 -0700 | [diff] [blame] | 16 | children []*node |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 17 | } |
| 18 | |
Todd Wang | c2cba88 | 2014-10-14 10:59:56 -0700 | [diff] [blame] | 19 | func (s *Sorter) getOrAddNode(value interface{}) *node { |
| 20 | if s.values == nil { |
| 21 | s.values = make(map[interface{}]int) |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 22 | } |
Todd Wang | c2cba88 | 2014-10-14 10:59:56 -0700 | [diff] [blame] | 23 | if index, ok := s.values[value]; ok { |
| 24 | return s.nodes[index] |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 25 | } |
Todd Wang | c2cba88 | 2014-10-14 10:59:56 -0700 | [diff] [blame] | 26 | s.values[value] = len(s.nodes) |
| 27 | newNode := &node{value: value} |
| 28 | s.nodes = append(s.nodes, newNode) |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 29 | return newNode |
| 30 | } |
| 31 | |
Todd Wang | c2cba88 | 2014-10-14 10:59:56 -0700 | [diff] [blame] | 32 | // AddNode adds a node. Arbitrary value types are supported, but the values |
| 33 | // must be comparable; they'll be used as map keys. Typically this is only used |
| 34 | // to add nodes with no incoming or outgoing edges. |
| 35 | func (s *Sorter) AddNode(value interface{}) { |
| 36 | s.getOrAddNode(value) |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 37 | } |
| 38 | |
Todd Wang | c2cba88 | 2014-10-14 10:59:56 -0700 | [diff] [blame] | 39 | // AddEdge adds nodes from and to, and adds an edge from -> to. You don't need |
| 40 | // to call AddNode first; the nodes will be implicitly added if they don't |
| 41 | // already exist. The direction means that from depends on to; i.e. to will |
| 42 | // appear before from in the sorted output. Cycles are allowed. |
| 43 | func (s *Sorter) AddEdge(from interface{}, to interface{}) { |
| 44 | fromN, toN := s.getOrAddNode(from), s.getOrAddNode(to) |
| 45 | fromN.children = append(fromN.children, toN) |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 46 | } |
| 47 | |
Todd Wang | c2cba88 | 2014-10-14 10:59:56 -0700 | [diff] [blame] | 48 | // Sort returns the topologically sorted nodes, along with some of the cycles |
| 49 | // (if any) that were encountered. You're guaranteed that len(cycles)==0 iff |
| 50 | // there are no cycles in the graph, otherwise an arbitrary (but non-empty) list |
| 51 | // of cycles is returned. |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 52 | // |
| 53 | // If there are cycles the sorting is best-effort; portions of the graph that |
| 54 | // are acyclic will still be ordered correctly, and the cyclic portions have an |
| 55 | // arbitrary ordering. |
| 56 | // |
Todd Wang | c2cba88 | 2014-10-14 10:59:56 -0700 | [diff] [blame] | 57 | // Sort is deterministic; given the same sequence of inputs it always returns |
| 58 | // the same output, even if the inputs are only partially ordered. |
| 59 | func (s *Sorter) Sort() (sorted []interface{}, cycles [][]interface{}) { |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 60 | // The strategy is the standard simple approach of performing DFS on the |
| 61 | // graph. Details are outlined in the above wikipedia article. |
Todd Wang | c2cba88 | 2014-10-14 10:59:56 -0700 | [diff] [blame] | 62 | done := make(map[*node]bool) |
| 63 | for _, n := range s.nodes { |
| 64 | cycles = appendCycles(cycles, n.visit(done, make(map[*node]bool), &sorted)) |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 65 | } |
| 66 | return |
| 67 | } |
| 68 | |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 69 | // visit performs DFS on the graph, and fills in sorted and cycles as it |
| 70 | // traverses. We use done to indicate a node has been fully explored, and |
| 71 | // visiting to indicate a node is currently being explored. |
| 72 | // |
| 73 | // The cycle collection strategy is to wait until we've hit a repeated node in |
| 74 | // visiting, and add that node to cycles and return. Thereafter as the |
| 75 | // recursive stack is unwound, nodes append themselves to the end of each cycle, |
| 76 | // until we're back at the repeated node. This guarantees that if the graph is |
| 77 | // cyclic we'll return at least one of the cycles. |
Todd Wang | c2cba88 | 2014-10-14 10:59:56 -0700 | [diff] [blame] | 78 | func (n *node) visit(done, visiting map[*node]bool, sorted *[]interface{}) (cycles [][]interface{}) { |
| 79 | if done[n] { |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 80 | return |
| 81 | } |
Todd Wang | c2cba88 | 2014-10-14 10:59:56 -0700 | [diff] [blame] | 82 | if visiting[n] { |
| 83 | cycles = [][]interface{}{{n.value}} |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 84 | return |
| 85 | } |
Todd Wang | c2cba88 | 2014-10-14 10:59:56 -0700 | [diff] [blame] | 86 | visiting[n] = true |
| 87 | for _, child := range n.children { |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 88 | cycles = appendCycles(cycles, child.visit(done, visiting, sorted)) |
| 89 | } |
Todd Wang | c2cba88 | 2014-10-14 10:59:56 -0700 | [diff] [blame] | 90 | done[n] = true |
| 91 | *sorted = append(*sorted, n.value) |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 92 | // Update cycles. If it's empty none of our children detected any cycles, and |
| 93 | // there's nothing to do. Otherwise we append ourselves to the cycle, iff the |
| 94 | // cycle hasn't completed yet. We know the cycle has completed if the first |
| 95 | // and last item in the cycle are the same, with an exception for the single |
| 96 | // item case; self-cycles are represented as the same node appearing twice. |
| 97 | for cx := range cycles { |
| 98 | len := len(cycles[cx]) |
| 99 | if len == 1 || cycles[cx][0] != cycles[cx][len-1] { |
Todd Wang | c2cba88 | 2014-10-14 10:59:56 -0700 | [diff] [blame] | 100 | cycles[cx] = append(cycles[cx], n.value) |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 101 | } |
| 102 | } |
| 103 | return |
| 104 | } |
| 105 | |
| 106 | // appendCycles returns the combined cycles in a and b. |
| 107 | func appendCycles(a [][]interface{}, b [][]interface{}) [][]interface{} { |
| 108 | for _, bcycle := range b { |
| 109 | a = append(a, bcycle) |
| 110 | } |
| 111 | return a |
| 112 | } |
| 113 | |
Todd Wang | c2cba88 | 2014-10-14 10:59:56 -0700 | [diff] [blame] | 114 | // DumpCycles dumps the cycles returned from Sorter.Sort, using toString to |
| 115 | // convert each node into a string. |
| 116 | func DumpCycles(cycles [][]interface{}, toString func(n interface{}) string) string { |
| 117 | var str string |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 118 | for cyclex, cycle := range cycles { |
| 119 | if cyclex > 0 { |
| 120 | str += " " |
| 121 | } |
| 122 | str += "[" |
| 123 | for nodex, node := range cycle { |
| 124 | if nodex > 0 { |
| 125 | str += " <= " |
| 126 | } |
Todd Wang | c2cba88 | 2014-10-14 10:59:56 -0700 | [diff] [blame] | 127 | str += toString(node) |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 128 | } |
| 129 | str += "]" |
| 130 | } |
Todd Wang | c2cba88 | 2014-10-14 10:59:56 -0700 | [diff] [blame] | 131 | return str |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 132 | } |