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 := &ltNode{
   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  }