eager_dijkstra_shortest_paths.hpp 16 KB

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