eager_dijkstra_shortest_paths.hpp 16 KB

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