github.com/gopherd/gonum@v0.0.4/graph/path/bench_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 path 6 7 import ( 8 "fmt" 9 "sync" 10 "testing" 11 12 "github.com/gopherd/gonum/graph" 13 "github.com/gopherd/gonum/graph/graphs/gen" 14 "github.com/gopherd/gonum/graph/simple" 15 "github.com/gopherd/gonum/graph/traverse" 16 ) 17 18 var ( 19 gnpUndirected_10_tenth = gnpUndirected(10, 0.1) 20 gnpUndirected_100_tenth = gnpUndirected(100, 0.1) 21 gnpUndirected_1000_tenth = gnpUndirected(1000, 0.1) 22 gnpUndirected_10_half = gnpUndirected(10, 0.5) 23 gnpUndirected_100_half = gnpUndirected(100, 0.5) 24 gnpUndirected_1000_half = gnpUndirected(1000, 0.5) 25 26 nswUndirected_10_2_2_2 = navigableSmallWorldUndirected(10, 2, 2, 2) 27 nswUndirected_10_2_5_2 = navigableSmallWorldUndirected(10, 2, 5, 2) 28 nswUndirected_100_5_10_2 = navigableSmallWorldUndirected(100, 5, 10, 2) 29 nswUndirected_100_5_20_2 = navigableSmallWorldUndirected(100, 5, 20, 2) 30 ) 31 32 func gnpUndirected(n int, p float64) func() graph.Undirected { 33 var once sync.Once 34 var cache graph.Undirected 35 return func() graph.Undirected { 36 once.Do(func() { 37 g := simple.NewUndirectedGraph() 38 err := gen.Gnp(g, n, p, nil) 39 if err != nil { 40 panic(fmt.Sprintf("path: bad test: %v", err)) 41 } 42 cache = g 43 }) 44 return cache 45 } 46 } 47 48 func navigableSmallWorldUndirected(n, p, q int, r float64) func() graph.Undirected { 49 var once sync.Once 50 var cache graph.Undirected 51 return func() graph.Undirected { 52 once.Do(func() { 53 g := simple.NewUndirectedGraph() 54 err := gen.NavigableSmallWorld(g, []int{n, n}, p, q, r, nil) 55 if err != nil { 56 panic(fmt.Sprintf("path: bad test: %v", err)) 57 } 58 cache = g 59 }) 60 return cache 61 } 62 } 63 64 func manhattan(size int) func(x, y graph.Node) float64 { 65 return func(x, y graph.Node) float64 { 66 return manhattanBetween(coordinatesForID(x, size, size), coordinatesForID(y, size, size)) 67 } 68 } 69 70 func coordinatesForID(n graph.Node, c, r int) [2]int { 71 id := n.ID() 72 if id >= int64(c*r) { 73 panic("out of range") 74 } 75 return [2]int{int(id) / r, int(id) % r} 76 } 77 78 // manhattanBetween returns the Manhattan distance between a and b. 79 func manhattanBetween(a, b [2]int) float64 { 80 var d int 81 for i, v := range a { 82 d += abs(v - b[i]) 83 } 84 return float64(d) 85 } 86 87 func abs(a int) int { 88 if a < 0 { 89 return -a 90 } 91 return a 92 } 93 94 func BenchmarkAStarUndirected(b *testing.B) { 95 benchmarks := []struct { 96 name string 97 graph graph.Undirected 98 h Heuristic 99 }{ 100 {"GNP Undirected 10 tenth", gnpUndirected_10_tenth(), nil}, 101 {"GNP Undirected 100 tenth", gnpUndirected_100_tenth(), nil}, 102 {"GNP Undirected 1000 tenth", gnpUndirected_1000_tenth(), nil}, 103 {"GNP Undirected 10 half", gnpUndirected_10_half(), nil}, 104 {"GNP Undirected 100 half", gnpUndirected_100_half(), nil}, 105 {"GNP Undirected 1000 half", gnpUndirected_1000_half(), nil}, 106 107 {"NSW Undirected 10 2 2 2", nswUndirected_10_2_2_2(), nil}, 108 {"NSW Undirected 10 2 2 2 heuristic", nswUndirected_10_2_2_2(), manhattan(10)}, 109 {"NSW Undirected 10 2 5 2", nswUndirected_10_2_5_2(), nil}, 110 {"NSW Undirected 10 2 5 2 heuristic", nswUndirected_10_2_5_2(), manhattan(10)}, 111 {"NSW Undirected 100 5 10 2", nswUndirected_100_5_10_2(), nil}, 112 {"NSW Undirected 100 5 10 2 heuristic", nswUndirected_100_5_10_2(), manhattan(100)}, 113 {"NSW Undirected 100 5 20 2", nswUndirected_100_5_20_2(), nil}, 114 {"NSW Undirected 100 5 20 2 heuristic", nswUndirected_100_5_20_2(), manhattan(100)}, 115 } 116 117 for _, bm := range benchmarks { 118 b.Run(bm.name, func(b *testing.B) { 119 var expanded int 120 for i := 0; i < b.N; i++ { 121 _, expanded = AStar(simple.Node(0), simple.Node(1), bm.graph, bm.h) 122 } 123 if expanded == 0 { 124 b.Fatal("unexpected number of expanded nodes") 125 } 126 }) 127 } 128 } 129 130 var ( 131 gnpDirected_500_tenth = gnpDirected(500, 0.1) 132 gnpDirected_1000_tenth = gnpDirected(1000, 0.1) 133 gnpDirected_2000_tenth = gnpDirected(2000, 0.1) 134 gnpDirected_500_half = gnpDirected(500, 0.5) 135 gnpDirected_1000_half = gnpDirected(1000, 0.5) 136 gnpDirected_2000_half = gnpDirected(2000, 0.5) 137 gnpDirected_500_full = gnpDirected(500, 1) 138 gnpDirected_1000_full = gnpDirected(1000, 1) 139 gnpDirected_2000_full = gnpDirected(2000, 1) 140 ) 141 142 func gnpDirected(n int, p float64) func() graph.Directed { 143 var once sync.Once 144 var cache graph.Directed 145 return func() graph.Directed { 146 once.Do(func() { 147 g := simple.NewDirectedGraph() 148 err := gen.Gnp(g, n, p, nil) 149 if err != nil { 150 panic(fmt.Sprintf("path: bad test: %v", err)) 151 } 152 cache = g 153 }) 154 return cache 155 } 156 } 157 158 func BenchmarkBellmanFordFrom(b *testing.B) { 159 benchmarks := []struct { 160 name string 161 graph graph.Directed 162 }{ 163 {"500 tenth", gnpDirected_500_tenth()}, 164 {"1000 tenth", gnpDirected_1000_tenth()}, 165 {"2000 tenth", gnpDirected_2000_tenth()}, 166 {"500 half", gnpDirected_500_half()}, 167 {"1000 half", gnpDirected_1000_half()}, 168 {"2000 half", gnpDirected_2000_half()}, 169 {"500 full", gnpDirected_500_full()}, 170 {"1000 full", gnpDirected_1000_full()}, 171 {"2000 full", gnpDirected_2000_full()}, 172 } 173 174 type incremental struct { 175 traverse.Graph 176 } 177 for _, bm := range benchmarks { 178 for _, tg := range []struct { 179 typ string 180 g traverse.Graph 181 }{ 182 {g: bm.graph}, 183 {typ: " incremental", g: incremental{bm.graph}}, 184 } { 185 b.Run(bm.name+tg.typ, func(b *testing.B) { 186 for i := 0; i < b.N; i++ { 187 BellmanFordFrom(bm.graph.Node(0), tg.g) 188 } 189 }) 190 } 191 } 192 }