mirror of
https://github.com/gonum/gonum.git
synced 2025-10-05 15:16:59 +08:00

Tests run repeatedly do not reinitialise state, meaning that a second run of the tests will have leftover state from the previous run. This ensures that repeated runs are identical with the exception of map iteration order.
232 lines
6.8 KiB
Go
232 lines
6.8 KiB
Go
// Copyright ©2019 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 layout_test
|
|
|
|
import (
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"golang.org/x/exp/rand"
|
|
|
|
"gonum.org/v1/gonum/graph"
|
|
"gonum.org/v1/gonum/graph/simple"
|
|
"gonum.org/v1/gonum/spatial/r2"
|
|
"gonum.org/v1/plot"
|
|
"gonum.org/v1/plot/vg"
|
|
|
|
. "gonum.org/v1/gonum/graph/layout"
|
|
)
|
|
|
|
func TestEadesR2(t *testing.T) {
|
|
eadesR2Tests := []struct {
|
|
name string
|
|
g graph.Graph
|
|
param EadesR2
|
|
wantIters int
|
|
}{
|
|
{
|
|
name: "line",
|
|
g: func() graph.Graph {
|
|
edges := []simple.Edge{
|
|
{F: simple.Node(0), T: simple.Node(1)},
|
|
}
|
|
g := simple.NewUndirectedGraph()
|
|
for _, e := range edges {
|
|
g.SetEdge(e)
|
|
}
|
|
return orderedGraph{g}
|
|
}(),
|
|
param: EadesR2{Repulsion: 1, Rate: 0.1, Updates: 100, Theta: 0.1, Src: rand.NewSource(1)},
|
|
wantIters: 100,
|
|
},
|
|
{
|
|
name: "square",
|
|
g: func() graph.Graph {
|
|
edges := []simple.Edge{
|
|
{F: simple.Node(0), T: simple.Node(1)},
|
|
{F: simple.Node(0), T: simple.Node(2)},
|
|
{F: simple.Node(1), T: simple.Node(3)},
|
|
{F: simple.Node(2), T: simple.Node(3)},
|
|
}
|
|
g := simple.NewUndirectedGraph()
|
|
for _, e := range edges {
|
|
g.SetEdge(e)
|
|
}
|
|
return orderedGraph{g}
|
|
}(),
|
|
param: EadesR2{Repulsion: 1, Rate: 0.1, Updates: 100, Theta: 0.1, Src: rand.NewSource(1)},
|
|
wantIters: 100,
|
|
},
|
|
{
|
|
name: "tetrahedron",
|
|
g: func() graph.Graph {
|
|
edges := []simple.Edge{
|
|
{F: simple.Node(0), T: simple.Node(1)},
|
|
{F: simple.Node(0), T: simple.Node(2)},
|
|
{F: simple.Node(0), T: simple.Node(3)},
|
|
{F: simple.Node(1), T: simple.Node(2)},
|
|
{F: simple.Node(1), T: simple.Node(3)},
|
|
{F: simple.Node(2), T: simple.Node(3)},
|
|
}
|
|
g := simple.NewUndirectedGraph()
|
|
for _, e := range edges {
|
|
g.SetEdge(e)
|
|
}
|
|
return orderedGraph{g}
|
|
}(),
|
|
param: EadesR2{Repulsion: 1, Rate: 0.1, Updates: 100, Theta: 0.1, Src: rand.NewSource(1)},
|
|
wantIters: 100,
|
|
},
|
|
{
|
|
name: "sheet",
|
|
g: func() graph.Graph {
|
|
edges := []simple.Edge{
|
|
{F: simple.Node(0), T: simple.Node(1)},
|
|
{F: simple.Node(0), T: simple.Node(3)},
|
|
{F: simple.Node(1), T: simple.Node(2)},
|
|
{F: simple.Node(1), T: simple.Node(4)},
|
|
{F: simple.Node(2), T: simple.Node(5)},
|
|
{F: simple.Node(3), T: simple.Node(4)},
|
|
{F: simple.Node(3), T: simple.Node(6)},
|
|
{F: simple.Node(4), T: simple.Node(5)},
|
|
{F: simple.Node(4), T: simple.Node(7)},
|
|
{F: simple.Node(5), T: simple.Node(8)},
|
|
{F: simple.Node(6), T: simple.Node(7)},
|
|
{F: simple.Node(7), T: simple.Node(8)},
|
|
}
|
|
g := simple.NewUndirectedGraph()
|
|
for _, e := range edges {
|
|
g.SetEdge(e)
|
|
}
|
|
return orderedGraph{g}
|
|
}(),
|
|
param: EadesR2{Repulsion: 1, Rate: 0.1, Updates: 100, Theta: 0.1, Src: rand.NewSource(1)},
|
|
wantIters: 100,
|
|
},
|
|
{
|
|
name: "tube",
|
|
g: func() graph.Graph {
|
|
edges := []simple.Edge{
|
|
{F: simple.Node(0), T: simple.Node(1)},
|
|
{F: simple.Node(0), T: simple.Node(2)},
|
|
{F: simple.Node(0), T: simple.Node(3)},
|
|
{F: simple.Node(1), T: simple.Node(2)},
|
|
{F: simple.Node(1), T: simple.Node(4)},
|
|
{F: simple.Node(2), T: simple.Node(5)},
|
|
{F: simple.Node(3), T: simple.Node(4)},
|
|
{F: simple.Node(3), T: simple.Node(5)},
|
|
{F: simple.Node(3), T: simple.Node(6)},
|
|
{F: simple.Node(4), T: simple.Node(5)},
|
|
{F: simple.Node(4), T: simple.Node(7)},
|
|
{F: simple.Node(5), T: simple.Node(8)},
|
|
{F: simple.Node(6), T: simple.Node(7)},
|
|
{F: simple.Node(6), T: simple.Node(8)},
|
|
{F: simple.Node(7), T: simple.Node(8)},
|
|
}
|
|
g := simple.NewUndirectedGraph()
|
|
for _, e := range edges {
|
|
g.SetEdge(e)
|
|
}
|
|
return orderedGraph{g}
|
|
}(),
|
|
param: EadesR2{Repulsion: 1, Rate: 0.1, Updates: 100, Theta: 0.1, Src: rand.NewSource(1)},
|
|
wantIters: 100,
|
|
},
|
|
{
|
|
// This test does not produce a good layout, but is here to
|
|
// ensure that Update does not panic with steep decent rates.
|
|
name: "tube-steep",
|
|
g: func() graph.Graph {
|
|
edges := []simple.Edge{
|
|
{F: simple.Node(0), T: simple.Node(1)},
|
|
{F: simple.Node(0), T: simple.Node(2)},
|
|
{F: simple.Node(0), T: simple.Node(3)},
|
|
{F: simple.Node(1), T: simple.Node(2)},
|
|
{F: simple.Node(1), T: simple.Node(4)},
|
|
{F: simple.Node(2), T: simple.Node(5)},
|
|
{F: simple.Node(3), T: simple.Node(4)},
|
|
{F: simple.Node(3), T: simple.Node(5)},
|
|
{F: simple.Node(3), T: simple.Node(6)},
|
|
{F: simple.Node(4), T: simple.Node(5)},
|
|
{F: simple.Node(4), T: simple.Node(7)},
|
|
{F: simple.Node(5), T: simple.Node(8)},
|
|
{F: simple.Node(6), T: simple.Node(7)},
|
|
{F: simple.Node(6), T: simple.Node(8)},
|
|
{F: simple.Node(7), T: simple.Node(8)},
|
|
}
|
|
g := simple.NewUndirectedGraph()
|
|
for _, e := range edges {
|
|
g.SetEdge(e)
|
|
}
|
|
return orderedGraph{g}
|
|
}(),
|
|
param: EadesR2{Repulsion: 1, Rate: 1, Updates: 100, Theta: 0.1, Src: rand.NewSource(1)},
|
|
wantIters: 99,
|
|
},
|
|
|
|
{
|
|
name: "wp_page", // https://en.wikipedia.org/wiki/PageRank#/media/File:PageRanks-Example.jpg
|
|
g: func() graph.Graph {
|
|
edges := []simple.Edge{
|
|
{F: simple.Node(0), T: simple.Node(3)},
|
|
{F: simple.Node(1), T: simple.Node(2)},
|
|
{F: simple.Node(1), T: simple.Node(3)},
|
|
{F: simple.Node(1), T: simple.Node(4)},
|
|
{F: simple.Node(1), T: simple.Node(5)},
|
|
{F: simple.Node(1), T: simple.Node(6)},
|
|
{F: simple.Node(1), T: simple.Node(7)},
|
|
{F: simple.Node(1), T: simple.Node(8)},
|
|
{F: simple.Node(3), T: simple.Node(4)},
|
|
{F: simple.Node(4), T: simple.Node(5)},
|
|
{F: simple.Node(4), T: simple.Node(6)},
|
|
{F: simple.Node(4), T: simple.Node(7)},
|
|
{F: simple.Node(4), T: simple.Node(8)},
|
|
{F: simple.Node(4), T: simple.Node(9)},
|
|
{F: simple.Node(4), T: simple.Node(10)},
|
|
}
|
|
g := simple.NewUndirectedGraph()
|
|
for _, e := range edges {
|
|
g.SetEdge(e)
|
|
}
|
|
return orderedGraph{g}
|
|
}(),
|
|
param: EadesR2{Repulsion: 1, Rate: 0.1, Updates: 100, Theta: 0.1, Src: rand.NewSource(1)},
|
|
wantIters: 100,
|
|
},
|
|
}
|
|
|
|
for _, test := range eadesR2Tests {
|
|
eades := test.param
|
|
o := NewOptimizerR2(test.g, eades.Update)
|
|
var n int
|
|
for o.Update() {
|
|
n++
|
|
}
|
|
if n != test.wantIters {
|
|
t.Errorf("unexpected number of iterations for %q: got:%d want:%d", test.name, n, test.wantIters)
|
|
}
|
|
|
|
p := plot.New()
|
|
p.Add(render{o})
|
|
p.HideAxes()
|
|
path := filepath.Join("testdata", test.name+".png")
|
|
err := p.Save(10*vg.Centimeter, 10*vg.Centimeter, path)
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
continue
|
|
}
|
|
ok := checkRenderedLayout(t, path)
|
|
if !ok {
|
|
got := make(map[int64]r2.Vec)
|
|
nodes := test.g.Nodes()
|
|
for nodes.Next() {
|
|
id := nodes.Node().ID()
|
|
got[id] = o.Coord2(id)
|
|
}
|
|
t.Logf("got node positions: %#v", got)
|
|
}
|
|
}
|
|
}
|