github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/graph/path/dijkstra_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  	"math"
     9  	"reflect"
    10  	"sort"
    11  	"testing"
    12  
    13  	"github.com/jingcheng-WU/gonum/graph"
    14  	"github.com/jingcheng-WU/gonum/graph/internal/ordered"
    15  	"github.com/jingcheng-WU/gonum/graph/path/internal/testgraphs"
    16  	"github.com/jingcheng-WU/gonum/graph/simple"
    17  	"github.com/jingcheng-WU/gonum/graph/traverse"
    18  )
    19  
    20  func TestDijkstraFrom(t *testing.T) {
    21  	t.Parallel()
    22  	for _, test := range testgraphs.ShortestPathTests {
    23  		g := test.Graph()
    24  		for _, e := range test.Edges {
    25  			g.SetWeightedEdge(e)
    26  		}
    27  
    28  		for _, tg := range []struct {
    29  			typ string
    30  			g   traverse.Graph
    31  		}{
    32  			{"complete", g.(graph.Graph)},
    33  			{"incremental", incremental{g.(graph.Weighted)}},
    34  		} {
    35  			var (
    36  				pt Shortest
    37  
    38  				panicked bool
    39  			)
    40  			func() {
    41  				defer func() {
    42  					panicked = recover() != nil
    43  				}()
    44  				pt = DijkstraFrom(test.Query.From(), tg.g)
    45  			}()
    46  			if panicked || test.HasNegativeWeight {
    47  				if !test.HasNegativeWeight {
    48  					t.Errorf("%q %s: unexpected panic", test.Name, tg.typ)
    49  				}
    50  				if !panicked {
    51  					t.Errorf("%q %s: expected panic for negative edge weight", test.Name, tg.typ)
    52  				}
    53  				continue
    54  			}
    55  
    56  			if pt.From().ID() != test.Query.From().ID() {
    57  				t.Fatalf("%q %s: unexpected from node ID: got:%d want:%d", test.Name, tg.typ, pt.From().ID(), test.Query.From().ID())
    58  			}
    59  
    60  			p, weight := pt.To(test.Query.To().ID())
    61  			if weight != test.Weight {
    62  				t.Errorf("%q %s: unexpected weight from To: got:%f want:%f",
    63  					test.Name, tg.typ, weight, test.Weight)
    64  			}
    65  			if weight := pt.WeightTo(test.Query.To().ID()); weight != test.Weight {
    66  				t.Errorf("%q %s: unexpected weight from Weight: got:%f want:%f",
    67  					test.Name, tg.typ, weight, test.Weight)
    68  			}
    69  
    70  			var got []int64
    71  			for _, n := range p {
    72  				got = append(got, n.ID())
    73  			}
    74  			ok := len(got) == 0 && len(test.WantPaths) == 0
    75  			for _, sp := range test.WantPaths {
    76  				if reflect.DeepEqual(got, sp) {
    77  					ok = true
    78  					break
    79  				}
    80  			}
    81  			if !ok {
    82  				t.Errorf("%q %s: unexpected shortest path:\ngot: %v\nwant from:%v",
    83  					test.Name, tg.typ, p, test.WantPaths)
    84  			}
    85  
    86  			np, weight := pt.To(test.NoPathFor.To().ID())
    87  			if pt.From().ID() == test.NoPathFor.From().ID() && (np != nil || !math.IsInf(weight, 1)) {
    88  				t.Errorf("%q %s: unexpected path:\ngot: path=%v weight=%f\nwant:path=<nil> weight=+Inf",
    89  					test.Name, tg.typ, np, weight)
    90  			}
    91  		}
    92  	}
    93  }
    94  
    95  func TestDijkstraAllFrom(t *testing.T) {
    96  	t.Parallel()
    97  	for _, test := range testgraphs.ShortestPathTests {
    98  		g := test.Graph()
    99  		for _, e := range test.Edges {
   100  			g.SetWeightedEdge(e)
   101  		}
   102  
   103  		for _, tg := range []struct {
   104  			typ string
   105  			g   traverse.Graph
   106  		}{
   107  			{"complete", g.(graph.Graph)},
   108  			{"incremental", incremental{g.(graph.Weighted)}},
   109  		} {
   110  			var (
   111  				pt ShortestAlts
   112  
   113  				panicked bool
   114  			)
   115  			func() {
   116  				defer func() {
   117  					panicked = recover() != nil
   118  				}()
   119  				pt = DijkstraAllFrom(test.Query.From(), tg.g)
   120  			}()
   121  			if panicked || test.HasNegativeWeight {
   122  				if !test.HasNegativeWeight {
   123  					t.Errorf("%q %s: unexpected panic", test.Name, tg.typ)
   124  				}
   125  				if !panicked {
   126  					t.Errorf("%q %s: expected panic for negative edge weight", test.Name, tg.typ)
   127  				}
   128  				continue
   129  			}
   130  
   131  			if pt.From().ID() != test.Query.From().ID() {
   132  				t.Fatalf("%q %s: unexpected from node ID: got:%d want:%d", test.Name, tg.typ, pt.From().ID(), test.Query.From().ID())
   133  			}
   134  
   135  			// Test single path results.
   136  			p, weight, unique := pt.To(test.Query.To().ID())
   137  			if weight != test.Weight {
   138  				t.Errorf("%q %s: unexpected weight from To: got:%f want:%f",
   139  					test.Name, tg.typ, weight, test.Weight)
   140  			}
   141  			if weight := pt.WeightTo(test.Query.To().ID()); weight != test.Weight {
   142  				t.Errorf("%q %s: unexpected weight from Weight: got:%f want:%f",
   143  					test.Name, tg.typ, weight, test.Weight)
   144  			}
   145  
   146  			var gotPath []int64
   147  			for _, n := range p {
   148  				gotPath = append(gotPath, n.ID())
   149  			}
   150  			ok := len(gotPath) == 0 && len(test.WantPaths) == 0
   151  			for _, sp := range test.WantPaths {
   152  				if reflect.DeepEqual(gotPath, sp) {
   153  					ok = true
   154  					break
   155  				}
   156  			}
   157  			if !ok {
   158  				t.Errorf("%q: unexpected shortest path:\ngot: %v\nwant from:%v",
   159  					test.Name, p, test.WantPaths)
   160  			}
   161  			if unique != test.HasUniquePath {
   162  				t.Errorf("%q: unexpected uniqueness from To: got:%t want:%t (%d paths)",
   163  					test.Name, unique, test.HasUniquePath, len(test.WantPaths))
   164  			}
   165  
   166  			// Test multiple path results.
   167  			paths, weight := pt.AllTo(test.Query.To().ID())
   168  			if weight != test.Weight {
   169  				t.Errorf("%q: unexpected weight from AllTo: got:%f want:%f",
   170  					test.Name, weight, test.Weight)
   171  			}
   172  			if weight := pt.WeightTo(test.Query.To().ID()); weight != test.Weight {
   173  				t.Errorf("%q: unexpected weight from Weight: got:%f want:%f",
   174  					test.Name, weight, test.Weight)
   175  			}
   176  
   177  			var gotPaths [][]int64
   178  			if len(paths) != 0 {
   179  				gotPaths = make([][]int64, len(paths))
   180  			}
   181  			for i, p := range paths {
   182  				for _, v := range p {
   183  					gotPaths[i] = append(gotPaths[i], v.ID())
   184  				}
   185  			}
   186  			sort.Sort(ordered.BySliceValues(gotPaths))
   187  			if !reflect.DeepEqual(gotPaths, test.WantPaths) {
   188  				t.Errorf("testing %q: unexpected shortest paths:\ngot: %v\nwant:%v",
   189  					test.Name, gotPaths, test.WantPaths)
   190  			}
   191  
   192  			// Test absent paths.
   193  			np, weight, unique := pt.To(test.NoPathFor.To().ID())
   194  			if pt.From().ID() == test.NoPathFor.From().ID() && !(np == nil && math.IsInf(weight, 1) && !unique) {
   195  				t.Errorf("%q: unexpected path:\ngot: path=%v weight=%f unique=%t\nwant:path=<nil> weight=+Inf unique=false",
   196  					test.Name, np, weight, unique)
   197  			}
   198  		}
   199  	}
   200  }
   201  
   202  type weightedTraverseGraph interface {
   203  	traverse.Graph
   204  	Weighted
   205  }
   206  
   207  type incremental struct {
   208  	weightedTraverseGraph
   209  }
   210  
   211  func TestDijkstraAllPaths(t *testing.T) {
   212  	t.Parallel()
   213  	for _, test := range testgraphs.ShortestPathTests {
   214  		g := test.Graph()
   215  		for _, e := range test.Edges {
   216  			g.SetWeightedEdge(e)
   217  		}
   218  
   219  		var (
   220  			pt AllShortest
   221  
   222  			panicked bool
   223  		)
   224  		func() {
   225  			defer func() {
   226  				panicked = recover() != nil
   227  			}()
   228  			pt = DijkstraAllPaths(g.(graph.Graph))
   229  		}()
   230  		if panicked || test.HasNegativeWeight {
   231  			if !test.HasNegativeWeight {
   232  				t.Errorf("%q: unexpected panic", test.Name)
   233  			}
   234  			if !panicked {
   235  				t.Errorf("%q: expected panic for negative edge weight", test.Name)
   236  			}
   237  			continue
   238  		}
   239  
   240  		// Check all random paths returned are OK.
   241  		for i := 0; i < 10; i++ {
   242  			p, weight, unique := pt.Between(test.Query.From().ID(), test.Query.To().ID())
   243  			if weight != test.Weight {
   244  				t.Errorf("%q: unexpected weight from Between: got:%f want:%f",
   245  					test.Name, weight, test.Weight)
   246  			}
   247  			if weight := pt.Weight(test.Query.From().ID(), test.Query.To().ID()); weight != test.Weight {
   248  				t.Errorf("%q: unexpected weight from Weight: got:%f want:%f",
   249  					test.Name, weight, test.Weight)
   250  			}
   251  			if unique != test.HasUniquePath {
   252  				t.Errorf("%q: unexpected number of paths: got: unique=%t want: unique=%t",
   253  					test.Name, unique, test.HasUniquePath)
   254  			}
   255  
   256  			var got []int64
   257  			for _, n := range p {
   258  				got = append(got, n.ID())
   259  			}
   260  			ok := len(got) == 0 && len(test.WantPaths) == 0
   261  			for _, sp := range test.WantPaths {
   262  				if reflect.DeepEqual(got, sp) {
   263  					ok = true
   264  					break
   265  				}
   266  			}
   267  			if !ok {
   268  				t.Errorf("%q: unexpected shortest path:\ngot: %v\nwant from:%v",
   269  					test.Name, p, test.WantPaths)
   270  			}
   271  		}
   272  
   273  		np, weight, unique := pt.Between(test.NoPathFor.From().ID(), test.NoPathFor.To().ID())
   274  		if np != nil || !math.IsInf(weight, 1) || unique {
   275  			t.Errorf("%q: unexpected path:\ngot: path=%v weight=%f unique=%t\nwant:path=<nil> weight=+Inf unique=false",
   276  				test.Name, np, weight, unique)
   277  		}
   278  
   279  		paths, weight := pt.AllBetween(test.Query.From().ID(), test.Query.To().ID())
   280  		if weight != test.Weight {
   281  			t.Errorf("%q: unexpected weight from Between: got:%f want:%f",
   282  				test.Name, weight, test.Weight)
   283  		}
   284  
   285  		var got [][]int64
   286  		if len(paths) != 0 {
   287  			got = make([][]int64, len(paths))
   288  		}
   289  		for i, p := range paths {
   290  			for _, v := range p {
   291  				got[i] = append(got[i], v.ID())
   292  			}
   293  		}
   294  		sort.Sort(ordered.BySliceValues(got))
   295  		if !reflect.DeepEqual(got, test.WantPaths) {
   296  			t.Errorf("testing %q: unexpected shortest paths:\ngot: %v\nwant:%v",
   297  				test.Name, got, test.WantPaths)
   298  		}
   299  
   300  		nps, weight := pt.AllBetween(test.NoPathFor.From().ID(), test.NoPathFor.To().ID())
   301  		if nps != nil || !math.IsInf(weight, 1) {
   302  			t.Errorf("%q: unexpected path:\ngot: paths=%v weight=%f\nwant:path=<nil> weight=+Inf",
   303  				test.Name, nps, weight)
   304  		}
   305  	}
   306  }
   307  
   308  func TestAllShortestAbsentNode(t *testing.T) {
   309  	t.Parallel()
   310  	g := simple.NewUndirectedGraph()
   311  	g.SetEdge(simple.Edge{F: simple.Node(1), T: simple.Node(2)})
   312  	paths := DijkstraAllPaths(g)
   313  	// Confirm we have a good paths tree.
   314  	if _, cost := paths.AllBetween(1, 2); cost != 1 {
   315  		t.Errorf("unexpected cost between existing nodes: got:%v want:1", cost)
   316  	}
   317  
   318  	gotPath, cost, unique := paths.Between(0, 0)
   319  	if cost != 0 {
   320  		t.Errorf("unexpected cost from absent node to itself: got:%v want:0", cost)
   321  	}
   322  	if !unique {
   323  		t.Error("unexpected non-unique path from absent node to itself")
   324  	}
   325  	wantPath := []graph.Node{node(0)}
   326  	if !reflect.DeepEqual(gotPath, wantPath) {
   327  		t.Errorf("unexpected path from absent node to itself: got:%#v want:%#v", gotPath, wantPath)
   328  	}
   329  
   330  	gotPaths, cost := paths.AllBetween(0, 0)
   331  	if cost != 0 {
   332  		t.Errorf("unexpected cost from absent node to itself: got:%v want:0", cost)
   333  	}
   334  	wantPaths := [][]graph.Node{{node(0)}}
   335  	if !reflect.DeepEqual(gotPaths, wantPaths) {
   336  		t.Errorf("unexpected paths from absent node to itself: got:%#v want:%#v", gotPaths, wantPaths)
   337  	}
   338  }