| // 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" |
| |
| "golang.org/x/exp/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 |
| } |
| |
| // add adds a node to the Shortest, initialising its stored index and returning, and |
| // setting the distance and position as unconnected. add will panic if the node is |
| // already present. |
| func (p *Shortest) add(u graph.Node) int { |
| uid := u.ID() |
| if _, exists := p.indexOf[uid]; exists { |
| panic("shortest: adding existing node") |
| } |
| idx := len(p.nodes) |
| p.indexOf[uid] = idx |
| p.nodes = append(p.nodes, u) |
| p.dist = append(p.dist, math.Inf(1)) |
| p.next = append(p.next, -1) |
| return idx |
| } |
| |
| 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(vid int64) float64 { |
| to, toOK := p.indexOf[vid] |
| 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(vid int64) (path []graph.Node, weight float64) { |
| to, toOK := p.indexOf[vid] |
| 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[vid]]) |
| } |
| |
| // 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 { |
| if len(nodes) == 0 { |
| return 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(uid, vid int64) float64 { |
| from, fromOK := p.indexOf[uid] |
| to, toOK := p.indexOf[vid] |
| 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(uid, vid int64) (path []graph.Node, weight float64, unique bool) { |
| from, fromOK := p.indexOf[uid] |
| to, toOK := p.indexOf[vid] |
| if !fromOK || !toOK || len(p.at(from, to)) == 0 { |
| if uid == vid { |
| 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(uid, vid int64) (paths [][]graph.Node, weight float64) { |
| from, fromOK := p.indexOf[uid] |
| to, toOK := p.indexOf[vid] |
| if !fromOK || !toOK || len(p.at(from, to)) == 0 { |
| if uid == vid { |
| return [][]graph.Node{{p.nodes[from]}}, 0 |
| } |
| return nil, math.Inf(1) |
| } |
| |
| var n graph.Node |
| if p.forward { |
| n = p.nodes[from] |
| } else { |
| n = p.nodes[to] |
| } |
| 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 |
| } |