github.com/gopherd/gonum@v0.0.4/graph/path/shortest.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 10 "math/rand" 11 12 "github.com/gopherd/gonum/floats/scalar" 13 "github.com/gopherd/gonum/graph" 14 "github.com/gopherd/gonum/graph/internal/ordered" 15 "github.com/gopherd/gonum/graph/internal/set" 16 "github.com/gopherd/gonum/mat" 17 ) 18 19 // Shortest is a shortest-path tree created by the BellmanFordFrom, DijkstraFrom 20 // or AStar single-source shortest path functions. 21 type Shortest struct { 22 // from holds the source node given to 23 // the function that returned the 24 // Shortest value. 25 from graph.Node 26 27 // nodes hold the nodes of the analysed 28 // graph. 29 nodes []graph.Node 30 // indexOf contains a mapping between 31 // the id-dense representation of the 32 // graph and the potentially id-sparse 33 // nodes held in nodes. 34 indexOf map[int64]int 35 36 // dist and next represent the shortest 37 // paths between nodes. 38 // 39 // Indices into dist and next are 40 // mapped through indexOf. 41 // 42 // dist contains the distances 43 // from the from node for each 44 // node in the graph. 45 dist []float64 46 // next contains the shortest-path 47 // tree of the graph. The index is a 48 // linear mapping of to-dense-id. 49 next []int 50 51 // hasNegativeCycle indicates 52 // whether the Shortest includes 53 // a negative cycle. This should 54 // be set by the function that 55 // returned the Shortest value. 56 hasNegativeCycle bool 57 58 // negCosts holds negative costs 59 // between pairs of nodes to report 60 // negative cycles. 61 // negCosts must be initialised by 62 // routines that can handle negative 63 // edge weights. 64 negCosts map[negEdge]float64 65 } 66 67 // newShortestFrom returns a shortest path tree for paths from u 68 // initialised with the given nodes. The nodes held by the returned 69 // Shortest may be lazily added. 70 func newShortestFrom(u graph.Node, nodes []graph.Node) Shortest { 71 indexOf := make(map[int64]int, len(nodes)) 72 uid := u.ID() 73 for i, n := range nodes { 74 indexOf[n.ID()] = i 75 if n.ID() == uid { 76 u = n 77 } 78 } 79 80 p := Shortest{ 81 from: u, 82 83 nodes: nodes, 84 indexOf: indexOf, 85 86 dist: make([]float64, len(nodes)), 87 next: make([]int, len(nodes)), 88 } 89 for i := range nodes { 90 p.dist[i] = math.Inf(1) 91 p.next[i] = -1 92 } 93 p.dist[indexOf[uid]] = 0 94 95 return p 96 } 97 98 // add adds a node to the Shortest, initialising its stored index and returning, and 99 // setting the distance and position as unconnected. add will panic if the node is 100 // already present. 101 func (p *Shortest) add(u graph.Node) int { 102 uid := u.ID() 103 if _, exists := p.indexOf[uid]; exists { 104 panic("shortest: adding existing node") 105 } 106 idx := len(p.nodes) 107 p.indexOf[uid] = idx 108 p.nodes = append(p.nodes, u) 109 p.dist = append(p.dist, math.Inf(1)) 110 p.next = append(p.next, -1) 111 return idx 112 } 113 114 // set sets the weight of the path from the node in p.nodes indexed by mid to the node 115 // indexed by to. 116 func (p Shortest) set(to int, weight float64, mid int) { 117 p.dist[to] = weight 118 p.next[to] = mid 119 if weight < 0 { 120 e := negEdge{from: mid, to: to} 121 c, ok := p.negCosts[e] 122 if !ok { 123 p.negCosts[e] = weight 124 } else if weight < c { 125 // The only ways that we can have a new weight that is 126 // lower than the previous weight is if either the edge 127 // has already been traversed in a negative cycle, or 128 // the edge is reachable from a negative cycle. 129 // Either way the reported path is returned with a 130 // negative infinite path weight. 131 p.negCosts[e] = math.Inf(-1) 132 } 133 } 134 } 135 136 // From returns the starting node of the paths held by the Shortest. 137 func (p Shortest) From() graph.Node { return p.from } 138 139 // WeightTo returns the weight of the minimum path to v. If the path to v includes 140 // a negative cycle, the returned weight will not reflect the true path weight. 141 func (p Shortest) WeightTo(vid int64) float64 { 142 to, toOK := p.indexOf[vid] 143 if !toOK { 144 return math.Inf(1) 145 } 146 return p.dist[to] 147 } 148 149 // To returns a shortest path to v and the weight of the path. If the path 150 // to v includes a negative cycle, one pass through the cycle will be included 151 // in path, but any path leading into the negative cycle will be lost, and 152 // weight will be returned as -Inf. 153 func (p Shortest) To(vid int64) (path []graph.Node, weight float64) { 154 to, toOK := p.indexOf[vid] 155 if !toOK || math.IsInf(p.dist[to], 1) { 156 return nil, math.Inf(1) 157 } 158 from := p.indexOf[p.from.ID()] 159 path = []graph.Node{p.nodes[to]} 160 weight = math.Inf(1) 161 if p.hasNegativeCycle { 162 seen := make(set.Ints) 163 seen.Add(from) 164 for to != from { 165 next := p.next[to] 166 if math.IsInf(p.negCosts[negEdge{from: next, to: to}], -1) { 167 weight = math.Inf(-1) 168 } 169 if seen.Has(to) { 170 break 171 } 172 seen.Add(to) 173 path = append(path, p.nodes[next]) 174 to = next 175 } 176 } else { 177 n := len(p.nodes) 178 for to != from { 179 to = p.next[to] 180 path = append(path, p.nodes[to]) 181 if n < 0 { 182 panic("path: unexpected negative cycle") 183 } 184 n-- 185 } 186 } 187 ordered.Reverse(path) 188 return path, math.Min(weight, p.dist[p.indexOf[vid]]) 189 } 190 191 // ShortestAlts is a shortest-path tree created by the BellmanFordAllFrom or DijkstraAllFrom 192 // single-source shortest path functions. 193 type ShortestAlts struct { 194 // from holds the source node given to 195 // the function that returned the 196 // ShortestAlts value. 197 from graph.Node 198 199 // nodes hold the nodes of the analysed 200 // graph. 201 nodes []graph.Node 202 // indexOf contains a mapping between 203 // the id-dense representation of the 204 // graph and the potentially id-sparse 205 // nodes held in nodes. 206 indexOf map[int64]int 207 208 // dist and next represent the shortest 209 // paths between nodes. 210 // 211 // Indices into dist and next are 212 // mapped through indexOf. 213 // 214 // dist contains the distances 215 // from the from node for each 216 // node in the graph. 217 dist []float64 218 // next contains the shortest-path 219 // tree of the graph. The index is a 220 // linear mapping of to-dense-id. 221 next [][]int 222 223 // hasNegativeCycle indicates 224 // whether the ShortestAlts includes 225 // a negative cycle. This should 226 // be set by the function that 227 // returned the ShortestAlts value. 228 hasNegativeCycle bool 229 230 // negCosts holds negative costs 231 // between pairs of nodes to report 232 // negative cycles. 233 // negCosts must be initialised by 234 // routines that can handle negative 235 // edge weights. 236 negCosts map[negEdge]float64 237 } 238 239 // newShortestAltsFrom returns a shortest path tree for all paths from u 240 // initialised with the given nodes. The nodes held by the returned 241 // Shortest may be lazily added. 242 func newShortestAltsFrom(u graph.Node, nodes []graph.Node) ShortestAlts { 243 indexOf := make(map[int64]int, len(nodes)) 244 uid := u.ID() 245 for i, n := range nodes { 246 indexOf[n.ID()] = i 247 if n.ID() == uid { 248 u = n 249 } 250 } 251 252 p := ShortestAlts{ 253 from: u, 254 255 nodes: nodes, 256 indexOf: indexOf, 257 258 dist: make([]float64, len(nodes)), 259 next: make([][]int, len(nodes)), 260 } 261 for i := range nodes { 262 p.dist[i] = math.Inf(1) 263 p.next[i] = nil 264 } 265 p.dist[indexOf[uid]] = 0 266 267 return p 268 } 269 270 // add adds a node to the ShortestAlts, initialising its stored index and returning, and 271 // setting the distance and position as unconnected. add will panic if the node is 272 // already present. 273 func (p *ShortestAlts) add(u graph.Node) int { 274 uid := u.ID() 275 if _, exists := p.indexOf[uid]; exists { 276 panic("shortest: adding existing node") 277 } 278 idx := len(p.nodes) 279 p.indexOf[uid] = idx 280 p.nodes = append(p.nodes, u) 281 p.dist = append(p.dist, math.Inf(1)) 282 p.next = append(p.next, nil) 283 return idx 284 } 285 286 // set sets the weight of the path from the node in p.nodes indexed by mid to the node 287 // indexed by to. 288 func (p ShortestAlts) set(to int, weight float64, mid int) { 289 p.dist[to] = weight 290 p.next[to] = []int{mid} 291 if weight < 0 { 292 e := negEdge{from: mid, to: to} 293 c, ok := p.negCosts[e] 294 if !ok { 295 p.negCosts[e] = weight 296 } else if weight < c { 297 // The only ways that we can have a new weight that is 298 // lower than the previous weight is if either the edge 299 // has already been traversed in a negative cycle, or 300 // the edge is reachable from a negative cycle. 301 // Either way the reported path is returned with a 302 // negative infinite path weight. 303 p.negCosts[e] = math.Inf(-1) 304 } 305 } 306 } 307 308 // addPath adds a new path from the node in p.nodes indexed by mid to the node indexed 309 // by to. The weight of the path is expected to be the same as already existing paths 310 // between these nodes, but no check is made for this. 311 func (p ShortestAlts) addPath(to, mid int) { 312 // These are likely to be rare, so just loop over collisions. 313 for _, v := range p.next[to] { 314 if mid == v { 315 return 316 } 317 } 318 p.next[to] = append(p.next[to], mid) 319 } 320 321 // From returns the starting node of the paths held by the ShortestAlts. 322 func (p ShortestAlts) From() graph.Node { return p.from } 323 324 // WeightTo returns the weight of the minimum path to v. If the path to v includes 325 // a negative cycle, the returned weight will not reflect the true path weight. 326 func (p ShortestAlts) WeightTo(vid int64) float64 { 327 to, toOK := p.indexOf[vid] 328 if !toOK { 329 return math.Inf(1) 330 } 331 return p.dist[to] 332 } 333 334 // To returns a shortest path to v and the weight of the path. If more than 335 // one shortest path exists between u and v, a randomly chosen path will be 336 // returned and unique is returned false. If a cycle with zero weight exists 337 // in the path, it will not be included, but unique will be returned false. 338 // If the path to v includes a negative cycle, one pass through the cycle will 339 // be included in path, but any path leading into the negative cycle will be 340 // lost, and weight will be returned as -Inf. 341 func (p ShortestAlts) To(vid int64) (path []graph.Node, weight float64, unique bool) { 342 to, toOK := p.indexOf[vid] 343 if !toOK || math.IsInf(p.dist[to], 1) { 344 return nil, math.Inf(1), false 345 } 346 from := p.indexOf[p.from.ID()] 347 unique = true 348 path = []graph.Node{p.nodes[to]} 349 if p.hasNegativeCycle { 350 weight = math.Inf(1) 351 seen := make(set.Ints) 352 seen.Add(from) 353 for to != from { 354 c := p.next[to] 355 var next int 356 if len(c) != 1 { 357 unique = false 358 next = c[rand.Intn(len(c))] 359 } else { 360 next = c[0] 361 } 362 if math.IsInf(p.negCosts[negEdge{from: next, to: to}], -1) { 363 weight = math.Inf(-1) 364 unique = false 365 } 366 if seen.Has(to) { 367 break 368 } 369 seen.Add(to) 370 path = append(path, p.nodes[next]) 371 to = next 372 } 373 weight = math.Min(weight, p.dist[p.indexOf[vid]]) 374 } else { 375 seen := make([]int, len(p.nodes)) 376 for i := range seen { 377 seen[i] = -1 378 } 379 seen[to] = 0 380 381 var next int 382 for from != to { 383 c := p.next[to] 384 if len(c) != 1 { 385 unique = false 386 next = c[rand.Intn(len(c))] 387 } else { 388 next = c[0] 389 } 390 if seen[next] >= 0 { 391 path = path[:seen[next]] 392 } 393 seen[next] = len(path) 394 path = append(path, p.nodes[next]) 395 to = next 396 } 397 weight = p.dist[p.indexOf[vid]] 398 } 399 400 ordered.Reverse(path) 401 return path, weight, unique 402 } 403 404 // AllTo returns all shortest paths to v and the weight of the paths. Paths 405 // containing zero-weight cycles are not returned. If a negative cycle exists between 406 // u and v, paths is returned nil and weight is returned as -Inf. 407 func (p ShortestAlts) AllTo(vid int64) (paths [][]graph.Node, weight float64) { 408 from := p.indexOf[p.from.ID()] 409 to, toOK := p.indexOf[vid] 410 if !toOK || len(p.next[to]) == 0 { 411 if p.from.ID() == vid { 412 return [][]graph.Node{{p.nodes[from]}}, 0 413 } 414 return nil, math.Inf(1) 415 } 416 417 _, weight, unique := p.To(vid) 418 if math.IsInf(weight, -1) && !unique { 419 return nil, math.Inf(-1) 420 } 421 422 seen := make([]bool, len(p.nodes)) 423 paths = p.allTo(from, to, seen, []graph.Node{p.nodes[to]}, nil) 424 weight = p.dist[to] 425 426 return paths, weight 427 } 428 429 // allTo recursively constructs a slice of paths extending from the node 430 // indexed into p.nodes by from to the node indexed by to. len(seen) must match 431 // the number of nodes held by the receiver. The path parameter is the current 432 // working path and the results are written into paths. 433 func (p ShortestAlts) allTo(from, to int, seen []bool, path []graph.Node, paths [][]graph.Node) [][]graph.Node { 434 seen[to] = true 435 if from == to { 436 if path == nil { 437 return paths 438 } 439 ordered.Reverse(path) 440 return append(paths, path) 441 } 442 first := true 443 for _, to := range p.next[to] { 444 if seen[to] { 445 continue 446 } 447 if first { 448 path = append([]graph.Node(nil), path...) 449 first = false 450 } 451 path = path[:len(path):len(path)] 452 paths = p.allTo(from, to, append([]bool(nil), seen...), append(path, p.nodes[to]), paths) 453 } 454 return paths 455 } 456 457 // negEdge is a key into the negative costs map used by Shortest and ShortestAlts. 458 type negEdge struct{ from, to int } 459 460 // AllShortest is a shortest-path tree created by the DijkstraAllPaths, FloydWarshall 461 // or JohnsonAllPaths all-pairs shortest paths functions. 462 type AllShortest struct { 463 // nodes hold the nodes of the analysed 464 // graph. 465 nodes []graph.Node 466 // indexOf contains a mapping between 467 // the id-dense representation of the 468 // graph and the potentially id-sparse 469 // nodes held in nodes. 470 indexOf map[int64]int 471 472 // dist, next and forward represent 473 // the shortest paths between nodes. 474 // 475 // Indices into dist and next are 476 // mapped through indexOf. 477 // 478 // dist contains the pairwise 479 // distances between nodes. 480 // 481 // Internally, edges on negative 482 // cycles are given a special NaN 483 // weight, NaN(0xdefaced). 484 // This is returned to the user as 485 // -Inf. This approach allows -Inf 486 // weight edges on simple paths to be 487 // distinguished from -Inf weight 488 // paths that contain negative cycles. 489 // The distinction is visible to the 490 // user through whether then path 491 // returned with a -Inf weight is 492 // nil or contains a set of nodes. 493 dist *mat.Dense 494 // next contains the shortest-path 495 // tree of the graph. The first index 496 // is a linear mapping of from-dense-id 497 // and to-dense-id, to-major with a 498 // stride equal to len(nodes); the 499 // slice indexed to is the list of 500 // intermediates leading from the 'from' 501 // node to the 'to' node represented 502 // by dense id. 503 // The interpretation of next is 504 // dependent on the state of forward. 505 next [][]int 506 // forward indicates the direction of 507 // path reconstruction. Forward 508 // reconstruction is used for Floyd- 509 // Warshall and reverse is used for 510 // Dijkstra. 511 forward bool 512 } 513 514 var ( 515 // defaced is NaN(0xdefaced) used as a marker for -Inf weight edges 516 // within paths containing negative cycles. Routines marking these 517 // edges should use this value. 518 defaced = scalar.NaNWith(0xdefaced) 519 // defacedBits is the bit pattern we look for in AllShortest to 520 // identify the edges. 521 defacedBits = math.Float64bits(defaced) 522 ) 523 524 // newAllShortest returns an all-pairs shortest path forest for paths with the 525 // given nodes. The forward flag indicates whether the path reconstruction is 526 // performed in the forward (Floyd-Warshall) or reverse (Dijkstra/Johnson's) order. 527 func newAllShortest(nodes []graph.Node, forward bool) AllShortest { 528 if len(nodes) == 0 { 529 return AllShortest{} 530 } 531 indexOf := make(map[int64]int, len(nodes)) 532 for i, n := range nodes { 533 indexOf[n.ID()] = i 534 } 535 dist := make([]float64, len(nodes)*len(nodes)) 536 for i := range dist { 537 dist[i] = math.Inf(1) 538 } 539 return AllShortest{ 540 nodes: nodes, 541 indexOf: indexOf, 542 543 dist: mat.NewDense(len(nodes), len(nodes), dist), 544 next: make([][]int, len(nodes)*len(nodes)), 545 forward: forward, 546 } 547 } 548 549 // at returns a slice of node indexes into p.nodes for nodes that are mid points 550 // between nodes indexed by from and to. 551 func (p AllShortest) at(from, to int) (mid []int) { 552 return p.next[from+to*len(p.nodes)] 553 } 554 555 // set sets the weights of paths between node indexes into p.nodes for from and to 556 // passing through the nodes indexed by mid. 557 func (p AllShortest) set(from, to int, weight float64, mid ...int) { 558 p.dist.Set(from, to, weight) 559 p.next[from+to*len(p.nodes)] = append(p.next[from+to*len(p.nodes)][:0], mid...) 560 } 561 562 // add adds paths between node indexed in p.nodes by from and to passing through 563 // the nodes indexed by mid. 564 func (p AllShortest) add(from, to int, mid ...int) { 565 loop: // These are likely to be rare, so just loop over collisions. 566 for _, k := range mid { 567 for _, v := range p.next[from+to*len(p.nodes)] { 568 if k == v { 569 continue loop 570 } 571 } 572 p.next[from+to*len(p.nodes)] = append(p.next[from+to*len(p.nodes)], k) 573 } 574 } 575 576 // Weight returns the weight of the minimum path between u and v. 577 func (p AllShortest) Weight(uid, vid int64) float64 { 578 from, fromOK := p.indexOf[uid] 579 to, toOK := p.indexOf[vid] 580 if !fromOK || !toOK { 581 return math.Inf(1) 582 } 583 w := p.dist.At(from, to) 584 if math.Float64bits(w) == defacedBits { 585 return math.Inf(-1) 586 } 587 return w 588 } 589 590 // Between returns a shortest path from u to v and the weight of the path. If more than 591 // one shortest path exists between u and v, a randomly chosen path will be returned and 592 // unique is returned false. If a cycle with zero weight exists in the path, it will not 593 // be included, but unique will be returned false. If a negative cycle exists on the path 594 // from u to v, path will be returned nil, weight will be -Inf and unique will be false. 595 func (p AllShortest) Between(uid, vid int64) (path []graph.Node, weight float64, unique bool) { 596 from, fromOK := p.indexOf[uid] 597 to, toOK := p.indexOf[vid] 598 if !fromOK || !toOK || len(p.at(from, to)) == 0 { 599 if uid == vid { 600 if !fromOK { 601 return []graph.Node{node(uid)}, 0, true 602 } 603 return []graph.Node{p.nodes[from]}, 0, true 604 } 605 return nil, math.Inf(1), false 606 } 607 608 weight = p.dist.At(from, to) 609 if math.Float64bits(weight) == defacedBits { 610 return nil, math.Inf(-1), false 611 } 612 613 seen := make([]int, len(p.nodes)) 614 for i := range seen { 615 seen[i] = -1 616 } 617 var n graph.Node 618 if p.forward { 619 n = p.nodes[from] 620 seen[from] = 0 621 } else { 622 n = p.nodes[to] 623 seen[to] = 0 624 } 625 626 path = []graph.Node{n} 627 unique = true 628 629 var next int 630 for from != to { 631 c := p.at(from, to) 632 if len(c) != 1 { 633 unique = false 634 next = c[rand.Intn(len(c))] 635 } else { 636 next = c[0] 637 } 638 if seen[next] >= 0 { 639 path = path[:seen[next]] 640 } 641 seen[next] = len(path) 642 path = append(path, p.nodes[next]) 643 if p.forward { 644 from = next 645 } else { 646 to = next 647 } 648 } 649 if !p.forward { 650 ordered.Reverse(path) 651 } 652 653 return path, weight, unique 654 } 655 656 // AllBetween returns all shortest paths from u to v and the weight of the paths. Paths 657 // containing zero-weight cycles are not returned. If a negative cycle exists between 658 // u and v, paths is returned nil and weight is returned as -Inf. 659 func (p AllShortest) AllBetween(uid, vid int64) (paths [][]graph.Node, weight float64) { 660 from, fromOK := p.indexOf[uid] 661 to, toOK := p.indexOf[vid] 662 if !fromOK || !toOK || len(p.at(from, to)) == 0 { 663 if uid == vid { 664 if !fromOK { 665 return [][]graph.Node{{node(uid)}}, 0 666 } 667 return [][]graph.Node{{p.nodes[from]}}, 0 668 } 669 return nil, math.Inf(1) 670 } 671 672 weight = p.dist.At(from, to) 673 if math.Float64bits(weight) == defacedBits { 674 return nil, math.Inf(-1) 675 } 676 677 var n graph.Node 678 if p.forward { 679 n = p.nodes[from] 680 } else { 681 n = p.nodes[to] 682 } 683 seen := make([]bool, len(p.nodes)) 684 paths = p.allBetween(from, to, seen, []graph.Node{n}, nil) 685 686 return paths, weight 687 } 688 689 // allBetween recursively constructs a slice of paths extending from the node 690 // indexed into p.nodes by from to the node indexed by to. len(seen) must match 691 // the number of nodes held by the receiver. The path parameter is the current 692 // working path and the results are written into paths. 693 func (p AllShortest) allBetween(from, to int, seen []bool, path []graph.Node, paths [][]graph.Node) [][]graph.Node { 694 if p.forward { 695 seen[from] = true 696 } else { 697 seen[to] = true 698 } 699 if from == to { 700 if path == nil { 701 return paths 702 } 703 if !p.forward { 704 ordered.Reverse(path) 705 } 706 return append(paths, path) 707 } 708 first := true 709 for _, n := range p.at(from, to) { 710 if seen[n] { 711 continue 712 } 713 if first { 714 path = append([]graph.Node(nil), path...) 715 first = false 716 } 717 if p.forward { 718 from = n 719 } else { 720 to = n 721 } 722 path = path[:len(path):len(path)] 723 paths = p.allBetween(from, to, append([]bool(nil), seen...), append(path, p.nodes[n]), paths) 724 } 725 return paths 726 } 727 728 type node int64 729 730 func (n node) ID() int64 { return int64(n) }