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 }