gonum.org/v1/gonum@v0.15.1-0.20240517103525-f853624cb1bb/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  	"slices"
    10  
    11  	"gonum.org/v1/gonum/graph"
    12  	"gonum.org/v1/gonum/graph/internal/set"
    13  	"gonum.org/v1/gonum/internal/order"
    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) { order.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  		slices.Reverse(sc)
    75  		err = sc
    76  	}
    77  	slices.Reverse(sorted)
    78  	return sorted, err
    79  }
    80  
    81  // TarjanSCC returns the strongly connected components of the graph g using Tarjan's algorithm.
    82  //
    83  // A strongly connected component of a graph is a set of vertices where it's possible to reach any
    84  // vertex in the set from any other (meaning there's a cycle between them.)
    85  //
    86  // Generally speaking, a directed graph where the number of strongly connected components is equal
    87  // to the number of nodes is acyclic, unless you count reflexive edges as a cycle (which requires
    88  // only a little extra testing.)
    89  func TarjanSCC(g graph.Directed) [][]graph.Node {
    90  	return tarjanSCCstabilized(g, nil)
    91  }
    92  
    93  func tarjanSCCstabilized(g graph.Directed, order func([]graph.Node)) [][]graph.Node {
    94  	nodes := graph.NodesOf(g.Nodes())
    95  	var succ func(id int64) []graph.Node
    96  	if order == nil {
    97  		succ = func(id int64) []graph.Node {
    98  			return graph.NodesOf(g.From(id))
    99  		}
   100  	} else {
   101  		order(nodes)
   102  		slices.Reverse(nodes)
   103  
   104  		succ = func(id int64) []graph.Node {
   105  			to := graph.NodesOf(g.From(id))
   106  			order(to)
   107  			slices.Reverse(to)
   108  			return to
   109  		}
   110  	}
   111  
   112  	t := tarjan{
   113  		succ: succ,
   114  
   115  		indexTable: make(map[int64]int, len(nodes)),
   116  		lowLink:    make(map[int64]int, len(nodes)),
   117  		onStack:    make(set.Ints[int64]),
   118  	}
   119  	for _, v := range nodes {
   120  		if t.indexTable[v.ID()] == 0 {
   121  			t.strongconnect(v)
   122  		}
   123  	}
   124  	return t.sccs
   125  }
   126  
   127  // tarjan implements Tarjan's strongly connected component finding
   128  // algorithm. The implementation is from the pseudocode at
   129  //
   130  // http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm?oldid=642744644
   131  type tarjan struct {
   132  	succ func(id int64) []graph.Node
   133  
   134  	index      int
   135  	indexTable map[int64]int
   136  	lowLink    map[int64]int
   137  	onStack    set.Ints[int64]
   138  
   139  	stack []graph.Node
   140  
   141  	sccs [][]graph.Node
   142  }
   143  
   144  // strongconnect is the strongconnect function described in the
   145  // wikipedia article.
   146  func (t *tarjan) strongconnect(v graph.Node) {
   147  	vID := v.ID()
   148  
   149  	// Set the depth index for v to the smallest unused index.
   150  	t.index++
   151  	t.indexTable[vID] = t.index
   152  	t.lowLink[vID] = t.index
   153  	t.stack = append(t.stack, v)
   154  	t.onStack.Add(vID)
   155  
   156  	// Consider successors of v.
   157  	for _, w := range t.succ(vID) {
   158  		wID := w.ID()
   159  		if t.indexTable[wID] == 0 {
   160  			// Successor w has not yet been visited; recur on it.
   161  			t.strongconnect(w)
   162  			t.lowLink[vID] = min(t.lowLink[vID], t.lowLink[wID])
   163  		} else if t.onStack.Has(wID) {
   164  			// Successor w is in stack s and hence in the current SCC.
   165  			t.lowLink[vID] = min(t.lowLink[vID], t.indexTable[wID])
   166  		}
   167  	}
   168  
   169  	// If v is a root node, pop the stack and generate an SCC.
   170  	if t.lowLink[vID] == t.indexTable[vID] {
   171  		// Start a new strongly connected component.
   172  		var (
   173  			scc []graph.Node
   174  			w   graph.Node
   175  		)
   176  		for {
   177  			w, t.stack = t.stack[len(t.stack)-1], t.stack[:len(t.stack)-1]
   178  			t.onStack.Remove(w.ID())
   179  			// Add w to current strongly connected component.
   180  			scc = append(scc, w)
   181  			if w.ID() == vID {
   182  				break
   183  			}
   184  		}
   185  		// Output the current strongly connected component.
   186  		t.sccs = append(t.sccs, scc)
   187  	}
   188  }