199 lines
5.7 KiB
Go
199 lines
5.7 KiB
Go
// Copyright ©2015 The Gonum Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package topo
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
|
|
"gonum.org/v1/gonum/graph"
|
|
"gonum.org/v1/gonum/graph/internal/ordered"
|
|
"gonum.org/v1/gonum/graph/internal/set"
|
|
)
|
|
|
|
// Unorderable is an error containing sets of unorderable graph.Nodes.
|
|
type Unorderable [][]graph.Node
|
|
|
|
// Error satisfies the error interface.
|
|
func (e Unorderable) Error() string {
|
|
const maxNodes = 10
|
|
var n int
|
|
for _, c := range e {
|
|
n += len(c)
|
|
}
|
|
if n > maxNodes {
|
|
// Don't return errors that are too long.
|
|
return fmt.Sprintf("topo: no topological ordering: %d nodes in %d cyclic components", n, len(e))
|
|
}
|
|
return fmt.Sprintf("topo: no topological ordering: cyclic components: %v", [][]graph.Node(e))
|
|
}
|
|
|
|
func lexical(nodes []graph.Node) { sort.Sort(ordered.ByID(nodes)) }
|
|
|
|
// Sort performs a topological sort of the directed graph g returning the 'from' to 'to'
|
|
// sort order. If a topological ordering is not possible, an Unorderable error is returned
|
|
// listing cyclic components in g with each cyclic component's members sorted by ID. When
|
|
// an Unorderable error is returned, each cyclic component's topological position within
|
|
// the sorted nodes is marked with a nil graph.Node.
|
|
func Sort(g graph.Directed) (sorted []graph.Node, err error) {
|
|
sccs := TarjanSCC(g)
|
|
return sortedFrom(sccs, lexical)
|
|
}
|
|
|
|
// SortStabilized performs a topological sort of the directed graph g returning the 'from'
|
|
// to 'to' sort order, or the order defined by the in place order sort function where there
|
|
// is no unambiguous topological ordering. If a topological ordering is not possible, an
|
|
// Unorderable error is returned listing cyclic components in g with each cyclic component's
|
|
// members sorted by the provided order function. If order is nil, nodes are ordered lexically
|
|
// by node ID. When an Unorderable error is returned, each cyclic component's topological
|
|
// position within the sorted nodes is marked with a nil graph.Node.
|
|
func SortStabilized(g graph.Directed, order func([]graph.Node)) (sorted []graph.Node, err error) {
|
|
if order == nil {
|
|
order = lexical
|
|
}
|
|
sccs := tarjanSCCstabilized(g, order)
|
|
return sortedFrom(sccs, order)
|
|
}
|
|
|
|
func sortedFrom(sccs [][]graph.Node, order func([]graph.Node)) ([]graph.Node, error) {
|
|
sorted := make([]graph.Node, 0, len(sccs))
|
|
var sc Unorderable
|
|
for _, s := range sccs {
|
|
if len(s) != 1 {
|
|
order(s)
|
|
sc = append(sc, s)
|
|
sorted = append(sorted, nil)
|
|
continue
|
|
}
|
|
sorted = append(sorted, s[0])
|
|
}
|
|
var err error
|
|
if sc != nil {
|
|
for i, j := 0, len(sc)-1; i < j; i, j = i+1, j-1 {
|
|
sc[i], sc[j] = sc[j], sc[i]
|
|
}
|
|
err = sc
|
|
}
|
|
ordered.Reverse(sorted)
|
|
return sorted, err
|
|
}
|
|
|
|
// TarjanSCC returns the strongly connected components of the graph g using Tarjan's algorithm.
|
|
//
|
|
// A strongly connected component of a graph is a set of vertices where it's possible to reach any
|
|
// vertex in the set from any other (meaning there's a cycle between them.)
|
|
//
|
|
// Generally speaking, a directed graph where the number of strongly connected components is equal
|
|
// to the number of nodes is acyclic, unless you count reflexive edges as a cycle (which requires
|
|
// only a little extra testing.)
|
|
//
|
|
func TarjanSCC(g graph.Directed) [][]graph.Node {
|
|
return tarjanSCCstabilized(g, nil)
|
|
}
|
|
|
|
func tarjanSCCstabilized(g graph.Directed, order func([]graph.Node)) [][]graph.Node {
|
|
nodes := graph.NodesOf(g.Nodes())
|
|
var succ func(id int64) []graph.Node
|
|
if order == nil {
|
|
succ = func(id int64) []graph.Node {
|
|
return graph.NodesOf(g.From(id))
|
|
}
|
|
} else {
|
|
order(nodes)
|
|
ordered.Reverse(nodes)
|
|
|
|
succ = func(id int64) []graph.Node {
|
|
to := graph.NodesOf(g.From(id))
|
|
order(to)
|
|
ordered.Reverse(to)
|
|
return to
|
|
}
|
|
}
|
|
|
|
t := tarjan{
|
|
succ: succ,
|
|
|
|
indexTable: make(map[int64]int, len(nodes)),
|
|
lowLink: make(map[int64]int, len(nodes)),
|
|
onStack: make(set.Int64s),
|
|
}
|
|
for _, v := range nodes {
|
|
if t.indexTable[v.ID()] == 0 {
|
|
t.strongconnect(v)
|
|
}
|
|
}
|
|
return t.sccs
|
|
}
|
|
|
|
// tarjan implements Tarjan's strongly connected component finding
|
|
// algorithm. The implementation is from the pseudocode at
|
|
//
|
|
// http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm?oldid=642744644
|
|
//
|
|
type tarjan struct {
|
|
succ func(id int64) []graph.Node
|
|
|
|
index int
|
|
indexTable map[int64]int
|
|
lowLink map[int64]int
|
|
onStack set.Int64s
|
|
|
|
stack []graph.Node
|
|
|
|
sccs [][]graph.Node
|
|
}
|
|
|
|
// strongconnect is the strongconnect function described in the
|
|
// wikipedia article.
|
|
func (t *tarjan) strongconnect(v graph.Node) {
|
|
vID := v.ID()
|
|
|
|
// Set the depth index for v to the smallest unused index.
|
|
t.index++
|
|
t.indexTable[vID] = t.index
|
|
t.lowLink[vID] = t.index
|
|
t.stack = append(t.stack, v)
|
|
t.onStack.Add(vID)
|
|
|
|
// Consider successors of v.
|
|
for _, w := range t.succ(vID) {
|
|
wID := w.ID()
|
|
if t.indexTable[wID] == 0 {
|
|
// Successor w has not yet been visited; recur on it.
|
|
t.strongconnect(w)
|
|
t.lowLink[vID] = min(t.lowLink[vID], t.lowLink[wID])
|
|
} else if t.onStack.Has(wID) {
|
|
// Successor w is in stack s and hence in the current SCC.
|
|
t.lowLink[vID] = min(t.lowLink[vID], t.indexTable[wID])
|
|
}
|
|
}
|
|
|
|
// If v is a root node, pop the stack and generate an SCC.
|
|
if t.lowLink[vID] == t.indexTable[vID] {
|
|
// Start a new strongly connected component.
|
|
var (
|
|
scc []graph.Node
|
|
w graph.Node
|
|
)
|
|
for {
|
|
w, t.stack = t.stack[len(t.stack)-1], t.stack[:len(t.stack)-1]
|
|
t.onStack.Remove(w.ID())
|
|
// Add w to current strongly connected component.
|
|
scc = append(scc, w)
|
|
if w.ID() == vID {
|
|
break
|
|
}
|
|
}
|
|
// Output the current strongly connected component.
|
|
t.sccs = append(t.sccs, scc)
|
|
}
|
|
}
|
|
|
|
func min(a, b int) int {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|