gonum.org/v1/gonum@v0.14.0/graph/path/dijkstra.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  	"container/heap"
     9  
    10  	"gonum.org/v1/gonum/graph"
    11  	"gonum.org/v1/gonum/graph/traverse"
    12  )
    13  
    14  // DijkstraFrom returns a shortest-path tree for a shortest path from u to all nodes in
    15  // the graph g. If the graph does not implement Weighted, UniformCost is used.
    16  // DijkstraFrom will panic if g has a u-reachable negative edge weight.
    17  //
    18  // If g is a graph.Graph, all nodes of the graph will be stored in the shortest-path
    19  // tree, otherwise only nodes reachable from u will be stored.
    20  //
    21  // The time complexity of DijkstraFrom is O(|E|.log|V|).
    22  func DijkstraFrom(u graph.Node, g traverse.Graph) Shortest {
    23  	var path Shortest
    24  	if h, ok := g.(graph.Graph); ok {
    25  		if h.Node(u.ID()) == nil {
    26  			return Shortest{from: u}
    27  		}
    28  		path = newShortestFrom(u, graph.NodesOf(h.Nodes()))
    29  	} else {
    30  		if g.From(u.ID()) == graph.Empty {
    31  			return Shortest{from: u}
    32  		}
    33  		path = newShortestFrom(u, []graph.Node{u})
    34  	}
    35  
    36  	var weight Weighting
    37  	if wg, ok := g.(Weighted); ok {
    38  		weight = wg.Weight
    39  	} else {
    40  		weight = UniformCost(g)
    41  	}
    42  
    43  	// Dijkstra's algorithm here is implemented essentially as
    44  	// described in Function B.2 in figure 6 of UTCS Technical
    45  	// Report TR-07-54.
    46  	//
    47  	// This implementation deviates from the report as follows:
    48  	// - the value of path.dist for the start vertex u is initialized to 0;
    49  	// - outdated elements from the priority queue (i.e. with respect to the dist value)
    50  	//   are skipped.
    51  	//
    52  	// http://www.cs.utexas.edu/ftp/techreports/tr07-54.pdf
    53  	Q := priorityQueue{{node: u, dist: 0}}
    54  	for Q.Len() != 0 {
    55  		mid := heap.Pop(&Q).(distanceNode)
    56  		k := path.indexOf[mid.node.ID()]
    57  		if mid.dist > path.dist[k] {
    58  			continue
    59  		}
    60  		mnid := mid.node.ID()
    61  		to := g.From(mnid)
    62  		for to.Next() {
    63  			v := to.Node()
    64  			vid := v.ID()
    65  			j, ok := path.indexOf[vid]
    66  			if !ok {
    67  				j = path.add(v)
    68  			}
    69  			w, ok := weight(mnid, vid)
    70  			if !ok {
    71  				panic("dijkstra: unexpected invalid weight")
    72  			}
    73  			if w < 0 {
    74  				panic("dijkstra: negative edge weight")
    75  			}
    76  			joint := path.dist[k] + w
    77  			if joint < path.dist[j] {
    78  				heap.Push(&Q, distanceNode{node: v, dist: joint})
    79  				path.set(j, joint, k)
    80  			}
    81  		}
    82  	}
    83  
    84  	return path
    85  }
    86  
    87  // DijkstraAllFrom returns a shortest-path tree for shortest paths from u to all nodes in
    88  // the graph g. If the graph does not implement Weighted, UniformCost is used.
    89  // DijkstraAllFrom will panic if g has a u-reachable negative edge weight.
    90  //
    91  // If g is a graph.Graph, all nodes of the graph will be stored in the shortest-path
    92  // tree, otherwise only nodes reachable from u will be stored.
    93  //
    94  // The time complexity of DijkstraAllFrom is O(|E|.log|V|).
    95  func DijkstraAllFrom(u graph.Node, g traverse.Graph) ShortestAlts {
    96  	var path ShortestAlts
    97  	if h, ok := g.(graph.Graph); ok {
    98  		if h.Node(u.ID()) == nil {
    99  			return ShortestAlts{from: u}
   100  		}
   101  		path = newShortestAltsFrom(u, graph.NodesOf(h.Nodes()))
   102  	} else {
   103  		if g.From(u.ID()) == graph.Empty {
   104  			return ShortestAlts{from: u}
   105  		}
   106  		path = newShortestAltsFrom(u, []graph.Node{u})
   107  	}
   108  
   109  	var weight Weighting
   110  	if wg, ok := g.(Weighted); ok {
   111  		weight = wg.Weight
   112  	} else {
   113  		weight = UniformCost(g)
   114  	}
   115  
   116  	// Dijkstra's algorithm here is implemented essentially as
   117  	// described in Function B.2 in figure 6 of UTCS Technical
   118  	// Report TR-07-54.
   119  	//
   120  	// This implementation deviates from the report as follows:
   121  	// - the value of path.dist for the start vertex u is initialized to 0;
   122  	// - outdated elements from the priority queue (i.e. with respect to the dist value)
   123  	//   are skipped.
   124  	//
   125  	// http://www.cs.utexas.edu/ftp/techreports/tr07-54.pdf
   126  	Q := priorityQueue{{node: u, dist: 0}}
   127  	for Q.Len() != 0 {
   128  		mid := heap.Pop(&Q).(distanceNode)
   129  		k := path.indexOf[mid.node.ID()]
   130  		if mid.dist > path.dist[k] {
   131  			continue
   132  		}
   133  		mnid := mid.node.ID()
   134  		for _, v := range graph.NodesOf(g.From(mnid)) {
   135  			vid := v.ID()
   136  			j, ok := path.indexOf[vid]
   137  			if !ok {
   138  				j = path.add(v)
   139  			}
   140  			w, ok := weight(mnid, vid)
   141  			if !ok {
   142  				panic("dijkstra: unexpected invalid weight")
   143  			}
   144  			if w < 0 {
   145  				panic("dijkstra: negative edge weight")
   146  			}
   147  			joint := path.dist[k] + w
   148  			if joint < path.dist[j] {
   149  				heap.Push(&Q, distanceNode{node: v, dist: joint})
   150  				path.set(j, joint, k)
   151  			} else if joint == path.dist[j] {
   152  				path.addPath(j, k)
   153  			}
   154  		}
   155  	}
   156  
   157  	return path
   158  }
   159  
   160  // DijkstraAllPaths returns a shortest-path tree for shortest paths in the graph g.
   161  // If the graph does not implement graph.Weighter, UniformCost is used.
   162  // DijkstraAllPaths will panic if g has a negative edge weight.
   163  //
   164  // The time complexity of DijkstraAllPaths is O(|V|.|E|+|V|^2.log|V|).
   165  func DijkstraAllPaths(g graph.Graph) (paths AllShortest) {
   166  	paths = newAllShortest(graph.NodesOf(g.Nodes()), false)
   167  	dijkstraAllPaths(g, paths)
   168  	return paths
   169  }
   170  
   171  // dijkstraAllPaths is the all-paths implementation of Dijkstra. It is shared
   172  // between DijkstraAllPaths and JohnsonAllPaths to avoid repeated allocation
   173  // of the nodes slice and the indexOf map. It returns nothing, but stores the
   174  // result of the work in the paths parameter which is a reference type.
   175  func dijkstraAllPaths(g graph.Graph, paths AllShortest) {
   176  	var weight Weighting
   177  	if wg, ok := g.(graph.Weighted); ok {
   178  		weight = wg.Weight
   179  	} else {
   180  		weight = UniformCost(g)
   181  	}
   182  
   183  	var Q priorityQueue
   184  	for i, u := range paths.nodes {
   185  		// Dijkstra's algorithm here is implemented essentially as
   186  		// described in Function B.2 in figure 6 of UTCS Technical
   187  		// Report TR-07-54 with the addition of handling multiple
   188  		// co-equal paths.
   189  		//
   190  		// http://www.cs.utexas.edu/ftp/techreports/tr07-54.pdf
   191  
   192  		// Q must be empty at this point.
   193  		heap.Push(&Q, distanceNode{node: u, dist: 0})
   194  		for Q.Len() != 0 {
   195  			mid := heap.Pop(&Q).(distanceNode)
   196  			k := paths.indexOf[mid.node.ID()]
   197  			if mid.dist < paths.dist.At(i, k) {
   198  				paths.dist.Set(i, k, mid.dist)
   199  			}
   200  			mnid := mid.node.ID()
   201  			to := g.From(mnid)
   202  			for to.Next() {
   203  				v := to.Node()
   204  				vid := v.ID()
   205  				j := paths.indexOf[vid]
   206  				w, ok := weight(mnid, vid)
   207  				if !ok {
   208  					panic("dijkstra: unexpected invalid weight")
   209  				}
   210  				if w < 0 {
   211  					panic("dijkstra: negative edge weight")
   212  				}
   213  				joint := paths.dist.At(i, k) + w
   214  				if joint < paths.dist.At(i, j) {
   215  					heap.Push(&Q, distanceNode{node: v, dist: joint})
   216  					paths.set(i, j, joint, k)
   217  				} else if joint == paths.dist.At(i, j) {
   218  					paths.add(i, j, k)
   219  				}
   220  			}
   221  		}
   222  	}
   223  }
   224  
   225  type distanceNode struct {
   226  	node graph.Node
   227  	dist float64
   228  }
   229  
   230  // priorityQueue implements a no-dec priority queue.
   231  type priorityQueue []distanceNode
   232  
   233  func (q priorityQueue) Len() int            { return len(q) }
   234  func (q priorityQueue) Less(i, j int) bool  { return q[i].dist < q[j].dist }
   235  func (q priorityQueue) Swap(i, j int)       { q[i], q[j] = q[j], q[i] }
   236  func (q *priorityQueue) Push(n interface{}) { *q = append(*q, n.(distanceNode)) }
   237  func (q *priorityQueue) Pop() interface{} {
   238  	t := *q
   239  	var n interface{}
   240  	n, *q = t[len(t)-1], t[:len(t)-1]
   241  	return n
   242  }