github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/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 "sort" 10 11 "github.com/jingcheng-WU/gonum/graph" 12 "github.com/jingcheng-WU/gonum/graph/internal/ordered" 13 "github.com/jingcheng-WU/gonum/graph/internal/set" 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) { sort.Sort(ordered.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 for i, j := 0, len(sc)-1; i < j; i, j = i+1, j-1 { 75 sc[i], sc[j] = sc[j], sc[i] 76 } 77 err = sc 78 } 79 ordered.Reverse(sorted) 80 return sorted, err 81 } 82 83 // TarjanSCC returns the strongly connected components of the graph g using Tarjan's algorithm. 84 // 85 // A strongly connected component of a graph is a set of vertices where it's possible to reach any 86 // vertex in the set from any other (meaning there's a cycle between them.) 87 // 88 // Generally speaking, a directed graph where the number of strongly connected components is equal 89 // to the number of nodes is acyclic, unless you count reflexive edges as a cycle (which requires 90 // only a little extra testing.) 91 // 92 func TarjanSCC(g graph.Directed) [][]graph.Node { 93 return tarjanSCCstabilized(g, nil) 94 } 95 96 func tarjanSCCstabilized(g graph.Directed, order func([]graph.Node)) [][]graph.Node { 97 nodes := graph.NodesOf(g.Nodes()) 98 var succ func(id int64) []graph.Node 99 if order == nil { 100 succ = func(id int64) []graph.Node { 101 return graph.NodesOf(g.From(id)) 102 } 103 } else { 104 order(nodes) 105 ordered.Reverse(nodes) 106 107 succ = func(id int64) []graph.Node { 108 to := graph.NodesOf(g.From(id)) 109 order(to) 110 ordered.Reverse(to) 111 return to 112 } 113 } 114 115 t := tarjan{ 116 succ: succ, 117 118 indexTable: make(map[int64]int, len(nodes)), 119 lowLink: make(map[int64]int, len(nodes)), 120 onStack: make(set.Int64s), 121 } 122 for _, v := range nodes { 123 if t.indexTable[v.ID()] == 0 { 124 t.strongconnect(v) 125 } 126 } 127 return t.sccs 128 } 129 130 // tarjan implements Tarjan's strongly connected component finding 131 // algorithm. The implementation is from the pseudocode at 132 // 133 // http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm?oldid=642744644 134 // 135 type tarjan struct { 136 succ func(id int64) []graph.Node 137 138 index int 139 indexTable map[int64]int 140 lowLink map[int64]int 141 onStack set.Int64s 142 143 stack []graph.Node 144 145 sccs [][]graph.Node 146 } 147 148 // strongconnect is the strongconnect function described in the 149 // wikipedia article. 150 func (t *tarjan) strongconnect(v graph.Node) { 151 vID := v.ID() 152 153 // Set the depth index for v to the smallest unused index. 154 t.index++ 155 t.indexTable[vID] = t.index 156 t.lowLink[vID] = t.index 157 t.stack = append(t.stack, v) 158 t.onStack.Add(vID) 159 160 // Consider successors of v. 161 for _, w := range t.succ(vID) { 162 wID := w.ID() 163 if t.indexTable[wID] == 0 { 164 // Successor w has not yet been visited; recur on it. 165 t.strongconnect(w) 166 t.lowLink[vID] = min(t.lowLink[vID], t.lowLink[wID]) 167 } else if t.onStack.Has(wID) { 168 // Successor w is in stack s and hence in the current SCC. 169 t.lowLink[vID] = min(t.lowLink[vID], t.indexTable[wID]) 170 } 171 } 172 173 // If v is a root node, pop the stack and generate an SCC. 174 if t.lowLink[vID] == t.indexTable[vID] { 175 // Start a new strongly connected component. 176 var ( 177 scc []graph.Node 178 w graph.Node 179 ) 180 for { 181 w, t.stack = t.stack[len(t.stack)-1], t.stack[:len(t.stack)-1] 182 t.onStack.Remove(w.ID()) 183 // Add w to current strongly connected component. 184 scc = append(scc, w) 185 if w.ID() == vID { 186 break 187 } 188 } 189 // Output the current strongly connected component. 190 t.sccs = append(t.sccs, scc) 191 } 192 } 193 194 func min(a, b int) int { 195 if a < b { 196 return a 197 } 198 return b 199 }