// Copyright (C) 2004-2006 The Trustees of Indiana University. // Use, modification and distribution is subject to the Boost Software // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // Authors: Douglas Gregor // Andrew Lumsdaine /************************************************************************** * This source file implements the variation on Dijkstra's algorithm * * presented by Crauser et al. in: * * * * Andreas Crauser, Kurt Mehlhorn, Ulrich Meyer, and Peter * * Sanders. A Parallelization of Dijkstra's Shortest Path * * Algorithm. In Lubos Brim, Jozef Gruska, and Jiri Zlatuska, * * editors, Mathematical Foundations of Computer Science (MFCS), * * volume 1450 of Lecture Notes in Computer Science, pages * * 722--731, 1998. Springer. * * * * This implementation is, however, restricted to the distributed-memory * * case, where the work is distributed by virtue of the vertices being * * distributed. In a shared-memory (single address space) context, we * * would want to add an explicit balancing step. * **************************************************************************/ #ifndef BOOST_GRAPH_CRAUSER_ET_AL_SHORTEST_PATHS_HPP #define BOOST_GRAPH_CRAUSER_ET_AL_SHORTEST_PATHS_HPP #ifndef BOOST_GRAPH_USE_MPI #error "Parallel BGL files should not be included unless has been included" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PBGL_ACCOUNTING # include # include #endif // PBGL_ACCOUNTING #ifdef MUTABLE_QUEUE # include #endif namespace boost { namespace graph { namespace distributed { #ifdef PBGL_ACCOUNTING struct crauser_et_al_shortest_paths_stats_t { /* Total wall-clock time used by the algorithm.*/ accounting::time_type execution_time; /* The number of vertices deleted in each superstep. */ std::vector deleted_vertices; template void print(OutputStream& out) { double avg_deletions = std::accumulate(deleted_vertices.begin(), deleted_vertices.end(), 0.0); avg_deletions /= deleted_vertices.size(); out << "Problem = \"Single-Source Shortest Paths\"\n" << "Algorithm = \"Crauser et al\"\n" << "Function = crauser_et_al_shortest_paths\n" << "Wall clock time = " << accounting::print_time(execution_time) << "\nSupersteps = " << deleted_vertices.size() << "\n" << "Avg. deletions per superstep = " << avg_deletions << "\n"; } }; static crauser_et_al_shortest_paths_stats_t crauser_et_al_shortest_paths_stats; #endif namespace detail { /************************************************************************ * Function objects that perform distance comparisons modified by the * * minimum or maximum edge weights. * ************************************************************************/ template struct min_in_distance_compare : std::binary_function { min_in_distance_compare(DistanceMap d, MinInWeightMap m, Combine combine, Compare compare) : distance_map(d), min_in_weight(m), combine(combine), compare(compare) { } bool operator()(const Vertex& x, const Vertex& y) const { return compare(combine(get(distance_map, x), -get(min_in_weight, x)), combine(get(distance_map, y), -get(min_in_weight, y))); } private: DistanceMap distance_map; MinInWeightMap min_in_weight; Combine combine; Compare compare; }; template struct min_out_distance_compare : std::binary_function { min_out_distance_compare(DistanceMap d, MinOutWeightMap m, Combine combine, Compare compare) : distance_map(d), min_out_weight(m), combine(combine), compare(compare) { } bool operator()(const Vertex& x, const Vertex& y) const { return compare(combine(get(distance_map, x), get(min_out_weight, x)), combine(get(distance_map, y), get(min_out_weight, y))); } private: DistanceMap distance_map; MinOutWeightMap min_out_weight; Combine combine; Compare compare; }; /************************************************************************/ /************************************************************************ * Dijkstra queue that implements Crauser et al.'s criteria. This queue * * actually stores three separate priority queues, to help expose all * * vertices that can be processed in a single phase. * ************************************************************************/ template class crauser_et_al_dijkstra_queue : public graph::detail::remote_update_set< crauser_et_al_dijkstra_queue< Graph, Combine, Compare, VertexIndexMap, DistanceMap, PredecessorMap, MinOutWeightMap, MinInWeightMap>, typename boost::graph::parallel::process_group_type::type, typename dijkstra_msg_value::type, typename property_map::const_type> { typedef typename graph_traits::vertex_descriptor vertex_descriptor; typedef crauser_et_al_dijkstra_queue self_type; typedef dijkstra_msg_value msg_value_creator; typedef typename msg_value_creator::type msg_value_type; typedef typename graph_traits::vertices_size_type vertices_size_type; typedef typename property_map::const_type OwnerPropertyMap; typedef typename boost::graph::parallel::process_group_type::type process_group_type; typedef graph::detail::remote_update_set inherited; // Priority queue for tentative distances typedef indirect_cmp dist_queue_compare_type; typedef typename property_traits::value_type distance_type; #ifdef MUTABLE_QUEUE typedef mutable_queue, dist_queue_compare_type, VertexIndexMap> dist_queue_type; #else typedef relaxed_heap dist_queue_type; #endif // MUTABLE_QUEUE // Priority queue for OUT criteria typedef min_out_distance_compare out_queue_compare_type; #ifdef MUTABLE_QUEUE typedef mutable_queue, out_queue_compare_type, VertexIndexMap> out_queue_type; #else typedef relaxed_heap out_queue_type; #endif // MUTABLE_QUEUE // Priority queue for IN criteria typedef min_in_distance_compare in_queue_compare_type; #ifdef MUTABLE_QUEUE typedef mutable_queue, in_queue_compare_type, VertexIndexMap> in_queue_type; #else typedef relaxed_heap in_queue_type; #endif // MUTABLE_QUEUE typedef typename process_group_type::process_id_type process_id_type; public: typedef typename dist_queue_type::size_type size_type; typedef typename dist_queue_type::value_type value_type; crauser_et_al_dijkstra_queue(const Graph& g, const Combine& combine, const Compare& compare, const VertexIndexMap& id, const DistanceMap& distance_map, const PredecessorMap& predecessor_map, const MinOutWeightMap& min_out_weight, const MinInWeightMap& min_in_weight) : inherited(boost::graph::parallel::process_group(g), get(vertex_owner, g)), dist_queue(num_vertices(g), dist_queue_compare_type(distance_map, compare), id), out_queue(num_vertices(g), out_queue_compare_type(distance_map, min_out_weight, combine, compare), id), in_queue(num_vertices(g), in_queue_compare_type(distance_map, min_in_weight, combine, compare), id), g(g), distance_map(distance_map), predecessor_map(predecessor_map), min_out_weight(min_out_weight), min_in_weight(min_in_weight), min_distance(0), min_out_distance(0) #ifdef PBGL_ACCOUNTING , local_deletions(0) #endif { } void push(const value_type& x) { msg_value_type msg_value = msg_value_creator::create(get(distance_map, x), predecessor_value(get(predecessor_map, x))); inherited::update(x, msg_value); } void update(const value_type& x) { push(x); } void pop() { // Remove from distance queue dist_queue.remove(top_vertex); // Remove from OUT queue out_queue.remove(top_vertex); // Remove from IN queue in_queue.remove(top_vertex); #ifdef PBGL_ACCOUNTING ++local_deletions; #endif } vertex_descriptor& top() { return top_vertex; } const vertex_descriptor& top() const { return top_vertex; } bool empty() { inherited::collect(); // If there are no suitable messages, wait until we get something while (!has_suitable_vertex()) { if (do_synchronize()) return true; } // Return true only if nobody has any messages; false if we // have suitable messages return false; } bool do_synchronize() { using boost::parallel::all_reduce; using boost::parallel::minimum; inherited::synchronize(); // TBD: could use combine here, but then we need to stop using // minimum() as the function object. distance_type local_distances[2]; local_distances[0] = dist_queue.empty()? (std::numeric_limits::max)() : get(distance_map, dist_queue.top()); local_distances[1] = out_queue.empty()? (std::numeric_limits::max)() : (get(distance_map, out_queue.top()) + get(min_out_weight, out_queue.top())); distance_type distances[2]; all_reduce(this->process_group, local_distances, local_distances + 2, distances, minimum()); min_distance = distances[0]; min_out_distance = distances[1]; #ifdef PBGL_ACCOUNTING std::size_t deletions = 0; all_reduce(this->process_group, &local_deletions, &local_deletions + 1, &deletions, std::plus()); if (process_id(this->process_group) == 0) { crauser_et_al_shortest_paths_stats.deleted_vertices.push_back(deletions); } local_deletions = 0; BOOST_ASSERT(deletions > 0); #endif return min_distance == (std::numeric_limits::max)(); } private: vertex_descriptor predecessor_value(vertex_descriptor v) const { return v; } vertex_descriptor predecessor_value(property_traits::reference) const { return graph_traits::null_vertex(); } bool has_suitable_vertex() const { if (!dist_queue.empty()) { top_vertex = dist_queue.top(); if (get(distance_map, dist_queue.top()) <= min_out_distance) return true; } if (!in_queue.empty()) { top_vertex = in_queue.top(); return (get(distance_map, top_vertex) - get(min_in_weight, top_vertex)) <= min_distance; } return false; } public: void receive_update(process_id_type source, vertex_descriptor vertex, distance_type distance) { // Update the queue if the received distance is better than // the distance we know locally if (distance < get(distance_map, vertex) || (distance == get(distance_map, vertex) && source == process_id(this->process_group))) { // Update the local distance map put(distance_map, vertex, distance); bool is_in_queue = dist_queue.contains(vertex); if (!is_in_queue) { dist_queue.push(vertex); out_queue.push(vertex); in_queue.push(vertex); } else { dist_queue.update(vertex); out_queue.update(vertex); in_queue.update(vertex); } } } void receive_update(process_id_type source, vertex_descriptor vertex, std::pair p) { if (p.first <= get(distance_map, vertex)) { put(predecessor_map, vertex, p.second); receive_update(source, vertex, p.first); } } private: dist_queue_type dist_queue; out_queue_type out_queue; in_queue_type in_queue; mutable value_type top_vertex; const Graph& g; DistanceMap distance_map; PredecessorMap predecessor_map; MinOutWeightMap min_out_weight; MinInWeightMap min_in_weight; distance_type min_distance; distance_type min_out_distance; #ifdef PBGL_ACCOUNTING std::size_t local_deletions; #endif }; /************************************************************************/ /************************************************************************ * Initialize the property map that contains the minimum incoming edge * * weight for each vertex. There are separate implementations for * * directed, bidirectional, and undirected graph. * ************************************************************************/ template void initialize_min_in_weights(const Graph& g, MinInWeightMap min_in_weight, WeightMap weight, Inf inf, Compare compare, directed_tag, incidence_graph_tag) { // Send minimum weights off to the owners set_property_map_role(vertex_distance, min_in_weight); BGL_FORALL_VERTICES_T(v, g, Graph) { BGL_FORALL_OUTEDGES_T(v, e, g, Graph) { if (get(weight, e) < get(min_in_weight, target(e, g))) put(min_in_weight, target(e, g), get(weight, e)); } } using boost::graph::parallel::process_group; synchronize(process_group(g)); // Replace any infinities with zeros BGL_FORALL_VERTICES_T(v, g, Graph) { if (get(min_in_weight, v) == inf) put(min_in_weight, v, 0); } } template void initialize_min_in_weights(const Graph& g, MinInWeightMap min_in_weight, WeightMap weight, Inf inf, Compare compare, directed_tag, bidirectional_graph_tag) { #if 0 typename property_map::const_type local = get(vertex_local, g); // This code assumes that the properties of the in-edges are // available locally. This is not necessarily the case, so don't // do this yet. set_property_map_role(vertex_distance, min_in_weight); BGL_FORALL_VERTICES_T(v, g, Graph) { if (in_edges(v, g).first != in_edges(v, g).second) { std::cerr << "weights(" << g.distribution().global(get(local, v)) << ") = "; BGL_FORALL_INEDGES_T(v, e, g, Graph) { std::cerr << get(weight, e) << ' '; } std::cerr << std::endl; put(min_in_weight, v, *std::min_element (make_property_map_iterator(weight, in_edges(v, g).first), make_property_map_iterator(weight, in_edges(v, g).second), compare)); } else { put(min_in_weight, v, 0); } std::cerr << "miw(" << g.distribution().global(get(local, v)) << ") = " << get(min_in_weight, v) << std::endl; } #else initialize_min_in_weights(g, min_in_weight, weight, inf, compare, directed_tag(), incidence_graph_tag()); #endif } template inline void initialize_min_in_weights(const Graph&, MinInWeightMap, WeightMap, Inf, Compare, undirected_tag, bidirectional_graph_tag) { // In weights are the same as out weights, so do nothing } /************************************************************************/ /************************************************************************ * Initialize the property map that contains the minimum outgoing edge * * weight for each vertex. * ************************************************************************/ template void initialize_min_out_weights(const Graph& g, MinOutWeightMap min_out_weight, WeightMap weight, Compare compare) { typedef typename property_traits::value_type weight_type; BGL_FORALL_VERTICES_T(v, g, Graph) { if (out_edges(v, g).first != out_edges(v, g).second) { put(min_out_weight, v, *std::min_element (make_property_map_iterator(weight, out_edges(v, g).first), make_property_map_iterator(weight, out_edges(v, g).second), compare)); if (get(min_out_weight, v) < weight_type(0)) boost::throw_exception(negative_edge()); } } } /************************************************************************/ } // end namespace detail template void crauser_et_al_shortest_paths (const DistributedGraph& g, typename graph_traits::vertex_descriptor s, PredecessorMap predecessor, DistanceMap distance, WeightMap weight, IndexMap index_map, ColorMap color_map, Compare compare, Combine combine, DistInf inf, DistZero zero, DijkstraVisitor vis) { #ifdef PBGL_ACCOUNTING crauser_et_al_shortest_paths_stats.deleted_vertices.clear(); crauser_et_al_shortest_paths_stats.execution_time = accounting::get_time(); #endif // Property map that stores the lowest edge weight outgoing from // each vertex. If a vertex has no out-edges, the stored weight // is zero. typedef typename property_traits::value_type weight_type; typedef iterator_property_map MinOutWeightMap; std::vector min_out_weights_vec(num_vertices(g), inf); MinOutWeightMap min_out_weight(&min_out_weights_vec.front(), index_map); detail::initialize_min_out_weights(g, min_out_weight, weight, compare); // Property map that stores the lowest edge weight incoming to // each vertex. For undirected graphs, this will just be a // shallow copy of the version for outgoing edges. typedef typename graph_traits::directed_category directed_category; const bool is_undirected = is_same::value; typedef MinOutWeightMap MinInWeightMap; std::vector min_in_weights_vec(is_undirected? 1 : num_vertices(g), inf); MinInWeightMap min_in_weight(&min_in_weights_vec.front(), index_map); typedef typename graph_traits::traversal_category category; detail::initialize_min_in_weights(g, min_in_weight, weight, inf, compare, directed_category(), category()); // Initialize local portion of property maps typename graph_traits::vertex_iterator ui, ui_end; for (boost::tie(ui, ui_end) = vertices(g); ui != ui_end; ++ui) { put(distance, *ui, inf); put(predecessor, *ui, *ui); } put(distance, s, zero); // Dijkstra Queue typedef detail::crauser_et_al_dijkstra_queue Queue; Queue Q(g, combine, compare, index_map, distance, predecessor, min_out_weight, is_undirected? min_out_weight : min_in_weight); // Parallel Dijkstra visitor ::boost::detail::dijkstra_bfs_visitor< DijkstraVisitor, Queue, WeightMap, boost::parallel::caching_property_map, boost::parallel::caching_property_map, Combine, Compare > bfs_vis(vis, Q, weight, boost::parallel::make_caching_property_map(predecessor), boost::parallel::make_caching_property_map(distance), combine, compare, zero); set_property_map_role(vertex_color, color_map); set_property_map_role(vertex_distance, distance); breadth_first_search(g, s, Q, bfs_vis, color_map); #ifdef PBGL_ACCOUNTING crauser_et_al_shortest_paths_stats.execution_time = accounting::get_time() - crauser_et_al_shortest_paths_stats.execution_time; #endif } template void crauser_et_al_shortest_paths (const DistributedGraph& g, typename graph_traits::vertex_descriptor s, PredecessorMap predecessor, DistanceMap distance, WeightMap weight) { typedef typename property_traits::value_type distance_type; std::vector colors(num_vertices(g), white_color); crauser_et_al_shortest_paths(g, s, predecessor, distance, weight, get(vertex_index, g), make_iterator_property_map(&colors[0], get(vertex_index, g)), std::less(), closed_plus(), (std::numeric_limits::max)(), distance_type(), dijkstra_visitor<>()); } template void crauser_et_al_shortest_paths (const DistributedGraph& g, typename graph_traits::vertex_descriptor s, PredecessorMap predecessor, DistanceMap distance) { crauser_et_al_shortest_paths(g, s, predecessor, distance, get(edge_weight, g)); } } // end namespace distributed #ifdef PBGL_ACCOUNTING using distributed::crauser_et_al_shortest_paths_stats; #endif using distributed::crauser_et_al_shortest_paths; } } // end namespace boost::graph #endif // BOOST_GRAPH_CRAUSER_ET_AL_SHORTEST_PATHS_HPP