123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514 |
- // Copyright (C) 2007 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 Delta-stepping algorithm: *
- * *
- * Ulrich Meyer and Peter Sanders. Parallel Shortest Path for Arbitrary *
- * Graphs. In Proceedings from the 6th International Euro-Par *
- * Conference on Parallel Processing, pages 461--470, 2000. *
- * *
- * Ulrich Meyer, Peter Sanders: [Delta]-stepping: A Parallelizable *
- * Shortest Path Algorithm. J. Algorithms 49(1): 114-152, 2003. *
- * *
- * There are several potential optimizations we could still perform for *
- * this algorithm implementation: *
- * *
- * - Implement "shortcuts", which bound the number of reinsertions *
- * in a single bucket (to one). The computation of shortcuts looks *
- * expensive in a distributed-memory setting, but it could be *
- * ammortized over many queries. *
- * *
- * - The size of the "buckets" data structure can be bounded to *
- * max_edge_weight/delta buckets, if we treat the buckets as a *
- * circular array. *
- * *
- * - If we partition light/heavy edges ahead of time, we could improve *
- * relaxation performance by only iterating over the right portion *
- * of the out-edge list each time. *
- * *
- * - Implement a more sophisticated algorithm for guessing "delta", *
- * based on the shortcut-finding algorithm. *
- **************************************************************************/
- #ifndef BOOST_GRAPH_DELTA_STEPPING_SHORTEST_PATHS_HPP
- #define BOOST_GRAPH_DELTA_STEPPING_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/config.hpp>
- #include <boost/graph/graph_traits.hpp>
- #include <boost/property_map/property_map.hpp>
- #include <boost/property_map/parallel/parallel_property_maps.hpp>
- #include <boost/graph/iteration_macros.hpp>
- #include <limits>
- #include <list>
- #include <vector>
- #include <boost/graph/parallel/container_traits.hpp>
- #include <boost/graph/parallel/properties.hpp>
- #include <boost/graph/distributed/detail/dijkstra_shortest_paths.hpp>
- #include <utility> // for std::pair
- #include <functional> // for std::logical_or
- #include <boost/graph/parallel/algorithm.hpp> // for all_reduce
- #include <cassert>
- #include <algorithm> // for std::min, std::max
- #include <boost/graph/parallel/simple_trigger.hpp>
- #ifdef PBGL_DELTA_STEPPING_DEBUG
- # include <iostream> // for std::cerr
- #endif
- namespace boost { namespace graph { namespace distributed {
- template<typename Graph, typename PredecessorMap, typename DistanceMap,
- typename EdgeWeightMap>
- class delta_stepping_impl {
- typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
- typedef typename graph_traits<Graph>::degree_size_type Degree;
- typedef typename property_traits<EdgeWeightMap>::value_type Dist;
- typedef typename boost::graph::parallel::process_group_type<Graph>::type
- ProcessGroup;
- typedef std::list<Vertex> Bucket;
- typedef typename Bucket::iterator BucketIterator;
- typedef typename std::vector<Bucket*>::size_type BucketIndex;
- typedef detail::dijkstra_msg_value<DistanceMap, PredecessorMap> MessageValue;
- enum {
- // Relax a remote vertex. The message contains a pair<Vertex,
- // MessageValue>, the first part of which is the vertex whose
- // tentative distance is being relaxed and the second part
- // contains either the new distance (if there is no predecessor
- // map) or a pair with the distance and predecessor.
- msg_relax
- };
- public:
- delta_stepping_impl(const Graph& g,
- PredecessorMap predecessor,
- DistanceMap distance,
- EdgeWeightMap weight,
- Dist delta);
- delta_stepping_impl(const Graph& g,
- PredecessorMap predecessor,
- DistanceMap distance,
- EdgeWeightMap weight);
- void run(Vertex s);
- private:
- // Relax the edge (u, v), creating a new best path of distance x.
- void relax(Vertex u, Vertex v, Dist x);
- // Synchronize all of the processes, by receiving all messages that
- // have not yet been received.
- void synchronize();
- // Handle a relax message that contains only the target and
- // distance. This kind of message will be received when the
- // predecessor map is a dummy_property_map.
- void handle_relax_message(Vertex v, Dist x) { relax(v, v, x); }
- // Handle a relax message that contains the source (predecessor),
- // target, and distance. This kind of message will be received when
- // the predecessor map is not a dummy_property_map.
- void handle_relax_message(Vertex v, const std::pair<Dist, Vertex>& p)
- { relax(p.second, v, p.first); }
-
- // Setup triggers for msg_relax messages
- void setup_triggers();
- void handle_msg_relax(int /*source*/, int /*tag*/,
- const std::pair<Vertex, typename MessageValue::type>& data,
- trigger_receive_context)
- { handle_relax_message(data.first, data.second); }
- const Graph& g;
- PredecessorMap predecessor;
- DistanceMap distance;
- EdgeWeightMap weight;
- Dist delta;
- ProcessGroup pg;
- typename property_map<Graph, vertex_owner_t>::const_type owner;
- typename property_map<Graph, vertex_local_t>::const_type local;
- // A "property map" that contains the position of each vertex in
- // whatever bucket it resides in.
- std::vector<BucketIterator> position_in_bucket;
- // Bucket data structure. The ith bucket contains all local vertices
- // with (tentative) distance in the range [i*delta,
- // (i+1)*delta).
- std::vector<Bucket*> buckets;
- // This "dummy" list is used only so that we can initialize the
- // position_in_bucket property map with non-singular iterators. This
- // won't matter for most implementations of the C++ Standard
- // Library, but it avoids undefined behavior and allows us to run
- // with library "debug modes".
- std::list<Vertex> dummy_list;
- // A "property map" that states which vertices have been deleted
- // from the bucket in this iteration.
- std::vector<bool> vertex_was_deleted;
- };
- template<typename Graph, typename PredecessorMap, typename DistanceMap,
- typename EdgeWeightMap>
- delta_stepping_impl<Graph, PredecessorMap, DistanceMap, EdgeWeightMap>::
- delta_stepping_impl(const Graph& g,
- PredecessorMap predecessor,
- DistanceMap distance,
- EdgeWeightMap weight,
- Dist delta)
- : g(g),
- predecessor(predecessor),
- distance(distance),
- weight(weight),
- delta(delta),
- pg(boost::graph::parallel::process_group_adl(g), attach_distributed_object()),
- owner(get(vertex_owner, g)),
- local(get(vertex_local, g))
- {
- setup_triggers();
- }
- template<typename Graph, typename PredecessorMap, typename DistanceMap,
- typename EdgeWeightMap>
- delta_stepping_impl<Graph, PredecessorMap, DistanceMap, EdgeWeightMap>::
- delta_stepping_impl(const Graph& g,
- PredecessorMap predecessor,
- DistanceMap distance,
- EdgeWeightMap weight)
- : g(g),
- predecessor(predecessor),
- distance(distance),
- weight(weight),
- pg(boost::graph::parallel::process_group_adl(g), attach_distributed_object()),
- owner(get(vertex_owner, g)),
- local(get(vertex_local, g))
- {
- using boost::parallel::all_reduce;
- using boost::parallel::maximum;
- using std::max;
- // Compute the maximum edge weight and degree
- Dist max_edge_weight = 0;
- Degree max_degree = 0;
- BGL_FORALL_VERTICES_T(u, g, Graph) {
- max_degree = max BOOST_PREVENT_MACRO_SUBSTITUTION (max_degree, out_degree(u, g));
- BGL_FORALL_OUTEDGES_T(u, e, g, Graph)
- max_edge_weight = max BOOST_PREVENT_MACRO_SUBSTITUTION (max_edge_weight, get(weight, e));
- }
- max_edge_weight = all_reduce(pg, max_edge_weight, maximum<Dist>());
- max_degree = all_reduce(pg, max_degree, maximum<Degree>());
- // Take a guess at delta, based on what works well for random
- // graphs.
- delta = max_edge_weight / max_degree;
- if (delta == 0)
- delta = 1;
- setup_triggers();
- }
- template<typename Graph, typename PredecessorMap, typename DistanceMap,
- typename EdgeWeightMap>
- void
- delta_stepping_impl<Graph, PredecessorMap, DistanceMap, EdgeWeightMap>::
- run(Vertex s)
- {
- Dist inf = (std::numeric_limits<Dist>::max)();
- // None of the vertices are stored in the bucket.
- position_in_bucket.clear();
- position_in_bucket.resize(num_vertices(g), dummy_list.end());
- // None of the vertices have been deleted
- vertex_was_deleted.clear();
- vertex_was_deleted.resize(num_vertices(g), false);
- // No path from s to any other vertex, yet
- BGL_FORALL_VERTICES_T(v, g, Graph)
- put(distance, v, inf);
- // The distance to the starting node is zero
- if (get(owner, s) == process_id(pg))
- // Put "s" into its bucket (bucket 0)
- relax(s, s, 0);
- else
- // Note that we know the distance to s is zero
- cache(distance, s, 0);
- BucketIndex max_bucket = (std::numeric_limits<BucketIndex>::max)();
- BucketIndex current_bucket = 0;
- do {
- // Synchronize with all of the other processes.
- synchronize();
- // Find the next bucket that has something in it.
- while (current_bucket < buckets.size()
- && (!buckets[current_bucket] || buckets[current_bucket]->empty()))
- ++current_bucket;
- if (current_bucket >= buckets.size())
- current_bucket = max_bucket;
- #ifdef PBGL_DELTA_STEPPING_DEBUG
- std::cerr << "#" << process_id(pg) << ": lowest bucket is #"
- << current_bucket << std::endl;
- #endif
- // Find the smallest bucket (over all processes) that has vertices
- // that need to be processed.
- using boost::parallel::all_reduce;
- using boost::parallel::minimum;
- current_bucket = all_reduce(pg, current_bucket, minimum<BucketIndex>());
- if (current_bucket == max_bucket)
- // There are no non-empty buckets in any process; exit.
- break;
- #ifdef PBGL_DELTA_STEPPING_DEBUG
- if (process_id(pg) == 0)
- std::cerr << "Processing bucket #" << current_bucket << std::endl;
- #endif
- // Contains the set of vertices that have been deleted in the
- // relaxation of "light" edges. Note that we keep track of which
- // vertices were deleted with the property map
- // "vertex_was_deleted".
- std::vector<Vertex> deleted_vertices;
- // Repeatedly relax light edges
- bool nonempty_bucket;
- do {
- // Someone has work to do in this bucket.
- if (current_bucket < buckets.size() && buckets[current_bucket]) {
- Bucket& bucket = *buckets[current_bucket];
- // For each element in the bucket
- while (!bucket.empty()) {
- Vertex u = bucket.front();
- #ifdef PBGL_DELTA_STEPPING_DEBUG
- std::cerr << "#" << process_id(pg) << ": processing vertex "
- << get(vertex_global, g, u).second << "@"
- << get(vertex_global, g, u).first
- << std::endl;
- #endif
- // Remove u from the front of the bucket
- bucket.pop_front();
-
- // Insert u into the set of deleted vertices, if it hasn't
- // been done already.
- if (!vertex_was_deleted[get(local, u)]) {
- vertex_was_deleted[get(local, u)] = true;
- deleted_vertices.push_back(u);
- }
- // Relax each light edge.
- Dist u_dist = get(distance, u);
- BGL_FORALL_OUTEDGES_T(u, e, g, Graph)
- if (get(weight, e) <= delta) // light edge
- relax(u, target(e, g), u_dist + get(weight, e));
- }
- }
- // Synchronize with all of the other processes.
- synchronize();
- // Is the bucket empty now?
- nonempty_bucket = (current_bucket < buckets.size()
- && buckets[current_bucket]
- && !buckets[current_bucket]->empty());
- } while (all_reduce(pg, nonempty_bucket, std::logical_or<bool>()));
- // Relax heavy edges for each of the vertices that we previously
- // deleted.
- for (typename std::vector<Vertex>::iterator iter = deleted_vertices.begin();
- iter != deleted_vertices.end(); ++iter) {
- // Relax each heavy edge.
- Vertex u = *iter;
- Dist u_dist = get(distance, u);
- BGL_FORALL_OUTEDGES_T(u, e, g, Graph)
- if (get(weight, e) > delta) // heavy edge
- relax(u, target(e, g), u_dist + get(weight, e));
- }
- // Go to the next bucket: the current bucket must already be empty.
- ++current_bucket;
- } while (true);
- // Delete all of the buckets.
- for (typename std::vector<Bucket*>::iterator iter = buckets.begin();
- iter != buckets.end(); ++iter) {
- if (*iter) {
- delete *iter;
- *iter = 0;
- }
- }
- }
- template<typename Graph, typename PredecessorMap, typename DistanceMap,
- typename EdgeWeightMap>
- void
- delta_stepping_impl<Graph, PredecessorMap, DistanceMap, EdgeWeightMap>::
- relax(Vertex u, Vertex v, Dist x)
- {
- #ifdef PBGL_DELTA_STEPPING_DEBUG
- std::cerr << "#" << process_id(pg) << ": relax("
- << get(vertex_global, g, u).second << "@"
- << get(vertex_global, g, u).first << ", "
- << get(vertex_global, g, v).second << "@"
- << get(vertex_global, g, v).first << ", "
- << x << ")" << std::endl;
- #endif
- if (x < get(distance, v)) {
- // We're relaxing the edge to vertex v.
- if (get(owner, v) == process_id(pg)) {
- // Compute the new bucket index for v
- BucketIndex new_index = static_cast<BucketIndex>(x / delta);
-
- // Make sure there is enough room in the buckets data structure.
- if (new_index >= buckets.size()) buckets.resize(new_index + 1, 0);
- // Make sure that we have allocated the bucket itself.
- if (!buckets[new_index]) buckets[new_index] = new Bucket;
- if (get(distance, v) != (std::numeric_limits<Dist>::max)()
- && !vertex_was_deleted[get(local, v)]) {
- // We're moving v from an old bucket into a new one. Compute
- // the old index, then splice it in.
- BucketIndex old_index
- = static_cast<BucketIndex>(get(distance, v) / delta);
- buckets[new_index]->splice(buckets[new_index]->end(),
- *buckets[old_index],
- position_in_bucket[get(local, v)]);
- } else {
- // We're inserting v into a bucket for the first time. Put it
- // at the end.
- buckets[new_index]->push_back(v);
- }
- // v is now at the last position in the new bucket
- position_in_bucket[get(local, v)] = buckets[new_index]->end();
- --position_in_bucket[get(local, v)];
- // Update predecessor and tentative distance information
- put(predecessor, v, u);
- put(distance, v, x);
- } else {
- #ifdef PBGL_DELTA_STEPPING_DEBUG
- std::cerr << "#" << process_id(pg) << ": sending relax("
- << get(vertex_global, g, u).second << "@"
- << get(vertex_global, g, u).first << ", "
- << get(vertex_global, g, v).second << "@"
- << get(vertex_global, g, v).first << ", "
- << x << ") to " << get(owner, v) << std::endl;
-
- #endif
- // The vertex is remote: send a request to the vertex's owner
- send(pg, get(owner, v), msg_relax,
- std::make_pair(v, MessageValue::create(x, u)));
- // Cache tentative distance information
- cache(distance, v, x);
- }
- }
- }
- template<typename Graph, typename PredecessorMap, typename DistanceMap,
- typename EdgeWeightMap>
- void
- delta_stepping_impl<Graph, PredecessorMap, DistanceMap, EdgeWeightMap>::
- synchronize()
- {
- using boost::parallel::synchronize;
- // Synchronize at the process group level.
- synchronize(pg);
- // Receive any relaxation request messages.
- // typedef typename ProcessGroup::process_id_type process_id_type;
- // while (optional<std::pair<process_id_type, int> > stp = probe(pg)) {
- // // Receive the relaxation message
- // assert(stp->second == msg_relax);
- // std::pair<Vertex, typename MessageValue::type> data;
- // receive(pg, stp->first, stp->second, data);
- // // Turn it into a "relax" call
- // handle_relax_message(data.first, data.second);
- // }
- }
- template<typename Graph, typename PredecessorMap, typename DistanceMap,
- typename EdgeWeightMap>
- void
- delta_stepping_impl<Graph, PredecessorMap, DistanceMap, EdgeWeightMap>::
- setup_triggers()
- {
- using boost::graph::parallel::simple_trigger;
-
- simple_trigger(pg, msg_relax, this,
- &delta_stepping_impl::handle_msg_relax);
- }
- template<typename Graph, typename PredecessorMap, typename DistanceMap,
- typename EdgeWeightMap>
- void
- delta_stepping_shortest_paths
- (const Graph& g,
- typename graph_traits<Graph>::vertex_descriptor s,
- PredecessorMap predecessor, DistanceMap distance, EdgeWeightMap weight,
- typename property_traits<EdgeWeightMap>::value_type delta)
- {
- // The "distance" map needs to act like one, retrieving the default
- // value of infinity.
- set_property_map_role(vertex_distance, distance);
- // Construct the implementation object, which will perform all of
- // the actual work.
- delta_stepping_impl<Graph, PredecessorMap, DistanceMap, EdgeWeightMap>
- impl(g, predecessor, distance, weight, delta);
- // Run the delta-stepping algorithm. The results will show up in
- // "predecessor" and "weight".
- impl.run(s);
- }
- template<typename Graph, typename PredecessorMap, typename DistanceMap,
- typename EdgeWeightMap>
- void
- delta_stepping_shortest_paths
- (const Graph& g,
- typename graph_traits<Graph>::vertex_descriptor s,
- PredecessorMap predecessor, DistanceMap distance, EdgeWeightMap weight)
- {
- // The "distance" map needs to act like one, retrieving the default
- // value of infinity.
- set_property_map_role(vertex_distance, distance);
- // Construct the implementation object, which will perform all of
- // the actual work.
- delta_stepping_impl<Graph, PredecessorMap, DistanceMap, EdgeWeightMap>
- impl(g, predecessor, distance, weight);
- // Run the delta-stepping algorithm. The results will show up in
- // "predecessor" and "weight".
- impl.run(s);
- }
-
- } } } // end namespace boost::graph::distributed
- #endif // BOOST_GRAPH_DELTA_STEPPING_SHORTEST_PATHS_HPP
|