1 // Copyright (C) 2004-2006 The Trustees of Indiana University.
2
3 // Use, modification and distribution is subject to the Boost Software
4 // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
6
7 // Authors: Douglas Gregor
8 // Andrew Lumsdaine
9
10 /**************************************************************************
11 * This source file implements a variation on distributed Dijkstra's *
12 * algorithm that can expose additional parallelism by permitting *
13 * vertices within a certain distance from the minimum to be processed, *
14 * even though they may not be at their final distance. This can *
15 * introduce looping, but the algorithm will still terminate so long as *
16 * there are no negative loops. *
17 **************************************************************************/
18 #ifndef BOOST_GRAPH_EAGER_DIJKSTRA_SHORTEST_PATHS_HPP
19 #define BOOST_GRAPH_EAGER_DIJKSTRA_SHORTEST_PATHS_HPP
20
21 #ifndef BOOST_GRAPH_USE_MPI
22 #error "Parallel BGL files should not be included unless <boost/graph/use_mpi.hpp> has been included"
23 #endif
24
25 #include <boost/assert.hpp>
26 #include <boost/graph/distributed/detail/dijkstra_shortest_paths.hpp>
27 #include <boost/property_map/parallel/caching_property_map.hpp>
28 #include <boost/pending/indirect_cmp.hpp>
29 #include <boost/graph/distributed/detail/remote_update_set.hpp>
30 #include <vector>
31 #include <boost/graph/breadth_first_search.hpp>
32 #include <boost/graph/dijkstra_shortest_paths.hpp>
33 #include <boost/graph/parallel/container_traits.hpp>
34 #include <boost/pending/relaxed_heap.hpp>
35
36 #ifdef PBGL_ACCOUNTING
37 # include <boost/graph/accounting.hpp>
38 # include <numeric>
39 #endif // PBGL_ACCOUNTING
40
41 #ifdef MUTABLE_QUEUE
42 # include <boost/pending/mutable_queue.hpp>
43 #endif
44
45 namespace boost { namespace graph { namespace distributed {
46
47 #ifdef PBGL_ACCOUNTING
48 struct eager_dijkstra_shortest_paths_stats_t
49 {
50 /* The value of the lookahead parameter. */
51 double lookahead;
52
53 /* Total wall-clock time used by the algorithm.*/
54 accounting::time_type execution_time;
55
56 /* The number of vertices deleted in each superstep. */
57 std::vector<std::size_t> deleted_vertices;
58
59 template<typename OutputStream>
printboost::graph::distributed::eager_dijkstra_shortest_paths_stats_t60 void print(OutputStream& out)
61 {
62 double avg_deletions = std::accumulate(deleted_vertices.begin(),
63 deleted_vertices.end(),
64 0.0);
65 avg_deletions /= deleted_vertices.size();
66
67 out << "Problem = \"Single-Source Shortest Paths\"\n"
68 << "Algorithm = \"Eager Dijkstra\"\n"
69 << "Function = eager_dijkstra_shortest_paths\n"
70 << "(P) Lookahead = " << lookahead << "\n"
71 << "Wall clock time = " << accounting::print_time(execution_time)
72 << "\nSupersteps = " << deleted_vertices.size() << "\n"
73 << "Avg. deletions per superstep = " << avg_deletions << "\n";
74 }
75 };
76
77 static eager_dijkstra_shortest_paths_stats_t eager_dijkstra_shortest_paths_stats;
78 #endif
79
80 namespace detail {
81
82 // Borrowed from BGL's dijkstra_shortest_paths
83 template <class UniformCostVisitor, class Queue,
84 class WeightMap, class PredecessorMap, class DistanceMap,
85 class BinaryFunction, class BinaryPredicate>
86 struct parallel_dijkstra_bfs_visitor : bfs_visitor<>
87 {
88 typedef typename property_traits<DistanceMap>::value_type distance_type;
89
parallel_dijkstra_bfs_visitorboost::graph::distributed::detail::parallel_dijkstra_bfs_visitor90 parallel_dijkstra_bfs_visitor(UniformCostVisitor vis, Queue& Q,
91 WeightMap w, PredecessorMap p, DistanceMap d,
92 BinaryFunction combine, BinaryPredicate compare,
93 distance_type zero)
94 : m_vis(vis), m_Q(Q), m_weight(w), m_predecessor(p), m_distance(d),
95 m_combine(combine), m_compare(compare), m_zero(zero) { }
96
97 template <class Vertex, class Graph>
initialize_vertexboost::graph::distributed::detail::parallel_dijkstra_bfs_visitor98 void initialize_vertex(Vertex u, Graph& g)
99 { m_vis.initialize_vertex(u, g); }
100 template <class Vertex, class Graph>
discover_vertexboost::graph::distributed::detail::parallel_dijkstra_bfs_visitor101 void discover_vertex(Vertex u, Graph& g) { m_vis.discover_vertex(u, g); }
102 template <class Vertex, class Graph>
examine_vertexboost::graph::distributed::detail::parallel_dijkstra_bfs_visitor103 void examine_vertex(Vertex u, Graph& g) { m_vis.examine_vertex(u, g); }
104
105 /* Since the eager formulation of Parallel Dijkstra's algorithm can
106 loop, we may relax on *any* edge, not just those associated with
107 white and gray targets. */
108 template <class Edge, class Graph>
examine_edgeboost::graph::distributed::detail::parallel_dijkstra_bfs_visitor109 void examine_edge(Edge e, Graph& g) {
110 if (m_compare(get(m_weight, e), m_zero))
111 boost::throw_exception(negative_edge());
112
113 m_vis.examine_edge(e, g);
114
115 boost::parallel::caching_property_map<PredecessorMap> c_pred(m_predecessor);
116 boost::parallel::caching_property_map<DistanceMap> c_dist(m_distance);
117
118 distance_type old_distance = get(c_dist, target(e, g));
119
120 bool m_decreased = relax(e, g, m_weight, c_pred, c_dist,
121 m_combine, m_compare);
122
123 /* On x86 Linux with optimization, we sometimes get into a
124 horrible case where m_decreased is true but the distance hasn't
125 actually changed. This occurs when the comparison inside
126 relax() occurs with the 80-bit precision of the x87 floating
127 point unit, but the difference is lost when the resulting
128 values are written back to lower-precision memory (e.g., a
129 double). With the eager Dijkstra's implementation, this results
130 in looping. */
131 if (m_decreased && old_distance != get(c_dist, target(e, g))) {
132 m_Q.update(target(e, g));
133 m_vis.edge_relaxed(e, g);
134 } else
135 m_vis.edge_not_relaxed(e, g);
136 }
137 template <class Vertex, class Graph>
finish_vertexboost::graph::distributed::detail::parallel_dijkstra_bfs_visitor138 void finish_vertex(Vertex u, Graph& g) { m_vis.finish_vertex(u, g); }
139
140 UniformCostVisitor m_vis;
141 Queue& m_Q;
142 WeightMap m_weight;
143 PredecessorMap m_predecessor;
144 DistanceMap m_distance;
145 BinaryFunction m_combine;
146 BinaryPredicate m_compare;
147 distance_type m_zero;
148 };
149
150 /**********************************************************************
151 * Dijkstra queue that implements arbitrary "lookahead" *
152 **********************************************************************/
153 template<typename Graph, typename Combine, typename Compare,
154 typename VertexIndexMap, typename DistanceMap,
155 typename PredecessorMap>
156 class lookahead_dijkstra_queue
157 : public graph::detail::remote_update_set<
158 lookahead_dijkstra_queue<
159 Graph, Combine, Compare, VertexIndexMap, DistanceMap,
160 PredecessorMap>,
161 typename boost::graph::parallel::process_group_type<Graph>::type,
162 typename dijkstra_msg_value<DistanceMap, PredecessorMap>::type,
163 typename property_map<Graph, vertex_owner_t>::const_type>
164 {
165 typedef typename graph_traits<Graph>::vertex_descriptor
166 vertex_descriptor;
167 typedef lookahead_dijkstra_queue self_type;
168 typedef typename boost::graph::parallel::process_group_type<Graph>::type
169 process_group_type;
170 typedef dijkstra_msg_value<DistanceMap, PredecessorMap> msg_value_creator;
171 typedef typename msg_value_creator::type msg_value_type;
172 typedef typename property_map<Graph, vertex_owner_t>::const_type
173 OwnerPropertyMap;
174
175 typedef graph::detail::remote_update_set<self_type, process_group_type,
176 msg_value_type, OwnerPropertyMap>
177 inherited;
178
179 // Priority queue for tentative distances
180 typedef indirect_cmp<DistanceMap, Compare> queue_compare_type;
181
182 typedef typename property_traits<DistanceMap>::value_type distance_type;
183
184 #ifdef MUTABLE_QUEUE
185 typedef mutable_queue<vertex_descriptor, std::vector<vertex_descriptor>,
186 queue_compare_type, VertexIndexMap> queue_type;
187
188 #else
189 typedef relaxed_heap<vertex_descriptor, queue_compare_type,
190 VertexIndexMap> queue_type;
191 #endif // MUTABLE_QUEUE
192
193 typedef typename process_group_type::process_id_type process_id_type;
194
195 public:
196 typedef vertex_descriptor value_type;
197
lookahead_dijkstra_queue(const Graph & g,const Combine & combine,const Compare & compare,const VertexIndexMap & id,const DistanceMap & distance_map,const PredecessorMap & predecessor_map,distance_type lookahead)198 lookahead_dijkstra_queue(const Graph& g,
199 const Combine& combine,
200 const Compare& compare,
201 const VertexIndexMap& id,
202 const DistanceMap& distance_map,
203 const PredecessorMap& predecessor_map,
204 distance_type lookahead)
205 : inherited(boost::graph::parallel::process_group(g), get(vertex_owner, g)),
206 queue(num_vertices(g), queue_compare_type(distance_map, compare), id),
207 distance_map(distance_map),
208 predecessor_map(predecessor_map),
209 min_distance(0),
210 lookahead(lookahead)
211 #ifdef PBGL_ACCOUNTING
212 , local_deletions(0)
213 #endif
214 { }
215
push(const value_type & x)216 void push(const value_type& x)
217 {
218 msg_value_type msg_value =
219 msg_value_creator::create(get(distance_map, x),
220 predecessor_value(get(predecessor_map, x)));
221 inherited::update(x, msg_value);
222 }
223
update(const value_type & x)224 void update(const value_type& x) { push(x); }
225
pop()226 void pop()
227 {
228 queue.pop();
229 #ifdef PBGL_ACCOUNTING
230 ++local_deletions;
231 #endif
232 }
233
top()234 value_type& top() { return queue.top(); }
top() const235 const value_type& top() const { return queue.top(); }
236
empty()237 bool empty()
238 {
239 inherited::collect();
240
241 // If there are no suitable messages, wait until we get something
242 while (!has_suitable_vertex()) {
243 if (do_synchronize()) return true;
244 }
245
246 // Return true only if nobody has any messages; false if we
247 // have suitable messages
248 return false;
249 }
250
251 private:
predecessor_value(vertex_descriptor v) const252 vertex_descriptor predecessor_value(vertex_descriptor v) const
253 { return v; }
254
255 vertex_descriptor
predecessor_value(property_traits<dummy_property_map>::reference) const256 predecessor_value(property_traits<dummy_property_map>::reference) const
257 { return graph_traits<Graph>::null_vertex(); }
258
has_suitable_vertex() const259 bool has_suitable_vertex() const
260 {
261 return (!queue.empty()
262 && get(distance_map, queue.top()) <= min_distance + lookahead);
263 }
264
do_synchronize()265 bool do_synchronize()
266 {
267 using boost::parallel::all_reduce;
268 using boost::parallel::minimum;
269
270 inherited::synchronize();
271
272 // TBD: could use combine here, but then we need to stop using
273 // minimum<distance_type>() as the function object.
274 distance_type local_distance =
275 queue.empty()? (std::numeric_limits<distance_type>::max)()
276 : get(distance_map, queue.top());
277
278 all_reduce(this->process_group, &local_distance, &local_distance + 1,
279 &min_distance, minimum<distance_type>());
280
281 #ifdef PBGL_ACCOUNTING
282 std::size_t deletions = 0;
283 all_reduce(this->process_group, &local_deletions, &local_deletions + 1,
284 &deletions, std::plus<std::size_t>());
285 if (process_id(this->process_group) == 0)
286 eager_dijkstra_shortest_paths_stats.deleted_vertices
287 .push_back(deletions);
288 local_deletions = 0;
289 BOOST_ASSERT(deletions > 0);
290 #endif
291
292 return min_distance == (std::numeric_limits<distance_type>::max)();
293 }
294
295 public:
296 void
receive_update(process_id_type source,vertex_descriptor vertex,distance_type distance)297 receive_update(process_id_type source, vertex_descriptor vertex,
298 distance_type distance)
299 {
300 // Update the queue if the received distance is better than
301 // the distance we know locally
302 if (distance <= get(distance_map, vertex)) {
303
304 // Update the local distance map
305 put(distance_map, vertex, distance);
306
307 bool is_in_queue = queue.contains(vertex);
308
309 if (!is_in_queue)
310 queue.push(vertex);
311 else
312 queue.update(vertex);
313 }
314 }
315
316 void
receive_update(process_id_type source,vertex_descriptor vertex,std::pair<distance_type,vertex_descriptor> p)317 receive_update(process_id_type source, vertex_descriptor vertex,
318 std::pair<distance_type, vertex_descriptor> p)
319 {
320 if (p.first <= get(distance_map, vertex)) {
321 put(predecessor_map, vertex, p.second);
322 receive_update(source, vertex, p.first);
323 }
324 }
325
326 private:
327 queue_type queue;
328 DistanceMap distance_map;
329 PredecessorMap predecessor_map;
330 distance_type min_distance;
331 distance_type lookahead;
332 #ifdef PBGL_ACCOUNTING
333 std::size_t local_deletions;
334 #endif
335 };
336 /**********************************************************************/
337 } // end namespace detail
338
339 template<typename DistributedGraph, typename DijkstraVisitor,
340 typename PredecessorMap, typename DistanceMap, typename WeightMap,
341 typename IndexMap, typename ColorMap, typename Compare,
342 typename Combine, typename DistInf, typename DistZero>
343 void
eager_dijkstra_shortest_paths(const DistributedGraph & g,typename graph_traits<DistributedGraph>::vertex_descriptor s,PredecessorMap predecessor,DistanceMap distance,typename property_traits<DistanceMap>::value_type lookahead,WeightMap weight,IndexMap index_map,ColorMap color_map,Compare compare,Combine combine,DistInf inf,DistZero zero,DijkstraVisitor vis)344 eager_dijkstra_shortest_paths
345 (const DistributedGraph& g,
346 typename graph_traits<DistributedGraph>::vertex_descriptor s,
347 PredecessorMap predecessor, DistanceMap distance,
348 typename property_traits<DistanceMap>::value_type lookahead,
349 WeightMap weight, IndexMap index_map, ColorMap color_map,
350 Compare compare, Combine combine, DistInf inf, DistZero zero,
351 DijkstraVisitor vis)
352 {
353 #ifdef PBGL_ACCOUNTING
354 eager_dijkstra_shortest_paths_stats.deleted_vertices.clear();
355 eager_dijkstra_shortest_paths_stats.lookahead = lookahead;
356 eager_dijkstra_shortest_paths_stats.execution_time = accounting::get_time();
357 #endif
358
359 // Initialize local portion of property maps
360 typename graph_traits<DistributedGraph>::vertex_iterator ui, ui_end;
361 for (boost::tie(ui, ui_end) = vertices(g); ui != ui_end; ++ui) {
362 put(distance, *ui, inf);
363 put(predecessor, *ui, *ui);
364 }
365 put(distance, s, zero);
366
367 // Dijkstra Queue
368 typedef detail::lookahead_dijkstra_queue
369 <DistributedGraph, Combine, Compare, IndexMap, DistanceMap,
370 PredecessorMap> Queue;
371
372 Queue Q(g, combine, compare, index_map, distance,
373 predecessor, lookahead);
374
375 // Parallel Dijkstra visitor
376 detail::parallel_dijkstra_bfs_visitor
377 <DijkstraVisitor, Queue, WeightMap, PredecessorMap, DistanceMap, Combine,
378 Compare> bfs_vis(vis, Q, weight, predecessor, distance, combine, compare,
379 zero);
380
381 set_property_map_role(vertex_color, color_map);
382 set_property_map_role(vertex_distance, distance);
383
384 breadth_first_search(g, s, Q, bfs_vis, color_map);
385
386 #ifdef PBGL_ACCOUNTING
387 eager_dijkstra_shortest_paths_stats.execution_time =
388 accounting::get_time()
389 - eager_dijkstra_shortest_paths_stats.execution_time;
390 #endif
391 }
392
393 template<typename DistributedGraph, typename DijkstraVisitor,
394 typename PredecessorMap, typename DistanceMap, typename WeightMap>
395 void
eager_dijkstra_shortest_paths(const DistributedGraph & g,typename graph_traits<DistributedGraph>::vertex_descriptor s,PredecessorMap predecessor,DistanceMap distance,typename property_traits<DistanceMap>::value_type lookahead,WeightMap weight)396 eager_dijkstra_shortest_paths
397 (const DistributedGraph& g,
398 typename graph_traits<DistributedGraph>::vertex_descriptor s,
399 PredecessorMap predecessor, DistanceMap distance,
400 typename property_traits<DistanceMap>::value_type lookahead,
401 WeightMap weight)
402 {
403 typedef typename property_traits<DistanceMap>::value_type distance_type;
404
405 std::vector<default_color_type> colors(num_vertices(g), white_color);
406
407 eager_dijkstra_shortest_paths(g, s, predecessor, distance, lookahead, weight,
408 get(vertex_index, g),
409 make_iterator_property_map(&colors[0],
410 get(vertex_index,
411 g)),
412 std::less<distance_type>(),
413 closed_plus<distance_type>(),
414 distance_type(),
415 (std::numeric_limits<distance_type>::max)(),
416 dijkstra_visitor<>());
417 }
418
419 template<typename DistributedGraph, typename DijkstraVisitor,
420 typename PredecessorMap, typename DistanceMap>
421 void
eager_dijkstra_shortest_paths(const DistributedGraph & g,typename graph_traits<DistributedGraph>::vertex_descriptor s,PredecessorMap predecessor,DistanceMap distance,typename property_traits<DistanceMap>::value_type lookahead)422 eager_dijkstra_shortest_paths
423 (const DistributedGraph& g,
424 typename graph_traits<DistributedGraph>::vertex_descriptor s,
425 PredecessorMap predecessor, DistanceMap distance,
426 typename property_traits<DistanceMap>::value_type lookahead)
427 {
428 eager_dijkstra_shortest_paths(g, s, predecessor, distance, lookahead,
429 get(edge_weight, g));
430 }
431 } // end namespace distributed
432
433 #ifdef PBGL_ACCOUNTING
434 using distributed::eager_dijkstra_shortest_paths_stats;
435 #endif
436
437 using distributed::eager_dijkstra_shortest_paths;
438
439 } } // end namespace boost::graph
440
441 #endif // BOOST_GRAPH_EAGER_DIJKSTRA_SHORTEST_PATHS_HPP
442