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 }