github.com/gopherd/gonum@v0.0.4/graph/topo/tarjan.go (about) 1 // Copyright ©2015 The Gonum Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package topo 6 7 import ( 8 "fmt" 9 10 "github.com/gopherd/gonum/graph" 11 "github.com/gopherd/gonum/graph/internal/ordered" 12 "github.com/gopherd/gonum/graph/internal/set" 13 ) 14 15 // Unorderable is an error containing sets of unorderable graph.Nodes. 16 type Unorderable [][]graph.Node 17 18 // Error satisfies the error interface. 19 func (e Unorderable) Error() string { 20 const maxNodes = 10 21 var n int 22 for _, c := range e { 23 n += len(c) 24 } 25 if n > maxNodes { 26 // Don't return errors that are too long. 27 return fmt.Sprintf("topo: no topological ordering: %d nodes in %d cyclic components", n, len(e)) 28 } 29 return fmt.Sprintf("topo: no topological ordering: cyclic components: %v", [][]graph.Node(e)) 30 } 31 32 func lexical(nodes []graph.Node) { ordered.ByID(nodes) } 33 34 // Sort performs a topological sort of the directed graph g returning the 'from' to 'to' 35 // sort order. If a topological ordering is not possible, an Unorderable error is returned 36 // listing cyclic components in g with each cyclic component's members sorted by ID. When 37 // an Unorderable error is returned, each cyclic component's topological position within 38 // the sorted nodes is marked with a nil graph.Node. 39 func Sort(g graph.Directed) (sorted []graph.Node, err error) { 40 sccs := TarjanSCC(g) 41 return sortedFrom(sccs, lexical) 42 } 43 44 // SortStabilized performs a topological sort of the directed graph g returning the 'from' 45 // to 'to' sort order, or the order defined by the in place order sort function where there 46 // is no unambiguous topological ordering. If a topological ordering is not possible, an 47 // Unorderable error is returned listing cyclic components in g with each cyclic component's 48 // members sorted by the provided order function. If order is nil, nodes are ordered lexically 49 // by node ID. When an Unorderable error is returned, each cyclic component's topological 50 // position within the sorted nodes is marked with a nil graph.Node. 51 func SortStabilized(g graph.Directed, order func([]graph.Node)) (sorted []graph.Node, err error) { 52 if order == nil { 53 order = lexical 54 } 55 sccs := tarjanSCCstabilized(g, order) 56 return sortedFrom(sccs, order) 57 } 58 59 func sortedFrom(sccs [][]graph.Node, order func([]graph.Node)) ([]graph.Node, error) { 60 sorted := make([]graph.Node, 0, len(sccs)) 61 var sc Unorderable 62 for _, s := range sccs { 63 if len(s) != 1 { 64 order(s) 65 sc = append(sc, s) 66 sorted = append(sorted, nil) 67 continue 68 } 69 sorted = append(sorted, s[0]) 70 } 71 var err error 72 if sc != nil { 73 for i, j := 0, len(sc)-1; i < j; i, j = i+1, j-1 { 74 sc[i], sc[j] = sc[j], sc[i] 75 } 76 err = sc 77 } 78 ordered.Reverse(sorted) 79 return sorted, err 80 } 81 82 // TarjanSCC returns the strongly connected components of the graph g using Tarjan's algorithm. 83 // 84 // A strongly connected component of a graph is a set of vertices where it's possible to reach any 85 // vertex in the set from any other (meaning there's a cycle between them.) 86 // 87 // Generally speaking, a directed graph where the number of strongly connected components is equal 88 // to the number of nodes is acyclic, unless you count reflexive edges as a cycle (which requires 89 // only a little extra testing.) 90 // 91 func TarjanSCC(g graph.Directed) [][]graph.Node { 92 return tarjanSCCstabilized(g, nil) 93 } 94 95 func tarjanSCCstabilized(g graph.Directed, order func([]graph.Node)) [][]graph.Node { 96 nodes := graph.NodesOf(g.Nodes()) 97 var succ func(id int64) []graph.Node 98 if order == nil { 99 succ = func(id int64) []graph.Node { 100 return graph.NodesOf(g.From(id)) 101 } 102 } else { 103 order(nodes) 104 ordered.Reverse(nodes) 105 106 succ = func(id int64) []graph.Node { 107 to := graph.NodesOf(g.From(id)) 108 order(to) 109 ordered.Reverse(to) 110 return to 111 } 112 } 113 114 t := tarjan{ 115 succ: succ, 116 117 indexTable: make(map[int64]int, len(nodes)), 118 lowLink: make(map[int64]int, len(nodes)), 119 onStack: make(set.Int64s), 120 } 121 for _, v := range nodes { 122 if t.indexTable[v.ID()] == 0 { 123 t.strongconnect(v) 124 } 125 } 126 return t.sccs 127 } 128 129 // tarjan implements Tarjan's strongly connected component finding 130 // algorithm. The implementation is from the pseudocode at 131 // 132 // http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm?oldid=642744644 133 // 134 type tarjan struct { 135 succ func(id int64) []graph.Node 136 137 index int 138 indexTable map[int64]int 139 lowLink map[int64]int 140 onStack set.Int64s 141 142 stack []graph.Node 143 144 sccs [][]graph.Node 145 } 146 147 // strongconnect is the strongconnect function described in the 148 // wikipedia article. 149 func (t *tarjan) strongconnect(v graph.Node) { 150 vID := v.ID() 151 152 // Set the depth index for v to the smallest unused index. 153 t.index++ 154 t.indexTable[vID] = t.index 155 t.lowLink[vID] = t.index 156 t.stack = append(t.stack, v) 157 t.onStack.Add(vID) 158 159 // Consider successors of v. 160 for _, w := range t.succ(vID) { 161 wID := w.ID() 162 if t.indexTable[wID] == 0 { 163 // Successor w has not yet been visited; recur on it. 164 t.strongconnect(w) 165 t.lowLink[vID] = min(t.lowLink[vID], t.lowLink[wID]) 166 } else if t.onStack.Has(wID) { 167 // Successor w is in stack s and hence in the current SCC. 168 t.lowLink[vID] = min(t.lowLink[vID], t.indexTable[wID]) 169 } 170 } 171 172 // If v is a root node, pop the stack and generate an SCC. 173 if t.lowLink[vID] == t.indexTable[vID] { 174 // Start a new strongly connected component. 175 var ( 176 scc []graph.Node 177 w graph.Node 178 ) 179 for { 180 w, t.stack = t.stack[len(t.stack)-1], t.stack[:len(t.stack)-1] 181 t.onStack.Remove(w.ID()) 182 // Add w to current strongly connected component. 183 scc = append(scc, w) 184 if w.ID() == vID { 185 break 186 } 187 } 188 // Output the current strongly connected component. 189 t.sccs = append(t.sccs, scc) 190 } 191 } 192 193 func min(a, b int) int { 194 if a < b { 195 return a 196 } 197 return b 198 }