gonum.org/v1/gonum@v0.14.0/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 "gonum.org/v1/gonum/graph" 11 "gonum.org/v1/gonum/graph/internal/ordered" 12 "gonum.org/v1/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 func TarjanSCC(g graph.Directed) [][]graph.Node { 91 return tarjanSCCstabilized(g, nil) 92 } 93 94 func tarjanSCCstabilized(g graph.Directed, order func([]graph.Node)) [][]graph.Node { 95 nodes := graph.NodesOf(g.Nodes()) 96 var succ func(id int64) []graph.Node 97 if order == nil { 98 succ = func(id int64) []graph.Node { 99 return graph.NodesOf(g.From(id)) 100 } 101 } else { 102 order(nodes) 103 ordered.Reverse(nodes) 104 105 succ = func(id int64) []graph.Node { 106 to := graph.NodesOf(g.From(id)) 107 order(to) 108 ordered.Reverse(to) 109 return to 110 } 111 } 112 113 t := tarjan{ 114 succ: succ, 115 116 indexTable: make(map[int64]int, len(nodes)), 117 lowLink: make(map[int64]int, len(nodes)), 118 onStack: make(set.Int64s), 119 } 120 for _, v := range nodes { 121 if t.indexTable[v.ID()] == 0 { 122 t.strongconnect(v) 123 } 124 } 125 return t.sccs 126 } 127 128 // tarjan implements Tarjan's strongly connected component finding 129 // algorithm. The implementation is from the pseudocode at 130 // 131 // http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm?oldid=642744644 132 type tarjan struct { 133 succ func(id int64) []graph.Node 134 135 index int 136 indexTable map[int64]int 137 lowLink map[int64]int 138 onStack set.Int64s 139 140 stack []graph.Node 141 142 sccs [][]graph.Node 143 } 144 145 // strongconnect is the strongconnect function described in the 146 // wikipedia article. 147 func (t *tarjan) strongconnect(v graph.Node) { 148 vID := v.ID() 149 150 // Set the depth index for v to the smallest unused index. 151 t.index++ 152 t.indexTable[vID] = t.index 153 t.lowLink[vID] = t.index 154 t.stack = append(t.stack, v) 155 t.onStack.Add(vID) 156 157 // Consider successors of v. 158 for _, w := range t.succ(vID) { 159 wID := w.ID() 160 if t.indexTable[wID] == 0 { 161 // Successor w has not yet been visited; recur on it. 162 t.strongconnect(w) 163 t.lowLink[vID] = min(t.lowLink[vID], t.lowLink[wID]) 164 } else if t.onStack.Has(wID) { 165 // Successor w is in stack s and hence in the current SCC. 166 t.lowLink[vID] = min(t.lowLink[vID], t.indexTable[wID]) 167 } 168 } 169 170 // If v is a root node, pop the stack and generate an SCC. 171 if t.lowLink[vID] == t.indexTable[vID] { 172 // Start a new strongly connected component. 173 var ( 174 scc []graph.Node 175 w graph.Node 176 ) 177 for { 178 w, t.stack = t.stack[len(t.stack)-1], t.stack[:len(t.stack)-1] 179 t.onStack.Remove(w.ID()) 180 // Add w to current strongly connected component. 181 scc = append(scc, w) 182 if w.ID() == vID { 183 break 184 } 185 } 186 // Output the current strongly connected component. 187 t.sccs = append(t.sccs, scc) 188 } 189 } 190 191 func min(a, b int) int { 192 if a < b { 193 return a 194 } 195 return b 196 }