gonum.org/v1/gonum@v0.14.0/graph/layout/eades_test.go (about)

     1  // Copyright ©2019 The Gonum Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package layout_test
     6  
     7  import (
     8  	"path/filepath"
     9  	"testing"
    10  
    11  	"golang.org/x/exp/rand"
    12  
    13  	"gonum.org/v1/gonum/graph"
    14  	"gonum.org/v1/gonum/graph/simple"
    15  	"gonum.org/v1/gonum/spatial/r2"
    16  	"gonum.org/v1/plot"
    17  	"gonum.org/v1/plot/vg"
    18  
    19  	. "gonum.org/v1/gonum/graph/layout"
    20  )
    21  
    22  func TestEadesR2(t *testing.T) {
    23  	eadesR2Tests := []struct {
    24  		name      string
    25  		g         graph.Graph
    26  		param     EadesR2
    27  		wantIters int
    28  	}{
    29  		{
    30  			name: "line",
    31  			g: func() graph.Graph {
    32  				edges := []simple.Edge{
    33  					{F: simple.Node(0), T: simple.Node(1)},
    34  				}
    35  				g := simple.NewUndirectedGraph()
    36  				for _, e := range edges {
    37  					g.SetEdge(e)
    38  				}
    39  				return orderedGraph{g}
    40  			}(),
    41  			param:     EadesR2{Repulsion: 1, Rate: 0.1, Updates: 100, Theta: 0.1, Src: rand.NewSource(1)},
    42  			wantIters: 100,
    43  		},
    44  		{
    45  			name: "square",
    46  			g: func() graph.Graph {
    47  				edges := []simple.Edge{
    48  					{F: simple.Node(0), T: simple.Node(1)},
    49  					{F: simple.Node(0), T: simple.Node(2)},
    50  					{F: simple.Node(1), T: simple.Node(3)},
    51  					{F: simple.Node(2), T: simple.Node(3)},
    52  				}
    53  				g := simple.NewUndirectedGraph()
    54  				for _, e := range edges {
    55  					g.SetEdge(e)
    56  				}
    57  				return orderedGraph{g}
    58  			}(),
    59  			param:     EadesR2{Repulsion: 1, Rate: 0.1, Updates: 100, Theta: 0.1, Src: rand.NewSource(1)},
    60  			wantIters: 100,
    61  		},
    62  		{
    63  			name: "tetrahedron",
    64  			g: func() graph.Graph {
    65  				edges := []simple.Edge{
    66  					{F: simple.Node(0), T: simple.Node(1)},
    67  					{F: simple.Node(0), T: simple.Node(2)},
    68  					{F: simple.Node(0), T: simple.Node(3)},
    69  					{F: simple.Node(1), T: simple.Node(2)},
    70  					{F: simple.Node(1), T: simple.Node(3)},
    71  					{F: simple.Node(2), T: simple.Node(3)},
    72  				}
    73  				g := simple.NewUndirectedGraph()
    74  				for _, e := range edges {
    75  					g.SetEdge(e)
    76  				}
    77  				return orderedGraph{g}
    78  			}(),
    79  			param:     EadesR2{Repulsion: 1, Rate: 0.1, Updates: 100, Theta: 0.1, Src: rand.NewSource(1)},
    80  			wantIters: 100,
    81  		},
    82  		{
    83  			name: "sheet",
    84  			g: func() graph.Graph {
    85  				edges := []simple.Edge{
    86  					{F: simple.Node(0), T: simple.Node(1)},
    87  					{F: simple.Node(0), T: simple.Node(3)},
    88  					{F: simple.Node(1), T: simple.Node(2)},
    89  					{F: simple.Node(1), T: simple.Node(4)},
    90  					{F: simple.Node(2), T: simple.Node(5)},
    91  					{F: simple.Node(3), T: simple.Node(4)},
    92  					{F: simple.Node(3), T: simple.Node(6)},
    93  					{F: simple.Node(4), T: simple.Node(5)},
    94  					{F: simple.Node(4), T: simple.Node(7)},
    95  					{F: simple.Node(5), T: simple.Node(8)},
    96  					{F: simple.Node(6), T: simple.Node(7)},
    97  					{F: simple.Node(7), T: simple.Node(8)},
    98  				}
    99  				g := simple.NewUndirectedGraph()
   100  				for _, e := range edges {
   101  					g.SetEdge(e)
   102  				}
   103  				return orderedGraph{g}
   104  			}(),
   105  			param:     EadesR2{Repulsion: 1, Rate: 0.1, Updates: 100, Theta: 0.1, Src: rand.NewSource(1)},
   106  			wantIters: 100,
   107  		},
   108  		{
   109  			name: "tube",
   110  			g: func() graph.Graph {
   111  				edges := []simple.Edge{
   112  					{F: simple.Node(0), T: simple.Node(1)},
   113  					{F: simple.Node(0), T: simple.Node(2)},
   114  					{F: simple.Node(0), T: simple.Node(3)},
   115  					{F: simple.Node(1), T: simple.Node(2)},
   116  					{F: simple.Node(1), T: simple.Node(4)},
   117  					{F: simple.Node(2), T: simple.Node(5)},
   118  					{F: simple.Node(3), T: simple.Node(4)},
   119  					{F: simple.Node(3), T: simple.Node(5)},
   120  					{F: simple.Node(3), T: simple.Node(6)},
   121  					{F: simple.Node(4), T: simple.Node(5)},
   122  					{F: simple.Node(4), T: simple.Node(7)},
   123  					{F: simple.Node(5), T: simple.Node(8)},
   124  					{F: simple.Node(6), T: simple.Node(7)},
   125  					{F: simple.Node(6), T: simple.Node(8)},
   126  					{F: simple.Node(7), T: simple.Node(8)},
   127  				}
   128  				g := simple.NewUndirectedGraph()
   129  				for _, e := range edges {
   130  					g.SetEdge(e)
   131  				}
   132  				return orderedGraph{g}
   133  			}(),
   134  			param:     EadesR2{Repulsion: 1, Rate: 0.1, Updates: 100, Theta: 0.1, Src: rand.NewSource(1)},
   135  			wantIters: 100,
   136  		},
   137  		{
   138  			// This test does not produce a good layout, but is here to
   139  			// ensure that Update does not panic with steep decent rates.
   140  			name: "tube-steep",
   141  			g: func() graph.Graph {
   142  				edges := []simple.Edge{
   143  					{F: simple.Node(0), T: simple.Node(1)},
   144  					{F: simple.Node(0), T: simple.Node(2)},
   145  					{F: simple.Node(0), T: simple.Node(3)},
   146  					{F: simple.Node(1), T: simple.Node(2)},
   147  					{F: simple.Node(1), T: simple.Node(4)},
   148  					{F: simple.Node(2), T: simple.Node(5)},
   149  					{F: simple.Node(3), T: simple.Node(4)},
   150  					{F: simple.Node(3), T: simple.Node(5)},
   151  					{F: simple.Node(3), T: simple.Node(6)},
   152  					{F: simple.Node(4), T: simple.Node(5)},
   153  					{F: simple.Node(4), T: simple.Node(7)},
   154  					{F: simple.Node(5), T: simple.Node(8)},
   155  					{F: simple.Node(6), T: simple.Node(7)},
   156  					{F: simple.Node(6), T: simple.Node(8)},
   157  					{F: simple.Node(7), T: simple.Node(8)},
   158  				}
   159  				g := simple.NewUndirectedGraph()
   160  				for _, e := range edges {
   161  					g.SetEdge(e)
   162  				}
   163  				return orderedGraph{g}
   164  			}(),
   165  			param:     EadesR2{Repulsion: 1, Rate: 1, Updates: 100, Theta: 0.1, Src: rand.NewSource(1)},
   166  			wantIters: 99,
   167  		},
   168  
   169  		{
   170  			name: "wp_page", // https://en.wikipedia.org/wiki/PageRank#/media/File:PageRanks-Example.jpg
   171  			g: func() graph.Graph {
   172  				edges := []simple.Edge{
   173  					{F: simple.Node(0), T: simple.Node(3)},
   174  					{F: simple.Node(1), T: simple.Node(2)},
   175  					{F: simple.Node(1), T: simple.Node(3)},
   176  					{F: simple.Node(1), T: simple.Node(4)},
   177  					{F: simple.Node(1), T: simple.Node(5)},
   178  					{F: simple.Node(1), T: simple.Node(6)},
   179  					{F: simple.Node(1), T: simple.Node(7)},
   180  					{F: simple.Node(1), T: simple.Node(8)},
   181  					{F: simple.Node(3), T: simple.Node(4)},
   182  					{F: simple.Node(4), T: simple.Node(5)},
   183  					{F: simple.Node(4), T: simple.Node(6)},
   184  					{F: simple.Node(4), T: simple.Node(7)},
   185  					{F: simple.Node(4), T: simple.Node(8)},
   186  					{F: simple.Node(4), T: simple.Node(9)},
   187  					{F: simple.Node(4), T: simple.Node(10)},
   188  				}
   189  				g := simple.NewUndirectedGraph()
   190  				for _, e := range edges {
   191  					g.SetEdge(e)
   192  				}
   193  				return orderedGraph{g}
   194  			}(),
   195  			param:     EadesR2{Repulsion: 1, Rate: 0.1, Updates: 100, Theta: 0.1, Src: rand.NewSource(1)},
   196  			wantIters: 100,
   197  		},
   198  	}
   199  
   200  	for _, test := range eadesR2Tests {
   201  		eades := test.param
   202  		o := NewOptimizerR2(test.g, eades.Update)
   203  		var n int
   204  		for o.Update() {
   205  			n++
   206  		}
   207  		if n != test.wantIters {
   208  			t.Errorf("unexpected number of iterations for %q: got:%d want:%d", test.name, n, test.wantIters)
   209  		}
   210  
   211  		p := plot.New()
   212  		p.Add(render{o})
   213  		p.HideAxes()
   214  		path := filepath.Join("testdata", test.name+".png")
   215  		err := p.Save(10*vg.Centimeter, 10*vg.Centimeter, path)
   216  		if err != nil {
   217  			t.Errorf("unexpected error: %v", err)
   218  			continue
   219  		}
   220  		ok := checkRenderedLayout(t, path)
   221  		if !ok {
   222  			got := make(map[int64]r2.Vec)
   223  			nodes := test.g.Nodes()
   224  			for nodes.Next() {
   225  				id := nodes.Node().ID()
   226  				got[id] = o.Coord2(id)
   227  			}
   228  			t.Logf("got node positions: %#v", got)
   229  		}
   230  	}
   231  }