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