From ee7dd3742a59d7053f9d3fb25ef5a99b69f83c3f Mon Sep 17 00:00:00 2001 From: Dan Kortschak Date: Thu, 7 Feb 2019 20:57:33 +1030 Subject: [PATCH] graph/path: indicate negative cycle weights with -Inf instead of NaN The paths are undefined, but the limit of the path weight is -Inf. --- graph/path/floydwarshall.go | 10 +++++----- graph/path/negative_cycles_example_test.go | 6 +++--- graph/path/shortest.go | 12 ++++++------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/graph/path/floydwarshall.go b/graph/path/floydwarshall.go index 887a0d76..749e721d 100644 --- a/graph/path/floydwarshall.go +++ b/graph/path/floydwarshall.go @@ -13,7 +13,7 @@ import ( // FloydWarshall returns a shortest-path tree for the graph g or false indicating // that a negative cycle exists in the graph. If a negative cycle exists in the graph // the returned paths will be valid and edge weights on the negative cycle will be -// set to NaN. If the graph does not implement Weighted, UniformCost is used. +// set to -Inf. If the graph does not implement Weighted, UniformCost is used. // // The time complexity of FloydWarshall is O(|V|^3). func FloydWarshall(g graph.Graph) (paths AllShortest, ok bool) { @@ -65,7 +65,7 @@ func FloydWarshall(g graph.Graph) (paths AllShortest, ok bool) { if !ok { // If we have a negative cycle, mark all - // the edges in the cycles with NaN weight. + // the edges in the cycles with -Inf weight. d := paths.dist for i := range nodes { for j := range nodes { @@ -73,9 +73,9 @@ func FloydWarshall(g graph.Graph) (paths AllShortest, ok bool) { if math.IsInf(d.At(i, k), 1) || math.IsInf(d.At(k, j), 1) { continue } - if d.At(k, k) < 0 || math.IsNaN(d.At(k, k)) { - d.Set(k, k, math.NaN()) - d.Set(i, j, math.NaN()) + if d.At(k, k) < 0 { + d.Set(k, k, math.Inf(-1)) + d.Set(i, j, math.Inf(-1)) } } } diff --git a/graph/path/negative_cycles_example_test.go b/graph/path/negative_cycles_example_test.go index 579c4b5c..fe36da8d 100644 --- a/graph/path/negative_cycles_example_test.go +++ b/graph/path/negative_cycles_example_test.go @@ -46,7 +46,7 @@ func ExampleBellmanFordFrom_negativecycles() { } for _, id := range []int64{'a', 'b', 'c', 'd', 'e', 'f'} { p, w := pt.To(id) - if math.IsNaN(w) { + if math.IsInf(w, -1) { fmt.Printf("negative cycle in path to %c path:%c\n", id, p) } } @@ -87,7 +87,7 @@ func ExampleFloydWarshall_negativecycles() { ids := []int64{'a', 'b', 'c', 'd', 'e', 'f'} for _, id := range ids { - if math.IsNaN(pt.Weight(id, id)) { + if math.IsInf(pt.Weight(id, id), -1) { fmt.Printf("%c is in a negative cycle\n", id) } } @@ -95,7 +95,7 @@ func ExampleFloydWarshall_negativecycles() { for _, uid := range ids { for _, vid := range ids { _, w, unique := pt.Between(uid, vid) - if math.IsNaN(w) { + if math.IsInf(w, -1) { fmt.Printf("negative cycle in path from %c to %c unique=%t\n", uid, vid, unique) } } diff --git a/graph/path/shortest.go b/graph/path/shortest.go index 325203ec..c4284e29 100644 --- a/graph/path/shortest.go +++ b/graph/path/shortest.go @@ -119,7 +119,7 @@ func (p Shortest) WeightTo(vid int64) float64 { // 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. +// in path and weight will be returned as -Inf. func (p Shortest) To(vid int64) (path []graph.Node, weight float64) { to, toOK := p.indexOf[vid] if !toOK || math.IsInf(p.dist[to], 1) { @@ -133,7 +133,7 @@ func (p Shortest) To(vid int64) (path []graph.Node, weight float64) { seen.Add(from) for to != from { if seen.Has(to) { - weight = math.NaN() + weight = math.Inf(-1) break } seen.Add(to) @@ -253,7 +253,7 @@ func (p AllShortest) Weight(uid, vid int64) float64 { // 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. If a negative cycle exists on the path -// from u to v, path will be returned nil, weight will be NaN and unique will be false. +// from u to v, path will be returned nil, weight will be -Inf and unique will be 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] @@ -265,7 +265,7 @@ func (p AllShortest) Between(uid, vid int64) (path []graph.Node, weight float64, } weight = p.dist.At(from, to) - if math.IsNaN(weight) { + if math.IsInf(weight, -1) { return nil, weight, false } @@ -314,7 +314,7 @@ func (p AllShortest) Between(uid, vid int64) (path []graph.Node, weight float64, // AllBetween returns all shortest paths from u to v and the weight of the paths. Paths // containing zero-weight cycles are not returned. If a negative cycle exists between -// u and v, paths is returned nil and weight is returned as NaN. +// u and v, paths is returned nil and weight is returned as -Inf. func (p AllShortest) AllBetween(uid, vid int64) (paths [][]graph.Node, weight float64) { from, fromOK := p.indexOf[uid] to, toOK := p.indexOf[vid] @@ -326,7 +326,7 @@ func (p AllShortest) AllBetween(uid, vid int64) (paths [][]graph.Node, weight fl } weight = p.dist.At(from, to) - if math.IsNaN(weight) { + if math.IsInf(weight, -1) { return nil, weight }