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