github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/graph/topo/tarjan.go (about)

     1  // Copyright ©2015 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 topo
     6  
     7  import (
     8  	"fmt"
     9  	"sort"
    10  
    11  	"github.com/jingcheng-WU/gonum/graph"
    12  	"github.com/jingcheng-WU/gonum/graph/internal/ordered"
    13  	"github.com/jingcheng-WU/gonum/graph/internal/set"
    14  )
    15  
    16  // Unorderable is an error containing sets of unorderable graph.Nodes.
    17  type Unorderable [][]graph.Node
    18  
    19  // Error satisfies the error interface.
    20  func (e Unorderable) Error() string {
    21  	const maxNodes = 10
    22  	var n int
    23  	for _, c := range e {
    24  		n += len(c)
    25  	}
    26  	if n > maxNodes {
    27  		// Don't return errors that are too long.
    28  		return fmt.Sprintf("topo: no topological ordering: %d nodes in %d cyclic components", n, len(e))
    29  	}
    30  	return fmt.Sprintf("topo: no topological ordering: cyclic components: %v", [][]graph.Node(e))
    31  }
    32  
    33  func lexical(nodes []graph.Node) { sort.Sort(ordered.ByID(nodes)) }
    34  
    35  // Sort performs a topological sort of the directed graph g returning the 'from' to 'to'
    36  // sort order. If a topological ordering is not possible, an Unorderable error is returned
    37  // listing cyclic components in g with each cyclic component's members sorted by ID. When
    38  // an Unorderable error is returned, each cyclic component's topological position within
    39  // the sorted nodes is marked with a nil graph.Node.
    40  func Sort(g graph.Directed) (sorted []graph.Node, err error) {
    41  	sccs := TarjanSCC(g)
    42  	return sortedFrom(sccs, lexical)
    43  }
    44  
    45  // SortStabilized performs a topological sort of the directed graph g returning the 'from'
    46  // to 'to' sort order, or the order defined by the in place order sort function where there
    47  // is no unambiguous topological ordering. If a topological ordering is not possible, an
    48  // Unorderable error is returned listing cyclic components in g with each cyclic component's
    49  // members sorted by the provided order function. If order is nil, nodes are ordered lexically
    50  // by node ID. When an Unorderable error is returned, each cyclic component's topological
    51  // position within the sorted nodes is marked with a nil graph.Node.
    52  func SortStabilized(g graph.Directed, order func([]graph.Node)) (sorted []graph.Node, err error) {
    53  	if order == nil {
    54  		order = lexical
    55  	}
    56  	sccs := tarjanSCCstabilized(g, order)
    57  	return sortedFrom(sccs, order)
    58  }
    59  
    60  func sortedFrom(sccs [][]graph.Node, order func([]graph.Node)) ([]graph.Node, error) {
    61  	sorted := make([]graph.Node, 0, len(sccs))
    62  	var sc Unorderable
    63  	for _, s := range sccs {
    64  		if len(s) != 1 {
    65  			order(s)
    66  			sc = append(sc, s)
    67  			sorted = append(sorted, nil)
    68  			continue
    69  		}
    70  		sorted = append(sorted, s[0])
    71  	}
    72  	var err error
    73  	if sc != nil {
    74  		for i, j := 0, len(sc)-1; i < j; i, j = i+1, j-1 {
    75  			sc[i], sc[j] = sc[j], sc[i]
    76  		}
    77  		err = sc
    78  	}
    79  	ordered.Reverse(sorted)
    80  	return sorted, err
    81  }
    82  
    83  // TarjanSCC returns the strongly connected components of the graph g using Tarjan's algorithm.
    84  //
    85  // A strongly connected component of a graph is a set of vertices where it's possible to reach any
    86  // vertex in the set from any other (meaning there's a cycle between them.)
    87  //
    88  // Generally speaking, a directed graph where the number of strongly connected components is equal
    89  // to the number of nodes is acyclic, unless you count reflexive edges as a cycle (which requires
    90  // only a little extra testing.)
    91  //
    92  func TarjanSCC(g graph.Directed) [][]graph.Node {
    93  	return tarjanSCCstabilized(g, nil)
    94  }
    95  
    96  func tarjanSCCstabilized(g graph.Directed, order func([]graph.Node)) [][]graph.Node {
    97  	nodes := graph.NodesOf(g.Nodes())
    98  	var succ func(id int64) []graph.Node
    99  	if order == nil {
   100  		succ = func(id int64) []graph.Node {
   101  			return graph.NodesOf(g.From(id))
   102  		}
   103  	} else {
   104  		order(nodes)
   105  		ordered.Reverse(nodes)
   106  
   107  		succ = func(id int64) []graph.Node {
   108  			to := graph.NodesOf(g.From(id))
   109  			order(to)
   110  			ordered.Reverse(to)
   111  			return to
   112  		}
   113  	}
   114  
   115  	t := tarjan{
   116  		succ: succ,
   117  
   118  		indexTable: make(map[int64]int, len(nodes)),
   119  		lowLink:    make(map[int64]int, len(nodes)),
   120  		onStack:    make(set.Int64s),
   121  	}
   122  	for _, v := range nodes {
   123  		if t.indexTable[v.ID()] == 0 {
   124  			t.strongconnect(v)
   125  		}
   126  	}
   127  	return t.sccs
   128  }
   129  
   130  // tarjan implements Tarjan's strongly connected component finding
   131  // algorithm. The implementation is from the pseudocode at
   132  //
   133  // http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm?oldid=642744644
   134  //
   135  type tarjan struct {
   136  	succ func(id int64) []graph.Node
   137  
   138  	index      int
   139  	indexTable map[int64]int
   140  	lowLink    map[int64]int
   141  	onStack    set.Int64s
   142  
   143  	stack []graph.Node
   144  
   145  	sccs [][]graph.Node
   146  }
   147  
   148  // strongconnect is the strongconnect function described in the
   149  // wikipedia article.
   150  func (t *tarjan) strongconnect(v graph.Node) {
   151  	vID := v.ID()
   152  
   153  	// Set the depth index for v to the smallest unused index.
   154  	t.index++
   155  	t.indexTable[vID] = t.index
   156  	t.lowLink[vID] = t.index
   157  	t.stack = append(t.stack, v)
   158  	t.onStack.Add(vID)
   159  
   160  	// Consider successors of v.
   161  	for _, w := range t.succ(vID) {
   162  		wID := w.ID()
   163  		if t.indexTable[wID] == 0 {
   164  			// Successor w has not yet been visited; recur on it.
   165  			t.strongconnect(w)
   166  			t.lowLink[vID] = min(t.lowLink[vID], t.lowLink[wID])
   167  		} else if t.onStack.Has(wID) {
   168  			// Successor w is in stack s and hence in the current SCC.
   169  			t.lowLink[vID] = min(t.lowLink[vID], t.indexTable[wID])
   170  		}
   171  	}
   172  
   173  	// If v is a root node, pop the stack and generate an SCC.
   174  	if t.lowLink[vID] == t.indexTable[vID] {
   175  		// Start a new strongly connected component.
   176  		var (
   177  			scc []graph.Node
   178  			w   graph.Node
   179  		)
   180  		for {
   181  			w, t.stack = t.stack[len(t.stack)-1], t.stack[:len(t.stack)-1]
   182  			t.onStack.Remove(w.ID())
   183  			// Add w to current strongly connected component.
   184  			scc = append(scc, w)
   185  			if w.ID() == vID {
   186  				break
   187  			}
   188  		}
   189  		// Output the current strongly connected component.
   190  		t.sccs = append(t.sccs, scc)
   191  	}
   192  }
   193  
   194  func min(a, b int) int {
   195  	if a < b {
   196  		return a
   197  	}
   198  	return b
   199  }