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