mirror of
https://github.com/gonum/gonum.git
synced 2025-10-20 13:55:20 +08:00
309 lines
6.4 KiB
Go
309 lines
6.4 KiB
Go
// 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 (
|
|
"reflect"
|
|
"sort"
|
|
"testing"
|
|
|
|
"gonum.org/v1/gonum/graph"
|
|
"gonum.org/v1/gonum/graph/internal/ordered"
|
|
"gonum.org/v1/gonum/graph/simple"
|
|
)
|
|
|
|
type interval struct{ start, end int }
|
|
|
|
var tarjanTests = []struct {
|
|
g []intset
|
|
|
|
ambiguousOrder []interval
|
|
want [][]int64
|
|
|
|
sortedLength int
|
|
unorderableLength int
|
|
sortable bool
|
|
}{
|
|
{
|
|
g: []intset{
|
|
0: linksTo(1),
|
|
1: linksTo(2, 7),
|
|
2: linksTo(3, 6),
|
|
3: linksTo(4),
|
|
4: linksTo(2, 5),
|
|
6: linksTo(3, 5),
|
|
7: linksTo(0, 6),
|
|
},
|
|
|
|
want: [][]int64{
|
|
{5},
|
|
{2, 3, 4, 6},
|
|
{0, 1, 7},
|
|
},
|
|
|
|
sortedLength: 1,
|
|
unorderableLength: 2,
|
|
sortable: false,
|
|
},
|
|
{
|
|
g: []intset{
|
|
0: linksTo(1, 2, 3),
|
|
1: linksTo(2),
|
|
2: linksTo(3),
|
|
3: linksTo(1),
|
|
},
|
|
|
|
want: [][]int64{
|
|
{1, 2, 3},
|
|
{0},
|
|
},
|
|
|
|
sortedLength: 1,
|
|
unorderableLength: 1,
|
|
sortable: false,
|
|
},
|
|
{
|
|
g: []intset{
|
|
0: linksTo(1),
|
|
1: linksTo(0, 2),
|
|
2: linksTo(1),
|
|
},
|
|
|
|
want: [][]int64{
|
|
{0, 1, 2},
|
|
},
|
|
|
|
sortedLength: 0,
|
|
unorderableLength: 1,
|
|
sortable: false,
|
|
},
|
|
{
|
|
g: []intset{
|
|
0: linksTo(1),
|
|
1: linksTo(2, 3),
|
|
2: linksTo(4, 5),
|
|
3: linksTo(4, 5),
|
|
4: linksTo(6),
|
|
5: nil,
|
|
6: nil,
|
|
},
|
|
|
|
// Node pairs (2, 3) and (4, 5) are not
|
|
// relatively orderable within each pair.
|
|
ambiguousOrder: []interval{
|
|
{0, 3}, // This includes node 6 since it only needs to be before 4 in topo sort.
|
|
{3, 5},
|
|
},
|
|
want: [][]int64{
|
|
{6}, {5}, {4}, {3}, {2}, {1}, {0},
|
|
},
|
|
|
|
sortedLength: 7,
|
|
sortable: true,
|
|
},
|
|
{
|
|
g: []intset{
|
|
0: linksTo(1),
|
|
1: linksTo(2, 3, 4),
|
|
2: linksTo(0, 3),
|
|
3: linksTo(4),
|
|
4: linksTo(3),
|
|
},
|
|
|
|
// SCCs are not relatively ordable.
|
|
ambiguousOrder: []interval{
|
|
{0, 2},
|
|
},
|
|
want: [][]int64{
|
|
{0, 1, 2},
|
|
{3, 4},
|
|
},
|
|
|
|
sortedLength: 0,
|
|
unorderableLength: 2,
|
|
sortable: false,
|
|
},
|
|
}
|
|
|
|
func TestSort(t *testing.T) {
|
|
for i, test := range tarjanTests {
|
|
g := simple.NewDirectedGraph()
|
|
for u, e := range test.g {
|
|
// Add nodes that are not defined by an edge.
|
|
if g.Node(int64(u)) == nil {
|
|
g.AddNode(simple.Node(u))
|
|
}
|
|
for v := range e {
|
|
g.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)})
|
|
}
|
|
}
|
|
sorted, err := Sort(g)
|
|
var gotSortedLen int
|
|
for _, n := range sorted {
|
|
if n != nil {
|
|
gotSortedLen++
|
|
}
|
|
}
|
|
if gotSortedLen != test.sortedLength {
|
|
t.Errorf("unexpected number of sortable nodes for test %d: got:%d want:%d", i, gotSortedLen, test.sortedLength)
|
|
}
|
|
if err == nil != test.sortable {
|
|
t.Errorf("unexpected sortability for test %d: got error: %v want: nil-error=%t", i, err, test.sortable)
|
|
}
|
|
if err != nil && len(err.(Unorderable)) != test.unorderableLength {
|
|
t.Errorf("unexpected number of unorderable nodes for test %d: got:%d want:%d", i, len(err.(Unorderable)), test.unorderableLength)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestTarjanSCC(t *testing.T) {
|
|
for i, test := range tarjanTests {
|
|
g := simple.NewDirectedGraph()
|
|
for u, e := range test.g {
|
|
// Add nodes that are not defined by an edge.
|
|
if g.Node(int64(u)) == nil {
|
|
g.AddNode(simple.Node(u))
|
|
}
|
|
for v := range e {
|
|
g.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)})
|
|
}
|
|
}
|
|
gotSCCs := TarjanSCC(g)
|
|
// tarjan.strongconnect does range iteration over maps,
|
|
// so sort SCC members to ensure consistent ordering.
|
|
gotIDs := make([][]int64, len(gotSCCs))
|
|
for i, scc := range gotSCCs {
|
|
gotIDs[i] = make([]int64, len(scc))
|
|
for j, id := range scc {
|
|
gotIDs[i][j] = id.ID()
|
|
}
|
|
sort.Sort(ordered.Int64s(gotIDs[i]))
|
|
}
|
|
for _, iv := range test.ambiguousOrder {
|
|
sort.Sort(ordered.BySliceValues(test.want[iv.start:iv.end]))
|
|
sort.Sort(ordered.BySliceValues(gotIDs[iv.start:iv.end]))
|
|
}
|
|
if !reflect.DeepEqual(gotIDs, test.want) {
|
|
t.Errorf("unexpected Tarjan scc result for %d:\n\tgot:%v\n\twant:%v", i, gotIDs, test.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
var stabilizedSortTests = []struct {
|
|
g []intset
|
|
|
|
want []graph.Node
|
|
err error
|
|
}{
|
|
{
|
|
g: []intset{
|
|
0: linksTo(1),
|
|
1: linksTo(2, 7),
|
|
2: linksTo(3, 6),
|
|
3: linksTo(4),
|
|
4: linksTo(2, 5),
|
|
6: linksTo(3, 5),
|
|
7: linksTo(0, 6),
|
|
},
|
|
|
|
want: []graph.Node{nil, nil, simple.Node(5)},
|
|
err: Unorderable{
|
|
{simple.Node(0), simple.Node(1), simple.Node(7)},
|
|
{simple.Node(2), simple.Node(3), simple.Node(4), simple.Node(6)},
|
|
},
|
|
},
|
|
{
|
|
g: []intset{
|
|
0: linksTo(1, 2, 3),
|
|
1: linksTo(2),
|
|
2: linksTo(3),
|
|
3: linksTo(1),
|
|
},
|
|
|
|
want: []graph.Node{simple.Node(0), nil},
|
|
err: Unorderable{
|
|
{simple.Node(1), simple.Node(2), simple.Node(3)},
|
|
},
|
|
},
|
|
{
|
|
g: []intset{
|
|
0: linksTo(1),
|
|
1: linksTo(0, 2),
|
|
2: linksTo(1),
|
|
},
|
|
|
|
want: []graph.Node{nil},
|
|
err: Unorderable{
|
|
{simple.Node(0), simple.Node(1), simple.Node(2)},
|
|
},
|
|
},
|
|
{
|
|
g: []intset{
|
|
0: linksTo(1),
|
|
1: linksTo(2, 3),
|
|
2: linksTo(4, 5),
|
|
3: linksTo(4, 5),
|
|
4: linksTo(6),
|
|
5: nil,
|
|
6: nil,
|
|
},
|
|
|
|
want: []graph.Node{simple.Node(0), simple.Node(1), simple.Node(2), simple.Node(3), simple.Node(4), simple.Node(5), simple.Node(6)},
|
|
err: nil,
|
|
},
|
|
{
|
|
g: []intset{
|
|
0: linksTo(1),
|
|
1: linksTo(2, 3, 4),
|
|
2: linksTo(0, 3),
|
|
3: linksTo(4),
|
|
4: linksTo(3),
|
|
},
|
|
|
|
want: []graph.Node{nil, nil},
|
|
err: Unorderable{
|
|
{simple.Node(0), simple.Node(1), simple.Node(2)},
|
|
{simple.Node(3), simple.Node(4)},
|
|
},
|
|
},
|
|
{
|
|
g: []intset{
|
|
0: linksTo(1, 2, 3, 4, 5, 6),
|
|
1: linksTo(7),
|
|
2: linksTo(7),
|
|
3: linksTo(7),
|
|
4: linksTo(7),
|
|
5: linksTo(7),
|
|
6: linksTo(7),
|
|
7: nil,
|
|
},
|
|
|
|
want: []graph.Node{simple.Node(0), simple.Node(1), simple.Node(2), simple.Node(3), simple.Node(4), simple.Node(5), simple.Node(6), simple.Node(7)},
|
|
err: nil,
|
|
},
|
|
}
|
|
|
|
func TestSortStabilized(t *testing.T) {
|
|
for i, test := range stabilizedSortTests {
|
|
g := simple.NewDirectedGraph()
|
|
for u, e := range test.g {
|
|
// Add nodes that are not defined by an edge.
|
|
if g.Node(int64(u)) == nil {
|
|
g.AddNode(simple.Node(u))
|
|
}
|
|
for v := range e {
|
|
g.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)})
|
|
}
|
|
}
|
|
got, err := SortStabilized(g, nil)
|
|
if !reflect.DeepEqual(got, test.want) {
|
|
t.Errorf("unexpected sort result for test %d: got:%d want:%d", i, got, test.want)
|
|
}
|
|
if !reflect.DeepEqual(err, test.err) {
|
|
t.Errorf("unexpected sort error for test %d: got:%v want:%v", i, err, test.want)
|
|
}
|
|
}
|
|
}
|