1 module anansi.algorithms.dijkstra; 2 3 import anansi.container.priorityqueue; 4 import anansi.algorithms.bfs; 5 import anansi.algorithms.relax : relax; 6 import anansi.algorithms.vertex_queue : VertexQueue; 7 import anansi.traits; 8 import anansi.types; 9 import std.exception, std.math; 10 11 12 version(unittest) { import std.stdio; import anansi.adjacencylist; } 13 14 /** 15 * A priority queue item for sorting vertices by the cost to get to them. 16 */ 17 18 package struct DijkstraQueue(GraphT, DistanceMapT) { 19 static assert(isGraph!GraphT); 20 static assert(isPropertyMap!(DistanceMapT, GraphT.VertexDescriptor, real)); 21 22 alias Vertex = GraphT.VertexDescriptor; 23 24 package static struct Item { 25 public Vertex vertex; 26 public real cost; 27 28 public int opCmp(ref const(Item) other) const { 29 return cast(int) sgn(other.cost - this.cost); 30 } 31 } 32 33 this(ref const(DistanceMapT) distances) { 34 _distanceMap = &distances; 35 } 36 37 public void push(Vertex v) { 38 const auto distance = (*_distanceMap)[v]; 39 _queue.push(Item(v, distance)); 40 } 41 42 public void pop() { 43 _queue.pop(); 44 } 45 46 public Vertex front() const { 47 return _queue.front.vertex; 48 } 49 50 public void updateVertex(Vertex v) { 51 const auto distance = (*_distanceMap)[v]; 52 _queue.updateIf((ref Item x) => x.vertex == v, 53 (ref Item i) => i.cost = distance); 54 } 55 56 @property public bool empty() const { 57 return _queue.empty; 58 } 59 60 @property public size_t length() const { 61 return _queue.length; 62 } 63 64 PriorityQueue!Item _queue; 65 const (DistanceMapT*) _distanceMap; 66 } 67 68 unittest { 69 writeln("Dijkstra: queue items are ordered by increasing cost."); 70 alias G = AdjacencyList!(); 71 alias V = G.VertexDescriptor; 72 alias Item = DijkstraQueue!(G, real[V]).Item; 73 74 auto a = Item(0, 0.1); 75 auto b = Item(0, 0.2); 76 77 assert (a > b); 78 assert (!(b > a)); 79 assert (b < a); 80 assert (!(a < b)); 81 assert (a != b); 82 } 83 84 /** 85 * A default implementation of the Dijkstra visitor. 86 */ 87 struct NullDijkstraVisitor(GraphT) { 88 alias Vertex = GraphT.VertexDescriptor; 89 alias Edge = GraphT.EdgeDescriptor; 90 91 void initVertex(ref const(GraphT) g, Vertex v) {} 92 void discoverVertex(ref const(GraphT) g, Vertex v) {} 93 void examineVertex(ref const(GraphT) g, Vertex v) {} 94 void examineEdge(ref const(GraphT) g, Edge e) {} 95 void edgeRelaxed(ref const(GraphT) g, Edge e) {} 96 void edgeNotRelaxed(ref const(GraphT) g, Edge e) {} 97 void finishVertex(ref const(GraphT) g, Vertex e) {} 98 } 99 100 /** 101 * A BFS visitor that transforms a normal breadth-first search algoritm 102 * into Dijkstra's shortest paths. 103 * 104 * Remark: Not every `sumFunction` works correctly. 105 */ 106 package struct DijkstraBfsVisitor(GraphT, 107 QueueT, 108 DijkstraVisitorT, 109 DistanceMapT, 110 PredecessorMapT, 111 WeightMapT, 112 alias sumFunction = (a, b) => a+b) { 113 alias Vertex = GraphT.VertexDescriptor; 114 alias Edge = GraphT.EdgeDescriptor; 115 116 static assert(isReadablePropertyMap!(WeightMapT, Edge, real)); 117 static assert(isPropertyMap!(DistanceMapT, Vertex, real)); 118 static assert(isPropertyMap!(PredecessorMapT, Vertex, Vertex)); 119 120 this(ref DijkstraVisitorT visitor, 121 ref DistanceMapT distanceMap, 122 ref const(WeightMapT) weightMap, 123 ref PredecessorMapT predecessorMap, 124 ref QueueT queue) { 125 _visitor = &visitor; 126 _distanceMap = &distanceMap; 127 _weightMap = &weightMap; 128 _predecessorMap = &predecessorMap; 129 _queue = &queue; 130 } 131 132 /** 133 * Passes the call through to the supplied Dijkstra visitor. 134 */ 135 void initVertex(ref const(GraphT) g, Vertex v) { 136 _visitor.initVertex(g, v); 137 } 138 139 void discoverVertex(ref const(GraphT) g, Vertex v) { 140 _visitor.discoverVertex(g, v); 141 } 142 143 void examineVertex(ref const(GraphT) g, Vertex v) { 144 _visitor.examineVertex(g, v); 145 } 146 147 void examineEdge(ref const(GraphT) g, Edge e) { 148 // TODO: Document the behavior of this `static if`. 149 static if (__traits(compiles, (*_weightMap)[e])) { 150 auto weight = (*_weightMap)[e]; 151 } else { 152 auto weight = (*_weightMap)(e); 153 } 154 enforce (weight >= 0.0); 155 _visitor.examineEdge(g, e); 156 } 157 158 void treeEdge(ref const(GraphT) g, Edge e) { 159 bool decreased = relax(g, *_weightMap, 160 *_distanceMap, 161 *_predecessorMap, e); 162 if (decreased) 163 _visitor.edgeRelaxed(g, e); 164 else 165 _visitor.edgeNotRelaxed(g, e); 166 } 167 168 void nonTreeEdge(ref const(GraphT) g, Edge e) {} 169 170 void greyTarget(ref const(GraphT) g, Edge e) { 171 const Vertex v = g.target(e); 172 auto oldDistance = (*_distanceMap)[v]; 173 174 bool decreased = relax(g, *_weightMap, 175 *_distanceMap, 176 *_predecessorMap, e); 177 if (decreased) { 178 _queue.updateVertex(v); 179 _visitor.edgeRelaxed(g, e); 180 } else { 181 _visitor.edgeNotRelaxed(g, e); 182 } 183 } 184 185 void blackTarget(ref const(GraphT) g, Edge e) {} 186 187 void finishVertex(ref const(GraphT) g, Vertex v) { 188 _visitor.finishVertex(g, v); 189 } 190 191 private DijkstraVisitorT* _visitor; 192 private DistanceMapT* _distanceMap; 193 private const(WeightMapT*) _weightMap; 194 private PredecessorMapT* _predecessorMap; 195 private QueueT* _queue; 196 } 197 198 /** 199 * 200 */ 201 template dijkstraShortestPaths(GraphT, 202 VertexDescriptorT, 203 VisitorT = NullDijkstraVisitor!GraphT, 204 WeightMapT = real[VertexDescriptorT], 205 PredecessorMapT = VertexDescriptorT[VertexDescriptorT], 206 alias sumFunction = (a, b) => a+b) { 207 void dijkstraShortestPaths(ref const(GraphT) g, 208 VertexDescriptorT src, 209 ref const(WeightMapT) weights, 210 ref PredecessorMapT predecessorMap, 211 VisitorT visitor = VisitorT.init) { 212 213 static if (is(VertexDescriptorT == size_t)) { 214 auto colourMap = new Colour[g.vertexCount]; 215 auto distanceMap = new real[g.vertexCount]; 216 } 217 else { 218 real[VertexDescriptorT] distanceMap; 219 Colour[VertexDescriptorT] colourMap; 220 } 221 222 dijkstraShortestPaths(g, src, 223 weights, 224 predecessorMap, 225 visitor, 226 colourMap, 227 distanceMap); 228 } 229 } 230 231 /** 232 * 233 */ 234 template dijkstraShortestPaths(GraphT, 235 VisitorT, 236 VertexDescriptorT, 237 ColourMapT, 238 PredecessorMapT, 239 WeightMapT, 240 DistanceMapT, 241 alias sumFunction = (a, b) => a+b) { 242 void dijkstraShortestPaths(ref const(GraphT) g, 243 VertexDescriptorT src, 244 ref const(WeightMapT) weights, 245 ref PredecessorMapT predecessorMap, 246 VisitorT visitor, 247 ref ColourMapT colourMap, 248 ref DistanceMapT distanceMap) { 249 250 foreach(v; g.vertices) { 251 visitor.initVertex(g, v); 252 distanceMap[v] = real.infinity; 253 predecessorMap[v] = v; 254 colourMap[v] = Colour.White; 255 } 256 distanceMap[src] = 0.0; 257 258 dijkstraShortestPathsNoInit(g, src, visitor, 259 colourMap, 260 weights, 261 predecessorMap, 262 distanceMap); 263 } 264 } 265 266 template dijkstraShortestPathsNoInit(GraphT, 267 VertexDescriptorT, 268 VisitorT = NullDijkstraVisitor!GraphT, 269 ColourMapT = Colour[VertexDescriptorT], 270 PredecessorMapT = VertexDescriptorT[VertexDescriptorT], 271 WeightMapT = real[VertexDescriptorT], 272 DistanceMapT = real[VertexDescriptorT], 273 alias sumFunction = (a, b) => a+b) { 274 static assert(isGraph!GraphT); 275 276 alias EdgeDescriptorT = GraphT.EdgeDescriptor; 277 278 static assert(isPropertyMap!(ColourMapT, VertexDescriptorT, Colour)); 279 static assert(isPropertyMap!(PredecessorMapT, VertexDescriptorT, VertexDescriptorT)); 280 static assert(isReadablePropertyMap!(WeightMapT, EdgeDescriptorT, real)); 281 static assert(isPropertyMap!(DistanceMapT, VertexDescriptorT, real)); 282 283 284 void dijkstraShortestPathsNoInit(ref const(GraphT) g, 285 VertexDescriptorT src, 286 VisitorT visitor, 287 ref ColourMapT colourMap, 288 ref const(WeightMapT) weights, 289 ref PredecessorMapT predecessorMap, 290 ref DistanceMapT distanceMap) { 291 292 alias QueueT = VertexQueue!(GraphT, DistanceMapT); 293 auto queue = QueueT(distanceMap); 294 295 alias Dijkstra = DijkstraBfsVisitor!(GraphT, 296 QueueT, 297 VisitorT, 298 DistanceMapT, 299 PredecessorMapT, 300 WeightMapT, 301 sumFunction); 302 breadthFirstVisit( 303 g, src, colourMap, 304 queue, 305 Dijkstra(visitor, distanceMap, weights, predecessorMap, queue)); 306 } 307 } 308 309 version (unittest) { 310 import std.array, std.algorithm, std.conv, std.stdio; 311 import anansi.adjacencylist, anansi.traits, anansi.container.set; 312 private alias G = AdjacencyList!(VecS, VecS, DirectedS, char, string); 313 private alias Vertex = G.VertexDescriptor; 314 private alias Edge = G.EdgeDescriptor; 315 } 316 317 unittest { 318 writeln("Dijkstra: no edges shouldn't crash"); 319 G g; 320 real[Edge] weights; 321 Vertex[Vertex] predecessors; 322 g.addVertex('a'); 323 dijkstraShortestPaths(g, g.vertices.front, weights, predecessors); 324 } 325 326 unittest { 327 writeln("Dijkstra: shorter paths with multiple hops are preferred over long path with one hop."); 328 329 // / ----- 4 ----- \ 330 // a - 1 -> b - 1 -> c - 2 -> d 331 // \ \ ----- 5 ----- / 332 // 3 -> e 333 // 334 335 G g; 336 real[Edge] weights; 337 Vertex[Vertex] predecessor; 338 339 auto a = g.addVertex('a'); 340 auto b = g.addVertex('b'); 341 weights[g.addEdge(a, b, "a -> b").edge] = 1.0; 342 343 auto c = g.addVertex('c'); 344 weights[g.addEdge(b, c, "b -> c").edge] = 1.0; 345 weights[g.addEdge(a, c, "a -> c").edge] = 4.0; 346 347 auto d = g.addVertex('d'); 348 weights[g.addEdge(c, d, "c -> d").edge] = 2.0; 349 weights[g.addEdge(b, d, "b -> d").edge] = 5.0; 350 351 auto e = g.addVertex('e'); 352 weights[g.addEdge(a, e, "a -> e").edge] = 3.0; 353 354 355 dijkstraShortestPaths(g, g.vertices.front, weights, predecessor); 356 357 assert(predecessor[a] == a); 358 assert(predecessor[b] == a); 359 assert(predecessor[c] == b); 360 assert(predecessor[d] == c); 361 assert(predecessor[e] == a); 362 }