442 lines
16 KiB
C++
442 lines
16 KiB
C++
// 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 a variation on distributed Dijkstra's *
|
|
* algorithm that can expose additional parallelism by permitting *
|
|
* vertices within a certain distance from the minimum to be processed, *
|
|
* even though they may not be at their final distance. This can *
|
|
* introduce looping, but the algorithm will still terminate so long as *
|
|
* there are no negative loops. *
|
|
**************************************************************************/
|
|
#ifndef BOOST_GRAPH_EAGER_DIJKSTRA_SHORTEST_PATHS_HPP
|
|
#define BOOST_GRAPH_EAGER_DIJKSTRA_SHORTEST_PATHS_HPP
|
|
|
|
#ifndef BOOST_GRAPH_USE_MPI
|
|
#error "Parallel BGL files should not be included unless <boost/graph/use_mpi.hpp> has been included"
|
|
#endif
|
|
|
|
#include <boost/assert.hpp>
|
|
#include <boost/graph/distributed/detail/dijkstra_shortest_paths.hpp>
|
|
#include <boost/property_map/parallel/caching_property_map.hpp>
|
|
#include <boost/pending/indirect_cmp.hpp>
|
|
#include <boost/graph/distributed/detail/remote_update_set.hpp>
|
|
#include <vector>
|
|
#include <boost/graph/breadth_first_search.hpp>
|
|
#include <boost/graph/dijkstra_shortest_paths.hpp>
|
|
#include <boost/graph/parallel/container_traits.hpp>
|
|
#include <boost/pending/relaxed_heap.hpp>
|
|
|
|
#ifdef PBGL_ACCOUNTING
|
|
# include <boost/graph/accounting.hpp>
|
|
# include <numeric>
|
|
#endif // PBGL_ACCOUNTING
|
|
|
|
#ifdef MUTABLE_QUEUE
|
|
# include <boost/pending/mutable_queue.hpp>
|
|
#endif
|
|
|
|
namespace boost { namespace graph { namespace distributed {
|
|
|
|
#ifdef PBGL_ACCOUNTING
|
|
struct eager_dijkstra_shortest_paths_stats_t
|
|
{
|
|
/* The value of the lookahead parameter. */
|
|
double lookahead;
|
|
|
|
/* Total wall-clock time used by the algorithm.*/
|
|
accounting::time_type execution_time;
|
|
|
|
/* The number of vertices deleted in each superstep. */
|
|
std::vector<std::size_t> deleted_vertices;
|
|
|
|
template<typename OutputStream>
|
|
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 = \"Eager Dijkstra\"\n"
|
|
<< "Function = eager_dijkstra_shortest_paths\n"
|
|
<< "(P) Lookahead = " << lookahead << "\n"
|
|
<< "Wall clock time = " << accounting::print_time(execution_time)
|
|
<< "\nSupersteps = " << deleted_vertices.size() << "\n"
|
|
<< "Avg. deletions per superstep = " << avg_deletions << "\n";
|
|
}
|
|
};
|
|
|
|
static eager_dijkstra_shortest_paths_stats_t eager_dijkstra_shortest_paths_stats;
|
|
#endif
|
|
|
|
namespace detail {
|
|
|
|
// Borrowed from BGL's dijkstra_shortest_paths
|
|
template <class UniformCostVisitor, class Queue,
|
|
class WeightMap, class PredecessorMap, class DistanceMap,
|
|
class BinaryFunction, class BinaryPredicate>
|
|
struct parallel_dijkstra_bfs_visitor : bfs_visitor<>
|
|
{
|
|
typedef typename property_traits<DistanceMap>::value_type distance_type;
|
|
|
|
parallel_dijkstra_bfs_visitor(UniformCostVisitor vis, Queue& Q,
|
|
WeightMap w, PredecessorMap p, DistanceMap d,
|
|
BinaryFunction combine, BinaryPredicate compare,
|
|
distance_type zero)
|
|
: m_vis(vis), m_Q(Q), m_weight(w), m_predecessor(p), m_distance(d),
|
|
m_combine(combine), m_compare(compare), m_zero(zero) { }
|
|
|
|
template <class Vertex, class Graph>
|
|
void initialize_vertex(Vertex u, Graph& g)
|
|
{ m_vis.initialize_vertex(u, g); }
|
|
template <class Vertex, class Graph>
|
|
void discover_vertex(Vertex u, Graph& g) { m_vis.discover_vertex(u, g); }
|
|
template <class Vertex, class Graph>
|
|
void examine_vertex(Vertex u, Graph& g) { m_vis.examine_vertex(u, g); }
|
|
|
|
/* Since the eager formulation of Parallel Dijkstra's algorithm can
|
|
loop, we may relax on *any* edge, not just those associated with
|
|
white and gray targets. */
|
|
template <class Edge, class Graph>
|
|
void examine_edge(Edge e, Graph& g) {
|
|
if (m_compare(get(m_weight, e), m_zero))
|
|
boost::throw_exception(negative_edge());
|
|
|
|
m_vis.examine_edge(e, g);
|
|
|
|
boost::parallel::caching_property_map<PredecessorMap> c_pred(m_predecessor);
|
|
boost::parallel::caching_property_map<DistanceMap> c_dist(m_distance);
|
|
|
|
distance_type old_distance = get(c_dist, target(e, g));
|
|
|
|
bool m_decreased = relax(e, g, m_weight, c_pred, c_dist,
|
|
m_combine, m_compare);
|
|
|
|
/* On x86 Linux with optimization, we sometimes get into a
|
|
horrible case where m_decreased is true but the distance hasn't
|
|
actually changed. This occurs when the comparison inside
|
|
relax() occurs with the 80-bit precision of the x87 floating
|
|
point unit, but the difference is lost when the resulting
|
|
values are written back to lower-precision memory (e.g., a
|
|
double). With the eager Dijkstra's implementation, this results
|
|
in looping. */
|
|
if (m_decreased && old_distance != get(c_dist, target(e, g))) {
|
|
m_Q.update(target(e, g));
|
|
m_vis.edge_relaxed(e, g);
|
|
} else
|
|
m_vis.edge_not_relaxed(e, g);
|
|
}
|
|
template <class Vertex, class Graph>
|
|
void finish_vertex(Vertex u, Graph& g) { m_vis.finish_vertex(u, g); }
|
|
|
|
UniformCostVisitor m_vis;
|
|
Queue& m_Q;
|
|
WeightMap m_weight;
|
|
PredecessorMap m_predecessor;
|
|
DistanceMap m_distance;
|
|
BinaryFunction m_combine;
|
|
BinaryPredicate m_compare;
|
|
distance_type m_zero;
|
|
};
|
|
|
|
/**********************************************************************
|
|
* Dijkstra queue that implements arbitrary "lookahead" *
|
|
**********************************************************************/
|
|
template<typename Graph, typename Combine, typename Compare,
|
|
typename VertexIndexMap, typename DistanceMap,
|
|
typename PredecessorMap>
|
|
class lookahead_dijkstra_queue
|
|
: public graph::detail::remote_update_set<
|
|
lookahead_dijkstra_queue<
|
|
Graph, Combine, Compare, VertexIndexMap, DistanceMap,
|
|
PredecessorMap>,
|
|
typename boost::graph::parallel::process_group_type<Graph>::type,
|
|
typename dijkstra_msg_value<DistanceMap, PredecessorMap>::type,
|
|
typename property_map<Graph, vertex_owner_t>::const_type>
|
|
{
|
|
typedef typename graph_traits<Graph>::vertex_descriptor
|
|
vertex_descriptor;
|
|
typedef lookahead_dijkstra_queue self_type;
|
|
typedef typename boost::graph::parallel::process_group_type<Graph>::type
|
|
process_group_type;
|
|
typedef dijkstra_msg_value<DistanceMap, PredecessorMap> msg_value_creator;
|
|
typedef typename msg_value_creator::type msg_value_type;
|
|
typedef typename property_map<Graph, vertex_owner_t>::const_type
|
|
OwnerPropertyMap;
|
|
|
|
typedef graph::detail::remote_update_set<self_type, process_group_type,
|
|
msg_value_type, OwnerPropertyMap>
|
|
inherited;
|
|
|
|
// Priority queue for tentative distances
|
|
typedef indirect_cmp<DistanceMap, Compare> queue_compare_type;
|
|
|
|
typedef typename property_traits<DistanceMap>::value_type distance_type;
|
|
|
|
#ifdef MUTABLE_QUEUE
|
|
typedef mutable_queue<vertex_descriptor, std::vector<vertex_descriptor>,
|
|
queue_compare_type, VertexIndexMap> queue_type;
|
|
|
|
#else
|
|
typedef relaxed_heap<vertex_descriptor, queue_compare_type,
|
|
VertexIndexMap> queue_type;
|
|
#endif // MUTABLE_QUEUE
|
|
|
|
typedef typename process_group_type::process_id_type process_id_type;
|
|
|
|
public:
|
|
typedef vertex_descriptor value_type;
|
|
|
|
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)
|
|
: inherited(boost::graph::parallel::process_group(g), get(vertex_owner, g)),
|
|
queue(num_vertices(g), queue_compare_type(distance_map, compare), id),
|
|
distance_map(distance_map),
|
|
predecessor_map(predecessor_map),
|
|
min_distance(0),
|
|
lookahead(lookahead)
|
|
#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()
|
|
{
|
|
queue.pop();
|
|
#ifdef PBGL_ACCOUNTING
|
|
++local_deletions;
|
|
#endif
|
|
}
|
|
|
|
value_type& top() { return queue.top(); }
|
|
const value_type& top() const { return queue.top(); }
|
|
|
|
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;
|
|
}
|
|
|
|
private:
|
|
vertex_descriptor predecessor_value(vertex_descriptor v) const
|
|
{ return v; }
|
|
|
|
vertex_descriptor
|
|
predecessor_value(property_traits<dummy_property_map>::reference) const
|
|
{ return graph_traits<Graph>::null_vertex(); }
|
|
|
|
bool has_suitable_vertex() const
|
|
{
|
|
return (!queue.empty()
|
|
&& get(distance_map, queue.top()) <= min_distance + lookahead);
|
|
}
|
|
|
|
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<distance_type>() as the function object.
|
|
distance_type local_distance =
|
|
queue.empty()? (std::numeric_limits<distance_type>::max)()
|
|
: get(distance_map, queue.top());
|
|
|
|
all_reduce(this->process_group, &local_distance, &local_distance + 1,
|
|
&min_distance, minimum<distance_type>());
|
|
|
|
#ifdef PBGL_ACCOUNTING
|
|
std::size_t deletions = 0;
|
|
all_reduce(this->process_group, &local_deletions, &local_deletions + 1,
|
|
&deletions, std::plus<std::size_t>());
|
|
if (process_id(this->process_group) == 0)
|
|
eager_dijkstra_shortest_paths_stats.deleted_vertices
|
|
.push_back(deletions);
|
|
local_deletions = 0;
|
|
BOOST_ASSERT(deletions > 0);
|
|
#endif
|
|
|
|
return min_distance == (std::numeric_limits<distance_type>::max)();
|
|
}
|
|
|
|
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)) {
|
|
|
|
// Update the local distance map
|
|
put(distance_map, vertex, distance);
|
|
|
|
bool is_in_queue = queue.contains(vertex);
|
|
|
|
if (!is_in_queue)
|
|
queue.push(vertex);
|
|
else
|
|
queue.update(vertex);
|
|
}
|
|
}
|
|
|
|
void
|
|
receive_update(process_id_type source, vertex_descriptor vertex,
|
|
std::pair<distance_type, vertex_descriptor> p)
|
|
{
|
|
if (p.first <= get(distance_map, vertex)) {
|
|
put(predecessor_map, vertex, p.second);
|
|
receive_update(source, vertex, p.first);
|
|
}
|
|
}
|
|
|
|
private:
|
|
queue_type queue;
|
|
DistanceMap distance_map;
|
|
PredecessorMap predecessor_map;
|
|
distance_type min_distance;
|
|
distance_type lookahead;
|
|
#ifdef PBGL_ACCOUNTING
|
|
std::size_t local_deletions;
|
|
#endif
|
|
};
|
|
/**********************************************************************/
|
|
} // end namespace detail
|
|
|
|
template<typename DistributedGraph, typename DijkstraVisitor,
|
|
typename PredecessorMap, typename DistanceMap, typename WeightMap,
|
|
typename IndexMap, typename ColorMap, typename Compare,
|
|
typename Combine, typename DistInf, typename DistZero>
|
|
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)
|
|
{
|
|
#ifdef PBGL_ACCOUNTING
|
|
eager_dijkstra_shortest_paths_stats.deleted_vertices.clear();
|
|
eager_dijkstra_shortest_paths_stats.lookahead = lookahead;
|
|
eager_dijkstra_shortest_paths_stats.execution_time = accounting::get_time();
|
|
#endif
|
|
|
|
// Initialize local portion of property maps
|
|
typename graph_traits<DistributedGraph>::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::lookahead_dijkstra_queue
|
|
<DistributedGraph, Combine, Compare, IndexMap, DistanceMap,
|
|
PredecessorMap> Queue;
|
|
|
|
Queue Q(g, combine, compare, index_map, distance,
|
|
predecessor, lookahead);
|
|
|
|
// Parallel Dijkstra visitor
|
|
detail::parallel_dijkstra_bfs_visitor
|
|
<DijkstraVisitor, Queue, WeightMap, PredecessorMap, DistanceMap, Combine,
|
|
Compare> bfs_vis(vis, Q, weight, predecessor, 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
|
|
eager_dijkstra_shortest_paths_stats.execution_time =
|
|
accounting::get_time()
|
|
- eager_dijkstra_shortest_paths_stats.execution_time;
|
|
#endif
|
|
}
|
|
|
|
template<typename DistributedGraph, typename DijkstraVisitor,
|
|
typename PredecessorMap, typename DistanceMap, typename WeightMap>
|
|
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)
|
|
{
|
|
typedef typename property_traits<DistanceMap>::value_type distance_type;
|
|
|
|
std::vector<default_color_type> colors(num_vertices(g), white_color);
|
|
|
|
eager_dijkstra_shortest_paths(g, s, predecessor, distance, lookahead, weight,
|
|
get(vertex_index, g),
|
|
make_iterator_property_map(&colors[0],
|
|
get(vertex_index,
|
|
g)),
|
|
std::less<distance_type>(),
|
|
closed_plus<distance_type>(),
|
|
distance_type(),
|
|
(std::numeric_limits<distance_type>::max)(),
|
|
dijkstra_visitor<>());
|
|
}
|
|
|
|
template<typename DistributedGraph, typename DijkstraVisitor,
|
|
typename PredecessorMap, typename DistanceMap>
|
|
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)
|
|
{
|
|
eager_dijkstra_shortest_paths(g, s, predecessor, distance, lookahead,
|
|
get(edge_weight, g));
|
|
}
|
|
} // end namespace distributed
|
|
|
|
#ifdef PBGL_ACCOUNTING
|
|
using distributed::eager_dijkstra_shortest_paths_stats;
|
|
#endif
|
|
|
|
using distributed::eager_dijkstra_shortest_paths;
|
|
|
|
} } // end namespace boost::graph
|
|
|
|
#endif // BOOST_GRAPH_EAGER_DIJKSTRA_SHORTEST_PATHS_HPP
|