gonum.org/v1/gonum@v0.14.0/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 "gonum.org/v1/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 = &lt.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:  &lt.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  }