Algorithms Library Toolkit
A toolkit for algorithms, especially for algorithms on formal languages
Dijkstra.hpp
Go to the documentation of this file.
1
6// Copyright (c) 2017 Czech Technical University in Prague | Faculty of Information Technology. All rights reserved.
7
8#pragma once
9
10#include <list>
11#include <alib/set>
12#include <alib/map>
13#include <queue>
14#include <stdexcept>
15#include <functional>
16
19
20namespace graph {
21
22namespace shortest_path {
23
24class Dijkstra {
25// ---------------------------------------------------------------------------------------------------------------------
26
27 public:
28
43 template<
44 typename TGraph,
45 typename TNode,
46 typename F = std::function<bool(const TNode &,
47 const typename TGraph::edge_type::weight_type &)>>
48 static
49 void
50 run(const TGraph &graph,
51 const TNode &start,
52 F f_user = [](const TNode &,
53 const typename TGraph::edge_type::weight_type &) -> bool { return false; });
54
55// ---------------------------------------------------------------------------------------------------------------------
56
73 template<
74 typename TGraph,
75 typename TNode,
76 typename F = std::function<void(const TNode &,
77 const typename TGraph::edge_type::weight_type &)>>
78 static
79 ext::pair<ext::vector<TNode>, typename TGraph::edge_type::weight_type>
80 findPath(const TGraph &graph,
81 const TNode &start,
82 const TNode &goal,
83 F f_user = [](const TNode &,
84 const typename TGraph::edge_type::weight_type &) {});
85
86 template<typename TGraph, typename TNode>
87 static
88 ext::pair<ext::vector<TNode>, typename TGraph::edge_type::weight_type>
90 const TNode &start,
91 const TNode &goal) {
92 return findPath(graph, start, goal);
93 }
94
95// ---------------------------------------------------------------------------------------------------------------------
96
114 template<typename TGraph,
115 typename TNode,
116 typename F = std::function<void(const TNode &,
117 const typename TGraph::edge_type::weight_type &)>>
118 static
119 ext::pair<ext::vector<TNode>, typename TGraph::edge_type::weight_type>
120 findPathBidirectional(const TGraph &graph,
121 const TNode &start,
122 const TNode &goal,
123 F f_user = [](const TNode &,
124 const typename TGraph::edge_type::weight_type &) {});
125
126 template<typename TGraph, typename TNode>
127 static
128 ext::pair<ext::vector<TNode>, typename TGraph::edge_type::weight_type>
130 const TNode &start,
131 const TNode &goal) {
132 return findPathBidirectional(graph, start, goal);
133 }
134
135
136// =====================================================================================================================
137
138 private:
139
140 template<typename TNode, typename TWeight>
141 struct Data {
142 ext::set<ext::pair<TWeight, TNode>> queue; // priority queue
143 ext::map<TNode, TWeight> g; // distances (aka G score)
144 ext::map<TNode, TNode> p; // parents
145 };
146
147// ---------------------------------------------------------------------------------------------------------------------
148
149 template<typename TGraph, typename TNode, typename F1, typename F2>
150 static
151 ext::pair<ext::vector<TNode>, typename TGraph::edge_type::weight_type>
152 impl(const TGraph &graph,
153 const TNode &start,
154 const TNode &goal,
155 F1 f_user,
156 F2 f_stop);
157
158
159// ---------------------------------------------------------------------------------------------------------------------
160
161 template<typename FEdges, typename TNode, typename TWeight, typename F1, typename F2, typename F3>
162 static bool relaxation(FEdges successor_edges,
163 Data<TNode, TWeight> &data,
164 F1 f_user,
165 F2 f_stop,
166 F3 f_update);
167
168// ---------------------------------------------------------------------------------------------------------------------
169
170 template<typename TGraph, typename TNode, typename F1, typename F2>
171 static
172 ext::pair<ext::vector<TNode>, typename TGraph::edge_type::weight_type>
173 implBidirectional(const TGraph &graph,
174 const TNode &start,
175 const TNode &goal,
176 F1 f_user,
177 F2 f_stop);
178
179// ---------------------------------------------------------------------------------------------------------------------
180
181 template<typename TNode, typename TWeight>
182 inline static void init(Dijkstra::Data<TNode, TWeight> &data, const TNode &start);
183
184
185
186// ---------------------------------------------------------------------------------------------------------------------
187
188};
189
190// =====================================================================================================================
191
192template<typename TGraph, typename TNode, typename F>
193void Dijkstra::run(const TGraph &graph, const TNode &start, F f_user) {
194 // goal -> start: in order to avoid call constructor
195 impl(graph, start, start, f_user, [](const TNode &) -> bool { return false; });
196}
197
198// ---------------------------------------------------------------------------------------------------------------------
199
200template<typename TGraph, typename TNode, typename F>
201ext::pair<ext::vector<TNode>, typename TGraph::edge_type::weight_type>
203 const TNode &start,
204 const TNode &goal,
205 F f_user) {
206 // We need to change user function to return false for every node in order to have only one relaxation function
207 return impl(graph, start, goal,
208 [&](const TNode &n, const typename TGraph::edge_type::weight_type &w) -> bool {
209 f_user(n, w);
210 return false;
211 }, [&goal](const TNode &n) -> bool { return goal == n; });
212}
213
214// ---------------------------------------------------------------------------------------------------------------------
215
216template<typename TGraph, typename TNode, typename F>
217ext::pair<ext::vector<TNode>, typename TGraph::edge_type::weight_type>
219 const TNode &start,
220 const TNode &goal,
221 F f_user) {
222 // We need to change user function to return false for every node in order to have only one relaxation function
223 return implBidirectional(graph, start, goal,
224 [&](const TNode &n, const typename TGraph::edge_type::weight_type &w) -> bool {
225 f_user(n, w);
226 return false;
227 },
228 [](const TNode &) -> bool { return false; });
229}
230
231// =====================================================================================================================
232
233template<typename TGraph, typename TNode, typename F1, typename F2>
234ext::pair<ext::vector<TNode>, typename TGraph::edge_type::weight_type>
235Dijkstra::impl(const TGraph &graph,
236 const TNode &start,
237 const TNode &goal,
238 F1 f_user,
239 F2 f_stop) {
240 using weight_type = typename TGraph::edge_type::weight_type;
241
242 Data<TNode, weight_type> data;
243
244 // Init search
245 init(data, start);
246
247 while (!data.queue.empty()) {
248 bool stop = relaxation([&](const auto &node) -> auto { return graph.successorEdges(node); },
249 data,
250 f_user,
251 f_stop,
252 [](const TNode &) -> void {});
253
254 if (stop) {
255 break;
256 }
257 }
258
259 return common::ReconstructPath::reconstructWeightedPath(data.p, data.g, start, goal);
260}
261
262// ---------------------------------------------------------------------------------------------------------------------
263
264template<typename FEdges, typename TNode, typename TWeight, typename F1, typename F2, typename F3>
265bool Dijkstra::relaxation(FEdges successor_edges,
266 Data<TNode, TWeight> &data,
267 F1 f_user,
268 F2 f_stop,
269 F3 f_update) {
270 TNode n = data.queue.begin()->second;
271 data.queue.erase(data.queue.begin());
272
273 // Run user's function
274 if (f_user(n, data.g[n])) {
275 return true;
276 }
277
278 // Stop if reach the goal
279 if (f_stop(n)) {
280 return true;
281 }
282
283 for (const auto &s_edge: successor_edges(n)) {
284 const TNode &s = common::SupportFunction::other(s_edge, n); // successor
285
286 // Check for negative edge
287 if (s_edge.weight() < 0) {
288 throw std::out_of_range("Dijkstra: Detect negative weight on edge in graph.");
289 }
290
291 // Calculate new G score
292 TWeight gscore = data.g.at(n) + s_edge.weight();
293
294 // Search if the node s was already visited
295 auto search_d = data.g.find(s);
296
297 // If not or the distance can be improve do relaxation
298 if (search_d == data.g.end() || search_d->second > gscore) {
299 // Search if the node s is in OPEN
300 if (search_d != data.g.end()) {
301 auto search_q = data.queue.find(ext::make_pair(search_d->second, s));
302 if (search_q != data.queue.end()) {
303 // Erase node from priority queue
304 data.queue.erase(search_q);
305 }
306 }
307
308 data.g[s] = gscore;
309 data.p.insert_or_assign(s, n);
310 data.queue.insert(ext::make_pair(data.g[s], s));
311
312 f_update(s); // Update currently best path
313 }
314 }
315
316 return false;
317}
318
319// ---------------------------------------------------------------------------------------------------------------------
320
321template<typename TGraph, typename TNode, typename F1, typename F2>
322ext::pair<ext::vector<TNode>, typename TGraph::edge_type::weight_type>
323Dijkstra::implBidirectional(const TGraph &graph,
324 const TNode &start,
325 const TNode &goal,
326 F1 f_user,
327 F2 f_stop) {
328 using weight_type = typename TGraph::edge_type::weight_type;
329
330 // Check for negative edge
331 weight_type eps = common::SupportFunction::getMinEdgeValue(graph); // Smallest value of the weight in graph
332
333 weight_type p = std::numeric_limits<weight_type>::max(); // Currently best path weight
334 ext::vector<TNode> intersection_nodes; // Last one is currently best intersection node
335 Data<TNode, weight_type> forward_data;
336 Data<TNode, weight_type> backward_data;
337
338 // Init forward search
339 init(forward_data, start);
340 auto f_forward_update = [&](const auto &s) -> void {
341 if (backward_data.g.find(s) != backward_data.g.end()) {
342 if (forward_data.g.at(s) + backward_data.g.at(s) < p) {
343 p = forward_data.g.at(s) + backward_data.g.at(s);
344 intersection_nodes.push_back(s);
345 }
346 }
347 };
348
349 // Init backward search
350 init(backward_data, goal);
351 auto f_backward_update = [&](const auto &s) -> void {
352 if (forward_data.g.find(s) != forward_data.g.end()) {
353 if (backward_data.g.at(s) + forward_data.g.at(s) < p) {
354 p = backward_data.g.at(s) + forward_data.g.at(s);
355 intersection_nodes.push_back(s);
356 }
357 }
358 };
359
360 while (!forward_data.queue.empty() && !backward_data.queue.empty()) {
361 if (forward_data.queue.begin()->first + backward_data.queue.begin()->first + eps >= p) {
363 backward_data.p,
364 forward_data.g,
365 backward_data.g,
366 start,
367 goal,
368 intersection_nodes.back());
369 }
370
371 // Expand the lower value
372 if (forward_data.queue.begin()->first < backward_data.queue.begin()->first) {
373 // Forward search relaxationBidirectional
374 relaxation([&](const auto &node) -> auto { return graph.successorEdges(node); },
375 forward_data,
376 f_user,
377 f_stop,
378 f_forward_update);
379 } else {
380 // Backward search relaxationBidirectional
381 relaxation([&](const auto &node) -> auto { return graph.predecessorEdges(node); },
382 backward_data,
383 f_user,
384 f_stop,
385 f_backward_update);
386 }
387 }
388
390
391}
392// ---------------------------------------------------------------------------------------------------------------------
393
394template<typename TNode, typename TWeight>
395void Dijkstra::init(Dijkstra::Data<TNode, TWeight> &data, const TNode &start) {
396 data.g[start] = 0;
397 data.p.insert_or_assign(start, start);
398 data.queue.insert(ext::make_pair(data.g[start], start));
399}
400
401// ---------------------------------------------------------------------------------------------------------------------
402
403} // namespace shortest_path
404
405} // namespace graph
406
Class extending the map class from the standard library. Original reason is to allow printing of the ...
Definition: map.hpp:48
Class extending the pair class from the standard library. Original reason is to allow printing of the...
Definition: pair.hpp:43
Definition: set.hpp:44
Class extending the vector class from the standard library. Original reason is to allow printing of t...
Definition: vector.hpp:45
static ext::pair< ext::vector< TNode >, TWeight > reconstructWeightedPath(const ext::map< TNode, TNode > &p, const ext::map< TNode, TWeight > &g, const TNode &start, const TNode &goal)
Definition: ReconstructPath.hpp:132
static TGraph::edge_type::weight_type getMinEdgeValue(const TGraph &graph)
Definition: SupportFunction.hpp:71
static TNode const & other(const TEdge &e, const TNode &n)
Definition: Dijkstra.hpp:24
static ext::pair< ext::vector< TNode >, typename TGraph::edge_type::weight_type > findPath(const TGraph &graph, const TNode &start, const TNode &goal, F f_user=[](const TNode &, const typename TGraph::edge_type::weight_type &) {})
Definition: Dijkstra.hpp:202
static void run(const TGraph &graph, const TNode &start, F f_user=[](const TNode &, const typename TGraph::edge_type::weight_type &) -> bool { return false;})
Definition: Dijkstra.hpp:193
static ext::pair< ext::vector< TNode >, typename TGraph::edge_type::weight_type > findPathBidirectional(const TGraph &graph, const TNode &start, const TNode &goal, F f_user=[](const TNode &, const typename TGraph::edge_type::weight_type &) {})
Definition: Dijkstra.hpp:218
static ext::pair< ext::vector< TNode >, typename TGraph::edge_type::weight_type > findPathRegistration(const TGraph &graph, const TNode &start, const TNode &goal)
Definition: Dijkstra.hpp:89
static ext::pair< ext::vector< TNode >, typename TGraph::edge_type::weight_type > findPathBidirectionalRegistration(const TGraph &graph, const TNode &start, const TNode &goal)
Definition: Dijkstra.hpp:129
constexpr const T & max(const T &a)
Root case of maximum computation. The maximum from one value is the value itself.
Definition: algorithm.hpp:278
constexpr auto make_pair(T1 &&x, T2 &&y)
Definition: pair.hpp:79
Definition: ReconstructPath.hpp:14
void start(measurements::stealth_string name, measurements::Type type)
Definition: measurements.cpp:14
Definition: Node.cpp:11