gonum.org/v1/gonum@v0.14.0/graph/traverse/traverse_test.go (about) 1 // Copyright ©2015 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 traverse 6 7 import ( 8 "fmt" 9 "reflect" 10 "strings" 11 "testing" 12 13 "gonum.org/v1/gonum/graph" 14 "gonum.org/v1/gonum/graph/graphs/gen" 15 "gonum.org/v1/gonum/graph/internal/ordered" 16 "gonum.org/v1/gonum/graph/simple" 17 ) 18 19 var ( 20 // batageljZaversnikGraph is the example graph from 21 // figure 1 of http://arxiv.org/abs/cs/0310049v1 22 batageljZaversnikGraph = []intset{ 23 0: nil, 24 25 1: linksTo(2, 3), 26 2: linksTo(4), 27 3: linksTo(4), 28 4: linksTo(5), 29 5: nil, 30 31 6: linksTo(7, 8, 14), 32 7: linksTo(8, 11, 12, 14), 33 8: linksTo(14), 34 9: linksTo(11), 35 10: linksTo(11), 36 11: linksTo(12), 37 12: linksTo(18), 38 13: linksTo(14, 15), 39 14: linksTo(15, 17), 40 15: linksTo(16, 17), 41 16: nil, 42 17: linksTo(18, 19, 20), 43 18: linksTo(19, 20), 44 19: linksTo(20), 45 20: nil, 46 } 47 48 // wpBronKerboschGraph is the example given in the Bron-Kerbosch article on wikipedia (renumbered). 49 // http://en.wikipedia.org/w/index.php?title=Bron%E2%80%93Kerbosch_algorithm&oldid=656805858 50 wpBronKerboschGraph = []intset{ 51 0: linksTo(1, 4), 52 1: linksTo(2, 4), 53 2: linksTo(3), 54 3: linksTo(4, 5), 55 4: nil, 56 5: nil, 57 } 58 59 // g1595 is the graph shown in https://github.com/gonum/gonum/issues/1595 with the addition 60 // of an unconnected 0 node (due to limitations of intset). 61 g1595 = []intset{ 62 0: nil, 63 1: linksTo(2, 4), 64 2: linksTo(3), 65 3: nil, 66 4: nil, 67 } 68 ) 69 70 var breadthFirstTests = []struct { 71 g []intset 72 from graph.Node 73 edge func(graph.Edge) bool 74 until func(graph.Node, int) bool 75 final map[graph.Node]bool 76 want [][]int64 77 }{ 78 { 79 g: wpBronKerboschGraph, 80 from: simple.Node(1), 81 final: map[graph.Node]bool{nil: true}, 82 want: [][]int64{ 83 {1}, 84 {0, 2, 4}, 85 {3}, 86 {5}, 87 }, 88 }, 89 { 90 g: wpBronKerboschGraph, 91 edge: func(e graph.Edge) bool { 92 // Do not traverse an edge between 3 and 5. 93 return (e.From().ID() != 3 || e.To().ID() != 5) && (e.From().ID() != 5 || e.To().ID() != 3) 94 }, 95 from: simple.Node(1), 96 final: map[graph.Node]bool{nil: true}, 97 want: [][]int64{ 98 {1}, 99 {0, 2, 4}, 100 {3}, 101 }, 102 }, 103 { 104 g: wpBronKerboschGraph, 105 from: simple.Node(1), 106 until: func(n graph.Node, _ int) bool { return n == simple.Node(3) }, 107 final: map[graph.Node]bool{simple.Node(3): true}, 108 want: [][]int64{ 109 {1}, 110 {0, 2, 4}, 111 }, 112 }, 113 { 114 g: batageljZaversnikGraph, 115 from: simple.Node(13), 116 final: map[graph.Node]bool{nil: true}, 117 want: [][]int64{ 118 {13}, 119 {14, 15}, 120 {6, 7, 8, 16, 17}, 121 {11, 12, 18, 19, 20}, 122 {9, 10}, 123 }, 124 }, 125 { 126 g: batageljZaversnikGraph, 127 from: simple.Node(13), 128 until: func(_ graph.Node, d int) bool { return d > 2 }, 129 final: map[graph.Node]bool{ 130 simple.Node(11): true, 131 simple.Node(12): true, 132 simple.Node(18): true, 133 simple.Node(19): true, 134 simple.Node(20): true, 135 }, 136 want: [][]int64{ 137 {13}, 138 {14, 15}, 139 {6, 7, 8, 16, 17}, 140 }, 141 }, 142 } 143 144 func TestBreadthFirst(t *testing.T) { 145 for i, test := range breadthFirstTests { 146 g := simple.NewUndirectedGraph() 147 for u, e := range test.g { 148 // Add nodes that are not defined by an edge. 149 if g.Node(int64(u)) == nil { 150 g.AddNode(simple.Node(u)) 151 } 152 for v := range e { 153 g.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)}) 154 } 155 } 156 w := BreadthFirst{ 157 Traverse: test.edge, 158 } 159 var got [][]int64 160 final := w.Walk(g, test.from, func(n graph.Node, d int) bool { 161 if test.until != nil && test.until(n, d) { 162 return true 163 } 164 if d >= len(got) { 165 got = append(got, []int64(nil)) 166 } 167 got[d] = append(got[d], n.ID()) 168 return false 169 }) 170 if !test.final[final] { 171 t.Errorf("unexpected final node for test %d:\ngot: %v\nwant: %v", i, final, test.final) 172 } 173 for _, l := range got { 174 ordered.Int64s(l) 175 } 176 if !reflect.DeepEqual(got, test.want) { 177 t.Errorf("unexpected BFS level structure for test %d:\ngot: %v\nwant: %v", i, got, test.want) 178 } 179 } 180 } 181 182 var depthFirstTests = []struct { 183 g []intset 184 from graph.Node 185 edge func(graph.Edge) bool 186 until func(graph.Node) bool 187 final map[graph.Node]bool 188 want [][]int64 189 }{ 190 { 191 g: wpBronKerboschGraph, 192 from: simple.Node(1), 193 final: map[graph.Node]bool{nil: true}, 194 want: [][]int64{ 195 {1, 0, 4, 3, 5, 2}, 196 {1, 0, 4, 3, 2, 5}, 197 {1, 2, 3, 4, 0, 5}, 198 {1, 2, 3, 5, 4, 0}, 199 {1, 4, 0, 3, 5, 2}, 200 {1, 4, 0, 3, 2, 5}, 201 {1, 4, 3, 2, 5, 0}, 202 {1, 4, 3, 5, 2, 0}, 203 }, 204 }, 205 { 206 g: wpBronKerboschGraph, 207 edge: func(e graph.Edge) bool { 208 // Do not traverse an edge between 3 and 5. 209 return (e.From().ID() != 3 || e.To().ID() != 5) && (e.From().ID() != 5 || e.To().ID() != 3) 210 }, 211 from: simple.Node(1), 212 final: map[graph.Node]bool{nil: true}, 213 want: [][]int64{ 214 {1, 0, 4, 3, 2}, 215 {1, 2, 3, 4, 0}, 216 {1, 4, 0, 3, 2}, 217 {1, 4, 3, 2, 0}, 218 }, 219 }, 220 { 221 g: wpBronKerboschGraph, 222 from: simple.Node(1), 223 until: func(n graph.Node) bool { return n == simple.Node(3) }, 224 final: map[graph.Node]bool{simple.Node(3): true}, 225 }, 226 { 227 g: batageljZaversnikGraph, 228 from: simple.Node(0), 229 final: map[graph.Node]bool{nil: true}, 230 want: [][]int64{{0}}, 231 }, 232 { 233 g: batageljZaversnikGraph, 234 from: simple.Node(3), 235 final: map[graph.Node]bool{nil: true}, 236 want: [][]int64{ 237 {3, 1, 2, 4, 5}, 238 {3, 4, 2, 1, 5}, 239 {3, 4, 5, 2, 1}, 240 }, 241 }, 242 { 243 g: g1595, 244 from: simple.Node(1), 245 final: map[graph.Node]bool{nil: true}, 246 want: [][]int64{ 247 {1, 4, 2, 3}, 248 {1, 2, 3, 4}, 249 }, 250 }, 251 } 252 253 func TestDepthFirst(t *testing.T) { 254 for i, test := range depthFirstTests { 255 g := simple.NewUndirectedGraph() 256 for u, e := range test.g { 257 // Add nodes that are not defined by an edge. 258 if g.Node(int64(u)) == nil { 259 g.AddNode(simple.Node(u)) 260 } 261 for v := range e { 262 g.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)}) 263 } 264 } 265 w := DepthFirst{ 266 Traverse: test.edge, 267 } 268 var got []int64 269 final := w.Walk(g, test.from, func(n graph.Node) bool { 270 if test.until != nil && test.until(n) { 271 return true 272 } 273 got = append(got, n.ID()) 274 return false 275 }) 276 if !test.final[final] { 277 t.Errorf("unexpected final node for test %d:\ngot: %v\nwant: %v", i, final, test.final) 278 } 279 var ok bool 280 for _, want := range test.want { 281 if reflect.DeepEqual(got, want) { 282 ok = true 283 break 284 } 285 } 286 if test.want != nil && !ok { 287 var wanted strings.Builder 288 for _, w := range test.want { 289 fmt.Fprintf(&wanted, " %v\n", w) 290 } 291 t.Errorf("unexpected DFS traversed nodes for test %d:\ngot: %v\nwant one of:\n%s", i, got, &wanted) 292 } 293 } 294 } 295 296 var walkAllTests = []struct { 297 g []intset 298 edge func(graph.Edge) bool 299 want [][]int64 300 }{ 301 { 302 g: batageljZaversnikGraph, 303 want: [][]int64{ 304 {0}, 305 {1, 2, 3, 4, 5}, 306 {6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}, 307 }, 308 }, 309 { 310 g: batageljZaversnikGraph, 311 edge: func(e graph.Edge) bool { 312 // Do not traverse an edge between 3 and 5. 313 return (e.From().ID() != 4 || e.To().ID() != 5) && (e.From().ID() != 5 || e.To().ID() != 4) 314 }, 315 want: [][]int64{ 316 {0}, 317 {1, 2, 3, 4}, 318 {5}, 319 {6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}, 320 }, 321 }, 322 } 323 324 func TestWalkAll(t *testing.T) { 325 for i, test := range walkAllTests { 326 g := simple.NewUndirectedGraph() 327 328 for u, e := range test.g { 329 if g.Node(int64(u)) == nil { 330 g.AddNode(simple.Node(u)) 331 } 332 for v := range e { 333 if g.Node(int64(v)) == nil { 334 g.AddNode(simple.Node(v)) 335 } 336 g.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)}) 337 } 338 } 339 type walker interface { 340 WalkAll(g graph.Undirected, before, after func(), during func(graph.Node)) 341 } 342 for _, w := range []walker{ 343 &BreadthFirst{}, 344 &DepthFirst{}, 345 } { 346 var ( 347 c []graph.Node 348 cc [][]graph.Node 349 ) 350 switch w := w.(type) { 351 case *BreadthFirst: 352 w.Traverse = test.edge 353 case *DepthFirst: 354 w.Traverse = test.edge 355 default: 356 panic(fmt.Sprintf("bad walker type: %T", w)) 357 } 358 during := func(n graph.Node) { 359 c = append(c, n) 360 } 361 after := func() { 362 cc = append(cc, []graph.Node(nil)) 363 cc[len(cc)-1] = append(cc[len(cc)-1], c...) 364 c = c[:0] 365 } 366 w.WalkAll(g, nil, after, during) 367 368 got := make([][]int64, len(cc)) 369 for j, c := range cc { 370 ids := make([]int64, len(c)) 371 for k, n := range c { 372 ids[k] = n.ID() 373 } 374 ordered.Int64s(ids) 375 got[j] = ids 376 } 377 ordered.BySliceValues(got) 378 if !reflect.DeepEqual(got, test.want) { 379 t.Errorf("unexpected connected components for test %d using %T:\ngot: %v\nwant:%v", i, w, got, test.want) 380 } 381 } 382 } 383 } 384 385 // intset is an integer set. 386 type intset map[int]struct{} 387 388 func linksTo(i ...int) intset { 389 if len(i) == 0 { 390 return nil 391 } 392 s := make(intset) 393 for _, v := range i { 394 s[v] = struct{}{} 395 } 396 return s 397 } 398 399 var ( 400 gnpUndirected_10_tenth = gnpUndirected(10, 0.1) 401 gnpUndirected_100_tenth = gnpUndirected(100, 0.1) 402 gnpUndirected_1000_tenth = gnpUndirected(1000, 0.1) 403 gnpUndirected_10_half = gnpUndirected(10, 0.5) 404 gnpUndirected_100_half = gnpUndirected(100, 0.5) 405 gnpUndirected_1000_half = gnpUndirected(1000, 0.5) 406 ) 407 408 func gnpUndirected(n int, p float64) graph.Undirected { 409 g := simple.NewUndirectedGraph() 410 err := gen.Gnp(g, n, p, nil) 411 if err != nil { 412 panic(fmt.Sprintf("traverse: bad test: %v", err)) 413 } 414 return g 415 } 416 417 func benchmarkWalkAllBreadthFirst(b *testing.B, g graph.Undirected) { 418 n := g.Nodes().Len() 419 b.ResetTimer() 420 var bft BreadthFirst 421 for i := 0; i < b.N; i++ { 422 bft.WalkAll(g, nil, nil, nil) 423 } 424 if len(bft.visited) != n { 425 b.Fatalf("unexpected number of nodes visited: want: %d got %d", n, len(bft.visited)) 426 } 427 } 428 429 func BenchmarkWalkAllBreadthFirstGnp_10_tenth(b *testing.B) { 430 benchmarkWalkAllBreadthFirst(b, gnpUndirected_10_tenth) 431 } 432 func BenchmarkWalkAllBreadthFirstGnp_100_tenth(b *testing.B) { 433 benchmarkWalkAllBreadthFirst(b, gnpUndirected_100_tenth) 434 } 435 func BenchmarkWalkAllBreadthFirstGnp_1000_tenth(b *testing.B) { 436 benchmarkWalkAllBreadthFirst(b, gnpUndirected_1000_tenth) 437 } 438 func BenchmarkWalkAllBreadthFirstGnp_10_half(b *testing.B) { 439 benchmarkWalkAllBreadthFirst(b, gnpUndirected_10_half) 440 } 441 func BenchmarkWalkAllBreadthFirstGnp_100_half(b *testing.B) { 442 benchmarkWalkAllBreadthFirst(b, gnpUndirected_100_half) 443 } 444 func BenchmarkWalkAllBreadthFirstGnp_1000_half(b *testing.B) { 445 benchmarkWalkAllBreadthFirst(b, gnpUndirected_1000_half) 446 } 447 448 func benchmarkWalkAllDepthFirst(b *testing.B, g graph.Undirected) { 449 n := g.Nodes().Len() 450 b.ResetTimer() 451 var dft DepthFirst 452 for i := 0; i < b.N; i++ { 453 dft.WalkAll(g, nil, nil, nil) 454 } 455 if len(dft.visited) != n { 456 b.Fatalf("unexpected number of nodes visited: want: %d got %d", n, len(dft.visited)) 457 } 458 } 459 460 func BenchmarkWalkAllDepthFirstGnp_10_tenth(b *testing.B) { 461 benchmarkWalkAllDepthFirst(b, gnpUndirected_10_tenth) 462 } 463 func BenchmarkWalkAllDepthFirstGnp_100_tenth(b *testing.B) { 464 benchmarkWalkAllDepthFirst(b, gnpUndirected_100_tenth) 465 } 466 func BenchmarkWalkAllDepthFirstGnp_1000_tenth(b *testing.B) { 467 benchmarkWalkAllDepthFirst(b, gnpUndirected_1000_tenth) 468 } 469 func BenchmarkWalkAllDepthFirstGnp_10_half(b *testing.B) { 470 benchmarkWalkAllDepthFirst(b, gnpUndirected_10_half) 471 } 472 func BenchmarkWalkAllDepthFirstGnp_100_half(b *testing.B) { 473 benchmarkWalkAllDepthFirst(b, gnpUndirected_100_half) 474 } 475 func BenchmarkWalkAllDepthFirstGnp_1000_half(b *testing.B) { 476 benchmarkWalkAllDepthFirst(b, gnpUndirected_1000_half) 477 }