github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/graph/topo/johnson_cycles.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 "sort" 9 10 "github.com/jingcheng-WU/gonum/graph" 11 "github.com/jingcheng-WU/gonum/graph/internal/ordered" 12 "github.com/jingcheng-WU/gonum/graph/internal/set" 13 "github.com/jingcheng-WU/gonum/graph/iterator" 14 ) 15 16 // johnson implements Johnson's "Finding all the elementary 17 // circuits of a directed graph" algorithm. SIAM J. Comput. 4(1):1975. 18 // 19 // Comments in the johnson methods are kept in sync with the comments 20 // and labels from the paper. 21 type johnson struct { 22 adjacent johnsonGraph // SCC adjacency list. 23 b []set.Ints // Johnson's "B-list". 24 blocked []bool 25 s int 26 27 stack []graph.Node 28 29 result [][]graph.Node 30 } 31 32 // DirectedCyclesIn returns the set of elementary cycles in the graph g. 33 func DirectedCyclesIn(g graph.Directed) [][]graph.Node { 34 jg := johnsonGraphFrom(g) 35 j := johnson{ 36 adjacent: jg, 37 b: make([]set.Ints, len(jg.orig)), 38 blocked: make([]bool, len(jg.orig)), 39 } 40 41 // len(j.nodes) is the order of g. 42 for j.s < len(j.adjacent.orig)-1 { 43 // We use the previous SCC adjacency to reduce the work needed. 44 sccs := TarjanSCC(j.adjacent.subgraph(j.s)) 45 // A_k = adjacency structure of strong component K with least 46 // vertex in subgraph of G induced by {s, s+1, ... ,n}. 47 j.adjacent = j.adjacent.sccSubGraph(sccs, 2) // Only allow SCCs with >= 2 vertices. 48 if j.adjacent.order() == 0 { 49 break 50 } 51 52 // s = least vertex in V_k 53 if s := j.adjacent.leastVertexIndex(); s < j.s { 54 j.s = s 55 } 56 for i, v := range j.adjacent.orig { 57 if !j.adjacent.nodes.Has(v.ID()) { 58 continue 59 } 60 if len(j.adjacent.succ[v.ID()]) > 0 { 61 j.blocked[i] = false 62 j.b[i] = make(set.Ints) 63 } 64 } 65 //L3: 66 _ = j.circuit(j.s) 67 j.s++ 68 } 69 70 return j.result 71 } 72 73 // circuit is the CIRCUIT sub-procedure in the paper. 74 func (j *johnson) circuit(v int) bool { 75 f := false 76 n := j.adjacent.orig[v] 77 j.stack = append(j.stack, n) 78 j.blocked[v] = true 79 80 //L1: 81 for w := range j.adjacent.succ[n.ID()] { 82 w := j.adjacent.indexOf(w) 83 if w == j.s { 84 // Output circuit composed of stack followed by s. 85 r := make([]graph.Node, len(j.stack)+1) 86 copy(r, j.stack) 87 r[len(r)-1] = j.adjacent.orig[j.s] 88 j.result = append(j.result, r) 89 f = true 90 } else if !j.blocked[w] { 91 if j.circuit(w) { 92 f = true 93 } 94 } 95 } 96 97 //L2: 98 if f { 99 j.unblock(v) 100 } else { 101 for w := range j.adjacent.succ[n.ID()] { 102 j.b[j.adjacent.indexOf(w)].Add(v) 103 } 104 } 105 j.stack = j.stack[:len(j.stack)-1] 106 107 return f 108 } 109 110 // unblock is the UNBLOCK sub-procedure in the paper. 111 func (j *johnson) unblock(u int) { 112 j.blocked[u] = false 113 for w := range j.b[u] { 114 j.b[u].Remove(w) 115 if j.blocked[w] { 116 j.unblock(w) 117 } 118 } 119 } 120 121 // johnsonGraph is an edge list representation of a graph with helpers 122 // necessary for Johnson's algorithm 123 type johnsonGraph struct { 124 // Keep the original graph nodes and a 125 // look-up to into the non-sparse 126 // collection of potentially sparse IDs. 127 orig []graph.Node 128 index map[int64]int 129 130 nodes set.Int64s 131 succ map[int64]set.Int64s 132 } 133 134 // johnsonGraphFrom returns a deep copy of the graph g. 135 func johnsonGraphFrom(g graph.Directed) johnsonGraph { 136 nodes := graph.NodesOf(g.Nodes()) 137 sort.Sort(ordered.ByID(nodes)) 138 c := johnsonGraph{ 139 orig: nodes, 140 index: make(map[int64]int, len(nodes)), 141 142 nodes: make(set.Int64s, len(nodes)), 143 succ: make(map[int64]set.Int64s), 144 } 145 for i, u := range nodes { 146 uid := u.ID() 147 c.index[uid] = i 148 to := g.From(uid) 149 for to.Next() { 150 v := to.Node() 151 if c.succ[uid] == nil { 152 c.succ[uid] = make(set.Int64s) 153 c.nodes.Add(uid) 154 } 155 c.nodes.Add(v.ID()) 156 c.succ[uid].Add(v.ID()) 157 } 158 } 159 return c 160 } 161 162 // order returns the order of the graph. 163 func (g johnsonGraph) order() int { return g.nodes.Count() } 164 165 // indexOf returns the index of the retained node for the given node ID. 166 func (g johnsonGraph) indexOf(id int64) int { 167 return g.index[id] 168 } 169 170 // leastVertexIndex returns the index into orig of the least vertex. 171 func (g johnsonGraph) leastVertexIndex() int { 172 for _, v := range g.orig { 173 if g.nodes.Has(v.ID()) { 174 return g.indexOf(v.ID()) 175 } 176 } 177 panic("johnsonCycles: empty set") 178 } 179 180 // subgraph returns a subgraph of g induced by {s, s+1, ... , n}. The 181 // subgraph is destructively generated in g. 182 func (g johnsonGraph) subgraph(s int) johnsonGraph { 183 sn := g.orig[s].ID() 184 for u, e := range g.succ { 185 if u < sn { 186 g.nodes.Remove(u) 187 delete(g.succ, u) 188 continue 189 } 190 for v := range e { 191 if v < sn { 192 g.succ[u].Remove(v) 193 } 194 } 195 } 196 return g 197 } 198 199 // sccSubGraph returns the graph of the tarjan's strongly connected 200 // components with each SCC containing at least min vertices. 201 // sccSubGraph returns nil if there is no SCC with at least min 202 // members. 203 func (g johnsonGraph) sccSubGraph(sccs [][]graph.Node, min int) johnsonGraph { 204 if len(g.nodes) == 0 { 205 g.nodes = nil 206 g.succ = nil 207 return g 208 } 209 sub := johnsonGraph{ 210 orig: g.orig, 211 index: g.index, 212 nodes: make(set.Int64s), 213 succ: make(map[int64]set.Int64s), 214 } 215 216 var n int 217 for _, scc := range sccs { 218 if len(scc) < min { 219 continue 220 } 221 n++ 222 for _, u := range scc { 223 for _, v := range scc { 224 if _, ok := g.succ[u.ID()][v.ID()]; ok { 225 if sub.succ[u.ID()] == nil { 226 sub.succ[u.ID()] = make(set.Int64s) 227 sub.nodes.Add(u.ID()) 228 } 229 sub.nodes.Add(v.ID()) 230 sub.succ[u.ID()].Add(v.ID()) 231 } 232 } 233 } 234 } 235 if n == 0 { 236 g.nodes = nil 237 g.succ = nil 238 return g 239 } 240 241 return sub 242 } 243 244 // Nodes is required to satisfy Tarjan. 245 func (g johnsonGraph) Nodes() graph.Nodes { 246 n := make([]graph.Node, 0, len(g.nodes)) 247 for id := range g.nodes { 248 n = append(n, johnsonGraphNode(id)) 249 } 250 return iterator.NewOrderedNodes(n) 251 } 252 253 // Successors is required to satisfy Tarjan. 254 func (g johnsonGraph) From(id int64) graph.Nodes { 255 adj := g.succ[id] 256 if len(adj) == 0 { 257 return graph.Empty 258 } 259 succ := make([]graph.Node, 0, len(adj)) 260 for id := range adj { 261 succ = append(succ, johnsonGraphNode(id)) 262 } 263 return iterator.NewOrderedNodes(succ) 264 } 265 266 func (johnsonGraph) Has(int64) bool { 267 panic("topo: unintended use of johnsonGraph") 268 } 269 func (johnsonGraph) Node(int64) graph.Node { 270 panic("topo: unintended use of johnsonGraph") 271 } 272 func (johnsonGraph) HasEdgeBetween(_, _ int64) bool { 273 panic("topo: unintended use of johnsonGraph") 274 } 275 func (johnsonGraph) Edge(_, _ int64) graph.Edge { 276 panic("topo: unintended use of johnsonGraph") 277 } 278 func (johnsonGraph) HasEdgeFromTo(_, _ int64) bool { 279 panic("topo: unintended use of johnsonGraph") 280 } 281 func (johnsonGraph) To(int64) graph.Nodes { 282 panic("topo: unintended use of johnsonGraph") 283 } 284 285 type johnsonGraphNode int64 286 287 func (n johnsonGraphNode) ID() int64 { return int64(n) }