| // Copyright ©2017 The Gonum Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package flow |
| |
| import "gonum.org/v1/gonum/graph" |
| |
| // DominatorsSLT returns a dominator tree for all nodes in the flow graph |
| // g starting from the given root node using the sophisticated version of |
| // the Lengauer-Tarjan algorithm. The SLT algorithm may outperform the |
| // simple LT algorithm for very large dense graphs. |
| func DominatorsSLT(root graph.Node, g graph.Directed) DominatorTree { |
| // The algorithm used here is essentially the |
| // sophisticated Lengauer and Tarjan algorithm |
| // described in |
| // https://doi.org/10.1145%2F357062.357071 |
| |
| lt := sLengauerTarjan{ |
| indexOf: make(map[int64]int), |
| base: sltNode{semi: -1}, |
| } |
| lt.base.label = <.base |
| |
| // step 1. |
| lt.dfs(g, root) |
| |
| for i := len(lt.nodes) - 1; i > 0; i-- { |
| w := lt.nodes[i] |
| |
| // step 2. |
| for _, v := range w.pred { |
| u := lt.eval(v) |
| |
| if u.semi < w.semi { |
| w.semi = u.semi |
| } |
| } |
| |
| lt.nodes[w.semi].bucket[w] = struct{}{} |
| lt.link(w.parent, w) |
| |
| // step 3. |
| for v := range w.parent.bucket { |
| delete(w.parent.bucket, v) |
| |
| u := lt.eval(v) |
| if u.semi < v.semi { |
| v.dom = u |
| } else { |
| v.dom = w.parent |
| } |
| } |
| } |
| |
| // step 4. |
| for _, w := range lt.nodes[1:] { |
| if w.dom.node.ID() != lt.nodes[w.semi].node.ID() { |
| w.dom = w.dom.dom |
| } |
| } |
| |
| // Construct the public-facing dominator tree structure. |
| dominatorOf := make(map[int64]graph.Node) |
| dominatedBy := make(map[int64][]graph.Node) |
| for _, w := range lt.nodes[1:] { |
| dominatorOf[w.node.ID()] = w.dom.node |
| did := w.dom.node.ID() |
| dominatedBy[did] = append(dominatedBy[did], w.node) |
| } |
| return DominatorTree{root: root, dominatorOf: dominatorOf, dominatedBy: dominatedBy} |
| } |
| |
| // sLengauerTarjan holds global state of the Lengauer-Tarjan algorithm. |
| // This is a mapping between nodes and the postordering of the nodes. |
| type sLengauerTarjan struct { |
| // nodes is the nodes traversed during the |
| // Lengauer-Tarjan depth-first-search. |
| nodes []*sltNode |
| // indexOf contains a mapping between |
| // the id-dense representation of the |
| // graph and the potentially id-sparse |
| // nodes held in nodes. |
| // |
| // This corresponds to the vertex |
| // number of the node in the Lengauer- |
| // Tarjan algorithm. |
| indexOf map[int64]int |
| |
| // base is the base label for balanced |
| // tree path compression used in the |
| // sophisticated Lengauer-Tarjan |
| // algorith, |
| base sltNode |
| } |
| |
| // sltNode is a graph node with accounting for the Lengauer-Tarjan |
| // algorithm. |
| // |
| // For the purposes of documentation the ltNode is given the name w. |
| type sltNode struct { |
| node graph.Node |
| |
| // parent is vertex which is the parent of w |
| // in the spanning tree generated by the search. |
| parent *sltNode |
| |
| // pred is the set of vertices v such that (v, w) |
| // is an edge of the graph. |
| pred []*sltNode |
| |
| // semi is a number defined as follows: |
| // (i) After w is numbered but before its semidominator |
| // is computed, semi is the number of w. |
| // (ii) After the semidominator of w is computed, semi |
| // is the number of the semidominator of w. |
| semi int |
| |
| // size is the tree size of w used in the |
| // sophisticated algorithm. |
| size int |
| |
| // child is the child node of w used in the |
| // sophisticated algorithm. |
| child *sltNode |
| |
| // bucket is the set of vertices whose |
| // semidominator is w. |
| bucket map[*sltNode]struct{} |
| |
| // dom is vertex defined as follows: |
| // (i) After step 3, if the semidominator of w is its |
| // immediate dominator, then dom is the immediate |
| // dominator of w. Otherwise dom is a vertex v |
| // whose number is smaller than w and whose immediate |
| // dominator is also w's immediate dominator. |
| // (ii) After step 4, dom is the immediate dominator of w. |
| dom *sltNode |
| |
| // In general ancestor is nil only if w is a tree root |
| // in the forest; otherwise ancestor is an ancestor |
| // of w in the forest. |
| ancestor *sltNode |
| |
| // Initially label is w. It is adjusted during |
| // the algorithm to maintain invariant (3) in the |
| // Lengauer and Tarjan paper. |
| label *sltNode |
| } |
| |
| // dfs is the Sophisticated Lengauer-Tarjan DFS procedure. |
| func (lt *sLengauerTarjan) dfs(g graph.Directed, v graph.Node) { |
| i := len(lt.nodes) |
| lt.indexOf[v.ID()] = i |
| ltv := &sltNode{ |
| node: v, |
| semi: i, |
| size: 1, |
| child: <.base, |
| bucket: make(map[*sltNode]struct{}), |
| } |
| ltv.label = ltv |
| lt.nodes = append(lt.nodes, ltv) |
| |
| to := g.From(v.ID()) |
| for to.Next() { |
| w := to.Node() |
| wid := w.ID() |
| |
| idx, ok := lt.indexOf[wid] |
| if !ok { |
| lt.dfs(g, w) |
| |
| // We place this below the recursive call |
| // in contrast to the original algorithm |
| // since w needs to be initialised, and |
| // this happens in the child call to dfs. |
| idx, ok = lt.indexOf[wid] |
| if !ok { |
| panic("path: unintialized node") |
| } |
| lt.nodes[idx].parent = ltv |
| } |
| ltw := lt.nodes[idx] |
| ltw.pred = append(ltw.pred, ltv) |
| } |
| } |
| |
| // compress is the Sophisticated Lengauer-Tarjan COMPRESS procedure. |
| func (lt *sLengauerTarjan) compress(v *sltNode) { |
| if v.ancestor.ancestor != nil { |
| lt.compress(v.ancestor) |
| if v.ancestor.label.semi < v.label.semi { |
| v.label = v.ancestor.label |
| } |
| v.ancestor = v.ancestor.ancestor |
| } |
| } |
| |
| // eval is the Sophisticated Lengauer-Tarjan EVAL function. |
| func (lt *sLengauerTarjan) eval(v *sltNode) *sltNode { |
| if v.ancestor == nil { |
| return v.label |
| } |
| lt.compress(v) |
| if v.ancestor.label.semi >= v.label.semi { |
| return v.label |
| } |
| return v.ancestor.label |
| } |
| |
| // link is the Sophisticated Lengauer-Tarjan LINK procedure. |
| func (*sLengauerTarjan) link(v, w *sltNode) { |
| s := w |
| for w.label.semi < s.child.label.semi { |
| if s.size+s.child.child.size >= 2*s.child.size { |
| s.child.ancestor = s |
| s.child = s.child.child |
| } else { |
| s.child.size = s.size |
| s.ancestor = s.child |
| s = s.child |
| } |
| } |
| s.label = w.label |
| v.size += w.size |
| if v.size < 2*w.size { |
| s, v.child = v.child, s |
| } |
| for s != nil { |
| s.ancestor = v |
| s = s.child |
| } |
| } |