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