mirror of
https://github.com/gonum/gonum.git
synced 2025-10-15 19:50:48 +08:00

Also add checks for cases where negative cycles exist but are not marked to protect against future shortest path function additions.
347 lines
8.3 KiB
Go
347 lines
8.3 KiB
Go
// Copyright ©2015 The gonum Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package path
|
|
|
|
import (
|
|
"math"
|
|
"math/rand"
|
|
|
|
"gonum.org/v1/gonum/graph"
|
|
"gonum.org/v1/gonum/graph/internal/ordered"
|
|
"gonum.org/v1/gonum/graph/internal/set"
|
|
"gonum.org/v1/gonum/mat"
|
|
)
|
|
|
|
// Shortest is a shortest-path tree created by the BellmanFordFrom or DijkstraFrom
|
|
// single-source shortest path functions.
|
|
type Shortest struct {
|
|
// from holds the source node given to
|
|
// the function that returned the
|
|
// Shortest value.
|
|
from graph.Node
|
|
|
|
// nodes hold the nodes of the analysed
|
|
// graph.
|
|
nodes []graph.Node
|
|
// indexOf contains a mapping between
|
|
// the id-dense representation of the
|
|
// graph and the potentially id-sparse
|
|
// nodes held in nodes.
|
|
indexOf map[int64]int
|
|
|
|
// dist and next represent the shortest
|
|
// paths between nodes.
|
|
//
|
|
// Indices into dist and next are
|
|
// mapped through indexOf.
|
|
//
|
|
// dist contains the distances
|
|
// from the from node for each
|
|
// node in the graph.
|
|
dist []float64
|
|
// next contains the shortest-path
|
|
// tree of the graph. The index is a
|
|
// linear mapping of to-dense-id.
|
|
next []int
|
|
|
|
// hasNegativeCycle indicates
|
|
// whether the Shortest includes
|
|
// a negative cycle. This should
|
|
// be set by the function that
|
|
// returned the Shortest value.
|
|
hasNegativeCycle bool
|
|
}
|
|
|
|
func newShortestFrom(u graph.Node, nodes []graph.Node) Shortest {
|
|
indexOf := make(map[int64]int, len(nodes))
|
|
uid := u.ID()
|
|
for i, n := range nodes {
|
|
indexOf[n.ID()] = i
|
|
if n.ID() == uid {
|
|
u = n
|
|
}
|
|
}
|
|
|
|
p := Shortest{
|
|
from: u,
|
|
|
|
nodes: nodes,
|
|
indexOf: indexOf,
|
|
|
|
dist: make([]float64, len(nodes)),
|
|
next: make([]int, len(nodes)),
|
|
}
|
|
for i := range nodes {
|
|
p.dist[i] = math.Inf(1)
|
|
p.next[i] = -1
|
|
}
|
|
p.dist[indexOf[uid]] = 0
|
|
|
|
return p
|
|
}
|
|
|
|
func (p Shortest) set(to int, weight float64, mid int) {
|
|
p.dist[to] = weight
|
|
p.next[to] = mid
|
|
}
|
|
|
|
// From returns the starting node of the paths held by the Shortest.
|
|
func (p Shortest) From() graph.Node { return p.from }
|
|
|
|
// WeightTo returns the weight of the minimum path to v. If the path to v includes
|
|
// a negative cycle, the returned weight will not reflect the true path weight.
|
|
func (p Shortest) WeightTo(v graph.Node) float64 {
|
|
to, toOK := p.indexOf[v.ID()]
|
|
if !toOK {
|
|
return math.Inf(1)
|
|
}
|
|
return p.dist[to]
|
|
}
|
|
|
|
// To returns a shortest path to v and the weight of the path. If the path
|
|
// to v includes a negative cycle, one pass through the cycle will be included
|
|
// in path and weight will be returned as NaN.
|
|
func (p Shortest) To(v graph.Node) (path []graph.Node, weight float64) {
|
|
to, toOK := p.indexOf[v.ID()]
|
|
if !toOK || math.IsInf(p.dist[to], 1) {
|
|
return nil, math.Inf(1)
|
|
}
|
|
from := p.indexOf[p.from.ID()]
|
|
path = []graph.Node{p.nodes[to]}
|
|
weight = math.Inf(1)
|
|
if p.hasNegativeCycle {
|
|
seen := make(set.Ints)
|
|
seen.Add(from)
|
|
for to != from {
|
|
if seen.Has(to) {
|
|
weight = math.NaN()
|
|
break
|
|
}
|
|
seen.Add(to)
|
|
path = append(path, p.nodes[p.next[to]])
|
|
to = p.next[to]
|
|
}
|
|
} else {
|
|
n := len(p.nodes)
|
|
for to != from {
|
|
path = append(path, p.nodes[p.next[to]])
|
|
to = p.next[to]
|
|
if n < 0 {
|
|
panic("path: unexpected negative cycle")
|
|
}
|
|
n--
|
|
}
|
|
}
|
|
ordered.Reverse(path)
|
|
return path, math.Min(weight, p.dist[p.indexOf[v.ID()]])
|
|
}
|
|
|
|
// AllShortest is a shortest-path tree created by the DijkstraAllPaths, FloydWarshall
|
|
// or JohnsonAllPaths all-pairs shortest paths functions.
|
|
type AllShortest struct {
|
|
// nodes hold the nodes of the analysed
|
|
// graph.
|
|
nodes []graph.Node
|
|
// indexOf contains a mapping between
|
|
// the id-dense representation of the
|
|
// graph and the potentially id-sparse
|
|
// nodes held in nodes.
|
|
indexOf map[int64]int
|
|
|
|
// dist, next and forward represent
|
|
// the shortest paths between nodes.
|
|
//
|
|
// Indices into dist and next are
|
|
// mapped through indexOf.
|
|
//
|
|
// dist contains the pairwise
|
|
// distances between nodes.
|
|
dist *mat.Dense
|
|
// next contains the shortest-path
|
|
// tree of the graph. The first index
|
|
// is a linear mapping of from-dense-id
|
|
// and to-dense-id, to-major with a
|
|
// stride equal to len(nodes); the
|
|
// slice indexed to is the list of
|
|
// intermediates leading from the 'from'
|
|
// node to the 'to' node represented
|
|
// by dense id.
|
|
// The interpretation of next is
|
|
// dependent on the state of forward.
|
|
next [][]int
|
|
// forward indicates the direction of
|
|
// path reconstruction. Forward
|
|
// reconstruction is used for Floyd-
|
|
// Warshall and reverse is used for
|
|
// Dijkstra.
|
|
forward bool
|
|
}
|
|
|
|
func newAllShortest(nodes []graph.Node, forward bool) AllShortest {
|
|
indexOf := make(map[int64]int, len(nodes))
|
|
for i, n := range nodes {
|
|
indexOf[n.ID()] = i
|
|
}
|
|
dist := make([]float64, len(nodes)*len(nodes))
|
|
for i := range dist {
|
|
dist[i] = math.Inf(1)
|
|
}
|
|
return AllShortest{
|
|
nodes: nodes,
|
|
indexOf: indexOf,
|
|
|
|
dist: mat.NewDense(len(nodes), len(nodes), dist),
|
|
next: make([][]int, len(nodes)*len(nodes)),
|
|
forward: forward,
|
|
}
|
|
}
|
|
|
|
func (p AllShortest) at(from, to int) (mid []int) {
|
|
return p.next[from+to*len(p.nodes)]
|
|
}
|
|
|
|
func (p AllShortest) set(from, to int, weight float64, mid ...int) {
|
|
p.dist.Set(from, to, weight)
|
|
p.next[from+to*len(p.nodes)] = append(p.next[from+to*len(p.nodes)][:0], mid...)
|
|
}
|
|
|
|
func (p AllShortest) add(from, to int, mid ...int) {
|
|
loop: // These are likely to be rare, so just loop over collisions.
|
|
for _, k := range mid {
|
|
for _, v := range p.next[from+to*len(p.nodes)] {
|
|
if k == v {
|
|
continue loop
|
|
}
|
|
}
|
|
p.next[from+to*len(p.nodes)] = append(p.next[from+to*len(p.nodes)], k)
|
|
}
|
|
}
|
|
|
|
// Weight returns the weight of the minimum path between u and v.
|
|
func (p AllShortest) Weight(u, v graph.Node) float64 {
|
|
from, fromOK := p.indexOf[u.ID()]
|
|
to, toOK := p.indexOf[v.ID()]
|
|
if !fromOK || !toOK {
|
|
return math.Inf(1)
|
|
}
|
|
return p.dist.At(from, to)
|
|
}
|
|
|
|
// Between returns a shortest path from u to v and the weight of the path. If more than
|
|
// one shortest path exists between u and v, a randomly chosen path will be returned and
|
|
// unique is returned false. If a cycle with zero weight exists in the path, it will not
|
|
// be included, but unique will be returned false.
|
|
func (p AllShortest) Between(u, v graph.Node) (path []graph.Node, weight float64, unique bool) {
|
|
from, fromOK := p.indexOf[u.ID()]
|
|
to, toOK := p.indexOf[v.ID()]
|
|
if !fromOK || !toOK || len(p.at(from, to)) == 0 {
|
|
if u.ID() == v.ID() {
|
|
return []graph.Node{p.nodes[from]}, 0, true
|
|
}
|
|
return nil, math.Inf(1), false
|
|
}
|
|
|
|
seen := make([]int, len(p.nodes))
|
|
for i := range seen {
|
|
seen[i] = -1
|
|
}
|
|
var n graph.Node
|
|
if p.forward {
|
|
n = p.nodes[from]
|
|
seen[from] = 0
|
|
} else {
|
|
n = p.nodes[to]
|
|
seen[to] = 0
|
|
}
|
|
|
|
path = []graph.Node{n}
|
|
weight = p.dist.At(from, to)
|
|
unique = true
|
|
|
|
var next int
|
|
for from != to {
|
|
c := p.at(from, to)
|
|
if len(c) != 1 {
|
|
unique = false
|
|
next = c[rand.Intn(len(c))]
|
|
} else {
|
|
next = c[0]
|
|
}
|
|
if seen[next] >= 0 {
|
|
path = path[:seen[next]]
|
|
}
|
|
seen[next] = len(path)
|
|
path = append(path, p.nodes[next])
|
|
if p.forward {
|
|
from = next
|
|
} else {
|
|
to = next
|
|
}
|
|
}
|
|
if !p.forward {
|
|
ordered.Reverse(path)
|
|
}
|
|
|
|
return path, weight, unique
|
|
}
|
|
|
|
// AllBetween returns all shortest paths from u to v and the weight of the paths. Paths
|
|
// containing zero-weight cycles are not returned.
|
|
func (p AllShortest) AllBetween(u, v graph.Node) (paths [][]graph.Node, weight float64) {
|
|
from, fromOK := p.indexOf[u.ID()]
|
|
to, toOK := p.indexOf[v.ID()]
|
|
if !fromOK || !toOK || len(p.at(from, to)) == 0 {
|
|
if u.ID() == v.ID() {
|
|
return [][]graph.Node{{p.nodes[from]}}, 0
|
|
}
|
|
return nil, math.Inf(1)
|
|
}
|
|
|
|
var n graph.Node
|
|
if p.forward {
|
|
n = u
|
|
} else {
|
|
n = v
|
|
}
|
|
seen := make([]bool, len(p.nodes))
|
|
paths = p.allBetween(from, to, seen, []graph.Node{n}, nil)
|
|
|
|
return paths, p.dist.At(from, to)
|
|
}
|
|
|
|
func (p AllShortest) allBetween(from, to int, seen []bool, path []graph.Node, paths [][]graph.Node) [][]graph.Node {
|
|
if p.forward {
|
|
seen[from] = true
|
|
} else {
|
|
seen[to] = true
|
|
}
|
|
if from == to {
|
|
if path == nil {
|
|
return paths
|
|
}
|
|
if !p.forward {
|
|
ordered.Reverse(path)
|
|
}
|
|
return append(paths, path)
|
|
}
|
|
first := true
|
|
for _, n := range p.at(from, to) {
|
|
if seen[n] {
|
|
continue
|
|
}
|
|
if first {
|
|
path = append([]graph.Node(nil), path...)
|
|
first = false
|
|
}
|
|
if p.forward {
|
|
from = n
|
|
} else {
|
|
to = n
|
|
}
|
|
paths = p.allBetween(from, to, append([]bool(nil), seen...), append(path, p.nodes[n]), paths)
|
|
}
|
|
return paths
|
|
}
|