forked from LaconicNetwork/kompose
162 lines
4.3 KiB
Go
162 lines
4.3 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"
|
|
|
|
"github.com/gonum/graph"
|
|
"github.com/gonum/graph/internal"
|
|
)
|
|
|
|
// 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))
|
|
}
|
|
|
|
// 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)
|
|
sorted = make([]graph.Node, 0, len(sccs))
|
|
var sc Unorderable
|
|
for _, s := range sccs {
|
|
if len(s) != 1 {
|
|
sort.Sort(byID(s))
|
|
sc = append(sc, s)
|
|
sorted = append(sorted, nil)
|
|
continue
|
|
}
|
|
sorted = append(sorted, s[0])
|
|
}
|
|
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
|
|
}
|
|
reverse(sorted)
|
|
return sorted, err
|
|
}
|
|
|
|
func reverse(p []graph.Node) {
|
|
for i, j := 0, len(p)-1; i < j; i, j = i+1, j-1 {
|
|
p[i], p[j] = p[j], p[i]
|
|
}
|
|
}
|
|
|
|
// 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 {
|
|
nodes := g.Nodes()
|
|
t := tarjan{
|
|
succ: g.From,
|
|
|
|
indexTable: make(map[int]int, len(nodes)),
|
|
lowLink: make(map[int]int, len(nodes)),
|
|
onStack: make(internal.IntSet, len(nodes)),
|
|
}
|
|
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(graph.Node) []graph.Node
|
|
|
|
index int
|
|
indexTable map[int]int
|
|
lowLink map[int]int
|
|
onStack internal.IntSet
|
|
|
|
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(v) {
|
|
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
|
|
}
|