github.com/gopherd/gonum@v0.0.4/graph/path/johnson_apsp.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  	"math/rand"
    11  
    12  	"github.com/gopherd/gonum/graph"
    13  	"github.com/gopherd/gonum/graph/simple"
    14  )
    15  
    16  // JohnsonAllPaths returns a shortest-path tree for shortest paths in the graph g.
    17  // If the graph does not implement Weighted, UniformCost is used. If a negative cycle
    18  // exists in g, ok will be returned false and paths will not contain valid data.
    19  //
    20  // The time complexity of JohnsonAllPaths is O(|V|.|E|+|V|^2.log|V|).
    21  func JohnsonAllPaths(g graph.Graph) (paths AllShortest, ok bool) {
    22  	adjusted := johnsonWeightAdjuster{Graph: g}
    23  	if wg, ok := g.(Weighted); ok {
    24  		adjusted.weight = wg.Weight
    25  	} else {
    26  		adjusted.weight = UniformCost(g)
    27  	}
    28  
    29  	paths = newAllShortest(graph.NodesOf(g.Nodes()), false)
    30  
    31  	var q int64
    32  	sign := int64(-1)
    33  	for {
    34  		// Choose a random node ID until we find
    35  		// one that is not in g.
    36  		q = sign * rand.Int63()
    37  		if _, exists := paths.indexOf[q]; !exists {
    38  			break
    39  		}
    40  		sign *= -1
    41  	}
    42  
    43  	adjusted.adjustBy, ok = BellmanFordFrom(johnsonGraphNode(q), johnsonReWeight{adjusted, q})
    44  	if !ok {
    45  		return paths, false
    46  	}
    47  
    48  	dijkstraAllPaths(adjusted, paths)
    49  
    50  	for i, u := range paths.nodes {
    51  		hu := adjusted.adjustBy.WeightTo(u.ID())
    52  		for j, v := range paths.nodes {
    53  			if i == j {
    54  				continue
    55  			}
    56  			hv := adjusted.adjustBy.WeightTo(v.ID())
    57  			paths.dist.Set(i, j, paths.dist.At(i, j)-hu+hv)
    58  		}
    59  	}
    60  
    61  	return paths, ok
    62  }
    63  
    64  // johnsonWeightAdjuster is an edge re-weighted graph constructed
    65  // by the first phase of the Johnson algorithm such that no negative
    66  // edge weights exist in the graph.
    67  type johnsonWeightAdjuster struct {
    68  	graph.Graph
    69  	weight Weighting
    70  
    71  	adjustBy Shortest
    72  }
    73  
    74  var _ graph.Weighted = johnsonWeightAdjuster{}
    75  
    76  func (g johnsonWeightAdjuster) Node(id int64) graph.Node {
    77  	panic("path: unintended use of johnsonWeightAdjuster")
    78  }
    79  
    80  func (g johnsonWeightAdjuster) WeightedEdge(_, _ int64) graph.WeightedEdge {
    81  	panic("path: unintended use of johnsonWeightAdjuster")
    82  }
    83  
    84  func (g johnsonWeightAdjuster) Weight(xid, yid int64) (w float64, ok bool) {
    85  	w, ok = g.weight(xid, yid)
    86  	return w + g.adjustBy.WeightTo(xid) - g.adjustBy.WeightTo(yid), ok
    87  }
    88  
    89  func (johnsonWeightAdjuster) HasEdgeBetween(_, _ int64) bool {
    90  	panic("path: unintended use of johnsonWeightAdjuster")
    91  }
    92  
    93  // johnsonReWeight provides a query node to allow edge re-weighting
    94  // using the Bellman-Ford algorithm for the first phase of the
    95  // Johnson algorithm.
    96  type johnsonReWeight struct {
    97  	johnsonWeightAdjuster
    98  	q int64
    99  }
   100  
   101  func (g johnsonReWeight) Node(id int64) graph.Node {
   102  	if id != g.q {
   103  		panic("path: unintended use of johnsonReWeight")
   104  	}
   105  	return simple.Node(id)
   106  }
   107  
   108  func (g johnsonReWeight) Nodes() graph.Nodes {
   109  	return newJohnsonNodeIterator(g.q, g.Graph.Nodes())
   110  }
   111  
   112  func (g johnsonReWeight) From(id int64) graph.Nodes {
   113  	if id == g.q {
   114  		return g.Graph.Nodes()
   115  	}
   116  	return g.Graph.From(id)
   117  }
   118  
   119  func (g johnsonReWeight) Edge(uid, vid int64) graph.Edge {
   120  	if uid == g.q && g.Graph.Node(vid) != nil {
   121  		return simple.Edge{F: johnsonGraphNode(g.q), T: simple.Node(vid)}
   122  	}
   123  	return g.Graph.Edge(uid, vid)
   124  }
   125  
   126  func (g johnsonReWeight) Weight(xid, yid int64) (w float64, ok bool) {
   127  	switch g.q {
   128  	case xid:
   129  		return 0, true
   130  	case yid:
   131  		return math.Inf(1), false
   132  	default:
   133  		return g.weight(xid, yid)
   134  	}
   135  }
   136  
   137  type johnsonGraphNode int64
   138  
   139  func (n johnsonGraphNode) ID() int64 { return int64(n) }
   140  
   141  func newJohnsonNodeIterator(q int64, nodes graph.Nodes) *johnsonNodeIterator {
   142  	return &johnsonNodeIterator{q: q, nodes: nodes}
   143  }
   144  
   145  type johnsonNodeIterator struct {
   146  	q          int64
   147  	nodes      graph.Nodes
   148  	qUsed, qOK bool
   149  }
   150  
   151  func (it *johnsonNodeIterator) Len() int {
   152  	var len int
   153  	if it.nodes != nil {
   154  		len = it.nodes.Len()
   155  		if len < 0 {
   156  			return len
   157  		}
   158  	}
   159  	if !it.qUsed {
   160  		len++
   161  	}
   162  	return len
   163  }
   164  
   165  func (it *johnsonNodeIterator) Next() bool {
   166  	if it.nodes != nil {
   167  		ok := it.nodes.Next()
   168  		if ok {
   169  			return true
   170  		}
   171  	}
   172  	if !it.qUsed {
   173  		it.qOK = true
   174  		it.qUsed = true
   175  		return true
   176  	}
   177  	it.qOK = false
   178  	return false
   179  }
   180  
   181  func (it *johnsonNodeIterator) Node() graph.Node {
   182  	if it.qOK {
   183  		return johnsonGraphNode(it.q)
   184  	}
   185  	if it.nodes == nil {
   186  		return nil
   187  	}
   188  	return it.nodes.Node()
   189  }
   190  
   191  func (it *johnsonNodeIterator) Reset() {
   192  	it.qOK = false
   193  	it.qUsed = false
   194  	if it.nodes == nil {
   195  		return
   196  	}
   197  	it.nodes.Reset()
   198  }