github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/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 "github.com/jingcheng-WU/gonum/graph" 14 "github.com/jingcheng-WU/gonum/graph/simple" 15 "github.com/jingcheng-WU/gonum/spatial/r2" 16 "gonum.org/v1/plot" 17 "gonum.org/v1/plot/vg" 18 19 . "github.com/jingcheng-WU/gonum/graph/layout" 20 ) 21 22 var eadesR2Tests = []struct { 23 name string 24 g graph.Graph 25 param EadesR2 26 wantIters int 27 }{ 28 { 29 name: "line", 30 g: func() graph.Graph { 31 edges := []simple.Edge{ 32 {F: simple.Node(0), T: simple.Node(1)}, 33 } 34 g := simple.NewUndirectedGraph() 35 for _, e := range edges { 36 g.SetEdge(e) 37 } 38 return orderedGraph{g} 39 }(), 40 param: EadesR2{Repulsion: 1, Rate: 0.1, Updates: 100, Theta: 0.1, Src: rand.NewSource(1)}, 41 wantIters: 100, 42 }, 43 { 44 name: "square", 45 g: func() graph.Graph { 46 edges := []simple.Edge{ 47 {F: simple.Node(0), T: simple.Node(1)}, 48 {F: simple.Node(0), T: simple.Node(2)}, 49 {F: simple.Node(1), T: simple.Node(3)}, 50 {F: simple.Node(2), T: simple.Node(3)}, 51 } 52 g := simple.NewUndirectedGraph() 53 for _, e := range edges { 54 g.SetEdge(e) 55 } 56 return orderedGraph{g} 57 }(), 58 param: EadesR2{Repulsion: 1, Rate: 0.1, Updates: 100, Theta: 0.1, Src: rand.NewSource(1)}, 59 wantIters: 100, 60 }, 61 { 62 name: "tetrahedron", 63 g: func() graph.Graph { 64 edges := []simple.Edge{ 65 {F: simple.Node(0), T: simple.Node(1)}, 66 {F: simple.Node(0), T: simple.Node(2)}, 67 {F: simple.Node(0), T: simple.Node(3)}, 68 {F: simple.Node(1), T: simple.Node(2)}, 69 {F: simple.Node(1), T: simple.Node(3)}, 70 {F: simple.Node(2), T: simple.Node(3)}, 71 } 72 g := simple.NewUndirectedGraph() 73 for _, e := range edges { 74 g.SetEdge(e) 75 } 76 return orderedGraph{g} 77 }(), 78 param: EadesR2{Repulsion: 1, Rate: 0.1, Updates: 100, Theta: 0.1, Src: rand.NewSource(1)}, 79 wantIters: 100, 80 }, 81 { 82 name: "sheet", 83 g: func() graph.Graph { 84 edges := []simple.Edge{ 85 {F: simple.Node(0), T: simple.Node(1)}, 86 {F: simple.Node(0), T: simple.Node(3)}, 87 {F: simple.Node(1), T: simple.Node(2)}, 88 {F: simple.Node(1), T: simple.Node(4)}, 89 {F: simple.Node(2), T: simple.Node(5)}, 90 {F: simple.Node(3), T: simple.Node(4)}, 91 {F: simple.Node(3), T: simple.Node(6)}, 92 {F: simple.Node(4), T: simple.Node(5)}, 93 {F: simple.Node(4), T: simple.Node(7)}, 94 {F: simple.Node(5), T: simple.Node(8)}, 95 {F: simple.Node(6), T: simple.Node(7)}, 96 {F: simple.Node(7), T: simple.Node(8)}, 97 } 98 g := simple.NewUndirectedGraph() 99 for _, e := range edges { 100 g.SetEdge(e) 101 } 102 return orderedGraph{g} 103 }(), 104 param: EadesR2{Repulsion: 1, Rate: 0.1, Updates: 100, Theta: 0.1, Src: rand.NewSource(1)}, 105 wantIters: 100, 106 }, 107 { 108 name: "tube", 109 g: func() graph.Graph { 110 edges := []simple.Edge{ 111 {F: simple.Node(0), T: simple.Node(1)}, 112 {F: simple.Node(0), T: simple.Node(2)}, 113 {F: simple.Node(0), T: simple.Node(3)}, 114 {F: simple.Node(1), T: simple.Node(2)}, 115 {F: simple.Node(1), T: simple.Node(4)}, 116 {F: simple.Node(2), T: simple.Node(5)}, 117 {F: simple.Node(3), T: simple.Node(4)}, 118 {F: simple.Node(3), T: simple.Node(5)}, 119 {F: simple.Node(3), T: simple.Node(6)}, 120 {F: simple.Node(4), T: simple.Node(5)}, 121 {F: simple.Node(4), T: simple.Node(7)}, 122 {F: simple.Node(5), T: simple.Node(8)}, 123 {F: simple.Node(6), T: simple.Node(7)}, 124 {F: simple.Node(6), T: simple.Node(8)}, 125 {F: simple.Node(7), T: simple.Node(8)}, 126 } 127 g := simple.NewUndirectedGraph() 128 for _, e := range edges { 129 g.SetEdge(e) 130 } 131 return orderedGraph{g} 132 }(), 133 param: EadesR2{Repulsion: 1, Rate: 0.1, Updates: 100, Theta: 0.1, Src: rand.NewSource(1)}, 134 wantIters: 100, 135 }, 136 { 137 // This test does not produce a good layout, but is here to 138 // ensure that Update does not panic with steep decent rates. 139 name: "tube-steep", 140 g: func() graph.Graph { 141 edges := []simple.Edge{ 142 {F: simple.Node(0), T: simple.Node(1)}, 143 {F: simple.Node(0), T: simple.Node(2)}, 144 {F: simple.Node(0), T: simple.Node(3)}, 145 {F: simple.Node(1), T: simple.Node(2)}, 146 {F: simple.Node(1), T: simple.Node(4)}, 147 {F: simple.Node(2), T: simple.Node(5)}, 148 {F: simple.Node(3), T: simple.Node(4)}, 149 {F: simple.Node(3), T: simple.Node(5)}, 150 {F: simple.Node(3), T: simple.Node(6)}, 151 {F: simple.Node(4), T: simple.Node(5)}, 152 {F: simple.Node(4), T: simple.Node(7)}, 153 {F: simple.Node(5), T: simple.Node(8)}, 154 {F: simple.Node(6), T: simple.Node(7)}, 155 {F: simple.Node(6), T: simple.Node(8)}, 156 {F: simple.Node(7), T: simple.Node(8)}, 157 } 158 g := simple.NewUndirectedGraph() 159 for _, e := range edges { 160 g.SetEdge(e) 161 } 162 return orderedGraph{g} 163 }(), 164 param: EadesR2{Repulsion: 1, Rate: 1, Updates: 100, Theta: 0.1, Src: rand.NewSource(1)}, 165 wantIters: 99, 166 }, 167 168 { 169 name: "wp_page", // https://en.wikipedia.org/wiki/PageRank#/media/File:PageRanks-Example.jpg 170 g: func() graph.Graph { 171 edges := []simple.Edge{ 172 {F: simple.Node(0), T: simple.Node(3)}, 173 {F: simple.Node(1), T: simple.Node(2)}, 174 {F: simple.Node(1), T: simple.Node(3)}, 175 {F: simple.Node(1), T: simple.Node(4)}, 176 {F: simple.Node(1), T: simple.Node(5)}, 177 {F: simple.Node(1), T: simple.Node(6)}, 178 {F: simple.Node(1), T: simple.Node(7)}, 179 {F: simple.Node(1), T: simple.Node(8)}, 180 {F: simple.Node(3), T: simple.Node(4)}, 181 {F: simple.Node(4), T: simple.Node(5)}, 182 {F: simple.Node(4), T: simple.Node(6)}, 183 {F: simple.Node(4), T: simple.Node(7)}, 184 {F: simple.Node(4), T: simple.Node(8)}, 185 {F: simple.Node(4), T: simple.Node(9)}, 186 {F: simple.Node(4), T: simple.Node(10)}, 187 } 188 g := simple.NewUndirectedGraph() 189 for _, e := range edges { 190 g.SetEdge(e) 191 } 192 return orderedGraph{g} 193 }(), 194 param: EadesR2{Repulsion: 1, Rate: 0.1, Updates: 100, Theta: 0.1, Src: rand.NewSource(1)}, 195 wantIters: 100, 196 }, 197 } 198 199 func TestEadesR2(t *testing.T) { 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 }