github.com/gopherd/gonum@v0.0.4/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  	"github.com/gopherd/gonum/graph"
    14  	"github.com/gopherd/gonum/graph/graphs/gen"
    15  	"github.com/gopherd/gonum/graph/internal/ordered"
    16  	"github.com/gopherd/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  }