gonum.org/v1/gonum@v0.14.0/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  	"gonum.org/v1/gonum/graph"
    13  	"gonum.org/v1/gonum/graph/graphs/gen"
    14  	"gonum.org/v1/gonum/graph/simple"
    15  	"gonum.org/v1/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  }