gonum.org/v1/gonum@v0.14.0/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  	"gonum.org/v1/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  }