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 }