forked from LaconicNetwork/kompose
Upgrade OpenShift and its dependencies.
OpenShift version 1.4.0-alpha.0
This commit is contained in:
+225
@@ -0,0 +1,225 @@
|
||||
// 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 (
|
||||
"github.com/gonum/graph"
|
||||
"github.com/gonum/graph/internal"
|
||||
)
|
||||
|
||||
// VertexOrdering returns the vertex ordering and the k-cores of
|
||||
// the undirected graph g.
|
||||
func VertexOrdering(g graph.Undirected) (order []graph.Node, cores [][]graph.Node) {
|
||||
nodes := g.Nodes()
|
||||
|
||||
// The algorithm used here is essentially as described at
|
||||
// http://en.wikipedia.org/w/index.php?title=Degeneracy_%28graph_theory%29&oldid=640308710
|
||||
|
||||
// Initialize an output list L.
|
||||
var l []graph.Node
|
||||
|
||||
// Compute a number d_v for each vertex v in G,
|
||||
// the number of neighbors of v that are not already in L.
|
||||
// Initially, these numbers are just the degrees of the vertices.
|
||||
dv := make(map[int]int, len(nodes))
|
||||
var (
|
||||
maxDegree int
|
||||
neighbours = make(map[int][]graph.Node)
|
||||
)
|
||||
for _, n := range nodes {
|
||||
adj := g.From(n)
|
||||
neighbours[n.ID()] = adj
|
||||
dv[n.ID()] = len(adj)
|
||||
if len(adj) > maxDegree {
|
||||
maxDegree = len(adj)
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize an array D such that D[i] contains a list of the
|
||||
// vertices v that are not already in L for which d_v = i.
|
||||
d := make([][]graph.Node, maxDegree+1)
|
||||
for _, n := range nodes {
|
||||
deg := dv[n.ID()]
|
||||
d[deg] = append(d[deg], n)
|
||||
}
|
||||
|
||||
// Initialize k to 0.
|
||||
k := 0
|
||||
// Repeat n times:
|
||||
s := []int{0}
|
||||
for _ = range nodes { // TODO(kortschak): Remove blank assignment when go1.3.3 is no longer supported.
|
||||
// Scan the array cells D[0], D[1], ... until
|
||||
// finding an i for which D[i] is nonempty.
|
||||
var (
|
||||
i int
|
||||
di []graph.Node
|
||||
)
|
||||
for i, di = range d {
|
||||
if len(di) != 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Set k to max(k,i).
|
||||
if i > k {
|
||||
k = i
|
||||
s = append(s, make([]int, k-len(s)+1)...)
|
||||
}
|
||||
|
||||
// Select a vertex v from D[i]. Add v to the
|
||||
// beginning of L and remove it from D[i].
|
||||
var v graph.Node
|
||||
v, d[i] = di[len(di)-1], di[:len(di)-1]
|
||||
l = append(l, v)
|
||||
s[k]++
|
||||
delete(dv, v.ID())
|
||||
|
||||
// For each neighbor w of v not already in L,
|
||||
// subtract one from d_w and move w to the
|
||||
// cell of D corresponding to the new value of d_w.
|
||||
for _, w := range neighbours[v.ID()] {
|
||||
dw, ok := dv[w.ID()]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
for i, n := range d[dw] {
|
||||
if n.ID() == w.ID() {
|
||||
d[dw][i], d[dw] = d[dw][len(d[dw])-1], d[dw][:len(d[dw])-1]
|
||||
dw--
|
||||
d[dw] = append(d[dw], w)
|
||||
break
|
||||
}
|
||||
}
|
||||
dv[w.ID()] = dw
|
||||
}
|
||||
}
|
||||
|
||||
for i, j := 0, len(l)-1; i < j; i, j = i+1, j-1 {
|
||||
l[i], l[j] = l[j], l[i]
|
||||
}
|
||||
cores = make([][]graph.Node, len(s))
|
||||
offset := len(l)
|
||||
for i, n := range s {
|
||||
cores[i] = l[offset-n : offset]
|
||||
offset -= n
|
||||
}
|
||||
return l, cores
|
||||
}
|
||||
|
||||
// BronKerbosch returns the set of maximal cliques of the undirected graph g.
|
||||
func BronKerbosch(g graph.Undirected) [][]graph.Node {
|
||||
nodes := g.Nodes()
|
||||
|
||||
// The algorithm used here is essentially BronKerbosch3 as described at
|
||||
// http://en.wikipedia.org/w/index.php?title=Bron%E2%80%93Kerbosch_algorithm&oldid=656805858
|
||||
|
||||
p := make(internal.Set, len(nodes))
|
||||
for _, n := range nodes {
|
||||
p.Add(n)
|
||||
}
|
||||
x := make(internal.Set)
|
||||
var bk bronKerbosch
|
||||
order, _ := VertexOrdering(g)
|
||||
for _, v := range order {
|
||||
neighbours := g.From(v)
|
||||
nv := make(internal.Set, len(neighbours))
|
||||
for _, n := range neighbours {
|
||||
nv.Add(n)
|
||||
}
|
||||
bk.maximalCliquePivot(g, []graph.Node{v}, make(internal.Set).Intersect(p, nv), make(internal.Set).Intersect(x, nv))
|
||||
p.Remove(v)
|
||||
x.Add(v)
|
||||
}
|
||||
return bk
|
||||
}
|
||||
|
||||
type bronKerbosch [][]graph.Node
|
||||
|
||||
func (bk *bronKerbosch) maximalCliquePivot(g graph.Undirected, r []graph.Node, p, x internal.Set) {
|
||||
if len(p) == 0 && len(x) == 0 {
|
||||
*bk = append(*bk, r)
|
||||
return
|
||||
}
|
||||
|
||||
neighbours := bk.choosePivotFrom(g, p, x)
|
||||
nu := make(internal.Set, len(neighbours))
|
||||
for _, n := range neighbours {
|
||||
nu.Add(n)
|
||||
}
|
||||
for _, v := range p {
|
||||
if nu.Has(v) {
|
||||
continue
|
||||
}
|
||||
neighbours := g.From(v)
|
||||
nv := make(internal.Set, len(neighbours))
|
||||
for _, n := range neighbours {
|
||||
nv.Add(n)
|
||||
}
|
||||
|
||||
var found bool
|
||||
for _, n := range r {
|
||||
if n.ID() == v.ID() {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
var sr []graph.Node
|
||||
if !found {
|
||||
sr = append(r[:len(r):len(r)], v)
|
||||
}
|
||||
|
||||
bk.maximalCliquePivot(g, sr, make(internal.Set).Intersect(p, nv), make(internal.Set).Intersect(x, nv))
|
||||
p.Remove(v)
|
||||
x.Add(v)
|
||||
}
|
||||
}
|
||||
|
||||
func (*bronKerbosch) choosePivotFrom(g graph.Undirected, p, x internal.Set) (neighbors []graph.Node) {
|
||||
// TODO(kortschak): Investigate the impact of pivot choice that maximises
|
||||
// |p ⋂ neighbours(u)| as a function of input size. Until then, leave as
|
||||
// compile time option.
|
||||
if !tomitaTanakaTakahashi {
|
||||
for _, n := range p {
|
||||
return g.From(n)
|
||||
}
|
||||
for _, n := range x {
|
||||
return g.From(n)
|
||||
}
|
||||
panic("bronKerbosch: empty set")
|
||||
}
|
||||
|
||||
var (
|
||||
max = -1
|
||||
pivot graph.Node
|
||||
)
|
||||
maxNeighbors := func(s internal.Set) {
|
||||
outer:
|
||||
for _, u := range s {
|
||||
nb := g.From(u)
|
||||
c := len(nb)
|
||||
if c <= max {
|
||||
continue
|
||||
}
|
||||
for n := range nb {
|
||||
if _, ok := p[n]; ok {
|
||||
continue
|
||||
}
|
||||
c--
|
||||
if c <= max {
|
||||
continue outer
|
||||
}
|
||||
}
|
||||
max = c
|
||||
pivot = u
|
||||
neighbors = nb
|
||||
}
|
||||
}
|
||||
maxNeighbors(p)
|
||||
maxNeighbors(x)
|
||||
if pivot == nil {
|
||||
panic("bronKerbosch: empty set")
|
||||
}
|
||||
return neighbors
|
||||
}
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
// 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 (
|
||||
"sort"
|
||||
|
||||
"github.com/gonum/graph"
|
||||
"github.com/gonum/graph/internal"
|
||||
)
|
||||
|
||||
// johnson implements Johnson's "Finding all the elementary
|
||||
// circuits of a directed graph" algorithm. SIAM J. Comput. 4(1):1975.
|
||||
//
|
||||
// Comments in the johnson methods are kept in sync with the comments
|
||||
// and labels from the paper.
|
||||
type johnson struct {
|
||||
adjacent johnsonGraph // SCC adjacency list.
|
||||
b []internal.IntSet // Johnson's "B-list".
|
||||
blocked []bool
|
||||
s int
|
||||
|
||||
stack []graph.Node
|
||||
|
||||
result [][]graph.Node
|
||||
}
|
||||
|
||||
// CyclesIn returns the set of elementary cycles in the graph g.
|
||||
func CyclesIn(g graph.Directed) [][]graph.Node {
|
||||
jg := johnsonGraphFrom(g)
|
||||
j := johnson{
|
||||
adjacent: jg,
|
||||
b: make([]internal.IntSet, len(jg.orig)),
|
||||
blocked: make([]bool, len(jg.orig)),
|
||||
}
|
||||
|
||||
// len(j.nodes) is the order of g.
|
||||
for j.s < len(j.adjacent.orig)-1 {
|
||||
// We use the previous SCC adjacency to reduce the work needed.
|
||||
sccs := TarjanSCC(j.adjacent.subgraph(j.s))
|
||||
// A_k = adjacency structure of strong component K with least
|
||||
// vertex in subgraph of G induced by {s, s+1, ... ,n}.
|
||||
j.adjacent = j.adjacent.sccSubGraph(sccs, 2) // Only allow SCCs with >= 2 vertices.
|
||||
if j.adjacent.order() == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// s = least vertex in V_k
|
||||
if s := j.adjacent.leastVertexIndex(); s < j.s {
|
||||
j.s = s
|
||||
}
|
||||
for i, v := range j.adjacent.orig {
|
||||
if !j.adjacent.nodes.Has(v.ID()) {
|
||||
continue
|
||||
}
|
||||
if len(j.adjacent.succ[v.ID()]) > 0 {
|
||||
j.blocked[i] = false
|
||||
j.b[i] = make(internal.IntSet)
|
||||
}
|
||||
}
|
||||
//L3:
|
||||
_ = j.circuit(j.s)
|
||||
j.s++
|
||||
}
|
||||
|
||||
return j.result
|
||||
}
|
||||
|
||||
// circuit is the CIRCUIT sub-procedure in the paper.
|
||||
func (j *johnson) circuit(v int) bool {
|
||||
f := false
|
||||
n := j.adjacent.orig[v]
|
||||
j.stack = append(j.stack, n)
|
||||
j.blocked[v] = true
|
||||
|
||||
//L1:
|
||||
for w := range j.adjacent.succ[n.ID()] {
|
||||
w = j.adjacent.indexOf(w)
|
||||
if w == j.s {
|
||||
// Output circuit composed of stack followed by s.
|
||||
r := make([]graph.Node, len(j.stack)+1)
|
||||
copy(r, j.stack)
|
||||
r[len(r)-1] = j.adjacent.orig[j.s]
|
||||
j.result = append(j.result, r)
|
||||
f = true
|
||||
} else if !j.blocked[w] {
|
||||
if j.circuit(w) {
|
||||
f = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//L2:
|
||||
if f {
|
||||
j.unblock(v)
|
||||
} else {
|
||||
for w := range j.adjacent.succ[n.ID()] {
|
||||
j.b[j.adjacent.indexOf(w)].Add(v)
|
||||
}
|
||||
}
|
||||
j.stack = j.stack[:len(j.stack)-1]
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
// unblock is the UNBLOCK sub-procedure in the paper.
|
||||
func (j *johnson) unblock(u int) {
|
||||
j.blocked[u] = false
|
||||
for w := range j.b[u] {
|
||||
j.b[u].Remove(w)
|
||||
if j.blocked[w] {
|
||||
j.unblock(w)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// johnsonGraph is an edge list representation of a graph with helpers
|
||||
// necessary for Johnson's algorithm
|
||||
type johnsonGraph struct {
|
||||
// Keep the original graph nodes and a
|
||||
// look-up to into the non-sparse
|
||||
// collection of potentially sparse IDs.
|
||||
orig []graph.Node
|
||||
index map[int]int
|
||||
|
||||
nodes internal.IntSet
|
||||
succ map[int]internal.IntSet
|
||||
}
|
||||
|
||||
// johnsonGraphFrom returns a deep copy of the graph g.
|
||||
func johnsonGraphFrom(g graph.Directed) johnsonGraph {
|
||||
nodes := g.Nodes()
|
||||
sort.Sort(byID(nodes))
|
||||
c := johnsonGraph{
|
||||
orig: nodes,
|
||||
index: make(map[int]int, len(nodes)),
|
||||
|
||||
nodes: make(internal.IntSet, len(nodes)),
|
||||
succ: make(map[int]internal.IntSet),
|
||||
}
|
||||
for i, u := range nodes {
|
||||
c.index[u.ID()] = i
|
||||
for _, v := range g.From(u) {
|
||||
if c.succ[u.ID()] == nil {
|
||||
c.succ[u.ID()] = make(internal.IntSet)
|
||||
c.nodes.Add(u.ID())
|
||||
}
|
||||
c.nodes.Add(v.ID())
|
||||
c.succ[u.ID()].Add(v.ID())
|
||||
}
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
type byID []graph.Node
|
||||
|
||||
func (n byID) Len() int { return len(n) }
|
||||
func (n byID) Less(i, j int) bool { return n[i].ID() < n[j].ID() }
|
||||
func (n byID) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
|
||||
|
||||
// order returns the order of the graph.
|
||||
func (g johnsonGraph) order() int { return g.nodes.Count() }
|
||||
|
||||
// indexOf returns the index of the retained node for the given node ID.
|
||||
func (g johnsonGraph) indexOf(id int) int {
|
||||
return g.index[id]
|
||||
}
|
||||
|
||||
// leastVertexIndex returns the index into orig of the least vertex.
|
||||
func (g johnsonGraph) leastVertexIndex() int {
|
||||
for _, v := range g.orig {
|
||||
if g.nodes.Has(v.ID()) {
|
||||
return g.indexOf(v.ID())
|
||||
}
|
||||
}
|
||||
panic("johnsonCycles: empty set")
|
||||
}
|
||||
|
||||
// subgraph returns a subgraph of g induced by {s, s+1, ... , n}. The
|
||||
// subgraph is destructively generated in g.
|
||||
func (g johnsonGraph) subgraph(s int) johnsonGraph {
|
||||
sn := g.orig[s].ID()
|
||||
for u, e := range g.succ {
|
||||
if u < sn {
|
||||
g.nodes.Remove(u)
|
||||
delete(g.succ, u)
|
||||
continue
|
||||
}
|
||||
for v := range e {
|
||||
if v < sn {
|
||||
g.succ[u].Remove(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
return g
|
||||
}
|
||||
|
||||
// sccSubGraph returns the graph of the tarjan's strongly connected
|
||||
// components with each SCC containing at least min vertices.
|
||||
// sccSubGraph returns nil if there is no SCC with at least min
|
||||
// members.
|
||||
func (g johnsonGraph) sccSubGraph(sccs [][]graph.Node, min int) johnsonGraph {
|
||||
if len(g.nodes) == 0 {
|
||||
g.nodes = nil
|
||||
g.succ = nil
|
||||
return g
|
||||
}
|
||||
sub := johnsonGraph{
|
||||
orig: g.orig,
|
||||
index: g.index,
|
||||
nodes: make(internal.IntSet),
|
||||
succ: make(map[int]internal.IntSet),
|
||||
}
|
||||
|
||||
var n int
|
||||
for _, scc := range sccs {
|
||||
if len(scc) < min {
|
||||
continue
|
||||
}
|
||||
n++
|
||||
for _, u := range scc {
|
||||
for _, v := range scc {
|
||||
if _, ok := g.succ[u.ID()][v.ID()]; ok {
|
||||
if sub.succ[u.ID()] == nil {
|
||||
sub.succ[u.ID()] = make(internal.IntSet)
|
||||
sub.nodes.Add(u.ID())
|
||||
}
|
||||
sub.nodes.Add(v.ID())
|
||||
sub.succ[u.ID()].Add(v.ID())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if n == 0 {
|
||||
g.nodes = nil
|
||||
g.succ = nil
|
||||
return g
|
||||
}
|
||||
|
||||
return sub
|
||||
}
|
||||
|
||||
// Nodes is required to satisfy Tarjan.
|
||||
func (g johnsonGraph) Nodes() []graph.Node {
|
||||
n := make([]graph.Node, 0, len(g.nodes))
|
||||
for id := range g.nodes {
|
||||
n = append(n, johnsonGraphNode(id))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Successors is required to satisfy Tarjan.
|
||||
func (g johnsonGraph) From(n graph.Node) []graph.Node {
|
||||
adj := g.succ[n.ID()]
|
||||
if len(adj) == 0 {
|
||||
return nil
|
||||
}
|
||||
succ := make([]graph.Node, 0, len(adj))
|
||||
for n := range adj {
|
||||
succ = append(succ, johnsonGraphNode(n))
|
||||
}
|
||||
return succ
|
||||
}
|
||||
|
||||
func (johnsonGraph) Has(graph.Node) bool {
|
||||
panic("search: unintended use of johnsonGraph")
|
||||
}
|
||||
func (johnsonGraph) HasEdge(_, _ graph.Node) bool {
|
||||
panic("search: unintended use of johnsonGraph")
|
||||
}
|
||||
func (johnsonGraph) Edge(_, _ graph.Node) graph.Edge {
|
||||
panic("search: unintended use of johnsonGraph")
|
||||
}
|
||||
func (johnsonGraph) HasEdgeFromTo(_, _ graph.Node) bool {
|
||||
panic("search: unintended use of johnsonGraph")
|
||||
}
|
||||
func (johnsonGraph) To(graph.Node) []graph.Node {
|
||||
panic("search: unintended use of johnsonGraph")
|
||||
}
|
||||
|
||||
type johnsonGraphNode int
|
||||
|
||||
func (n johnsonGraphNode) ID() int { return int(n) }
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// 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.
|
||||
|
||||
//+build !tomita
|
||||
|
||||
package topo
|
||||
|
||||
const tomitaTanakaTakahashi = false
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
// 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
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// 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.
|
||||
|
||||
//+build tomita
|
||||
|
||||
package topo
|
||||
|
||||
const tomitaTanakaTakahashi = true
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
// Copyright ©2014 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 (
|
||||
"github.com/gonum/graph"
|
||||
"github.com/gonum/graph/traverse"
|
||||
)
|
||||
|
||||
// IsPathIn returns whether path is a path in g.
|
||||
//
|
||||
// As special cases, IsPathIn returns true for a zero length path or for
|
||||
// a path of length 1 when the node in path exists in the graph.
|
||||
func IsPathIn(g graph.Graph, path []graph.Node) bool {
|
||||
switch len(path) {
|
||||
case 0:
|
||||
return true
|
||||
case 1:
|
||||
return g.Has(path[0])
|
||||
default:
|
||||
var canReach func(u, v graph.Node) bool
|
||||
switch g := g.(type) {
|
||||
case graph.Directed:
|
||||
canReach = g.HasEdgeFromTo
|
||||
default:
|
||||
canReach = g.HasEdge
|
||||
}
|
||||
|
||||
for i, u := range path[:len(path)-1] {
|
||||
if !canReach(u, path[i+1]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// ConnectedComponents returns the connected components of the undirected graph g.
|
||||
func ConnectedComponents(g graph.Undirected) [][]graph.Node {
|
||||
var (
|
||||
w traverse.DepthFirst
|
||||
c []graph.Node
|
||||
cc [][]graph.Node
|
||||
)
|
||||
during := func(n graph.Node) {
|
||||
c = append(c, n)
|
||||
}
|
||||
after := func() {
|
||||
cc = append(cc, []graph.Node(nil))
|
||||
cc[len(cc)-1] = append(cc[len(cc)-1], c...)
|
||||
c = c[:0]
|
||||
}
|
||||
w.WalkAll(g, nil, after, during)
|
||||
|
||||
return cc
|
||||
}
|
||||
Reference in New Issue
Block a user