github.com/gopherd/gonum@v0.0.4/graph/path/floydwarshall.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 "github.com/gopherd/gonum/graph" 11 ) 12 13 // FloydWarshall returns a shortest-path tree for the graph g or false indicating 14 // that a negative cycle exists in the graph. If a negative cycle exists in the graph 15 // the returned paths will be valid and edge weights on the negative cycle will be 16 // set to -Inf. If the graph does not implement Weighted, UniformCost is used. 17 // 18 // The time complexity of FloydWarshall is O(|V|^3). 19 func FloydWarshall(g graph.Graph) (paths AllShortest, ok bool) { 20 var weight Weighting 21 if wg, ok := g.(Weighted); ok { 22 weight = wg.Weight 23 } else { 24 weight = UniformCost(g) 25 } 26 27 nodes := graph.NodesOf(g.Nodes()) 28 paths = newAllShortest(nodes, true) 29 for i, u := range nodes { 30 paths.dist.Set(i, i, 0) 31 uid := u.ID() 32 to := g.From(uid) 33 for to.Next() { 34 vid := to.Node().ID() 35 j := paths.indexOf[vid] 36 w, ok := weight(uid, vid) 37 if !ok { 38 panic("floyd-warshall: unexpected invalid weight") 39 } 40 paths.set(i, j, w, j) 41 } 42 } 43 44 for k := range nodes { 45 for i := range nodes { 46 for j := range nodes { 47 ij := paths.dist.At(i, j) 48 joint := paths.dist.At(i, k) + paths.dist.At(k, j) 49 if ij > joint { 50 paths.set(i, j, joint, paths.at(i, k)...) 51 } else if ij-joint == 0 { 52 paths.add(i, j, paths.at(i, k)...) 53 } 54 } 55 } 56 } 57 58 ok = true 59 for i := range nodes { 60 if paths.dist.At(i, i) < 0 { 61 ok = false 62 break 63 } 64 } 65 66 if !ok { 67 // If we have a negative cycle, mark all 68 // the edges in the cycles with NaN(0xdefaced) 69 // weight. These weights are internal, being 70 // returned as -Inf in user calls. 71 72 d := paths.dist 73 for i := range nodes { 74 for j := range nodes { 75 for k := range nodes { 76 if math.IsInf(d.At(i, k), 1) || math.IsInf(d.At(k, j), 1) { 77 continue 78 } 79 if d.At(k, k) < 0 { 80 d.Set(k, k, defaced) 81 d.Set(i, j, defaced) 82 } else if math.Float64bits(d.At(k, k)) == defacedBits { 83 d.Set(i, j, defaced) 84 } 85 } 86 } 87 } 88 } 89 90 return paths, ok 91 }