github.com/gopherd/gonum@v0.0.4/graph/flow/control_flow_slt.go (about) 1 // Copyright ©2017 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 flow 6 7 import "github.com/gopherd/gonum/graph" 8 9 // DominatorsSLT returns a dominator tree for all nodes in the flow graph 10 // g starting from the given root node using the sophisticated version of 11 // the Lengauer-Tarjan algorithm. The SLT algorithm may outperform the 12 // simple LT algorithm for very large dense graphs. 13 func DominatorsSLT(root graph.Node, g graph.Directed) DominatorTree { 14 // The algorithm used here is essentially the 15 // sophisticated Lengauer and Tarjan algorithm 16 // described in 17 // https://doi.org/10.1145%2F357062.357071 18 19 lt := sLengauerTarjan{ 20 indexOf: make(map[int64]int), 21 base: sltNode{semi: -1}, 22 } 23 lt.base.label = <.base 24 25 // step 1. 26 lt.dfs(g, root) 27 28 for i := len(lt.nodes) - 1; i > 0; i-- { 29 w := lt.nodes[i] 30 31 // step 2. 32 for _, v := range w.pred { 33 u := lt.eval(v) 34 35 if u.semi < w.semi { 36 w.semi = u.semi 37 } 38 } 39 40 lt.nodes[w.semi].bucket[w] = struct{}{} 41 lt.link(w.parent, w) 42 43 // step 3. 44 for v := range w.parent.bucket { 45 delete(w.parent.bucket, v) 46 47 u := lt.eval(v) 48 if u.semi < v.semi { 49 v.dom = u 50 } else { 51 v.dom = w.parent 52 } 53 } 54 } 55 56 // step 4. 57 for _, w := range lt.nodes[1:] { 58 if w.dom.node.ID() != lt.nodes[w.semi].node.ID() { 59 w.dom = w.dom.dom 60 } 61 } 62 63 // Construct the public-facing dominator tree structure. 64 dominatorOf := make(map[int64]graph.Node) 65 dominatedBy := make(map[int64][]graph.Node) 66 for _, w := range lt.nodes[1:] { 67 dominatorOf[w.node.ID()] = w.dom.node 68 did := w.dom.node.ID() 69 dominatedBy[did] = append(dominatedBy[did], w.node) 70 } 71 return DominatorTree{root: root, dominatorOf: dominatorOf, dominatedBy: dominatedBy} 72 } 73 74 // sLengauerTarjan holds global state of the Lengauer-Tarjan algorithm. 75 // This is a mapping between nodes and the postordering of the nodes. 76 type sLengauerTarjan struct { 77 // nodes is the nodes traversed during the 78 // Lengauer-Tarjan depth-first-search. 79 nodes []*sltNode 80 // indexOf contains a mapping between 81 // the id-dense representation of the 82 // graph and the potentially id-sparse 83 // nodes held in nodes. 84 // 85 // This corresponds to the vertex 86 // number of the node in the Lengauer- 87 // Tarjan algorithm. 88 indexOf map[int64]int 89 90 // base is the base label for balanced 91 // tree path compression used in the 92 // sophisticated Lengauer-Tarjan 93 // algorith, 94 base sltNode 95 } 96 97 // sltNode is a graph node with accounting for the Lengauer-Tarjan 98 // algorithm. 99 // 100 // For the purposes of documentation the ltNode is given the name w. 101 type sltNode struct { 102 node graph.Node 103 104 // parent is vertex which is the parent of w 105 // in the spanning tree generated by the search. 106 parent *sltNode 107 108 // pred is the set of vertices v such that (v, w) 109 // is an edge of the graph. 110 pred []*sltNode 111 112 // semi is a number defined as follows: 113 // (i) After w is numbered but before its semidominator 114 // is computed, semi is the number of w. 115 // (ii) After the semidominator of w is computed, semi 116 // is the number of the semidominator of w. 117 semi int 118 119 // size is the tree size of w used in the 120 // sophisticated algorithm. 121 size int 122 123 // child is the child node of w used in the 124 // sophisticated algorithm. 125 child *sltNode 126 127 // bucket is the set of vertices whose 128 // semidominator is w. 129 bucket map[*sltNode]struct{} 130 131 // dom is vertex defined as follows: 132 // (i) After step 3, if the semidominator of w is its 133 // immediate dominator, then dom is the immediate 134 // dominator of w. Otherwise dom is a vertex v 135 // whose number is smaller than w and whose immediate 136 // dominator is also w's immediate dominator. 137 // (ii) After step 4, dom is the immediate dominator of w. 138 dom *sltNode 139 140 // In general ancestor is nil only if w is a tree root 141 // in the forest; otherwise ancestor is an ancestor 142 // of w in the forest. 143 ancestor *sltNode 144 145 // Initially label is w. It is adjusted during 146 // the algorithm to maintain invariant (3) in the 147 // Lengauer and Tarjan paper. 148 label *sltNode 149 } 150 151 // dfs is the Sophisticated Lengauer-Tarjan DFS procedure. 152 func (lt *sLengauerTarjan) dfs(g graph.Directed, v graph.Node) { 153 i := len(lt.nodes) 154 lt.indexOf[v.ID()] = i 155 ltv := &sltNode{ 156 node: v, 157 semi: i, 158 size: 1, 159 child: <.base, 160 bucket: make(map[*sltNode]struct{}), 161 } 162 ltv.label = ltv 163 lt.nodes = append(lt.nodes, ltv) 164 165 to := g.From(v.ID()) 166 for to.Next() { 167 w := to.Node() 168 wid := w.ID() 169 170 idx, ok := lt.indexOf[wid] 171 if !ok { 172 lt.dfs(g, w) 173 174 // We place this below the recursive call 175 // in contrast to the original algorithm 176 // since w needs to be initialised, and 177 // this happens in the child call to dfs. 178 idx, ok = lt.indexOf[wid] 179 if !ok { 180 panic("path: unintialized node") 181 } 182 lt.nodes[idx].parent = ltv 183 } 184 ltw := lt.nodes[idx] 185 ltw.pred = append(ltw.pred, ltv) 186 } 187 } 188 189 // compress is the Sophisticated Lengauer-Tarjan COMPRESS procedure. 190 func (lt *sLengauerTarjan) compress(v *sltNode) { 191 if v.ancestor.ancestor != nil { 192 lt.compress(v.ancestor) 193 if v.ancestor.label.semi < v.label.semi { 194 v.label = v.ancestor.label 195 } 196 v.ancestor = v.ancestor.ancestor 197 } 198 } 199 200 // eval is the Sophisticated Lengauer-Tarjan EVAL function. 201 func (lt *sLengauerTarjan) eval(v *sltNode) *sltNode { 202 if v.ancestor == nil { 203 return v.label 204 } 205 lt.compress(v) 206 if v.ancestor.label.semi >= v.label.semi { 207 return v.label 208 } 209 return v.ancestor.label 210 } 211 212 // link is the Sophisticated Lengauer-Tarjan LINK procedure. 213 func (*sLengauerTarjan) link(v, w *sltNode) { 214 s := w 215 for w.label.semi < s.child.label.semi { 216 if s.size+s.child.child.size >= 2*s.child.size { 217 s.child.ancestor = s 218 s.child = s.child.child 219 } else { 220 s.child.size = s.size 221 s.ancestor = s.child 222 s = s.child 223 } 224 } 225 s.label = w.label 226 v.size += w.size 227 if v.size < 2*w.size { 228 s, v.child = v.child, s 229 } 230 for s != nil { 231 s.ancestor = v 232 s = s.child 233 } 234 }