mirror of
https://github.com/gonum/gonum.git
synced 2025-10-05 15:16:59 +08:00
231 lines
6.8 KiB
Go
231 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 (
|
|
"math/rand/v2"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"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.NewPCG(1, 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.NewPCG(1, 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.NewPCG(1, 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.NewPCG(1, 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.NewPCG(1, 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.NewPCG(1, 1)},
|
|
wantIters: 96,
|
|
},
|
|
|
|
{
|
|
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.01, Updates: 100, Theta: 0.1, Src: rand.NewPCG(1, 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)
|
|
}
|
|
}
|
|
}
|