Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r52300 - in trunk: boost/graph boost/graph/distributed boost/graph/distributed/adjlist boost/graph/distributed/detail boost/graph/parallel boost/graph/parallel/detail boost/pending libs/graph/build libs/graph/src
From: jewillco_at_[hidden]
Date: 2009-04-09 17:11:04


Author: jewillco
Date: 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
New Revision: 52300
URL: http://svn.boost.org/trac/boost/changeset/52300

Log:
Merged headers and source files (but not examples, tests, or docs) from Parallel BGL
Added:
   trunk/boost/graph/distributed/
   trunk/boost/graph/distributed/adjacency_list.hpp (contents, props changed)
   trunk/boost/graph/distributed/adjlist/
   trunk/boost/graph/distributed/adjlist/handlers.hpp (contents, props changed)
   trunk/boost/graph/distributed/adjlist/initialize.hpp (contents, props changed)
   trunk/boost/graph/distributed/adjlist/redistribute.hpp (contents, props changed)
   trunk/boost/graph/distributed/adjlist/serialization.hpp (contents, props changed)
   trunk/boost/graph/distributed/betweenness_centrality.hpp (contents, props changed)
   trunk/boost/graph/distributed/boman_et_al_graph_coloring.hpp (contents, props changed)
   trunk/boost/graph/distributed/breadth_first_search.hpp (contents, props changed)
   trunk/boost/graph/distributed/compressed_sparse_row_graph.hpp (contents, props changed)
   trunk/boost/graph/distributed/concepts.hpp (contents, props changed)
   trunk/boost/graph/distributed/connected_components.hpp (contents, props changed)
   trunk/boost/graph/distributed/connected_components_parallel_search.hpp (contents, props changed)
   trunk/boost/graph/distributed/crauser_et_al_shortest_paths.hpp (contents, props changed)
   trunk/boost/graph/distributed/dehne_gotz_min_spanning_tree.hpp (contents, props changed)
   trunk/boost/graph/distributed/delta_stepping_shortest_paths.hpp (contents, props changed)
   trunk/boost/graph/distributed/depth_first_search.hpp (contents, props changed)
   trunk/boost/graph/distributed/detail/
   trunk/boost/graph/distributed/detail/dijkstra_shortest_paths.hpp (contents, props changed)
   trunk/boost/graph/distributed/detail/filtered_queue.hpp (contents, props changed)
   trunk/boost/graph/distributed/detail/mpi_process_group.tpp (contents, props changed)
   trunk/boost/graph/distributed/detail/queue.cpp (contents, props changed)
   trunk/boost/graph/distributed/detail/remote_update_set.hpp (contents, props changed)
   trunk/boost/graph/distributed/detail/tag_allocator.hpp (contents, props changed)
   trunk/boost/graph/distributed/dijkstra_shortest_paths.hpp (contents, props changed)
   trunk/boost/graph/distributed/distributed_graph_utility.hpp (contents, props changed)
   trunk/boost/graph/distributed/eager_dijkstra_shortest_paths.hpp (contents, props changed)
   trunk/boost/graph/distributed/filtered_graph.hpp (contents, props changed)
   trunk/boost/graph/distributed/fruchterman_reingold.hpp (contents, props changed)
   trunk/boost/graph/distributed/graphviz.hpp (contents, props changed)
   trunk/boost/graph/distributed/hohberg_biconnected_components.hpp (contents, props changed)
   trunk/boost/graph/distributed/local_subgraph.hpp (contents, props changed)
   trunk/boost/graph/distributed/mpi_process_group.hpp (contents, props changed)
   trunk/boost/graph/distributed/named_graph.hpp (contents, props changed)
   trunk/boost/graph/distributed/page_rank.hpp (contents, props changed)
   trunk/boost/graph/distributed/queue.hpp (contents, props changed)
   trunk/boost/graph/distributed/reverse_graph.hpp (contents, props changed)
   trunk/boost/graph/distributed/rmat_graph_generator.hpp (contents, props changed)
   trunk/boost/graph/distributed/selector.hpp (contents, props changed)
   trunk/boost/graph/distributed/shuffled_distribution.hpp (contents, props changed)
   trunk/boost/graph/distributed/st_connected.hpp (contents, props changed)
   trunk/boost/graph/distributed/strong_components.hpp (contents, props changed)
   trunk/boost/graph/distributed/two_bit_color_map.hpp (contents, props changed)
   trunk/boost/graph/distributed/unsafe_serialize.hpp (contents, props changed)
   trunk/boost/graph/distributed/vertex_list_adaptor.hpp (contents, props changed)
   trunk/boost/graph/parallel/
   trunk/boost/graph/parallel/algorithm.hpp (contents, props changed)
   trunk/boost/graph/parallel/basic_reduce.hpp (contents, props changed)
   trunk/boost/graph/parallel/container_traits.hpp (contents, props changed)
   trunk/boost/graph/parallel/detail/
   trunk/boost/graph/parallel/detail/inplace_all_to_all.hpp (contents, props changed)
   trunk/boost/graph/parallel/detail/property_holders.hpp (contents, props changed)
   trunk/boost/graph/parallel/detail/untracked_pair.hpp (contents, props changed)
   trunk/boost/graph/parallel/distribution.hpp (contents, props changed)
   trunk/boost/graph/parallel/process_group.hpp (contents, props changed)
   trunk/boost/graph/parallel/properties.hpp (contents, props changed)
   trunk/boost/graph/parallel/simple_trigger.hpp (contents, props changed)
   trunk/boost/graph/use_mpi.hpp (contents, props changed)
   trunk/libs/graph/src/mpi_process_group.cpp (contents, props changed)
   trunk/libs/graph/src/tag_allocator.cpp (contents, props changed)
Text files modified:
   trunk/boost/graph/breadth_first_search.hpp | 21 ++++++++++++++++++++
   trunk/boost/graph/connected_components.hpp | 3 ++
   trunk/boost/graph/depth_first_search.hpp | 3 ++
   trunk/boost/graph/dijkstra_shortest_paths.hpp | 4 +++
   trunk/boost/graph/fruchterman_reingold.hpp | 4 +++
   trunk/boost/graph/graphviz.hpp | 4 +++
   trunk/boost/graph/page_rank.hpp | 4 +++
   trunk/boost/graph/rmat_graph_generator.hpp | 4 +++
   trunk/boost/graph/strong_components.hpp | 4 +++
   trunk/boost/graph/topology.hpp | 21 ++++++++++++++++++++
   trunk/boost/graph/two_bit_color_map.hpp | 4 +++
   trunk/boost/pending/property_serialize.hpp | 42 ++++++++++++++++++++++++++++++++++++++++
   trunk/libs/graph/build/Jamfile.v2 | 27 ++++++++++++++++++++++++
   13 files changed, 144 insertions(+), 1 deletions(-)

Modified: trunk/boost/graph/breadth_first_search.hpp
==============================================================================
--- trunk/boost/graph/breadth_first_search.hpp (original)
+++ trunk/boost/graph/breadth_first_search.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -25,6 +25,10 @@
 #include <boost/graph/graph_concepts.hpp>
 #include <boost/graph/two_bit_color_map.hpp>
 
+#ifdef BOOST_GRAPH_USE_MPI
+#include <boost/graph/distributed/concepts.hpp>
+#endif // BOOST_GRAPH_USE_MPI
+
 namespace boost {
 
   template <class Visitor, class Graph>
@@ -230,6 +234,19 @@
          vis, color);
     }
 
+#ifdef BOOST_GRAPH_USE_MPI
+ template <class DistributedGraph, class ColorMap, class BFSVisitor,
+ class P, class T, class R>
+ void bfs_helper
+ (DistributedGraph& g,
+ typename graph_traits<DistributedGraph>::vertex_descriptor s,
+ ColorMap color,
+ BFSVisitor vis,
+ const bgl_named_params<P, T, R>& params,
+ BOOST_GRAPH_ENABLE_IF_MODELS(DistributedGraph, distributed_graph_tag,
+ void)* = 0);
+#endif // BOOST_GRAPH_USE_MPI
+
     //-------------------------------------------------------------------------
     // Choose between default color and color parameters. Using
     // function dispatching so that we don't require vertex index if
@@ -329,5 +346,9 @@
 
 } // namespace boost
 
+#ifdef BOOST_GRAPH_USE_MPI
+# include <boost/graph/distributed/breadth_first_search.hpp>
+#endif
+
 #endif // BOOST_GRAPH_BREADTH_FIRST_SEARCH_HPP
 

Modified: trunk/boost/graph/connected_components.hpp
==============================================================================
--- trunk/boost/graph/connected_components.hpp (original)
+++ trunk/boost/graph/connected_components.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -99,5 +99,8 @@
   
 } // namespace boost
 
+#ifdef BOOST_GRAPH_USE_MPI
+# include <boost/graph/distributed/connected_components.hpp>
+#endif
 
 #endif // BOOST_GRAPH_CONNECTED_COMPONENTS_HPP

Modified: trunk/boost/graph/depth_first_search.hpp
==============================================================================
--- trunk/boost/graph/depth_first_search.hpp (original)
+++ trunk/boost/graph/depth_first_search.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -358,5 +358,8 @@
   }
 } // namespace boost
 
+#ifdef BOOST_GRAPH_USE_MPI
+# include <boost/graph/distributed/depth_first_search.hpp>
+#endif
 
 #endif

Modified: trunk/boost/graph/dijkstra_shortest_paths.hpp
==============================================================================
--- trunk/boost/graph/dijkstra_shortest_paths.hpp (original)
+++ trunk/boost/graph/dijkstra_shortest_paths.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -480,4 +480,8 @@
 
 } // namespace boost
 
+#ifdef BOOST_GRAPH_USE_MPI
+# include <boost/graph/distributed/dijkstra_shortest_paths.hpp>
+#endif
+
 #endif // BOOST_GRAPH_DIJKSTRA_HPP

Added: trunk/boost/graph/distributed/adjacency_list.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/adjacency_list.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,3963 @@
+// Copyright (C) 2004-2006 The Trustees of Indiana University.
+// Copyright (C) 2007 Douglas Gregor
+
+// 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
+
+#ifndef BOOST_GRAPH_DISTRIBUTED_ADJACENCY_LIST_HPP
+#define BOOST_GRAPH_DISTRIBUTED_ADJACENCY_LIST_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/graph/adjacency_list.hpp>
+#include <boost/graph/properties.hpp>
+#include <boost/graph/graph_traits.hpp>
+#include <boost/graph/iteration_macros.hpp>
+#include <boost/graph/distributed/concepts.hpp>
+#include <boost/iterator/transform_iterator.hpp>
+#include <boost/property_map/property_map.hpp>
+#include <boost/graph/adjacency_iterator.hpp>
+#include <boost/property_map/parallel/distributed_property_map.hpp>
+#include <boost/property_map/parallel/local_property_map.hpp>
+#include <boost/graph/parallel/detail/property_holders.hpp>
+#include <boost/mpl/if.hpp>
+#include <boost/type_traits/is_same.hpp>
+#include <cassert>
+#include <list>
+#include <algorithm>
+#include <boost/limits.hpp>
+#include <boost/graph/parallel/properties.hpp>
+#include <boost/graph/parallel/distribution.hpp>
+#include <boost/graph/parallel/algorithm.hpp>
+#include <boost/graph/distributed/selector.hpp>
+#include <boost/graph/parallel/process_group.hpp>
+
+// Callbacks
+#include <boost/function/function2.hpp>
+
+// Serialization
+#include <boost/serialization/base_object.hpp>
+#include <boost/mpi/datatype.hpp>
+#include <boost/pending/property_serialize.hpp>
+#include <boost/graph/distributed/unsafe_serialize.hpp>
+
+// Named vertices
+#include <boost/graph/distributed/named_graph.hpp>
+
+#include <boost/graph/distributed/shuffled_distribution.hpp>
+
+namespace boost {
+
+ /// The type used to store an identifier that uniquely names a processor.
+ // NGE: I doubt we'll be running on more than 32768 procs for the time being
+ typedef /*int*/ short processor_id_type;
+
+ // Tell which processor the target of an edge resides on (for
+ // directed graphs) or which processor the other end point of the
+ // edge resides on (for undirected graphs).
+ enum edge_target_processor_id_t { edge_target_processor_id };
+ BOOST_INSTALL_PROPERTY(edge, target_processor_id);
+
+ // For undirected graphs, tells whether the edge is locally owned.
+ enum edge_locally_owned_t { edge_locally_owned };
+ BOOST_INSTALL_PROPERTY(edge, locally_owned);
+
+ // For bidirectional graphs, stores the incoming edges.
+ enum vertex_in_edges_t { vertex_in_edges };
+ BOOST_INSTALL_PROPERTY(vertex, in_edges);
+
+ /// Tag class for directed, distributed adjacency list
+ struct directed_distributed_adj_list_tag
+ : public virtual distributed_graph_tag,
+ public virtual distributed_vertex_list_graph_tag,
+ public virtual distributed_edge_list_graph_tag,
+ public virtual incidence_graph_tag,
+ public virtual adjacency_graph_tag {};
+
+ /// Tag class for bidirectional, distributed adjacency list
+ struct bidirectional_distributed_adj_list_tag
+ : public virtual distributed_graph_tag,
+ public virtual distributed_vertex_list_graph_tag,
+ public virtual distributed_edge_list_graph_tag,
+ public virtual incidence_graph_tag,
+ public virtual adjacency_graph_tag,
+ public virtual bidirectional_graph_tag {};
+
+ /// Tag class for undirected, distributed adjacency list
+ struct undirected_distributed_adj_list_tag
+ : public virtual distributed_graph_tag,
+ public virtual distributed_vertex_list_graph_tag,
+ public virtual distributed_edge_list_graph_tag,
+ public virtual incidence_graph_tag,
+ public virtual adjacency_graph_tag,
+ public virtual bidirectional_graph_tag {};
+
+ namespace detail {
+ template<typename Archiver, typename Directed, typename Vertex>
+ void
+ serialize(Archiver& ar, edge_base<Directed, Vertex>& e,
+ const unsigned int /*version*/)
+ {
+ ar & unsafe_serialize(e.m_source)
+ & unsafe_serialize(e.m_target);
+ }
+
+ template<typename Archiver, typename Directed, typename Vertex>
+ void
+ serialize(Archiver& ar, edge_desc_impl<Directed, Vertex>& e,
+ const unsigned int /*version*/)
+ {
+ ar & boost::serialization::base_object<edge_base<Directed, Vertex> >(e)
+ & unsafe_serialize(e.m_eproperty);
+ }
+ }
+
+ namespace detail { namespace parallel {
+
+ /**
+ * A distributed vertex descriptor. These descriptors contain both
+ * the ID of the processor that owns the vertex and a local vertex
+ * descriptor that identifies the particular vertex for that
+ * processor.
+ */
+ template<typename LocalDescriptor>
+ struct global_descriptor
+ {
+ typedef LocalDescriptor local_descriptor_type;
+
+ global_descriptor() : owner(), local() { }
+
+ global_descriptor(processor_id_type owner, LocalDescriptor local)
+ : owner(owner), local(local) { }
+
+ processor_id_type owner;
+ LocalDescriptor local;
+
+ /**
+ * A function object that, given a processor ID, generates
+ * distributed vertex descriptors from local vertex
+ * descriptors. This function object is used by the
+ * vertex_iterator of the distributed adjacency list.
+ */
+ struct generator
+ {
+ typedef global_descriptor<LocalDescriptor> result_type;
+ typedef LocalDescriptor argument_type;
+
+ generator() {}
+ generator(processor_id_type owner) : owner(owner) {}
+
+ result_type operator()(argument_type v) const
+ { return result_type(owner, v); }
+
+ private:
+ processor_id_type owner;
+ };
+
+ template<typename Archiver>
+ void serialize(Archiver& ar, const unsigned int /*version*/)
+ {
+ ar & owner & unsafe_serialize(local);
+ }
+ };
+
+ /// Determine the process that owns the given descriptor
+ template<typename LocalDescriptor>
+ inline processor_id_type owner(const global_descriptor<LocalDescriptor>& v)
+ { return v.owner; }
+
+ /// Determine the local portion of the given descriptor
+ template<typename LocalDescriptor>
+ inline LocalDescriptor local(const global_descriptor<LocalDescriptor>& v)
+ { return v.local; }
+
+ /// Compare distributed vertex descriptors for equality
+ template<typename LocalDescriptor>
+ inline bool
+ operator==(const global_descriptor<LocalDescriptor>& u,
+ const global_descriptor<LocalDescriptor>& v)
+ {
+ return u.owner == v.owner && u.local == v.local;
+ }
+
+ /// Compare distributed vertex descriptors for inequality
+ template<typename LocalDescriptor>
+ inline bool
+ operator!=(const global_descriptor<LocalDescriptor>& u,
+ const global_descriptor<LocalDescriptor>& v)
+ { return !(u == v); }
+
+ template<typename LocalDescriptor>
+ inline bool
+ operator<(const global_descriptor<LocalDescriptor>& u,
+ const global_descriptor<LocalDescriptor>& v)
+ {
+ return (u.owner) < v.owner || (u.owner == v.owner && (u.local) < v.local);
+ }
+
+ template<typename LocalDescriptor>
+ inline bool
+ operator<=(const global_descriptor<LocalDescriptor>& u,
+ const global_descriptor<LocalDescriptor>& v)
+ {
+ return u.owner <= v.owner || (u.owner == v.owner && u.local <= v.local);
+ }
+
+ template<typename LocalDescriptor>
+ inline bool
+ operator>(const global_descriptor<LocalDescriptor>& u,
+ const global_descriptor<LocalDescriptor>& v)
+ {
+ return v < u;
+ }
+
+ template<typename LocalDescriptor>
+ inline bool
+ operator>=(const global_descriptor<LocalDescriptor>& u,
+ const global_descriptor<LocalDescriptor>& v)
+ {
+ return v <= u;
+ }
+
+ // DPG TBD: Add <, <=, >=, > for global descriptors
+
+ /**
+ * A Readable Property Map that extracts a global descriptor pair
+ * from a global_descriptor.
+ */
+ template<typename LocalDescriptor>
+ struct global_descriptor_property_map
+ {
+ typedef std::pair<processor_id_type, LocalDescriptor> value_type;
+ typedef value_type reference;
+ typedef global_descriptor<LocalDescriptor> key_type;
+ typedef readable_property_map_tag category;
+ };
+
+ template<typename LocalDescriptor>
+ inline std::pair<processor_id_type, LocalDescriptor>
+ get(global_descriptor_property_map<LocalDescriptor>,
+ global_descriptor<LocalDescriptor> x)
+ {
+ return std::pair<processor_id_type, LocalDescriptor>(x.owner, x.local);
+ }
+
+ /**
+ * A Readable Property Map that extracts the owner of a global
+ * descriptor.
+ */
+ template<typename LocalDescriptor>
+ struct owner_property_map
+ {
+ typedef processor_id_type value_type;
+ typedef value_type reference;
+ typedef global_descriptor<LocalDescriptor> key_type;
+ typedef readable_property_map_tag category;
+ };
+
+ template<typename LocalDescriptor>
+ inline processor_id_type
+ get(owner_property_map<LocalDescriptor>,
+ global_descriptor<LocalDescriptor> x)
+ {
+ return x.owner;
+ }
+
+ /**
+ * A Readable Property Map that extracts the local descriptor from
+ * a global descriptor.
+ */
+ template<typename LocalDescriptor>
+ struct local_descriptor_property_map
+ {
+ typedef LocalDescriptor value_type;
+ typedef value_type reference;
+ typedef global_descriptor<LocalDescriptor> key_type;
+ typedef readable_property_map_tag category;
+ };
+
+ template<typename LocalDescriptor>
+ inline LocalDescriptor
+ get(local_descriptor_property_map<LocalDescriptor>,
+ global_descriptor<LocalDescriptor> x)
+ {
+ return x.local;
+ }
+
+ /**
+ * Stores an incoming edge for a bidirectional distributed
+ * adjacency list. The user does not see this type directly,
+ * because it is just an implementation detail.
+ */
+ template<typename Edge>
+ struct stored_in_edge
+ {
+ stored_in_edge(processor_id_type sp, Edge e)
+ : source_processor(sp), e(e) {}
+
+ processor_id_type source_processor;
+ Edge e;
+ };
+
+ /**
+ * A distributed edge descriptor. These descriptors contain the
+ * underlying edge descriptor, the processor IDs for both the
+ * source and the target of the edge, and a boolean flag that
+ * indicates which of the processors actually owns the edge.
+ */
+ template<typename Edge>
+ struct edge_descriptor
+ {
+ edge_descriptor(processor_id_type sp = processor_id_type(),
+ processor_id_type tp = processor_id_type(),
+ bool owns = false, Edge ld = Edge())
+ : source_processor(sp), target_processor(tp),
+ source_owns_edge(owns), local(ld) {}
+
+ processor_id_type owner() const
+ {
+ return source_owns_edge? source_processor : target_processor;
+ }
+
+ /// The processor that the source vertex resides on
+ processor_id_type source_processor;
+
+ /// The processor that the target vertex resides on
+ processor_id_type target_processor;
+
+ /// True when the source processor owns the edge, false when the
+ /// target processor owns the edge.
+ bool source_owns_edge;
+
+ /// The local edge descriptor.
+ Edge local;
+
+ /**
+ * Function object that generates edge descriptors for the
+ * out_edge_iterator of the given distributed adjacency list
+ * from the edge descriptors of the underlying adjacency list.
+ */
+ template<typename Graph>
+ class out_generator
+ {
+ typedef typename Graph::directed_selector directed_selector;
+
+ public:
+ typedef edge_descriptor<Edge> result_type;
+ typedef Edge argument_type;
+
+ out_generator() : g(0) {}
+ explicit out_generator(const Graph& g) : g(&g) {}
+
+ result_type operator()(argument_type e) const
+ { return map(e, directed_selector()); }
+
+ private:
+ result_type map(argument_type e, directedS) const
+ {
+ return result_type(g->processor(),
+ get(edge_target_processor_id, g->base(), e),
+ true, e);
+ }
+
+ result_type map(argument_type e, bidirectionalS) const
+ {
+ return result_type(g->processor(),
+ get(edge_target_processor_id, g->base(), e),
+ true, e);
+ }
+
+ result_type map(argument_type e, undirectedS) const
+ {
+ return result_type(g->processor(),
+ get(edge_target_processor_id, g->base(), e),
+ get(edge_locally_owned, g->base(), e),
+ e);
+ }
+
+ const Graph* g;
+ };
+
+ /**
+ * Function object that generates edge descriptors for the
+ * in_edge_iterator of the given distributed adjacency list
+ * from the edge descriptors of the underlying adjacency list.
+ */
+ template<typename Graph>
+ class in_generator
+ {
+ typedef typename Graph::directed_selector DirectedS;
+
+ public:
+ typedef typename ct_if<(is_same<DirectedS, bidirectionalS>::value),
+ stored_in_edge<Edge>,
+ Edge>::type argument_type;
+ typedef edge_descriptor<Edge> result_type;
+
+ in_generator() : g(0) {}
+ explicit in_generator(const Graph& g) : g(&g) {}
+
+ result_type operator()(argument_type e) const
+ { return map(e, DirectedS()); }
+
+ private:
+ /**
+ * For a bidirectional graph, we just generate the appropriate
+ * edge. No tricks.
+ */
+ result_type map(argument_type e, bidirectionalS) const
+ {
+ return result_type(e.source_processor,
+ g->processor(),
+ true,
+ e.e);
+ }
+
+ /**
+ * For an undirected graph, we generate descriptors for the
+ * incoming edges by swapping the source/target of the
+ * underlying edge descriptor (a hack). The target processor
+ * ID on the edge is actually the source processor for this
+ * edge, and our processor is the target processor. If the
+ * edge is locally owned, then it is owned by the target (us);
+ * otherwise it is owned by the source.
+ */
+ result_type map(argument_type e, undirectedS) const
+ {
+ typename Graph::local_edge_descriptor local_edge(e);
+ // TBD: This is a very, VERY lame hack that takes advantage
+ // of our knowledge of the internals of the BGL
+ // adjacency_list. There should be a cleaner way to handle
+ // this...
+ using std::swap;
+ swap(local_edge.m_source, local_edge.m_target);
+ return result_type(get(edge_target_processor_id, g->base(), e),
+ g->processor(),
+ !get(edge_locally_owned, g->base(), e),
+ local_edge);
+ }
+
+ const Graph* g;
+ };
+
+ private:
+ friend class boost::serialization::access;
+
+ template<typename Archiver>
+ void serialize(Archiver& ar, const unsigned int /*version*/)
+ {
+ ar
+ & source_processor
+ & target_processor
+ & source_owns_edge
+ & local;
+ }
+ };
+
+ /// Determine the process that owns this edge
+ template<typename Edge>
+ inline processor_id_type
+ owner(const edge_descriptor<Edge>& e)
+ { return e.source_owns_edge? e.source_processor : e.target_processor; }
+
+ /// Determine the local descriptor for this edge.
+ template<typename Edge>
+ inline Edge
+ local(const edge_descriptor<Edge>& e)
+ { return e.local; }
+
+ /**
+ * A Readable Property Map that extracts the owner and local
+ * descriptor of an edge descriptor.
+ */
+ template<typename Edge>
+ struct edge_global_property_map
+ {
+ typedef std::pair<processor_id_type, Edge> value_type;
+ typedef value_type reference;
+ typedef edge_descriptor<Edge> key_type;
+ typedef readable_property_map_tag category;
+ };
+
+ template<typename Edge>
+ inline std::pair<processor_id_type, Edge>
+ get(edge_global_property_map<Edge>, const edge_descriptor<Edge>& e)
+ {
+ typedef std::pair<processor_id_type, Edge> result_type;
+ return result_type(e.source_owns_edge? e.source_processor
+ /* target owns edge*/: e.target_processor,
+ e.local);
+ }
+
+ /**
+ * A Readable Property Map that extracts the owner of an edge
+ * descriptor.
+ */
+ template<typename Edge>
+ struct edge_owner_property_map
+ {
+ typedef processor_id_type value_type;
+ typedef value_type reference;
+ typedef edge_descriptor<Edge> key_type;
+ typedef readable_property_map_tag category;
+ };
+
+ template<typename Edge>
+ inline processor_id_type
+ get(edge_owner_property_map<Edge>, const edge_descriptor<Edge>& e)
+ {
+ return e.source_owns_edge? e.source_processor : e.target_processor;
+ }
+
+ /**
+ * A Readable Property Map that extracts the local descriptor from
+ * an edge descriptor.
+ */
+ template<typename Edge>
+ struct edge_local_property_map
+ {
+ typedef Edge value_type;
+ typedef value_type reference;
+ typedef edge_descriptor<Edge> key_type;
+ typedef readable_property_map_tag category;
+ };
+
+ template<typename Edge>
+ inline Edge
+ get(edge_local_property_map<Edge>,
+ const edge_descriptor<Edge>& e)
+ {
+ return e.local;
+ }
+
+ /** Compare distributed edge descriptors for equality.
+ *
+ * \todo need edge_descriptor to know if it is undirected so we
+ * can compare both ways.
+ */
+ template<typename Edge>
+ inline bool
+ operator==(const edge_descriptor<Edge>& e1,
+ const edge_descriptor<Edge>& e2)
+ {
+ return (e1.source_processor == e2.source_processor
+ && e1.target_processor == e2.target_processor
+ && e1.local == e2.local);
+ }
+
+ /// Compare distributed edge descriptors for inequality.
+ template<typename Edge>
+ inline bool
+ operator!=(const edge_descriptor<Edge>& e1,
+ const edge_descriptor<Edge>& e2)
+ { return !(e1 == e2); }
+
+ /**
+ * Configuration for the distributed adjacency list. We use this
+ * parameter to store all of the configuration details for the
+ * implementation of the distributed adjacency list, which allows us to
+ * get at the distribution type in the maybe_named_graph.
+ */
+ template<typename OutEdgeListS, typename ProcessGroup,
+ typename InVertexListS, typename InDistribution,
+ typename DirectedS, typename VertexProperty,
+ typename EdgeProperty, typename GraphProperty,
+ typename EdgeListS>
+ struct adjacency_list_config
+ {
+ typedef typename mpl::if_<is_same<InVertexListS, defaultS>,
+ vecS, InVertexListS>::type
+ VertexListS;
+
+ /// Introduce the target processor ID property for each edge
+ typedef property<edge_target_processor_id_t, processor_id_type,
+ EdgeProperty> edge_property_with_id;
+
+ /// For undirected graphs, introduce the locally-owned property for edges
+ typedef typename ct_if<(is_same<DirectedS, undirectedS>::value),
+ property<edge_locally_owned_t, bool,
+ edge_property_with_id>,
+ edge_property_with_id>::type
+ base_edge_property_type;
+
+ /// The edge descriptor type for the local subgraph
+ typedef typename adjacency_list_traits<OutEdgeListS,
+ VertexListS,
+ directedS>::edge_descriptor
+ local_edge_descriptor;
+
+ /// For bidirectional graphs, the type of an incoming stored edge
+ typedef stored_in_edge<local_edge_descriptor> bidir_stored_edge;
+
+ /// The container type that will store incoming edges for a
+ /// bidirectional graph.
+ typedef typename container_gen<EdgeListS, bidir_stored_edge>::type
+ in_edge_list_type;
+
+ // Bidirectional graphs have an extra vertex property to store
+ // the incoming edges.
+ typedef typename ct_if<(is_same<DirectedS, bidirectionalS>::value),
+ property<vertex_in_edges_t, in_edge_list_type,
+ VertexProperty>,
+ VertexProperty>::type
+ base_vertex_property_type;
+
+ // The type of the distributed adjacency list
+ typedef adjacency_list<OutEdgeListS,
+ distributedS<ProcessGroup,
+ VertexListS,
+ InDistribution>,
+ DirectedS, VertexProperty, EdgeProperty,
+ GraphProperty, EdgeListS>
+ graph_type;
+
+ // The type of the underlying adjacency list implementation
+ typedef adjacency_list<OutEdgeListS, VertexListS, directedS,
+ base_vertex_property_type,
+ base_edge_property_type,
+ GraphProperty,
+ EdgeListS>
+ inherited;
+
+ typedef InDistribution in_distribution_type;
+ typedef typename inherited::vertices_size_type vertices_size_type;
+
+ typedef typename ::boost::graph::distributed::select_distribution<
+ in_distribution_type, VertexProperty, vertices_size_type,
+ ProcessGroup>::type
+ base_distribution_type;
+
+ typedef ::boost::graph::distributed::shuffled_distribution<
+ base_distribution_type> distribution_type;
+
+ typedef VertexProperty vertex_property_type;
+ typedef EdgeProperty edge_property_type;
+ typedef ProcessGroup process_group_type;
+
+ typedef VertexListS vertex_list_selector;
+ typedef OutEdgeListS out_edge_list_selector;
+ typedef DirectedS directed_selector;
+ typedef GraphProperty graph_property_type;
+ typedef EdgeListS edge_list_selector;
+ };
+
+ // Maybe initialize the indices of each vertex
+ template<typename IteratorPair, typename VertexIndexMap>
+ void
+ maybe_initialize_vertex_indices(IteratorPair p, VertexIndexMap to_index,
+ read_write_property_map_tag)
+ {
+ typedef typename property_traits<VertexIndexMap>::value_type index_t;
+ index_t next_index = 0;
+ while (p.first != p.second)
+ put(to_index, *p.first++, next_index++);
+ }
+
+ template<typename IteratorPair, typename VertexIndexMap>
+ inline void
+ maybe_initialize_vertex_indices(IteratorPair p, VertexIndexMap to_index,
+ readable_property_map_tag)
+ {
+ // Do nothing
+ }
+
+ template<typename IteratorPair, typename VertexIndexMap>
+ inline void
+ maybe_initialize_vertex_indices(IteratorPair p, VertexIndexMap to_index)
+ {
+ typedef typename property_traits<VertexIndexMap>::category category;
+ maybe_initialize_vertex_indices(p, to_index, category());
+ }
+
+ template<typename IteratorPair>
+ inline void
+ maybe_initialize_vertex_indices(IteratorPair p,
+ ::boost::detail::error_property_not_found)
+ { }
+
+ /***********************************************************************
+ * Message Payloads *
+ ***********************************************************************/
+
+ /**
+ * Data stored with a msg_add_edge message, which requests the
+ * remote addition of an edge.
+ */
+ template<typename Vertex, typename LocalVertex>
+ struct msg_add_edge_data
+ {
+ msg_add_edge_data() { }
+
+ msg_add_edge_data(Vertex source, Vertex target)
+ : source(source.local), target(target) { }
+
+ /// The source of the edge; the processor will be the
+ /// receiving processor.
+ LocalVertex source;
+
+ /// The target of the edge.
+ Vertex target;
+
+ template<typename Archiver>
+ void serialize(Archiver& ar, const unsigned int /*version*/)
+ {
+ ar & unsafe_serialize(source) & target;
+ }
+ };
+
+ /**
+ * Like @c msg_add_edge_data, but also includes a user-specified
+ * property value to be attached to the edge.
+ */
+ template<typename Vertex, typename LocalVertex, typename EdgeProperty>
+ struct msg_add_edge_with_property_data
+ : msg_add_edge_data<Vertex, LocalVertex>,
+ maybe_store_property<EdgeProperty>
+ {
+ private:
+ typedef msg_add_edge_data<Vertex, LocalVertex> inherited_data;
+ typedef maybe_store_property<EdgeProperty> inherited_property;
+
+ public:
+ msg_add_edge_with_property_data() { }
+
+ msg_add_edge_with_property_data(Vertex source,
+ Vertex target,
+ const EdgeProperty& property)
+ : inherited_data(source, target),
+ inherited_property(property) { }
+
+ template<typename Archiver>
+ void serialize(Archiver& ar, const unsigned int /*version*/)
+ {
+ ar & boost::serialization::base_object<inherited_data>(*this)
+ & boost::serialization::base_object<inherited_property>(*this);
+ }
+ };
+
+ //------------------------------------------------------------------------
+ // Distributed adjacency list property map details
+ /**
+ * Metafunction that extracts the given property from the given
+ * distributed adjacency list type. This could be implemented much
+ * more cleanly, but even newer versions of GCC (e.g., 3.2.3)
+ * cannot properly handle partial specializations involving
+ * enumerator types.
+ */
+ template<typename Property>
+ struct get_adj_list_pmap
+ {
+ template<typename Graph>
+ struct apply
+ {
+ typedef Graph graph_type;
+ typedef typename graph_type::process_group_type process_group_type;
+ typedef typename graph_type::inherited base_graph_type;
+ typedef typename property_map<base_graph_type, Property>::type
+ local_pmap;
+ typedef typename property_map<base_graph_type, Property>::const_type
+ local_const_pmap;
+
+ typedef graph_traits<graph_type> traits;
+ typedef typename graph_type::local_vertex_descriptor local_vertex;
+ typedef typename property_traits<local_pmap>::key_type local_key_type;
+
+ typedef typename property_traits<local_pmap>::value_type value_type;
+
+ typedef typename property_map<Graph, vertex_global_t>::const_type
+ vertex_global_map;
+ typedef typename property_map<Graph, edge_global_t>::const_type
+ edge_global_map;
+
+ typedef typename mpl::if_c<(is_same<local_key_type,
+ local_vertex>::value),
+ vertex_global_map, edge_global_map>::type
+ global_map;
+
+ public:
+ typedef ::boost::parallel::distributed_property_map<
+ process_group_type, global_map, local_pmap> type;
+
+ typedef ::boost::parallel::distributed_property_map<
+ process_group_type, global_map, local_const_pmap> const_type;
+ };
+ };
+
+ /**
+ * The local vertex index property map is actually a mapping from
+ * the local vertex descriptors to vertex indices.
+ */
+ template<>
+ struct get_adj_list_pmap<vertex_local_index_t>
+ {
+ template<typename Graph>
+ struct apply
+ : ::boost::property_map<typename Graph::inherited, vertex_index_t>
+ { };
+ };
+
+ /**
+ * The vertex index property map maps from global descriptors
+ * (e.g., the vertex descriptor of a distributed adjacency list)
+ * to the underlying local index. It is not valid to use this
+ * property map with nonlocal descriptors.
+ */
+ template<>
+ struct get_adj_list_pmap<vertex_index_t>
+ {
+ template<typename Graph>
+ struct apply
+ {
+ private:
+ typedef typename property_map<Graph, vertex_global_t>::const_type
+ global_map;
+
+ typedef property_map<typename Graph::inherited, vertex_index_t> local;
+
+ public:
+ typedef local_property_map<typename Graph::process_group_type,
+ global_map,
+ typename local::type> type;
+ typedef local_property_map<typename Graph::process_group_type,
+ global_map,
+ typename local::const_type> const_type;
+ };
+ };
+
+ /**
+ * The vertex owner property map maps from vertex descriptors to
+ * the processor that owns the vertex.
+ */
+ template<>
+ struct get_adj_list_pmap<vertex_global_t>
+ {
+ template<typename Graph>
+ struct apply
+ {
+ private:
+ typedef typename Graph::local_vertex_descriptor
+ local_vertex_descriptor;
+ public:
+ typedef global_descriptor_property_map<local_vertex_descriptor> type;
+ typedef type const_type;
+ };
+ };
+
+ /**
+ * The vertex owner property map maps from vertex descriptors to
+ * the processor that owns the vertex.
+ */
+ template<>
+ struct get_adj_list_pmap<vertex_owner_t>
+ {
+ template<typename Graph>
+ struct apply
+ {
+ private:
+ typedef typename Graph::local_vertex_descriptor
+ local_vertex_descriptor;
+ public:
+ typedef owner_property_map<local_vertex_descriptor> type;
+ typedef type const_type;
+ };
+ };
+
+ /**
+ * The vertex local property map maps from vertex descriptors to
+ * the local descriptor for that vertex.
+ */
+ template<>
+ struct get_adj_list_pmap<vertex_local_t>
+ {
+ template<typename Graph>
+ struct apply
+ {
+ private:
+ typedef typename Graph::local_vertex_descriptor
+ local_vertex_descriptor;
+ public:
+ typedef local_descriptor_property_map<local_vertex_descriptor> type;
+ typedef type const_type;
+ };
+ };
+
+ /**
+ * The edge global property map maps from edge descriptors to
+ * a pair of the owning processor and local descriptor.
+ */
+ template<>
+ struct get_adj_list_pmap<edge_global_t>
+ {
+ template<typename Graph>
+ struct apply
+ {
+ private:
+ typedef typename Graph::local_edge_descriptor
+ local_edge_descriptor;
+ public:
+ typedef edge_global_property_map<local_edge_descriptor> type;
+ typedef type const_type;
+ };
+ };
+
+ /**
+ * The edge owner property map maps from edge descriptors to
+ * the processor that owns the edge.
+ */
+ template<>
+ struct get_adj_list_pmap<edge_owner_t>
+ {
+ template<typename Graph>
+ struct apply
+ {
+ private:
+ typedef typename Graph::local_edge_descriptor
+ local_edge_descriptor;
+ public:
+ typedef edge_owner_property_map<local_edge_descriptor> type;
+ typedef type const_type;
+ };
+ };
+
+ /**
+ * The edge local property map maps from edge descriptors to
+ * the local descriptor for that edge.
+ */
+ template<>
+ struct get_adj_list_pmap<edge_local_t>
+ {
+ template<typename Graph>
+ struct apply
+ {
+ private:
+ typedef typename Graph::local_edge_descriptor
+ local_edge_descriptor;
+ public:
+ typedef edge_local_property_map<local_edge_descriptor> type;
+ typedef type const_type;
+ };
+ };
+ //------------------------------------------------------------------------
+
+ // Directed graphs do not have in edges, so this is a no-op
+ template<typename Graph>
+ inline void
+ remove_in_edge(typename Graph::edge_descriptor, Graph&, directedS)
+ { }
+
+ // Bidirectional graphs have in edges stored in the
+ // vertex_in_edges property.
+ template<typename Graph>
+ inline void
+ remove_in_edge(typename Graph::edge_descriptor e, Graph& g, bidirectionalS)
+ {
+ typedef typename Graph::in_edge_list_type in_edge_list_type;
+ in_edge_list_type& in_edges =
+ get(vertex_in_edges, g.base())[target(e, g).local];
+ typename in_edge_list_type::iterator i = in_edges.begin();
+ while (i != in_edges.end()
+ && !(i->source_processor == source(e, g).owner)
+ && i->e == e.local)
+ ++i;
+
+ assert(i != in_edges.end());
+ in_edges.erase(i);
+ }
+
+ // Undirected graphs have in edges stored as normal edges.
+ template<typename Graph>
+ inline void
+ remove_in_edge(typename Graph::edge_descriptor e, Graph& g, undirectedS)
+ {
+ typedef typename Graph::inherited base_type;
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+
+ // TBD: can we make this more efficient?
+ // Removing edge (v, u). v is local
+ base_type& bg = g.base();
+ vertex_descriptor u = source(e, g);
+ vertex_descriptor v = target(e, g);
+ if (v.owner != process_id(g.process_group())) {
+ using std::swap;
+ swap(u, v);
+ }
+
+ typename graph_traits<base_type>::out_edge_iterator ei, ei_end;
+ for (tie(ei, ei_end) = out_edges(v.local, bg); ei != ei_end; ++ei)
+ {
+ if (target(*ei, g.base()) == u.local
+ // TBD: deal with parallel edges properly && *ei == e
+ && get(edge_target_processor_id, bg, *ei) == u.owner) {
+ remove_edge(ei, bg);
+ return;
+ }
+ }
+
+ if (v.owner == process_id(g.process_group())) {
+
+ }
+ }
+
+ //------------------------------------------------------------------------
+ // Lazy addition of edges
+
+ // Work around the fact that an adjacency_list with vecS vertex
+ // storage automatically adds edges when the descriptor is
+ // out-of-range.
+ template <class Graph, class Config, class Base>
+ inline std::pair<typename Config::edge_descriptor, bool>
+ add_local_edge(typename Config::vertex_descriptor u,
+ typename Config::vertex_descriptor v,
+ const typename Config::edge_property_type& p,
+ vec_adj_list_impl<Graph, Config, Base>& g_)
+ {
+ adj_list_helper<Config, Base>& g = g_;
+ return add_edge(u, v, p, g);
+ }
+
+ template <class Graph, class Config, class Base>
+ inline std::pair<typename Config::edge_descriptor, bool>
+ add_local_edge(typename Config::vertex_descriptor u,
+ typename Config::vertex_descriptor v,
+ const typename Config::edge_property_type& p,
+ boost::adj_list_impl<Graph, Config, Base>& g)
+ {
+ return add_edge(u, v, p, g);
+ }
+
+ template <class EdgeProperty,class EdgeDescriptor>
+ struct msg_nonlocal_edge_data
+ : public detail::parallel::maybe_store_property<EdgeProperty>
+ {
+ typedef EdgeProperty edge_property_type;
+ typedef EdgeDescriptor local_edge_descriptor;
+ typedef detail::parallel::maybe_store_property<edge_property_type>
+ inherited;
+
+ msg_nonlocal_edge_data() {}
+ msg_nonlocal_edge_data(local_edge_descriptor e,
+ const edge_property_type& p)
+ : inherited(p), e(e) { }
+
+ local_edge_descriptor e;
+
+ template<typename Archiver>
+ void serialize(Archiver& ar, const unsigned int /*version*/)
+ {
+ ar & boost::serialization::base_object<inherited>(*this) & e;
+ }
+ };
+
+ template <class EdgeDescriptor>
+ struct msg_remove_edge_data
+ {
+ typedef EdgeDescriptor edge_descriptor;
+ msg_remove_edge_data() {}
+ explicit msg_remove_edge_data(edge_descriptor e) : e(e) {}
+
+ edge_descriptor e;
+
+ template<typename Archiver>
+ void serialize(Archiver& ar, const unsigned int /*version*/)
+ {
+ ar & e;
+ }
+ };
+
+ } } // end namespace detail::parallel
+
+ /**
+ * Adjacency list traits for a distributed adjacency list. Contains
+ * the vertex and edge descriptors, the directed-ness, and the
+ * parallel edges typedefs.
+ */
+ template<typename OutEdgeListS, typename ProcessGroup,
+ typename InVertexListS, typename InDistribution, typename DirectedS>
+ struct adjacency_list_traits<OutEdgeListS,
+ distributedS<ProcessGroup,
+ InVertexListS,
+ InDistribution>,
+ DirectedS>
+ {
+ private:
+ typedef typename mpl::if_<is_same<InVertexListS, defaultS>,
+ vecS,
+ InVertexListS>::type VertexListS;
+
+ typedef adjacency_list_traits<OutEdgeListS, VertexListS, directedS>
+ base_type;
+
+ public:
+ typedef typename base_type::vertex_descriptor local_vertex_descriptor;
+ typedef typename base_type::edge_descriptor local_edge_descriptor;
+
+ typedef typename boost::ct_if_t<typename DirectedS::is_bidir_t,
+ bidirectional_tag,
+ typename boost::ct_if_t<typename DirectedS::is_directed_t,
+ directed_tag, undirected_tag
+ >::type
+ >::type directed_category;
+
+ typedef typename parallel_edge_traits<OutEdgeListS>::type
+ edge_parallel_category;
+
+ typedef detail::parallel::global_descriptor<local_vertex_descriptor>
+ vertex_descriptor;
+
+ typedef detail::parallel::edge_descriptor<local_edge_descriptor>
+ edge_descriptor;
+ };
+
+#define PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS \
+ typename OutEdgeListS, typename ProcessGroup, typename InVertexListS, \
+ typename InDistribution, typename DirectedS, typename VertexProperty, \
+ typename EdgeProperty, typename GraphProperty, typename EdgeListS
+
+#define PBGL_DISTRIB_ADJLIST_TYPE \
+ adjacency_list<OutEdgeListS, \
+ distributedS<ProcessGroup, InVertexListS, InDistribution>, \
+ DirectedS, VertexProperty, EdgeProperty, GraphProperty, \
+ EdgeListS>
+
+#define PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS_CONFIG \
+ typename OutEdgeListS, typename ProcessGroup, typename InVertexListS, \
+ typename InDistribution, typename VertexProperty, \
+ typename EdgeProperty, typename GraphProperty, typename EdgeListS
+
+#define PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(directed) \
+ adjacency_list<OutEdgeListS, \
+ distributedS<ProcessGroup, InVertexListS, InDistribution>, \
+ directed, VertexProperty, EdgeProperty, GraphProperty, \
+ EdgeListS>
+
+ /** A distributed adjacency list.
+ *
+ * This class template partial specialization defines a distributed
+ * (or "partitioned") adjacency list graph. The distributed
+ * adjacency list is similar to the standard Boost Graph Library
+ * adjacency list, which stores a list of vertices and for each
+ * verted the list of edges outgoing from the vertex (and, in some
+ * cases, also the edges incoming to the vertex). The distributed
+ * adjacency list differs in that it partitions the graph into
+ * several subgraphs that are then divided among different
+ * processors (or nodes within a cluster). The distributed adjacency
+ * list attempts to maintain a high degree of compatibility with the
+ * standard, non-distributed adjacency list.
+ *
+ * The graph is partitioned by vertex, with each processor storing
+ * all of the required information for a particular subset of the
+ * vertices, including vertex properties, outgoing edges, and (for
+ * bidirectional graphs) incoming edges. This information is
+ * accessible only on the processor that owns the vertex: for
+ * instance, if processor 0 owns vertex @c v, no other processor can
+ * directly access the properties of @c v or enumerate its outgoing
+ * edges.
+ *
+ * Edges in a graph may be entirely local (connecting two local
+ * vertices), but more often it is the case that edges are
+ * non-local, meaning that the two vertices they connect reside in
+ * different processes. Edge properties are stored with the
+ * originating vertex for directed and bidirectional graphs, and are
+ * therefore only accessible from the processor that owns the
+ * originating vertex. Other processors may query the source and
+ * target of the edge, but cannot access its properties. This is
+ * particularly interesting when accessing the incoming edges of a
+ * bidirectional graph, which are not guaranteed to be stored on the
+ * processor that is able to perform the iteration. For undirected
+ * graphs the situation is more complicated, since no vertex clearly
+ * owns the edges: the list of edges incident to a vertex may
+ * contain a mix of local and non-local edges.
+ *
+ * The distributed adjacency list is able to model several of the
+ * existing Graph concepts. It models the Graph concept because it
+ * exposes vertex and edge descriptors in the normal way; these
+ * descriptors model the GlobalDescriptor concept (because they have
+ * an owner and a local descriptor), and as such the distributed
+ * adjacency list models the DistributedGraph concept. The adjacency
+ * list also models the IncidenceGraph and AdjacencyGraph concepts,
+ * although this is only true so long as the domain of the valid
+ * expression arguments are restricted to vertices and edges stored
+ * locally. Likewise, bidirectional and undirected distributed
+ * adjacency lists model the BidirectionalGraph concept (vertex and
+ * edge domains must be respectived) and the distributed adjacency
+ * list models the MutableGraph concept (vertices and edges can only
+ * be added or removed locally). T he distributed adjacency list
+ * does not, however, model the VertexListGraph or EdgeListGraph
+ * concepts, because we can not efficiently enumerate all vertices
+ * or edges in the graph. Instead, the local subsets of vertices and
+ * edges can be enumerated (with the same syntax): the distributed
+ * adjacency list therefore models the DistributedVertexListGraph
+ * and DistributedEdgeListGraph concepts, because concurrent
+ * iteration over all of the vertices or edges stored on each
+ * processor will visit each vertex or edge.
+ *
+ * The distributed adjacency list is distinguished from the
+ * non-distributed version by the vertex list descriptor, which will
+ * be @c distributedS<ProcessGroup,VertexListS>. Here,
+ * the VertexListS type plays the same role as the VertexListS type
+ * in the non-distributed adjacency list: it allows one to select
+ * the data structure that will be used to store the local
+ * vertices. The ProcessGroup type, on the other hand, is unique to
+ * distributed data structures: it is the type that abstracts a
+ * group of cooperating processes, and it used for process
+ * identification, communication, and synchronization, among other
+ * things. Different process group types represent different
+ * communication mediums (e.g., MPI, PVM, TCP) or different models
+ * of communication (LogP, CGM, BSP, synchronous, etc.). This
+ * distributed adjacency list assumes a model based on non-blocking
+ * sends.
+ *
+ * Distribution of vertices across different processors is
+ * accomplished in two different ways. When initially constructing
+ * the graph, the user may provide a distribution object (that
+ * models the Distribution concept), which will determine the
+ * distribution of vertices to each process. Additionally, the @c
+ * add_vertex and @c add_edge operations add vertices or edges
+ * stored on the local processor. For @c add_edge, this is
+ * accomplished by requiring that the source vertex of the new edge
+ * be local to the process executing @c add_edge.
+ *
+ * Internal properties of a distributed adjacency list are
+ * accessible in the same manner as internal properties for a
+ * non-distributed adjacency list for local vertices or
+ * edges. Access to properties for remote vertices or edges occurs
+ * with the same syntax, but involve communication with the owner of
+ * the information: for more information, refer to class template
+ * @ref distributed_property_map, which manages distributed
+ * property maps. Note that the distributed property maps created
+ * for internal properties determine their reduction operation via
+ * the metafunction @ref property_reduce, which for the vast
+ * majority of uses is correct behavior.
+ *
+ * Communication among the processes coordinating on a particular
+ * distributed graph relies on non-blocking message passing along
+ * with synchronization. Local portions of the distributed graph may
+ * be modified concurrently, including the introduction of non-local
+ * edges, but prior to accessing the graph it is recommended that
+ * the @c synchronize free function be invoked on the graph to clear
+ * up any pending interprocess communication and modifications. All
+ * processes will then be released from the synchronization barrier
+ * concurrently.
+ *
+ * \todo Determine precisely what we should do with nonlocal edges
+ * in undirected graphs. Our parallelization of certain algorithms
+ * relies on the ability to access edge property maps immediately
+ * (e.g., edge_weight_t), so it may be necessary to duplicate the
+ * edge properties in both processes (but then we need some form of
+ * coherence protocol).
+ *
+ * \todo What does the user do if @c property_reduce doesn't do the
+ * right thing?
+ */
+ template<typename OutEdgeListS, typename ProcessGroup,
+ typename InVertexListS, typename InDistribution, typename DirectedS,
+ typename VertexProperty, typename EdgeProperty,
+ typename GraphProperty, typename EdgeListS>
+ class adjacency_list<OutEdgeListS,
+ distributedS<ProcessGroup,
+ InVertexListS,
+ InDistribution>,
+ DirectedS, VertexProperty,
+ EdgeProperty, GraphProperty, EdgeListS>
+ : // Support for named vertices
+ public graph::distributed::maybe_named_graph<
+ adjacency_list<OutEdgeListS,
+ distributedS<ProcessGroup,
+ InVertexListS,
+ InDistribution>,
+ DirectedS, VertexProperty,
+ EdgeProperty, GraphProperty, EdgeListS>,
+ typename adjacency_list_traits<OutEdgeListS,
+ distributedS<ProcessGroup,
+ InVertexListS,
+ InDistribution>,
+ DirectedS>::vertex_descriptor,
+ typename adjacency_list_traits<OutEdgeListS,
+ distributedS<ProcessGroup,
+ InVertexListS,
+ InDistribution>,
+ DirectedS>::edge_descriptor,
+ detail::parallel::adjacency_list_config<OutEdgeListS, ProcessGroup,
+ InVertexListS, InDistribution,
+ DirectedS, VertexProperty,
+ EdgeProperty, GraphProperty,
+ EdgeListS> >
+ {
+ typedef detail::parallel::adjacency_list_config<OutEdgeListS, ProcessGroup,
+ InVertexListS, InDistribution,
+ DirectedS, VertexProperty,
+ EdgeProperty, GraphProperty,
+ EdgeListS>
+ config_type;
+
+ typedef adjacency_list_traits<OutEdgeListS,
+ distributedS<ProcessGroup,
+ InVertexListS,
+ InDistribution>,
+ DirectedS>
+ traits_type;
+
+ typedef typename DirectedS::is_directed_t is_directed;
+
+ typedef EdgeListS edge_list_selector;
+
+ public:
+ /// The container type that will store incoming edges for a
+ /// bidirectional graph.
+ typedef typename config_type::in_edge_list_type in_edge_list_type;
+// typedef typename inherited::edge_descriptor edge_descriptor;
+
+ /// The type of the underlying adjacency list implementation
+ typedef typename config_type::inherited inherited;
+
+ /// The type of properties stored in the local subgraph
+ /// Bidirectional graphs have an extra vertex property to store
+ /// the incoming edges.
+ typedef typename inherited::vertex_property_type
+ base_vertex_property_type;
+
+ /// The type of the distributed adjacency list (this type)
+ typedef typename config_type::graph_type graph_type;
+
+ /// Expose graph components and graph category
+ typedef typename traits_type::local_vertex_descriptor
+ local_vertex_descriptor;
+ typedef typename traits_type::local_edge_descriptor
+ local_edge_descriptor;
+ typedef typename traits_type::vertex_descriptor vertex_descriptor;
+ typedef typename traits_type::edge_descriptor edge_descriptor;
+
+ typedef typename traits_type::directed_category directed_category;
+ typedef typename inherited::edge_parallel_category
+ edge_parallel_category;
+ typedef typename inherited::graph_tag graph_tag;
+
+ // Current implementation requires the ability to have parallel
+ // edges in the underlying adjacency_list. Which processor each
+ // edge refers to is attached as an internal property. TBD:
+ // remove this restriction, which may require some rewriting.
+ BOOST_STATIC_ASSERT((is_same<edge_parallel_category,
+ allow_parallel_edge_tag>::value));
+
+ /** Determine the graph traversal category.
+ *
+ * A directed distributed adjacency list models the Distributed
+ * Graph, Incidence Graph, and Adjacency Graph
+ * concepts. Bidirectional and undirected graphs also model the
+ * Bidirectional Graph concept. Note that when modeling these
+ * concepts the domains of certain operations (e.g., in_edges)
+ * are restricted; see the distributed adjacency_list
+ * documentation.
+ */
+ typedef typename ct_if<
+ (is_same<DirectedS, directedS>::value),
+ directed_distributed_adj_list_tag,
+ typename ct_if<(is_same<DirectedS, bidirectionalS>::value),
+ bidirectional_distributed_adj_list_tag,
+ undirected_distributed_adj_list_tag>::type>
+ ::type traversal_category;
+
+ typedef typename inherited::degree_size_type degree_size_type;
+ typedef typename inherited::vertices_size_type vertices_size_type;
+ typedef typename inherited::edges_size_type edges_size_type;
+ typedef VertexProperty vertex_property_type;
+ typedef EdgeProperty edge_property_type;
+ typedef typename inherited::graph_property_type graph_property_type;
+ typedef typename inherited::vertex_bundled vertex_bundled;
+ typedef typename inherited::edge_bundled edge_bundled;
+
+ typedef typename container_gen<edge_list_selector, edge_descriptor>::type
+ local_edge_list_type;
+
+ private:
+ typedef typename ct_if<(is_same<DirectedS, bidirectionalS>::value),
+ typename in_edge_list_type::const_iterator,
+ typename inherited::out_edge_iterator>::type
+ base_in_edge_iterator;
+
+ typedef typename inherited::out_edge_iterator base_out_edge_iterator;
+ typedef typename graph_traits<inherited>::edge_iterator
+ base_edge_iterator;
+ typedef typename inherited::edge_property_type base_edge_property_type;
+
+ typedef typename local_edge_list_type::const_iterator
+ undirected_edge_iterator;
+
+ typedef InDistribution in_distribution_type;
+
+ typedef parallel::trigger_receive_context trigger_receive_context;
+
+ public:
+ /// Iterator over the (local) vertices of the graph
+ typedef transform_iterator<typename vertex_descriptor::generator,
+ typename inherited::vertex_iterator>
+ vertex_iterator;
+
+ /// Helper for out_edge_iterator
+ typedef typename edge_descriptor::template out_generator<adjacency_list>
+ out_edge_generator;
+
+ /// Iterator over the outgoing edges of a vertex
+ typedef transform_iterator<out_edge_generator,
+ typename inherited::out_edge_iterator>
+ out_edge_iterator;
+
+ /// Helper for in_edge_iterator
+ typedef typename edge_descriptor::template in_generator<adjacency_list>
+ in_edge_generator;
+
+ /// Iterator over the incoming edges of a vertex
+ typedef transform_iterator<in_edge_generator, base_in_edge_iterator>
+ in_edge_iterator;
+
+ /// Iterator over the neighbors of a vertex
+ typedef adjacency_iterator<
+ adjacency_list, vertex_descriptor, out_edge_iterator,
+ typename detail::iterator_traits<base_out_edge_iterator>
+ ::difference_type>
+ adjacency_iterator;
+
+ /// Iterator over the (local) edges in a graph
+ typedef typename ct_if<(is_same<DirectedS, undirectedS>::value),
+ undirected_edge_iterator,
+ transform_iterator<out_edge_generator,
+ base_edge_iterator>
+ >::type
+ edge_iterator;
+
+ public:
+ /// The type of the mixin for named vertices
+ typedef graph::distributed::maybe_named_graph<graph_type,
+ vertex_descriptor,
+ edge_descriptor,
+ config_type>
+ named_graph_mixin;
+
+ /// Process group used for communication
+ typedef ProcessGroup process_group_type;
+
+ /// How to refer to a process
+ typedef typename process_group_type::process_id_type process_id_type;
+
+ /// Whether this graph is directed, undirected, or bidirectional
+ typedef DirectedS directed_selector;
+
+ // Structure used for the lazy addition of vertices
+ struct lazy_add_vertex_with_property;
+ friend struct lazy_add_vertex_with_property;
+
+ // Structure used for the lazy addition of edges
+ struct lazy_add_edge;
+ friend struct lazy_add_edge;
+
+ // Structure used for the lazy addition of edges with properties
+ struct lazy_add_edge_with_property;
+ friend struct lazy_add_edge_with_property;
+
+ /// default_distribution_type is the type of the distribution used if the
+ /// user didn't specify an explicit one
+ typedef typename graph::distributed::select_distribution<
+ InDistribution, VertexProperty, vertices_size_type,
+ ProcessGroup>::default_type
+ default_distribution_type;
+
+ /// distribution_type is the type of the distribution instance stored in
+ /// the maybe_named_graph base class
+ typedef typename graph::distributed::select_distribution<
+ InDistribution, VertexProperty, vertices_size_type,
+ ProcessGroup>::type
+ base_distribution_type;
+
+ typedef graph::distributed::shuffled_distribution<
+ base_distribution_type> distribution_type;
+
+ private:
+ // FIXME: the original adjacency_list contained this comment:
+ // Default copy constructor and copy assignment operators OK??? TBD
+ // but the adj_list_impl contained these declarations:
+ adjacency_list(const adjacency_list& other);
+ adjacency_list& operator=(const adjacency_list& other);
+
+ public:
+ adjacency_list(const ProcessGroup& pg = ProcessGroup())
+ : named_graph_mixin(pg, default_distribution_type(pg, 0)),
+ m_local_graph(GraphProperty()),
+ process_group_(pg, graph::parallel::attach_distributed_object())
+ {
+ setup_triggers();
+ }
+
+ adjacency_list(const ProcessGroup& pg,
+ const base_distribution_type& distribution)
+ : named_graph_mixin(pg, distribution),
+ m_local_graph(GraphProperty()),
+ process_group_(pg, graph::parallel::attach_distributed_object())
+ {
+ setup_triggers();
+ }
+
+ adjacency_list(const GraphProperty& g,
+ const ProcessGroup& pg = ProcessGroup())
+ : named_graph_mixin(pg, default_distribution_type(pg, 0)),
+ m_local_graph(g),
+ process_group_(pg, graph::parallel::attach_distributed_object())
+ {
+ setup_triggers();
+ }
+
+ adjacency_list(vertices_size_type n,
+ const GraphProperty& p,
+ const ProcessGroup& pg,
+ const base_distribution_type& distribution)
+ : named_graph_mixin(pg, distribution),
+ m_local_graph(distribution.block_size(process_id(pg), n), p),
+ process_group_(pg, graph::parallel::attach_distributed_object())
+ {
+ setup_triggers();
+
+ detail::parallel::maybe_initialize_vertex_indices(vertices(base()),
+ get(vertex_index, base()));
+ }
+
+ adjacency_list(vertices_size_type n,
+ const ProcessGroup& pg,
+ const base_distribution_type& distribution)
+ : named_graph_mixin(pg, distribution),
+ m_local_graph(distribution.block_size(process_id(pg), n), GraphProperty()),
+ process_group_(pg, graph::parallel::attach_distributed_object())
+ {
+ setup_triggers();
+
+ detail::parallel::maybe_initialize_vertex_indices(vertices(base()),
+ get(vertex_index, base()));
+ }
+
+ adjacency_list(vertices_size_type n,
+ const GraphProperty& p,
+ const ProcessGroup& pg = ProcessGroup())
+ : named_graph_mixin(pg, default_distribution_type(pg, n)),
+ m_local_graph(this->distribution().block_size(process_id(pg), n), p),
+ process_group_(pg, graph::parallel::attach_distributed_object())
+ {
+ setup_triggers();
+
+ detail::parallel::maybe_initialize_vertex_indices(vertices(base()),
+ get(vertex_index, base()));
+ }
+
+ adjacency_list(vertices_size_type n,
+ const ProcessGroup& pg = ProcessGroup())
+ : named_graph_mixin(pg, default_distribution_type(pg, n)),
+ m_local_graph(this->distribution().block_size(process_id(pg), n),
+ GraphProperty()),
+ process_group_(pg, graph::parallel::attach_distributed_object())
+ {
+ setup_triggers();
+
+ detail::parallel::maybe_initialize_vertex_indices(vertices(base()),
+ get(vertex_index, base()));
+ }
+
+ /*
+ * We assume that every processor sees the same list of edges, so
+ * they skip over any that don't originate from themselves. This
+ * means that programs switching between a local and a distributed
+ * graph will keep the same semantics.
+ */
+ template <class EdgeIterator>
+ adjacency_list(EdgeIterator first, EdgeIterator last,
+ vertices_size_type n,
+ const ProcessGroup& pg = ProcessGroup(),
+ const GraphProperty& p = GraphProperty())
+ : named_graph_mixin(pg, default_distribution_type(pg, n)),
+ m_local_graph(this->distribution().block_size(process_id(pg), n), p),
+ process_group_(pg, graph::parallel::attach_distributed_object())
+ {
+ setup_triggers();
+
+ typedef typename config_type::VertexListS vertex_list_selector;
+ initialize(first, last, n, this->distribution(), vertex_list_selector());
+ detail::parallel::maybe_initialize_vertex_indices(vertices(base()),
+ get(vertex_index, base()));
+
+ }
+
+ template <class EdgeIterator, class EdgePropertyIterator>
+ adjacency_list(EdgeIterator first, EdgeIterator last,
+ EdgePropertyIterator ep_iter,
+ vertices_size_type n,
+ const ProcessGroup& pg = ProcessGroup(),
+ const GraphProperty& p = GraphProperty())
+ : named_graph_mixin(pg, default_distribution_type(pg, n)),
+ m_local_graph(this->distribution().block_size(process_id(pg), n), p),
+ process_group_(pg, graph::parallel::attach_distributed_object())
+ {
+ setup_triggers();
+
+ typedef typename config_type::VertexListS vertex_list_selector;
+ initialize(first, last, ep_iter, n, this->distribution(),
+ vertex_list_selector());
+ detail::parallel::maybe_initialize_vertex_indices(vertices(base()),
+ get(vertex_index, base()));
+
+ }
+
+ template <class EdgeIterator>
+ adjacency_list(EdgeIterator first, EdgeIterator last,
+ vertices_size_type n,
+ const ProcessGroup& pg,
+ const base_distribution_type& distribution,
+ const GraphProperty& p = GraphProperty())
+ : named_graph_mixin(pg, distribution),
+ m_local_graph(distribution.block_size(process_id(pg), n), p),
+ process_group_(pg, graph::parallel::attach_distributed_object())
+ {
+ setup_triggers();
+
+ typedef typename config_type::VertexListS vertex_list_selector;
+ initialize(first, last, n, this->distribution(), vertex_list_selector());
+ detail::parallel::maybe_initialize_vertex_indices(vertices(base()),
+ get(vertex_index, base()));
+
+ }
+
+ template <class EdgeIterator, class EdgePropertyIterator>
+ adjacency_list(EdgeIterator first, EdgeIterator last,
+ EdgePropertyIterator ep_iter,
+ vertices_size_type n,
+ const ProcessGroup& pg,
+ const base_distribution_type& distribution,
+ const GraphProperty& p = GraphProperty())
+ : named_graph_mixin(pg, distribution),
+ m_local_graph(this->distribution().block_size(process_id(pg), n), p),
+ process_group_(pg, graph::parallel::attach_distributed_object())
+ {
+ setup_triggers();
+
+ typedef typename config_type::VertexListS vertex_list_selector;
+ initialize(first, last, ep_iter, n, distribution,
+ vertex_list_selector());
+ detail::parallel::maybe_initialize_vertex_indices(vertices(base()),
+ get(vertex_index, base()));
+
+ }
+
+ ~adjacency_list()
+ {
+ synchronize(process_group_);
+ }
+
+ void clear()
+ {
+ base().clear();
+ local_edges_.clear();
+ named_graph_mixin::clearing_graph();
+ }
+
+ void swap(adjacency_list& other)
+ {
+ using std::swap;
+
+ base().swap(other);
+ swap(process_group_, other.process_group_);
+ }
+
+ static vertex_descriptor null_vertex()
+ {
+ return vertex_descriptor(processor_id_type(0),
+ inherited::null_vertex());
+ }
+
+ inherited& base() { return m_local_graph; }
+ const inherited& base() const { return m_local_graph; }
+
+ processor_id_type processor() const { return process_id(process_group_); }
+ process_group_type process_group() const { return process_group_.base(); }
+
+ local_edge_list_type& local_edges() { return local_edges_; }
+ const local_edge_list_type& local_edges() const { return local_edges_; }
+
+ // Redistribute the vertices of the graph by placing each vertex
+ // v on the processor get(vertex_to_processor, v).
+ template<typename VertexProcessorMap>
+ void redistribute(VertexProcessorMap vertex_to_processor);
+
+ // Directly access a vertex or edge bundle
+ vertex_bundled& operator[](vertex_descriptor v)
+ {
+ assert(v.owner == processor());
+ return base()[v.local];
+ }
+
+ const vertex_bundled& operator[](vertex_descriptor v) const
+ {
+ assert(v.owner == processor());
+ return base()[v.local];
+ }
+
+ edge_bundled& operator[](edge_descriptor e)
+ {
+ assert(e.owner() == processor());
+ return base()[e.local];
+ }
+
+ const edge_bundled& operator[](edge_descriptor e) const
+ {
+ assert(e.owner() == processor());
+ return base()[e.local];
+ }
+
+ template<typename OStreamConstructibleArchive>
+ void save(std::string const& filename) const;
+
+ template<typename IStreamConstructibleArchive>
+ void load(std::string const& filename);
+
+ // Callback that will be invoked whenever a new vertex is added locally
+ boost::function<void(vertex_descriptor, adjacency_list&)> on_add_vertex;
+
+ // Callback that will be invoked whenever a new edge is added locally
+ boost::function<void(edge_descriptor, adjacency_list&)> on_add_edge;
+
+ private:
+ // Request vertex->processor mapping for neighbors <does nothing>
+ template<typename VertexProcessorMap>
+ void
+ request_in_neighbors(vertex_descriptor,
+ VertexProcessorMap,
+ directedS) { }
+
+ // Request vertex->processor mapping for neighbors <does nothing>
+ template<typename VertexProcessorMap>
+ void
+ request_in_neighbors(vertex_descriptor,
+ VertexProcessorMap,
+ undirectedS) { }
+
+ // Request vertex->processor mapping for neighbors
+ template<typename VertexProcessorMap>
+ void
+ request_in_neighbors(vertex_descriptor v,
+ VertexProcessorMap vertex_to_processor,
+ bidirectionalS);
+
+ // Clear the list of in-edges, but don't tell the remote processor
+ void clear_in_edges_local(vertex_descriptor v, directedS) {}
+ void clear_in_edges_local(vertex_descriptor v, undirectedS) {}
+
+ void clear_in_edges_local(vertex_descriptor v, bidirectionalS)
+ { get(vertex_in_edges, base())[v.local].clear(); }
+
+ // Remove in-edges that have migrated <does nothing>
+ template<typename VertexProcessorMap>
+ void
+ remove_migrated_in_edges(vertex_descriptor,
+ VertexProcessorMap,
+ directedS) { }
+
+ // Remove in-edges that have migrated <does nothing>
+ template<typename VertexProcessorMap>
+ void
+ remove_migrated_in_edges(vertex_descriptor,
+ VertexProcessorMap,
+ undirectedS) { }
+
+ // Remove in-edges that have migrated
+ template<typename VertexProcessorMap>
+ void
+ remove_migrated_in_edges(vertex_descriptor v,
+ VertexProcessorMap vertex_to_processor,
+ bidirectionalS);
+
+ // Initialize the graph with the given edge list and vertex
+ // distribution. This variation works only when
+ // VertexListS=vecS, and we know how to create remote vertex
+ // descriptors based solely on the distribution.
+ template<typename EdgeIterator>
+ void
+ initialize(EdgeIterator first, EdgeIterator last,
+ vertices_size_type, const base_distribution_type& distribution,
+ vecS);
+
+ // Initialize the graph with the given edge list, edge
+ // properties, and vertex distribution. This variation works
+ // only when VertexListS=vecS, and we know how to create remote
+ // vertex descriptors based solely on the distribution.
+ template<typename EdgeIterator, typename EdgePropertyIterator>
+ void
+ initialize(EdgeIterator first, EdgeIterator last,
+ EdgePropertyIterator ep_iter,
+ vertices_size_type, const base_distribution_type& distribution,
+ vecS);
+
+ // Initialize the graph with the given edge list, edge
+ // properties, and vertex distribution.
+ template<typename EdgeIterator, typename EdgePropertyIterator,
+ typename VertexListS>
+ void
+ initialize(EdgeIterator first, EdgeIterator last,
+ EdgePropertyIterator ep_iter,
+ vertices_size_type n,
+ const base_distribution_type& distribution,
+ VertexListS);
+
+ // Initialize the graph with the given edge list and vertex
+ // distribution. This is nearly identical to the one below it,
+ // for which I should be flogged. However, this version does use
+ // slightly less memory than the version that accepts an edge
+ // property iterator.
+ template<typename EdgeIterator, typename VertexListS>
+ void
+ initialize(EdgeIterator first, EdgeIterator last,
+ vertices_size_type n,
+ const base_distribution_type& distribution,
+ VertexListS);
+
+ public:
+ //---------------------------------------------------------------------
+ // Build a vertex property instance for the underlying adjacency
+ // list from the given property instance of the type exposed to
+ // the user.
+ base_vertex_property_type
+ build_vertex_property(const vertex_property_type& p)
+ { return build_vertex_property(p, directed_selector()); }
+
+ base_vertex_property_type
+ build_vertex_property(const vertex_property_type& p, directedS)
+ {
+ return base_vertex_property_type(p);
+ }
+
+ base_vertex_property_type
+ build_vertex_property(const vertex_property_type& p, bidirectionalS)
+ {
+ return base_vertex_property_type(in_edge_list_type(), p);
+ }
+
+ base_vertex_property_type
+ build_vertex_property(const vertex_property_type& p, undirectedS)
+ {
+ return base_vertex_property_type(p);
+ }
+ //---------------------------------------------------------------------
+
+ //---------------------------------------------------------------------
+ // Build an edge property instance for the underlying adjacency
+ // list from the given property instance of the type exposed to
+ // the user.
+ base_edge_property_type build_edge_property(const edge_property_type& p)
+ { return build_edge_property(p, directed_selector()); }
+
+ base_edge_property_type
+ build_edge_property(const edge_property_type& p, directedS)
+ {
+ return base_edge_property_type(0, p);
+ }
+
+ base_edge_property_type
+ build_edge_property(const edge_property_type& p, bidirectionalS)
+ {
+ return base_edge_property_type(0, p);
+ }
+
+ base_edge_property_type
+ build_edge_property(const edge_property_type& p, undirectedS)
+ {
+ typedef typename base_edge_property_type::next_type
+ edge_property_with_id;
+ return base_edge_property_type(true, edge_property_with_id(0, p));
+ }
+ //---------------------------------------------------------------------
+
+ /** The set of messages that can be transmitted and received by
+ * a distributed adjacency list. This list will eventually be
+ * exhaustive, but is currently quite limited.
+ */
+ enum {
+ /**
+ * Request to add or find a vertex with the given vertex
+ * property. The data will be a vertex_property_type
+ * structure.
+ */
+ msg_add_vertex_with_property = 0,
+
+ /**
+ * Request to add or find a vertex with the given vertex
+ * property, and request that the remote processor return the
+ * descriptor for the added/found edge. The data will be a
+ * vertex_property_type structure.
+ */
+ msg_add_vertex_with_property_and_reply,
+
+ /**
+ * Reply to a msg_add_vertex_* message, containing the local
+ * vertex descriptor that was added or found.
+ */
+ msg_add_vertex_reply,
+
+ /**
+ * Request to add an edge remotely. The data will be a
+ * msg_add_edge_data structure.
+ */
+ msg_add_edge,
+
+ /**
+ * Request to add an edge remotely. The data will be a
+ * msg_add_edge_with_property_data structure.
+ */
+ msg_add_edge_with_property,
+
+ /**
+ * Request to add an edge remotely and reply back with the
+ * edge descriptor. The data will be a
+ * msg_add_edge_data structure.
+ */
+ msg_add_edge_with_reply,
+
+ /**
+ * Request to add an edge remotely and reply back with the
+ * edge descriptor. The data will be a
+ * msg_add_edge_with_property_data structure.
+ */
+ msg_add_edge_with_property_and_reply,
+
+ /**
+ * Reply message responding to an @c msg_add_edge_with_reply
+ * or @c msg_add_edge_with_property_and_reply messages. The
+ * data will be a std::pair<edge_descriptor, bool>.
+ */
+ msg_add_edge_reply,
+
+ /**
+ * Indicates that a nonlocal edge has been created that should
+ * be added locally. Only valid for bidirectional and
+ * undirected graphs. The message carries a
+ * msg_nonlocal_edge_data structure.
+ */
+ msg_nonlocal_edge,
+
+ /**
+ * Indicates that a remote edge should be removed. This
+ * message does not exist for directedS graphs but may refer
+ * to either in-edges or out-edges for undirectedS graphs.
+ */
+ msg_remove_edge,
+
+ /**
+ * Indicates the number of vertices and edges that will be
+ * relocated from the source processor to the target
+ * processor. The data will be a pair<vertices_size_type,
+ * edges_size_type>.
+ */
+ msg_num_relocated
+ };
+
+ typedef detail::parallel::msg_add_edge_data<vertex_descriptor,
+ local_vertex_descriptor>
+ msg_add_edge_data;
+
+ typedef detail::parallel::msg_add_edge_with_property_data
+ <vertex_descriptor, local_vertex_descriptor,
+ edge_property_type> msg_add_edge_with_property_data;
+
+ typedef boost::detail::parallel::msg_nonlocal_edge_data<
+ edge_property_type,local_edge_descriptor> msg_nonlocal_edge_data;
+
+ typedef boost::detail::parallel::msg_remove_edge_data<edge_descriptor>
+ msg_remove_edge_data;
+
+ void send_remove_edge_request(edge_descriptor e)
+ {
+ process_id_type dest = e.target_processor;
+ if (e.target_processor == process_id(process_group_))
+ dest = e.source_processor;
+ send(process_group_, dest, msg_remove_edge, msg_remove_edge_data(e));
+ }
+
+ /// Process incoming messages.
+ void setup_triggers();
+
+ void
+ handle_add_vertex_with_property(int source, int tag,
+ const vertex_property_type&,
+ trigger_receive_context);
+
+ local_vertex_descriptor
+ handle_add_vertex_with_property_and_reply(int source, int tag,
+ const vertex_property_type&,
+ trigger_receive_context);
+
+ void
+ handle_add_edge(int source, int tag, const msg_add_edge_data& data,
+ trigger_receive_context);
+
+ boost::parallel::detail::untracked_pair<edge_descriptor, bool>
+ handle_add_edge_with_reply(int source, int tag,
+ const msg_add_edge_data& data,
+ trigger_receive_context);
+
+ void
+ handle_add_edge_with_property(int source, int tag,
+ const msg_add_edge_with_property_data&,
+ trigger_receive_context);
+
+ boost::parallel::detail::untracked_pair<edge_descriptor, bool>
+ handle_add_edge_with_property_and_reply
+ (int source, int tag, const msg_add_edge_with_property_data&,
+ trigger_receive_context);
+
+ void
+ handle_nonlocal_edge(int source, int tag,
+ const msg_nonlocal_edge_data& data,
+ trigger_receive_context);
+
+ void
+ handle_remove_edge(int source, int tag,
+ const msg_remove_edge_data& data,
+ trigger_receive_context);
+
+ protected:
+ /** Add an edge (locally) that was received from another
+ * processor. This operation is a no-op for directed graphs,
+ * because all edges reside on the local processor. For
+ * bidirectional graphs, this routine places the edge onto the
+ * list of incoming edges for the target vertex. For undirected
+ * graphs, the edge is placed along with all of the other edges
+ * for the target vertex, but it is marked as a non-local edge
+ * descriptor.
+ *
+ * \todo There is a potential problem here, where we could
+ * unintentionally allow duplicate edges in undirected graphs
+ * because the same edge is added on two different processors
+ * simultaneously. It's not an issue now, because we require
+ * that the graph allow parallel edges. Once we do support
+ * containers such as setS or hash_setS that disallow parallel
+ * edges we will need to deal with this.
+ */
+ void
+ add_remote_edge(const msg_nonlocal_edge_data&,
+ processor_id_type, directedS)
+ { }
+
+
+ /**
+ * \overload
+ */
+ void
+ add_remote_edge(const msg_nonlocal_edge_data& data,
+ processor_id_type other_proc, bidirectionalS)
+ {
+ typedef detail::parallel::stored_in_edge<local_edge_descriptor> stored_edge;
+
+ stored_edge edge(other_proc, data.e);
+ local_vertex_descriptor v = target(data.e, base());
+ boost::graph_detail::push(get(vertex_in_edges, base())[v], edge);
+ }
+
+ /**
+ * \overload
+ */
+ void
+ add_remote_edge(const msg_nonlocal_edge_data& data,
+ processor_id_type other_proc, undirectedS)
+ {
+ std::pair<local_edge_descriptor, bool> edge =
+ detail::parallel::add_local_edge(target(data.e, base()),
+ source(data.e, base()),
+ build_edge_property(data.get_property()), base());
+ assert(edge.second);
+ put(edge_target_processor_id, base(), edge.first, other_proc);
+
+ if (edge.second && on_add_edge)
+ on_add_edge(edge_descriptor(processor(), other_proc, false,
+ edge.first),
+ *this);
+ }
+
+ void
+ remove_local_edge(const msg_remove_edge_data&, processor_id_type,
+ directedS)
+ { }
+
+ void
+ remove_local_edge(const msg_remove_edge_data& data,
+ processor_id_type other_proc, bidirectionalS)
+ {
+ /* When the source is local, we first check if the edge still
+ * exists (it may have been deleted locally) and, if so,
+ * remove it locally.
+ */
+ vertex_descriptor src = source(data.e, *this);
+ vertex_descriptor tgt = target(data.e, *this);
+
+ if (src.owner == process_id(process_group_)) {
+ base_out_edge_iterator ei, ei_end;
+ for (tie(ei, ei_end) = out_edges(src.local, base());
+ ei != ei_end; ++ei) {
+ // TBD: can't check the descriptor here, because it could
+ // have changed if we're allowing the removal of
+ // edges. Egads!
+ if (tgt.local == target(*ei, base())
+ && get(edge_target_processor_id, base(), *ei) == other_proc)
+ break;
+ }
+
+ if (ei != ei_end) boost::remove_edge(ei, base());
+
+ remove_local_edge_from_list(src, tgt, undirectedS());
+ } else {
+ assert(tgt.owner == process_id(process_group_));
+ in_edge_list_type& in_edges =
+ get(vertex_in_edges, base())[tgt.local];
+ typename in_edge_list_type::iterator ei;
+ for (ei = in_edges.begin(); ei != in_edges.end(); ++ei) {
+ if (src.local == source(ei->e, base())
+ && src.owner == ei->source_processor)
+ break;
+ }
+
+ if (ei != in_edges.end()) in_edges.erase(ei);
+ }
+ }
+
+ void
+ remove_local_edge(const msg_remove_edge_data& data,
+ processor_id_type other_proc, undirectedS)
+ {
+ vertex_descriptor local_vertex = source(data.e, *this);
+ vertex_descriptor remote_vertex = target(data.e, *this);
+ if (remote_vertex.owner == process_id(process_group_)) {
+ using std::swap;
+ swap(local_vertex, remote_vertex);
+ }
+
+ // Remove the edge from the out-edge list, if it is there
+ {
+ base_out_edge_iterator ei, ei_end;
+ for (tie(ei, ei_end) = out_edges(local_vertex.local, base());
+ ei != ei_end; ++ei) {
+ // TBD: can't check the descriptor here, because it could
+ // have changed if we're allowing the removal of
+ // edges. Egads!
+ if (remote_vertex.local == target(*ei, base())
+ && get(edge_target_processor_id, base(), *ei) == other_proc)
+ break;
+ }
+
+ if (ei != ei_end) boost::remove_edge(ei, base());
+ }
+
+ remove_local_edge_from_list(local_vertex, remote_vertex, undirectedS());
+ }
+
+ public:
+ void
+ remove_local_edge_from_list(vertex_descriptor, vertex_descriptor,
+ directedS)
+ {
+ }
+
+ void
+ remove_local_edge_from_list(vertex_descriptor, vertex_descriptor,
+ bidirectionalS)
+ {
+ }
+
+ void
+ remove_local_edge_from_list(vertex_descriptor src, vertex_descriptor tgt,
+ undirectedS)
+ {
+ // TBD: At some point we'll be able to improve the speed here
+ // because we'll know when the edge can't be in the local
+ // list.
+ {
+ typename local_edge_list_type::iterator ei;
+ for (ei = local_edges_.begin(); ei != local_edges_.end(); ++ei) {
+ if ((source(*ei, *this) == src
+ && target(*ei, *this) == tgt)
+ || (source(*ei, *this) == tgt
+ && target(*ei, *this) == src))
+ break;
+ }
+
+ if (ei != local_edges_.end()) local_edges_.erase(ei);
+ }
+
+ }
+
+ private:
+ /// The local subgraph
+ inherited m_local_graph;
+
+ /// The process group through which this distributed graph
+ /// communicates.
+ process_group_type process_group_;
+
+ // TBD: should only be available for undirected graphs, but for
+ // now it'll just be empty for directed and bidirectional
+ // graphs.
+ local_edge_list_type local_edges_;
+ };
+
+ //------------------------------------------------------------------------
+ // Lazy addition of vertices
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ struct PBGL_DISTRIB_ADJLIST_TYPE::lazy_add_vertex_with_property
+ {
+ /// Construct a lazy request to add a vertex
+ lazy_add_vertex_with_property(adjacency_list& self,
+ const vertex_property_type& property)
+ : self(self), property(property), committed(false) { }
+
+ /// Copying a lazy_add_vertex_with_property transfers the
+ /// responsibility for adding the vertex to the newly-constructed
+ /// object.
+ lazy_add_vertex_with_property(const lazy_add_vertex_with_property& other)
+ : self(other.self), property(other.property),
+ committed(other.committed)
+ {
+ other.committed = true;
+ }
+
+ /// If the vertex has not yet been added, add the vertex but don't
+ /// wait for a reply.
+ ~lazy_add_vertex_with_property();
+
+ /// Returns commit().
+ operator vertex_descriptor() const { return commit(); }
+
+ // Add the vertex. This operation will block if the vertex is
+ // being added remotely.
+ vertex_descriptor commit() const;
+
+ protected:
+ adjacency_list& self;
+ vertex_property_type property;
+ mutable bool committed;
+
+ private:
+ // No copy-assignment semantics
+ void operator=(lazy_add_vertex_with_property&);
+ };
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ PBGL_DISTRIB_ADJLIST_TYPE::lazy_add_vertex_with_property::
+ ~lazy_add_vertex_with_property()
+ {
+ /// If this vertex has already been created or will be created by
+ /// someone else, or if someone threw an exception, we will not
+ /// create the vertex now.
+ if (committed || std::uncaught_exception())
+ return;
+
+ committed = true;
+
+ process_id_type owner
+ = static_cast<graph_type&>(self).owner_by_property(property);
+ if (owner == self.processor()) {
+ /// Add the vertex locally.
+ vertex_descriptor v(owner,
+ add_vertex(self.build_vertex_property(property),
+ self.base()));
+ if (self.on_add_vertex)
+ self.on_add_vertex(v, self);
+ }
+ else
+ /// Ask the owner of this new vertex to add the vertex. We
+ /// don't need a reply.
+ send(self.process_group_, owner, msg_add_vertex_with_property,
+ property);
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ typename PBGL_DISTRIB_ADJLIST_TYPE::vertex_descriptor
+ PBGL_DISTRIB_ADJLIST_TYPE::lazy_add_vertex_with_property::
+ commit() const
+ {
+ assert(!this->committed);
+ this->committed = true;
+
+ process_id_type owner
+ = static_cast<graph_type&>(self).owner_by_property(property);
+ local_vertex_descriptor local_v;
+ if (owner == self.processor())
+ /// Add the vertex locally.
+ local_v = add_vertex(self.build_vertex_property(property),
+ self.base());
+ else {
+ // Request that the remote process add the vertex immediately
+ send_oob_with_reply(self.process_group_, owner,
+ msg_add_vertex_with_property_and_reply, property,
+ local_v);
+ }
+
+ vertex_descriptor v(owner, local_v);
+ if (self.on_add_vertex)
+ self.on_add_vertex(v, self);
+
+ // Build the full vertex descriptor to return
+ return v;
+ }
+
+
+ /**
+ * Data structure returned from add_edge that will "lazily" add
+ * the edge, either when it is converted to a
+ * @c pair<edge_descriptor, bool> or when the most recent copy has
+ * been destroyed.
+ */
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ struct PBGL_DISTRIB_ADJLIST_TYPE::lazy_add_edge
+ {
+ /// Construct a lazy request to add an edge
+ lazy_add_edge(adjacency_list& self,
+ vertex_descriptor source, vertex_descriptor target)
+ : self(self), source(source), target(target), committed(false) { }
+
+ /// Copying a lazy_add_edge transfers the responsibility for
+ /// adding the edge to the newly-constructed object.
+ lazy_add_edge(const lazy_add_edge& other)
+ : self(other.self), source(other.source), target(other.target),
+ committed(other.committed)
+ {
+ other.committed = true;
+ }
+
+ /// If the edge has not yet been added, add the edge but don't
+ /// wait for a reply.
+ ~lazy_add_edge();
+
+ /// Returns commit().
+ operator std::pair<edge_descriptor, bool>() const { return commit(); }
+
+ // Add the edge. This operation will block if a remote edge is
+ // being added.
+ std::pair<edge_descriptor, bool> commit() const;
+
+ protected:
+ std::pair<edge_descriptor, bool>
+ add_local_edge(const edge_property_type& property, directedS) const;
+
+ std::pair<edge_descriptor, bool>
+ add_local_edge(const edge_property_type& property, bidirectionalS) const;
+
+ std::pair<edge_descriptor, bool>
+ add_local_edge(const edge_property_type& property, undirectedS) const;
+
+ adjacency_list& self;
+ vertex_descriptor source;
+ vertex_descriptor target;
+ mutable bool committed;
+
+ private:
+ // No copy-assignment semantics
+ void operator=(lazy_add_edge&);
+ };
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ PBGL_DISTRIB_ADJLIST_TYPE::lazy_add_edge::~lazy_add_edge()
+ {
+ /// If this edge has already been created or will be created by
+ /// someone else, or if someone threw an exception, we will not
+ /// create the edge now.
+ if (committed || std::uncaught_exception())
+ return;
+
+ committed = true;
+
+ if (source.owner == self.processor())
+ this->add_local_edge(edge_property_type(), DirectedS());
+ else
+ // Request that the remote processor add an edge and, but
+ // don't wait for a reply.
+ send(self.process_group_, source.owner, msg_add_edge,
+ msg_add_edge_data(source, target));
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ std::pair<typename PBGL_DISTRIB_ADJLIST_TYPE::edge_descriptor, bool>
+ PBGL_DISTRIB_ADJLIST_TYPE::lazy_add_edge::commit() const
+ {
+ assert(!committed);
+ committed = true;
+
+ if (source.owner == self.processor())
+ return this->add_local_edge(edge_property_type(), DirectedS());
+ else {
+ // Request that the remote processor add an edge
+ boost::parallel::detail::untracked_pair<edge_descriptor, bool> result;
+ send_oob_with_reply(self.process_group_, source.owner,
+ msg_add_edge_with_reply,
+ msg_add_edge_data(source, target), result);
+ return result;
+ }
+ }
+
+ // Add a local edge into a directed graph
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ std::pair<typename PBGL_DISTRIB_ADJLIST_TYPE::edge_descriptor, bool>
+ PBGL_DISTRIB_ADJLIST_TYPE::lazy_add_edge::
+ add_local_edge(const edge_property_type& property, directedS) const
+ {
+ // Add the edge to the local part of the graph
+ std::pair<local_edge_descriptor, bool> inserted =
+ detail::parallel::add_local_edge(source.local, target.local,
+ self.build_edge_property(property),
+ self.base());
+
+ if (inserted.second)
+ // Keep track of the owner of the target
+ put(edge_target_processor_id, self.base(), inserted.first,
+ target.owner);
+
+ // Compose the edge descriptor and return the result
+ edge_descriptor e(source.owner, target.owner, true, inserted.first);
+
+ // Trigger the on_add_edge event
+ if (inserted.second && self.on_add_edge)
+ self.on_add_edge(e, self);
+
+ return std::pair<edge_descriptor, bool>(e, inserted.second);
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ std::pair<typename PBGL_DISTRIB_ADJLIST_TYPE::edge_descriptor, bool>
+ PBGL_DISTRIB_ADJLIST_TYPE::lazy_add_edge::
+ add_local_edge(const edge_property_type& property, bidirectionalS) const
+ {
+ // Add the directed edge.
+ std::pair<edge_descriptor, bool> result
+ = this->add_local_edge(property, directedS());
+
+ if (result.second) {
+ if (target.owner == self.processor()) {
+ // Edge is local, so add the stored edge to the in_edges list
+ typedef detail::parallel::stored_in_edge<local_edge_descriptor>
+ stored_edge;
+
+ stored_edge e(self.processor(), result.first.local);
+ boost::graph_detail::push(get(vertex_in_edges,
+ self.base())[target.local], e);
+ }
+ else {
+ // Edge is remote, so notify the target's owner that an edge
+ // has been added.
+ if (self.process_group_.trigger_context() == graph::parallel::trc_out_of_band)
+ send_oob(self.process_group_, target.owner, msg_nonlocal_edge,
+ msg_nonlocal_edge_data(result.first.local, property));
+ else
+ send(self.process_group_, target.owner, msg_nonlocal_edge,
+ msg_nonlocal_edge_data(result.first.local, property));
+ }
+ }
+
+ return result;
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ std::pair<typename PBGL_DISTRIB_ADJLIST_TYPE::edge_descriptor, bool>
+ PBGL_DISTRIB_ADJLIST_TYPE::lazy_add_edge::
+ add_local_edge(const edge_property_type& property, undirectedS) const
+ {
+ // Add the directed edge
+ std::pair<edge_descriptor, bool> result
+ = this->add_local_edge(property, directedS());
+
+ typedef detail::parallel::stored_in_edge<local_edge_descriptor>
+ stored_edge;
+
+ if (result.second) {
+ if (target.owner == self.processor()) {
+ // Edge is local, so add the new edge to the list
+
+ // TODO: This is not what we want to do for an undirected
+ // edge, because we haven't linked the source and target's
+ // representations of those edges.
+ local_edge_descriptor return_edge =
+ detail::parallel::add_local_edge(target.local, source.local,
+ self.build_edge_property(property),
+ self.base()).first;
+
+ put(edge_target_processor_id, self.base(), return_edge,
+ source.owner);
+ }
+ else {
+ // Edge is remote, so notify the target's owner that an edge
+ // has been added.
+ if (self.process_group_.trigger_context() == graph::parallel::trc_out_of_band)
+ send_oob(self.process_group_, target.owner, msg_nonlocal_edge,
+ msg_nonlocal_edge_data(result.first.local, property));
+ else
+ send(self.process_group_, target.owner, msg_nonlocal_edge,
+ msg_nonlocal_edge_data(result.first.local, property));
+
+ }
+
+ // Add this edge to the list of local edges
+ graph_detail::push(self.local_edges(), result.first);
+ }
+
+ return result;
+ }
+
+
+ /**
+ * Data structure returned from add_edge that will "lazily" add
+ * the edge with its property, either when it is converted to a
+ * pair<edge_descriptor, bool> or when the most recent copy has
+ * been destroyed.
+ */
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ struct PBGL_DISTRIB_ADJLIST_TYPE::lazy_add_edge_with_property
+ : lazy_add_edge
+ {
+ /// Construct a lazy request to add an edge
+ lazy_add_edge_with_property(adjacency_list& self,
+ vertex_descriptor source,
+ vertex_descriptor target,
+ const edge_property_type& property)
+ : lazy_add_edge(self, source, target), property(property) { }
+
+ /// Copying a lazy_add_edge transfers the responsibility for
+ /// adding the edge to the newly-constructed object.
+ lazy_add_edge_with_property(const lazy_add_edge& other)
+ : lazy_add_edge(other), property(other.property) { }
+
+ /// If the edge has not yet been added, add the edge but don't
+ /// wait for a reply.
+ ~lazy_add_edge_with_property();
+
+ /// Returns commit().
+ operator std::pair<edge_descriptor, bool>() const { return commit(); }
+
+ // Add the edge. This operation will block if a remote edge is
+ // being added.
+ std::pair<edge_descriptor, bool> commit() const;
+
+ private:
+ // No copy-assignment semantics
+ void operator=(lazy_add_edge_with_property&);
+
+ edge_property_type property;
+ };
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ PBGL_DISTRIB_ADJLIST_TYPE::lazy_add_edge_with_property::
+ ~lazy_add_edge_with_property()
+ {
+ /// If this edge has already been created or will be created by
+ /// someone else, or if someone threw an exception, we will not
+ /// create the edge now.
+ if (this->committed || std::uncaught_exception())
+ return;
+
+ this->committed = true;
+
+ if (this->source.owner == this->self.processor())
+ // Add a local edge
+ this->add_local_edge(property, DirectedS());
+ else
+ // Request that the remote processor add an edge and, but
+ // don't wait for a reply.
+ send(this->self.process_group_, this->source.owner,
+ msg_add_edge_with_property,
+ msg_add_edge_with_property_data(this->source, this->target,
+ property));
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ std::pair<typename PBGL_DISTRIB_ADJLIST_TYPE::edge_descriptor, bool>
+ PBGL_DISTRIB_ADJLIST_TYPE::lazy_add_edge_with_property::
+ commit() const
+ {
+ assert(!this->committed);
+ this->committed = true;
+
+ if (this->source.owner == this->self.processor())
+ // Add a local edge
+ return this->add_local_edge(property, DirectedS());
+ else {
+ // Request that the remote processor add an edge
+ boost::parallel::detail::untracked_pair<edge_descriptor, bool> result;
+ send_oob_with_reply(this->self.process_group_, this->source.owner,
+ msg_add_edge_with_property_and_reply,
+ msg_add_edge_with_property_data(this->source,
+ this->target,
+ property),
+ result);
+ return result;
+ }
+ }
+
+
+ /**
+ * Returns the set of vertices local to this processor. Note that
+ * although this routine matches a valid expression of a
+ * VertexListGraph, it does not meet the semantic requirements of
+ * VertexListGraph because it returns only local vertices (not all
+ * vertices).
+ */
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ std::pair<typename PBGL_DISTRIB_ADJLIST_TYPE
+ ::vertex_iterator,
+ typename PBGL_DISTRIB_ADJLIST_TYPE
+ ::vertex_iterator>
+ vertices(const PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef typename PBGL_DISTRIB_ADJLIST_TYPE
+ ::vertex_descriptor Vertex;
+
+ typedef typename Vertex::generator generator;
+
+ return std::make_pair(make_transform_iterator(vertices(g.base()).first,
+ generator(g.processor())),
+ make_transform_iterator(vertices(g.base()).second,
+ generator(g.processor())));
+ }
+
+ /**
+ * Returns the number of vertices local to this processor. Note that
+ * although this routine matches a valid expression of a
+ * VertexListGraph, it does not meet the semantic requirements of
+ * VertexListGraph because it returns only a count of local vertices
+ * (not all vertices).
+ */
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ typename PBGL_DISTRIB_ADJLIST_TYPE
+ ::vertices_size_type
+ num_vertices(const PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ return num_vertices(g.base());
+ }
+
+ /***************************************************************************
+ * Implementation of Incidence Graph concept
+ ***************************************************************************/
+ /**
+ * Returns the source of edge @param e in @param g.
+ */
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS, typename Edge>
+ typename PBGL_DISTRIB_ADJLIST_TYPE::vertex_descriptor
+ source(const detail::parallel::edge_descriptor<Edge>& e,
+ const PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef typename PBGL_DISTRIB_ADJLIST_TYPE
+ ::vertex_descriptor Vertex;
+ return Vertex(e.source_processor, source(e.local, g.base()));
+ }
+
+ /**
+ * Returns the target of edge @param e in @param g.
+ */
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS, typename Edge>
+ typename PBGL_DISTRIB_ADJLIST_TYPE::vertex_descriptor
+ target(const detail::parallel::edge_descriptor<Edge>& e,
+ const PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef typename PBGL_DISTRIB_ADJLIST_TYPE
+ ::vertex_descriptor Vertex;
+ return Vertex(e.target_processor, target(e.local, g.base()));
+ }
+
+ /**
+ * Return the set of edges outgoing from a particular vertex. The
+ * vertex @param v must be local to the processor executing this
+ * routine.
+ */
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ std::pair<typename PBGL_DISTRIB_ADJLIST_TYPE::out_edge_iterator,
+ typename PBGL_DISTRIB_ADJLIST_TYPE::out_edge_iterator>
+ out_edges(typename PBGL_DISTRIB_ADJLIST_TYPE::vertex_descriptor v,
+ const PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ assert(v.owner == g.processor());
+
+ typedef PBGL_DISTRIB_ADJLIST_TYPE impl;
+ typedef typename impl::out_edge_generator generator;
+
+ return std::make_pair(
+ make_transform_iterator(out_edges(v.local, g.base()).first,
+ generator(g)),
+ make_transform_iterator(out_edges(v.local, g.base()).second,
+ generator(g)));
+ }
+
+ /**
+ * Return the number of edges outgoing from a particular vertex. The
+ * vertex @param v must be local to the processor executing this
+ * routine.
+ */
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ typename PBGL_DISTRIB_ADJLIST_TYPE::degree_size_type
+ out_degree(typename PBGL_DISTRIB_ADJLIST_TYPE::vertex_descriptor v,
+ const PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ assert(v.owner == g.processor());
+
+ return out_degree(v.local, g.base());
+ }
+
+ /***************************************************************************
+ * Implementation of Bidirectional Graph concept
+ ***************************************************************************/
+ /**
+ * Returns the set of edges incoming to a particular vertex. The
+ * vertex @param v must be local to the processor executing this
+ * routine.
+ */
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS_CONFIG>
+ std::pair<typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(bidirectionalS)
+ ::in_edge_iterator,
+ typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(bidirectionalS)
+ ::in_edge_iterator>
+ in_edges(typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(bidirectionalS)
+ ::vertex_descriptor v,
+ const PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(bidirectionalS)& g)
+ {
+ assert(v.owner == g.processor());
+
+ typedef PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(bidirectionalS) impl;
+ typedef typename impl::inherited base_graph_type;
+ typedef typename impl::in_edge_generator generator;
+
+
+ typename property_map<base_graph_type, vertex_in_edges_t>::const_type
+ in_edges = get(vertex_in_edges, g.base());
+
+ return std::make_pair(make_transform_iterator(in_edges[v.local].begin(),
+ generator(g)),
+ make_transform_iterator(in_edges[v.local].end(),
+ generator(g)));
+ }
+
+ /**
+ * \overload
+ */
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS_CONFIG>
+ std::pair<typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(undirectedS)
+ ::in_edge_iterator,
+ typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(undirectedS)
+ ::in_edge_iterator>
+ in_edges(typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(undirectedS)
+ ::vertex_descriptor v,
+ const PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(undirectedS)& g)
+ {
+ assert(v.owner == g.processor());
+
+ typedef PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(undirectedS) impl;
+ typedef typename impl::in_edge_generator generator;
+
+ return std::make_pair(
+ make_transform_iterator(out_edges(v.local, g.base()).first,
+ generator(g)),
+ make_transform_iterator(out_edges(v.local, g.base()).second,
+ generator(g)));
+ }
+
+ /**
+ * Returns the number of edges incoming to a particular vertex. The
+ * vertex @param v must be local to the processor executing this
+ * routine.
+ */
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS_CONFIG>
+ typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(bidirectionalS)::degree_size_type
+ in_degree(typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(bidirectionalS)
+ ::vertex_descriptor v,
+ const PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(bidirectionalS)& g)
+ {
+ assert(v.owner == g.processor());
+
+ return get(vertex_in_edges, g.base())[v.local].size();
+ }
+
+ /**
+ * \overload
+ */
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS_CONFIG>
+ typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(undirectedS)::degree_size_type
+ in_degree(typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(undirectedS)
+ ::vertex_descriptor v,
+ const PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(undirectedS)& g)
+ {
+ assert(v.owner == g.processor());
+
+ return out_degree(v.local, g.base());
+ }
+
+ /**
+ * Returns the number of edges incident on the given vertex. The
+ * vertex @param v must be local to the processor executing this
+ * routine.
+ */
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS_CONFIG>
+ typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(undirectedS)
+ ::degree_size_type
+ degree(typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(undirectedS)
+ ::vertex_descriptor v,
+ const PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(undirectedS)& g)
+ {
+ assert(v.owner == g.processor());
+ return out_degree(v.local, g.base());
+ }
+
+ /**
+ * \overload
+ */
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS_CONFIG>
+ typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(bidirectionalS)
+ ::degree_size_type
+ degree(typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(bidirectionalS)
+ ::vertex_descriptor v,
+ const PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(bidirectionalS)& g)
+ {
+ assert(v.owner == g.processor());
+ return out_degree(v, g) + in_degree(v, g);
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ typename PBGL_DISTRIB_ADJLIST_TYPE::edges_size_type
+ num_edges(const PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ return num_edges(g.base());
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS_CONFIG>
+ typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(undirectedS)::edges_size_type
+ num_edges(const PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(undirectedS)& g)
+ {
+ return g.local_edges().size();
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ std::pair<
+ typename PBGL_DISTRIB_ADJLIST_TYPE::edge_iterator,
+ typename PBGL_DISTRIB_ADJLIST_TYPE::edge_iterator>
+ edges(const PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef PBGL_DISTRIB_ADJLIST_TYPE impl;
+ typedef typename impl::out_edge_generator generator;
+
+ return std::make_pair(make_transform_iterator(edges(g.base()).first,
+ generator(g)),
+ make_transform_iterator(edges(g.base()).second,
+ generator(g)));
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS_CONFIG>
+ std::pair<
+ typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(undirectedS)::edge_iterator,
+ typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(undirectedS)::edge_iterator>
+ edges(const PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(undirectedS)& g)
+ {
+ return std::make_pair(g.local_edges().begin(), g.local_edges().end());
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ inline
+ typename PBGL_DISTRIB_ADJLIST_TYPE::vertex_descriptor
+ vertex(typename PBGL_DISTRIB_ADJLIST_TYPE::vertices_size_type n,
+ const PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef typename PBGL_DISTRIB_ADJLIST_TYPE::vertex_descriptor
+ vertex_descriptor;
+
+ return vertex_descriptor(g.distribution()(n), g.distribution().local(n));
+ }
+
+ /***************************************************************************
+ * Access to particular edges
+ ***************************************************************************/
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS_CONFIG>
+ std::pair<
+ typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(directedS)::edge_descriptor,
+ bool
+ >
+ edge(typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(directedS)::vertex_descriptor u,
+ typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(directedS)::vertex_descriptor v,
+ const PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(directedS)& g)
+ {
+ typedef typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(directedS)
+ ::edge_descriptor edge_descriptor;
+
+ // For directed graphs, u must be local
+ assert(u.owner == process_id(g.process_group()));
+
+ typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(directedS)
+ ::out_edge_iterator ei, ei_end;
+ for (tie(ei, ei_end) = out_edges(u, g); ei != ei_end; ++ei) {
+ if (target(*ei, g) == v) return std::make_pair(*ei, true);
+ }
+ return std::make_pair(edge_descriptor(), false);
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ std::pair<
+ typename PBGL_DISTRIB_ADJLIST_TYPE::edge_descriptor,
+ bool
+ >
+ edge(typename PBGL_DISTRIB_ADJLIST_TYPE::vertex_descriptor u,
+ typename PBGL_DISTRIB_ADJLIST_TYPE::vertex_descriptor v,
+ const PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef typename PBGL_DISTRIB_ADJLIST_TYPE
+ ::edge_descriptor edge_descriptor;
+
+ // For bidirectional and undirected graphs, u must be local or v
+ // must be local
+ if (u.owner == process_id(g.process_group())) {
+ typename PBGL_DISTRIB_ADJLIST_TYPE::out_edge_iterator ei, ei_end;
+ for (tie(ei, ei_end) = out_edges(u, g); ei != ei_end; ++ei) {
+ if (target(*ei, g) == v) return std::make_pair(*ei, true);
+ }
+ return std::make_pair(edge_descriptor(), false);
+ } else if (v.owner == process_id(g.process_group())) {
+ typename PBGL_DISTRIB_ADJLIST_TYPE::in_edge_iterator ei, ei_end;
+ for (tie(ei, ei_end) = in_edges(v, g); ei != ei_end; ++ei) {
+ if (source(*ei, g) == u) return std::make_pair(*ei, true);
+ }
+ return std::make_pair(edge_descriptor(), false);
+ } else {
+ assert(false);
+ exit(1);
+ }
+ }
+
+#if 0
+ // TBD: not yet supported
+ std::pair<out_edge_iterator, out_edge_iterator>
+ edge_range(vertex_descriptor u, vertex_descriptor v,
+ const adjacency_list& g);
+#endif
+
+ /***************************************************************************
+ * Implementation of Adjacency Graph concept
+ ***************************************************************************/
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ std::pair<typename PBGL_DISTRIB_ADJLIST_TYPE::adjacency_iterator,
+ typename PBGL_DISTRIB_ADJLIST_TYPE::adjacency_iterator>
+ adjacent_vertices(typename PBGL_DISTRIB_ADJLIST_TYPE::vertex_descriptor v,
+ const PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef typename PBGL_DISTRIB_ADJLIST_TYPE::adjacency_iterator iter;
+ return std::make_pair(iter(out_edges(v, g).first, &g),
+ iter(out_edges(v, g).second, &g));
+ }
+
+ /***************************************************************************
+ * Implementation of Mutable Graph concept
+ ***************************************************************************/
+
+ /************************************************************************
+ * add_edge
+ ************************************************************************/
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ typename PBGL_DISTRIB_ADJLIST_TYPE::lazy_add_edge
+ add_edge(typename PBGL_DISTRIB_ADJLIST_TYPE::vertex_descriptor u,
+ typename PBGL_DISTRIB_ADJLIST_TYPE::vertex_descriptor v,
+ PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef typename PBGL_DISTRIB_ADJLIST_TYPE::lazy_add_edge lazy_add_edge;
+
+ return lazy_add_edge(g, u, v);
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ typename PBGL_DISTRIB_ADJLIST_TYPE
+ ::lazy_add_edge_with_property
+ add_edge(typename PBGL_DISTRIB_ADJLIST_TYPE::vertex_descriptor u,
+ typename PBGL_DISTRIB_ADJLIST_TYPE::vertex_descriptor v,
+ typename PBGL_DISTRIB_ADJLIST_TYPE::edge_property_type const& p,
+ PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef typename PBGL_DISTRIB_ADJLIST_TYPE
+ ::lazy_add_edge_with_property lazy_add_edge_with_property;
+ return lazy_add_edge_with_property(g, u, v, p);
+ }
+
+ /************************************************************************
+ *
+ * remove_edge
+ *
+ ************************************************************************/
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ void
+ remove_edge(typename PBGL_DISTRIB_ADJLIST_TYPE::edge_descriptor e,
+ PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ assert(source(e, g).owner == g.processor()
+ || target(e, g).owner == g.processor());
+
+ if (target(e, g).owner == g.processor())
+ detail::parallel::remove_in_edge(e, g, DirectedS());
+ if (source(e, g).owner == g.processor())
+ remove_edge(e.local, g.base());
+
+ g.remove_local_edge_from_list(source(e, g), target(e, g), DirectedS());
+
+ if (source(e, g).owner != g.processor()
+ || (target(e, g).owner != g.processor()
+ && !(is_same<DirectedS, directedS>::value))) {
+ g.send_remove_edge_request(e);
+ }
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ void
+ remove_edge(typename PBGL_DISTRIB_ADJLIST_TYPE::vertex_descriptor u,
+ typename PBGL_DISTRIB_ADJLIST_TYPE::vertex_descriptor v,
+ PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef typename PBGL_DISTRIB_ADJLIST_TYPE
+ ::vertex_descriptor vertex_descriptor;
+ typedef typename PBGL_DISTRIB_ADJLIST_TYPE
+ ::edge_descriptor edge_descriptor;
+ std::pair<edge_descriptor, bool> the_edge = edge(u, v, g);
+ if (the_edge.second) remove_edge(the_edge.first, g);
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ inline void
+ remove_edge(typename PBGL_DISTRIB_ADJLIST_TYPE::out_edge_iterator ei,
+ PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ remove_edge(*ei, g);
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS_CONFIG>
+ inline void
+ remove_edge(typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(directedS)
+ ::out_edge_iterator ei,
+ PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(directedS)& g)
+ {
+ assert(source(*ei, g).owner == g.processor());
+ remove_edge(ei->local, g.base());
+ }
+
+ /************************************************************************
+ *
+ * remove_out_edge_if
+ *
+ ************************************************************************/
+ namespace parallel { namespace detail {
+ /**
+ * Function object that applies the underlying predicate to
+ * determine if an out-edge should be removed. If so, either
+ * removes the incoming edge (if it is stored locally) or sends a
+ * message to the owner of the target requesting that it remove
+ * the edge.
+ */
+ template<typename Graph, typename Predicate>
+ struct remove_out_edge_predicate
+ {
+ typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor;
+ typedef typename Graph::local_edge_descriptor argument_type;
+ typedef typename Graph::directed_selector directed_selector;
+ typedef bool result_type;
+
+ remove_out_edge_predicate(Graph& g, Predicate& predicate)
+ : g(g), predicate(predicate) { }
+
+ bool operator()(const argument_type& le)
+ {
+ typedef typename edge_descriptor::template out_generator<Graph>
+ generator;
+
+ edge_descriptor e = generator(g)(le);
+
+ if (predicate(e)) {
+ if (source(e, g).owner != target(e, g).owner
+ && !(is_same<directed_selector, directedS>::value))
+ g.send_remove_edge_request(e);
+ else
+ ::boost::detail::parallel::remove_in_edge(e, g,
+ directed_selector());
+
+ g.remove_local_edge_from_list(source(e, g), target(e, g),
+ directed_selector());
+ return true;
+ } else return false;
+ }
+
+ private:
+ Graph& g;
+ Predicate predicate;
+ };
+ } } // end namespace parallel::detail
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS, typename Predicate>
+ inline void
+ remove_out_edge_if
+ (typename PBGL_DISTRIB_ADJLIST_TYPE::vertex_descriptor u,
+ Predicate predicate,
+ PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef PBGL_DISTRIB_ADJLIST_TYPE Graph;
+ typedef parallel::detail::remove_out_edge_predicate<Graph, Predicate>
+ Pred;
+
+ assert(u.owner == g.processor());
+ remove_out_edge_if(u.local, Pred(g, predicate), g.base());
+ }
+
+ /************************************************************************
+ *
+ * remove_in_edge_if
+ *
+ ************************************************************************/
+ namespace parallel { namespace detail {
+ /**
+ * Function object that applies the underlying predicate to
+ * determine if an in-edge should be removed. If so, either
+ * removes the outgoing edge (if it is stored locally) or sends a
+ * message to the owner of the target requesting that it remove
+ * the edge. Only required for bidirectional graphs.
+ */
+ template<typename Graph, typename Predicate>
+ struct remove_in_edge_predicate
+ {
+ typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor;
+ typedef bool result_type;
+
+ remove_in_edge_predicate(Graph& g, const Predicate& predicate)
+ : g(g), predicate(predicate) { }
+
+ template<typename StoredEdge>
+ bool operator()(const StoredEdge& le)
+ {
+ typedef typename edge_descriptor::template in_generator<Graph>
+ generator;
+
+ edge_descriptor e = generator(g)(le);
+
+ if (predicate(e)) {
+ if (source(e, g).owner != target(e, g).owner)
+ g.send_remove_edge_request(e);
+ else
+ remove_edge(source(e, g).local, target(e, g).local, g.base());
+ return true;
+ } else return false;
+ }
+
+ private:
+ Graph& g;
+ Predicate predicate;
+ };
+ } } // end namespace parallel::detail
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS_CONFIG, typename Predicate>
+ inline void
+ remove_in_edge_if
+ (typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(bidirectionalS)
+ ::vertex_descriptor u,
+ Predicate predicate,
+ PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(bidirectionalS)& g)
+ {
+ typedef PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(bidirectionalS) Graph;
+ typedef parallel::detail::remove_in_edge_predicate<Graph, Predicate>
+ Pred;
+
+ assert(u.owner == g.processor());
+ graph_detail::erase_if(get(vertex_in_edges, g.base())[u.local],
+ Pred(g, predicate));
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS_CONFIG, typename Predicate>
+ inline void
+ remove_in_edge_if
+ (typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(undirectedS)
+ ::vertex_descriptor u,
+ Predicate predicate,
+ PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(undirectedS)& g)
+ {
+ remove_out_edge_if(u, predicate, g);
+ }
+
+ /************************************************************************
+ *
+ * remove_edge_if
+ *
+ ************************************************************************/
+ namespace parallel { namespace detail {
+ /**
+ * Function object that applies the underlying predicate to
+ * determine if a directed edge can be removed. This only applies
+ * to directed graphs.
+ */
+ template<typename Graph, typename Predicate>
+ struct remove_directed_edge_predicate
+ {
+ typedef typename Graph::local_edge_descriptor argument_type;
+ typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor;
+ typedef bool result_type;
+
+ remove_directed_edge_predicate(Graph& g, const Predicate& predicate)
+ : g(g), predicate(predicate) { }
+
+ bool operator()(const argument_type& le)
+ {
+ typedef typename edge_descriptor::template out_generator<Graph>
+ generator;
+
+ edge_descriptor e = generator(g)(le);
+ return predicate(e);
+ }
+
+ private:
+ Graph& g;
+ Predicate predicate;
+ };
+ } } // end namespace parallel::detail
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS_CONFIG, typename Predicate>
+ inline void
+ remove_edge_if(Predicate predicate,
+ PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(directedS)& g)
+ {
+ typedef PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(directedS) Graph;
+ typedef parallel::detail::remove_directed_edge_predicate<Graph,
+ Predicate> Pred;
+ remove_edge_if(Pred(g, predicate), g.base());
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS_CONFIG, typename Predicate>
+ inline void
+ remove_edge_if(Predicate predicate,
+ PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(bidirectionalS)& g)
+ {
+ typedef PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(bidirectionalS) Graph;
+ typedef parallel::detail::remove_out_edge_predicate<Graph,
+ Predicate> Pred;
+ remove_edge_if(Pred(g, predicate), g.base());
+ }
+
+ namespace parallel { namespace detail {
+ /**
+ * Function object that applies the underlying predicate to
+ * determine if an undirected edge should be removed. If so,
+ * removes the local edges associated with the edge and
+ * (potentially) sends a message to the remote processor that also
+ * is removing this edge.
+ */
+ template<typename Graph, typename Predicate>
+ struct remove_undirected_edge_predicate
+ {
+ typedef typename graph_traits<Graph>::edge_descriptor argument_type;
+ typedef bool result_type;
+
+ remove_undirected_edge_predicate(Graph& g, Predicate& predicate)
+ : g(g), predicate(predicate) { }
+
+ bool operator()(const argument_type& e)
+ {
+ if (predicate(e)) {
+ if (source(e, g).owner != target(e, g).owner)
+ g.send_remove_edge_request(e);
+ if (target(e, g).owner == g.processor())
+ ::boost::detail::parallel::remove_in_edge(e, g, undirectedS());
+ if (source(e, g).owner == g.processor())
+ remove_edge(e.local, g.base());
+ return true;
+ } else return false;
+ }
+
+ private:
+ Graph& g;
+ Predicate predicate;
+ };
+ } } // end namespace parallel::detail
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS_CONFIG, typename Predicate>
+ inline void
+ remove_edge_if(Predicate predicate,
+ PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(undirectedS)& g)
+ {
+ typedef PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(undirectedS) Graph;
+ typedef parallel::detail::remove_undirected_edge_predicate<Graph,
+ Predicate> Pred;
+ graph_detail::erase_if(g.local_edges(), Pred(g, predicate));
+ }
+
+ /************************************************************************
+ *
+ * clear_vertex
+ *
+ ************************************************************************/
+ namespace parallel { namespace detail {
+ struct always_true
+ {
+ typedef bool result_type;
+
+ template<typename T> bool operator()(const T&) const { return true; }
+ };
+ } } // end namespace parallel::detail
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS_CONFIG>
+ void
+ clear_vertex
+ (typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(bidirectionalS)
+ ::vertex_descriptor u,
+ PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(bidirectionalS)& g)
+ {
+ clear_out_edges(u, g);
+ clear_in_edges(u, g);
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS_CONFIG>
+ void
+ clear_vertex
+ (typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(undirectedS)
+ ::vertex_descriptor u,
+ PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(undirectedS)& g)
+ {
+ remove_out_edge_if(u, parallel::detail::always_true(), g);
+ }
+
+ /************************************************************************
+ *
+ * clear_out_edges
+ *
+ ************************************************************************/
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS_CONFIG>
+ void
+ clear_out_edges
+ (typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(directedS)::vertex_descriptor u,
+ PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(directedS)& g)
+ {
+ assert(u.owner == g.processor());
+ clear_out_edges(u.local, g.base());
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS_CONFIG>
+ void
+ clear_out_edges
+ (typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(bidirectionalS)
+ ::vertex_descriptor u,
+ PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(bidirectionalS)& g)
+ {
+ remove_out_edge_if(u, parallel::detail::always_true(), g);
+ }
+
+ /************************************************************************
+ *
+ * clear_in_edges
+ *
+ ************************************************************************/
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS_CONFIG>
+ void
+ clear_in_edges
+ (typename PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(bidirectionalS)
+ ::vertex_descriptor u,
+ PBGL_DISTRIB_ADJLIST_TYPE_CONFIG(bidirectionalS)& g)
+ {
+ remove_in_edge_if(u, parallel::detail::always_true(), g);
+ }
+
+ /************************************************************************
+ *
+ * add_vertex
+ *
+ ************************************************************************/
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ typename PBGL_DISTRIB_ADJLIST_TYPE::vertex_descriptor
+ add_vertex(PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef PBGL_DISTRIB_ADJLIST_TYPE graph_type;
+ typename graph_type::vertex_property_type p;
+ return add_vertex(p, g);
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ typename PBGL_DISTRIB_ADJLIST_TYPE::lazy_add_vertex_with_property
+ add_vertex(typename PBGL_DISTRIB_ADJLIST_TYPE::vertex_property_type const& p,
+ PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef typename PBGL_DISTRIB_ADJLIST_TYPE
+ ::lazy_add_vertex_with_property lazy_add_vertex;
+ return lazy_add_vertex(g, p);
+ }
+
+ /************************************************************************
+ *
+ * remove_vertex
+ *
+ ************************************************************************/
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ void
+ remove_vertex(typename PBGL_DISTRIB_ADJLIST_TYPE::vertex_descriptor u,
+ PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef typename PBGL_DISTRIB_ADJLIST_TYPE::graph_type graph_type;
+ typedef typename graph_type::named_graph_mixin named_graph_mixin;
+ assert(u.owner == g.processor());
+ static_cast<named_graph_mixin&>(static_cast<graph_type&>(g))
+ .removing_vertex(u);
+ g.distribution().clear();
+ remove_vertex(u.local, g.base());
+ }
+
+ /***************************************************************************
+ * Implementation of Property Graph concept
+ ***************************************************************************/
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS, typename Property>
+ struct property_map<PBGL_DISTRIB_ADJLIST_TYPE, Property>
+ : detail::parallel::get_adj_list_pmap<Property>
+ ::template apply<PBGL_DISTRIB_ADJLIST_TYPE>
+ { };
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS, typename Property>
+ struct property_map<PBGL_DISTRIB_ADJLIST_TYPE const, Property>
+ : boost::detail::parallel::get_adj_list_pmap<Property>
+// FIXME: in the original code the following was not const
+ ::template apply<PBGL_DISTRIB_ADJLIST_TYPE const>
+ { };
+
+ template<typename Property, PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ typename property_map<PBGL_DISTRIB_ADJLIST_TYPE, Property>::type
+ get(Property p, PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef PBGL_DISTRIB_ADJLIST_TYPE Graph;
+ typedef typename property_map<Graph, Property>::type result_type;
+ typedef typename property_traits<result_type>::value_type value_type;
+ typedef typename property_reduce<Property>::template apply<value_type>
+ reduce;
+
+ typedef typename property_traits<result_type>::key_type descriptor;
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+ typedef typename mpl::if_<is_same<descriptor, vertex_descriptor>,
+ vertex_global_t, edge_global_t>::type
+ global_map_t;
+
+ return result_type(g.process_group(), get(global_map_t(), g),
+ get(p, g.base()), reduce());
+ }
+
+ template<typename Property, PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ typename property_map<PBGL_DISTRIB_ADJLIST_TYPE, Property>::const_type
+ get(Property p, const PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef PBGL_DISTRIB_ADJLIST_TYPE Graph;
+ typedef typename property_map<Graph, Property>::const_type result_type;
+ typedef typename property_traits<result_type>::value_type value_type;
+ typedef typename property_reduce<Property>::template apply<value_type>
+ reduce;
+
+ typedef typename property_traits<result_type>::key_type descriptor;
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+ typedef typename mpl::if_<is_same<descriptor, vertex_descriptor>,
+ vertex_global_t, edge_global_t>::type
+ global_map_t;
+
+ return result_type(g.process_group(), get(global_map_t(), g),
+ get(p, g.base()), reduce());
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ typename property_map<PBGL_DISTRIB_ADJLIST_TYPE, vertex_local_index_t>::type
+ get(vertex_local_index_t, PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ return get(vertex_local_index, g.base());
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ typename property_map<PBGL_DISTRIB_ADJLIST_TYPE,
+ vertex_local_index_t>::const_type
+ get(vertex_local_index_t, const PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ return get(vertex_local_index, g.base());
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ typename property_map<PBGL_DISTRIB_ADJLIST_TYPE, vertex_global_t>::const_type
+ get(vertex_global_t, const PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef typename property_map<
+ PBGL_DISTRIB_ADJLIST_TYPE,
+ vertex_global_t>::const_type result_type;
+ return result_type();
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ typename property_map<PBGL_DISTRIB_ADJLIST_TYPE, vertex_global_t>::const_type
+ get(vertex_global_t, PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef typename property_map<
+ PBGL_DISTRIB_ADJLIST_TYPE,
+ vertex_global_t>::const_type result_type;
+ return result_type();
+ }
+
+ /// Retrieve a property map mapping from a vertex descriptor to its
+ /// owner.
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ typename property_map<PBGL_DISTRIB_ADJLIST_TYPE, vertex_owner_t>::type
+ get(vertex_owner_t, PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef typename property_map<
+ PBGL_DISTRIB_ADJLIST_TYPE,
+ vertex_owner_t>::type result_type;
+ return result_type();
+ }
+
+ /// Retrieve a property map mapping from a vertex descriptor to its
+ /// owner.
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ typename property_map<PBGL_DISTRIB_ADJLIST_TYPE, vertex_owner_t>::const_type
+ get(vertex_owner_t, const PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef typename property_map<
+ PBGL_DISTRIB_ADJLIST_TYPE,
+ vertex_owner_t>::const_type result_type;
+ return result_type();
+ }
+
+ /// Retrieve the owner of a vertex
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ inline processor_id_type
+ get(vertex_owner_t, PBGL_DISTRIB_ADJLIST_TYPE&,
+ typename PBGL_DISTRIB_ADJLIST_TYPE::vertex_descriptor v)
+ {
+ return v.owner;
+ }
+
+ /// Retrieve the owner of a vertex
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ inline processor_id_type
+ get(vertex_owner_t, const PBGL_DISTRIB_ADJLIST_TYPE&,
+ typename PBGL_DISTRIB_ADJLIST_TYPE::vertex_descriptor v)
+ {
+ return v.owner;
+ }
+
+ /// Retrieve a property map that maps from a vertex descriptor to
+ /// its local descriptor.
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ typename property_map<PBGL_DISTRIB_ADJLIST_TYPE, vertex_local_t>::type
+ get(vertex_local_t, PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef typename property_map<
+ PBGL_DISTRIB_ADJLIST_TYPE,
+ vertex_local_t>::type result_type;
+ return result_type();
+ }
+
+ /// Retrieve a property map that maps from a vertex descriptor to
+ /// its local descriptor.
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ typename property_map<PBGL_DISTRIB_ADJLIST_TYPE, vertex_local_t>::const_type
+ get(vertex_local_t, const PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef typename property_map<
+ PBGL_DISTRIB_ADJLIST_TYPE,
+ vertex_local_t>::const_type result_type;
+ return result_type();
+ }
+
+ /// Retrieve the local descriptor of a vertex
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ inline typename PBGL_DISTRIB_ADJLIST_TYPE::local_vertex_descriptor
+ get(vertex_local_t, PBGL_DISTRIB_ADJLIST_TYPE&,
+ typename PBGL_DISTRIB_ADJLIST_TYPE::vertex_descriptor v)
+ {
+ return v.local;
+ }
+
+ /// Retrieve the local descriptor of a vertex
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ inline typename PBGL_DISTRIB_ADJLIST_TYPE::local_vertex_descriptor
+ get(vertex_local_t, const PBGL_DISTRIB_ADJLIST_TYPE&,
+ typename PBGL_DISTRIB_ADJLIST_TYPE::vertex_descriptor v)
+ {
+ return v.local;
+ }
+
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ typename property_map<PBGL_DISTRIB_ADJLIST_TYPE, edge_global_t>::const_type
+ get(edge_global_t, const PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef typename property_map<
+ PBGL_DISTRIB_ADJLIST_TYPE,
+ edge_global_t>::const_type result_type;
+ return result_type();
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ typename property_map<PBGL_DISTRIB_ADJLIST_TYPE, edge_global_t>::const_type
+ get(edge_global_t, PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef typename property_map<
+ PBGL_DISTRIB_ADJLIST_TYPE,
+ edge_global_t>::const_type result_type;
+ return result_type();
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ typename property_map<PBGL_DISTRIB_ADJLIST_TYPE, edge_owner_t>::type
+ get(edge_owner_t, PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef typename property_map<
+ PBGL_DISTRIB_ADJLIST_TYPE,
+ edge_owner_t>::type result_type;
+ return result_type();
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ typename property_map<PBGL_DISTRIB_ADJLIST_TYPE, edge_owner_t>::const_type
+ get(edge_owner_t, const PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef typename property_map<
+ PBGL_DISTRIB_ADJLIST_TYPE,
+ edge_owner_t>::const_type result_type;
+ return result_type();
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ typename property_map<PBGL_DISTRIB_ADJLIST_TYPE, edge_local_t>::type
+ get(edge_local_t, PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef typename property_map<
+ PBGL_DISTRIB_ADJLIST_TYPE,
+ edge_local_t>::type result_type;
+ return result_type();
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ typename property_map<PBGL_DISTRIB_ADJLIST_TYPE, edge_local_t>::const_type
+ get(edge_local_t, const PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef typename property_map<
+ PBGL_DISTRIB_ADJLIST_TYPE,
+ edge_local_t>::const_type result_type;
+ return result_type();
+ }
+
+ template<typename Property, PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS,
+ typename Key>
+ inline
+ typename property_traits<typename property_map<
+ PBGL_DISTRIB_ADJLIST_TYPE, Property>::const_type
+ >::value_type
+ get(Property p, const PBGL_DISTRIB_ADJLIST_TYPE& g, const Key& key)
+ {
+ if (owner(key) == process_id(g.process_group()))
+ return get(p, g.base(), local(key));
+ else
+ assert(false);
+ }
+
+ template<typename Property, PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS,
+ typename Key, typename Value>
+ void
+ put(Property p, PBGL_DISTRIB_ADJLIST_TYPE& g, const Key& key, const Value& v)
+ {
+ if (owner(key) == process_id(g.process_group()))
+ put(p, g.base(), local(key), v);
+ else
+ assert(false);
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ typename property_map<PBGL_DISTRIB_ADJLIST_TYPE, vertex_index_t>::type
+ get(vertex_index_t vi, PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef PBGL_DISTRIB_ADJLIST_TYPE graph_type;
+ typedef typename property_map<graph_type, vertex_index_t>::type
+ result_type;
+ return result_type(g.process_group(), get(vertex_global, g),
+ get(vi, g.base()));
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ typename property_map<PBGL_DISTRIB_ADJLIST_TYPE, vertex_index_t>::const_type
+ get(vertex_index_t vi, const PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef PBGL_DISTRIB_ADJLIST_TYPE graph_type;
+ typedef typename property_map<graph_type, vertex_index_t>::const_type
+ result_type;
+ return result_type(g.process_group(), get(vertex_global, g),
+ get(vi, g.base()));
+ }
+
+ /***************************************************************************
+ * Implementation of bundled properties
+ ***************************************************************************/
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS, typename T, typename Bundle>
+ struct property_map<PBGL_DISTRIB_ADJLIST_TYPE, T Bundle::*>
+ : detail::parallel::get_adj_list_pmap<T Bundle::*>
+ ::template apply<PBGL_DISTRIB_ADJLIST_TYPE>
+ { };
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS, typename T, typename Bundle>
+ struct property_map<PBGL_DISTRIB_ADJLIST_TYPE const, T Bundle::*>
+ : detail::parallel::get_adj_list_pmap<T Bundle::*>
+ ::template apply<PBGL_DISTRIB_ADJLIST_TYPE const>
+ { };
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS, typename T, typename Bundle>
+ typename property_map<PBGL_DISTRIB_ADJLIST_TYPE, T Bundle::*>::type
+ get(T Bundle::* p, PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef PBGL_DISTRIB_ADJLIST_TYPE Graph;
+ typedef typename property_map<Graph, T Bundle::*>::type result_type;
+ typedef typename property_traits<result_type>::value_type value_type;
+ typedef typename property_reduce<T Bundle::*>::template apply<value_type>
+ reduce;
+
+ typedef typename property_traits<result_type>::key_type descriptor;
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+ typedef typename mpl::if_<is_same<descriptor, vertex_descriptor>,
+ vertex_global_t, edge_global_t>::type
+ global_map_t;
+
+ return result_type(g.process_group(), get(global_map_t(), g),
+ get(p, g.base()), reduce());
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS, typename T, typename Bundle>
+ typename property_map<PBGL_DISTRIB_ADJLIST_TYPE, T Bundle::*>::const_type
+ get(T Bundle::* p, const PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef PBGL_DISTRIB_ADJLIST_TYPE Graph;
+ typedef typename property_map<Graph, T Bundle::*>::const_type result_type;
+ typedef typename property_traits<result_type>::value_type value_type;
+ typedef typename property_reduce<T Bundle::*>::template apply<value_type>
+ reduce;
+
+ typedef typename property_traits<result_type>::key_type descriptor;
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+ typedef typename mpl::if_<is_same<descriptor, vertex_descriptor>,
+ vertex_global_t, edge_global_t>::type
+ global_map_t;
+
+ return result_type(g.process_group(), get(global_map_t(), g),
+ get(p, g.base()), reduce());
+ }
+
+ /***************************************************************************
+ * Implementation of DistributedGraph concept
+ ***************************************************************************/
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ void synchronize(const PBGL_DISTRIB_ADJLIST_TYPE& g)
+ {
+ typedef PBGL_DISTRIB_ADJLIST_TYPE graph_type;
+ synchronize(g.process_group());
+ }
+
+ template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+ ProcessGroup
+ process_group(const PBGL_DISTRIB_ADJLIST_TYPE& g)
+ { return g.process_group(); }
+
+ /***************************************************************************
+ * Specializations of is_mpi_datatype for Serializable entities
+ ***************************************************************************/
+ namespace mpi {
+ template<typename Directed, typename Vertex>
+ struct is_mpi_datatype<boost::detail::edge_base<Directed, Vertex> >
+ : is_mpi_datatype<Vertex> { };
+
+ template<typename Directed, typename Vertex>
+ struct is_mpi_datatype<boost::detail::edge_desc_impl<Directed, Vertex> >
+ : is_mpi_datatype<boost::detail::edge_base<Directed, Vertex> > { };
+
+ template<typename LocalDescriptor>
+ struct is_mpi_datatype<boost::detail::parallel::global_descriptor<LocalDescriptor> >
+ : is_mpi_datatype<LocalDescriptor> { };
+
+ template<typename Edge>
+ struct is_mpi_datatype<boost::detail::parallel::edge_descriptor<Edge> >
+ : is_mpi_datatype<Edge> { };
+
+ template<typename Vertex, typename LocalVertex>
+ struct is_mpi_datatype<boost::detail::parallel::
+ msg_add_edge_data<Vertex, LocalVertex> >
+ : is_mpi_datatype<Vertex> { };
+
+ template<typename Vertex, typename LocalVertex, typename EdgeProperty>
+ struct is_mpi_datatype<boost::detail::parallel::
+ msg_add_edge_with_property_data<Vertex,
+ LocalVertex,
+ EdgeProperty> >
+ : mpl::and_<is_mpi_datatype<Vertex>, is_mpi_datatype<EdgeProperty> > { };
+
+
+ template<typename EdgeProperty, typename EdgeDescriptor>
+ struct is_mpi_datatype<boost::detail::parallel::msg_nonlocal_edge_data<
+ EdgeProperty,EdgeDescriptor> >
+ : mpl::and_<
+ is_mpi_datatype<boost::detail::parallel::maybe_store_property<
+ EdgeProperty> >,
+ is_mpi_datatype<EdgeDescriptor> >
+ {};
+
+ template<typename EdgeDescriptor>
+ struct is_mpi_datatype<
+ boost::detail::parallel::msg_remove_edge_data<EdgeDescriptor> >
+ : is_mpi_datatype<EdgeDescriptor> {};
+ }
+
+ /***************************************************************************
+ * Specializations of is_bitwise_serializable for Serializable entities
+ ***************************************************************************/
+ namespace serialization {
+ template<typename Directed, typename Vertex>
+ struct is_bitwise_serializable<boost::detail::edge_base<Directed, Vertex> >
+ : is_bitwise_serializable<Vertex> { };
+
+ template<typename Directed, typename Vertex>
+ struct is_bitwise_serializable<boost::detail::edge_desc_impl<Directed, Vertex> >
+ : is_bitwise_serializable<boost::detail::edge_base<Directed, Vertex> > { };
+
+ template<typename LocalDescriptor>
+ struct is_bitwise_serializable<boost::detail::parallel::global_descriptor<LocalDescriptor> >
+ : is_bitwise_serializable<LocalDescriptor> { };
+
+ template<typename Edge>
+ struct is_bitwise_serializable<boost::detail::parallel::edge_descriptor<Edge> >
+ : is_bitwise_serializable<Edge> { };
+
+ template<typename Vertex, typename LocalVertex>
+ struct is_bitwise_serializable<boost::detail::parallel::
+ msg_add_edge_data<Vertex, LocalVertex> >
+ : is_bitwise_serializable<Vertex> { };
+
+ template<typename Vertex, typename LocalVertex, typename EdgeProperty>
+ struct is_bitwise_serializable<boost::detail::parallel::
+ msg_add_edge_with_property_data<Vertex,
+ LocalVertex,
+ EdgeProperty> >
+ : mpl::and_<is_bitwise_serializable<Vertex>,
+ is_bitwise_serializable<EdgeProperty> > { };
+
+ template<typename EdgeProperty, typename EdgeDescriptor>
+ struct is_bitwise_serializable<boost::detail::parallel::msg_nonlocal_edge_data<
+ EdgeProperty,EdgeDescriptor> >
+ : mpl::and_<
+ is_bitwise_serializable<
+ boost::detail::parallel::maybe_store_property<EdgeProperty> >,
+ is_bitwise_serializable<EdgeDescriptor> >
+ {};
+
+ template<typename EdgeDescriptor>
+ struct is_bitwise_serializable<
+ boost::detail::parallel::msg_remove_edge_data<EdgeDescriptor> >
+ : is_bitwise_serializable<EdgeDescriptor> {};
+
+ template<typename Directed, typename Vertex>
+ struct implementation_level<boost::detail::edge_base<Directed, Vertex> >
+ : mpl::int_<object_serializable> {};
+
+ template<typename Directed, typename Vertex>
+ struct implementation_level<boost::detail::edge_desc_impl<Directed, Vertex> >
+ : mpl::int_<object_serializable> {};
+
+ template<typename LocalDescriptor>
+ struct implementation_level<boost::detail::parallel::global_descriptor<LocalDescriptor> >
+ : mpl::int_<object_serializable> {};
+
+ template<typename Edge>
+ struct implementation_level<boost::detail::parallel::edge_descriptor<Edge> >
+ : mpl::int_<object_serializable> {};
+
+ template<typename Vertex, typename LocalVertex>
+ struct implementation_level<boost::detail::parallel::
+ msg_add_edge_data<Vertex, LocalVertex> >
+ : mpl::int_<object_serializable> {};
+
+ template<typename Vertex, typename LocalVertex, typename EdgeProperty>
+ struct implementation_level<boost::detail::parallel::
+ msg_add_edge_with_property_data<Vertex,
+ LocalVertex,
+ EdgeProperty> >
+ : mpl::int_<object_serializable> {};
+
+ template<typename EdgeProperty, typename EdgeDescriptor>
+ struct implementation_level<boost::detail::parallel::msg_nonlocal_edge_data<
+ EdgeProperty,EdgeDescriptor> >
+ : mpl::int_<object_serializable> {};
+
+ template<typename EdgeDescriptor>
+ struct implementation_level<
+ boost::detail::parallel::msg_remove_edge_data<EdgeDescriptor> >
+ : mpl::int_<object_serializable> {};
+
+ template<typename Directed, typename Vertex>
+ struct tracking_level<boost::detail::edge_base<Directed, Vertex> >
+ : mpl::int_<track_never> {};
+
+ template<typename Directed, typename Vertex>
+ struct tracking_level<boost::detail::edge_desc_impl<Directed, Vertex> >
+ : mpl::int_<track_never> {};
+
+ template<typename LocalDescriptor>
+ struct tracking_level<boost::detail::parallel::global_descriptor<LocalDescriptor> >
+ : mpl::int_<track_never> {};
+
+ template<typename Edge>
+ struct tracking_level<boost::detail::parallel::edge_descriptor<Edge> >
+ : mpl::int_<track_never> {};
+
+ template<typename Vertex, typename LocalVertex>
+ struct tracking_level<boost::detail::parallel::
+ msg_add_edge_data<Vertex, LocalVertex> >
+ : mpl::int_<track_never> {};
+
+ template<typename Vertex, typename LocalVertex, typename EdgeProperty>
+ struct tracking_level<boost::detail::parallel::
+ msg_add_edge_with_property_data<Vertex,
+ LocalVertex,
+ EdgeProperty> >
+ : mpl::int_<track_never> {};
+
+ template<typename EdgeProperty, typename EdgeDescriptor>
+ struct tracking_level<boost::detail::parallel::msg_nonlocal_edge_data<
+ EdgeProperty,EdgeDescriptor> >
+ : mpl::int_<track_never> {};
+
+ template<typename EdgeDescriptor>
+ struct tracking_level<
+ boost::detail::parallel::msg_remove_edge_data<EdgeDescriptor> >
+ : mpl::int_<track_never> {};
+ }
+
+ // Hash function for global descriptors
+ template<typename LocalDescriptor>
+ struct hash<detail::parallel::global_descriptor<LocalDescriptor> >
+ {
+ typedef detail::parallel::global_descriptor<LocalDescriptor> argument_type;
+ std::size_t operator()(argument_type const& x) const
+ {
+ std::size_t hash = hash_value(x.owner);
+ hash_combine(hash, x.local);
+ return hash;
+ }
+ };
+
+ // Hash function for parallel edge descriptors
+ template<typename Edge>
+ struct hash<detail::parallel::edge_descriptor<Edge> >
+ {
+ typedef detail::parallel::edge_descriptor<Edge> argument_type;
+
+ std::size_t operator()(argument_type const& x) const
+ {
+ std::size_t hash = hash_value(x.owner());
+ hash_combine(hash, x.local);
+ return hash;
+ }
+ };
+
+} // end namespace boost
+
+#include <boost/graph/distributed/adjlist/handlers.hpp>
+#include <boost/graph/distributed/adjlist/initialize.hpp>
+#include <boost/graph/distributed/adjlist/redistribute.hpp>
+#include <boost/graph/distributed/adjlist/serialization.hpp>
+
+#endif // BOOST_GRAPH_DISTRIBUTED_ADJACENCY_LIST_HPP

Added: trunk/boost/graph/distributed/adjlist/handlers.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/adjlist/handlers.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,148 @@
+// Copyright (C) 2007 Douglas Gregor
+
+// 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)
+
+// This file contains code for the distributed adjacency list's
+// message handlers. It should not be included directly by users.
+
+#ifndef BOOST_GRAPH_DISTRIBUTED_ADJLIST_HANDLERS_HPP
+#define BOOST_GRAPH_DISTRIBUTED_ADJLIST_HANDLERS_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/graph/parallel/simple_trigger.hpp>
+#include <boost/graph/parallel/detail/untracked_pair.hpp>
+
+namespace boost {
+
+template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+void
+PBGL_DISTRIB_ADJLIST_TYPE::
+setup_triggers()
+{
+ using boost::graph::parallel::simple_trigger;
+
+ simple_trigger(process_group_, msg_add_vertex_with_property, this,
+ &adjacency_list::handle_add_vertex_with_property);
+ simple_trigger(process_group_, msg_add_vertex_with_property_and_reply, this,
+ &adjacency_list::handle_add_vertex_with_property_and_reply);
+ simple_trigger(process_group_, msg_add_edge, this,
+ &adjacency_list::handle_add_edge);
+ simple_trigger(process_group_, msg_add_edge_with_reply, this,
+ &adjacency_list::handle_add_edge_with_reply);
+ simple_trigger(process_group_, msg_add_edge_with_property, this,
+ &adjacency_list::handle_add_edge_with_property);
+ simple_trigger(process_group_, msg_add_edge_with_property_and_reply, this,
+ &adjacency_list::handle_add_edge_with_property_and_reply);
+ simple_trigger(process_group_, msg_nonlocal_edge, this,
+ &adjacency_list::handle_nonlocal_edge);
+ simple_trigger(process_group_, msg_remove_edge, this,
+ &adjacency_list::handle_remove_edge);
+}
+
+template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+void
+PBGL_DISTRIB_ADJLIST_TYPE::
+handle_add_vertex_with_property(int source, int tag,
+ const vertex_property_type& data,
+ trigger_receive_context)
+{
+ vertex_descriptor v(this->processor(),
+ add_vertex(this->build_vertex_property(data),
+ this->base()));
+ if (on_add_vertex)
+ on_add_vertex(v, *this);
+}
+
+template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+typename PBGL_DISTRIB_ADJLIST_TYPE::local_vertex_descriptor
+PBGL_DISTRIB_ADJLIST_TYPE::
+handle_add_vertex_with_property_and_reply(int source, int tag,
+ const vertex_property_type& data,
+ trigger_receive_context)
+{
+ // Try to find a vertex with this name
+ local_vertex_descriptor local_v
+ = add_vertex(this->build_vertex_property(data), this->base());
+
+ vertex_descriptor v(processor(), local_v);
+ if (on_add_vertex)
+ on_add_vertex(v, *this);
+
+ return local_v;
+}
+
+template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+void
+PBGL_DISTRIB_ADJLIST_TYPE::
+handle_add_edge(int source, int tag, const msg_add_edge_data& data,
+ trigger_receive_context)
+{
+ add_edge(vertex_descriptor(processor(), data.source),
+ data.target, *this);
+}
+
+template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+boost::parallel::detail::untracked_pair<typename PBGL_DISTRIB_ADJLIST_TYPE::edge_descriptor, bool>
+PBGL_DISTRIB_ADJLIST_TYPE::
+handle_add_edge_with_reply(int source, int tag, const msg_add_edge_data& data,
+ trigger_receive_context)
+{
+ std::pair<typename PBGL_DISTRIB_ADJLIST_TYPE::edge_descriptor, bool> p =
+ add_edge(vertex_descriptor(processor(), data.source),data.target, *this);
+ return p;
+}
+
+template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+void
+PBGL_DISTRIB_ADJLIST_TYPE::
+handle_add_edge_with_property(int source, int tag,
+ const msg_add_edge_with_property_data& data,
+ trigger_receive_context)
+{
+ add_edge(vertex_descriptor(processor(), data.source),
+ data.target, data.get_property(), *this);
+}
+
+template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+boost::parallel::detail::untracked_pair<typename PBGL_DISTRIB_ADJLIST_TYPE::edge_descriptor, bool>
+PBGL_DISTRIB_ADJLIST_TYPE::
+handle_add_edge_with_property_and_reply
+ (int source, int tag,
+ const msg_add_edge_with_property_data& data,
+ trigger_receive_context)
+{
+ std::pair<typename PBGL_DISTRIB_ADJLIST_TYPE::edge_descriptor, bool> p =
+ add_edge(vertex_descriptor(processor(), data.source),
+ data.target, data.get_property(), *this);
+ return p;
+}
+
+template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+void
+PBGL_DISTRIB_ADJLIST_TYPE::
+handle_nonlocal_edge(int source, int tag,
+ const msg_nonlocal_edge_data& data,
+ trigger_receive_context)
+{
+ add_remote_edge(data, source, directed_selector());
+}
+
+template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+void
+PBGL_DISTRIB_ADJLIST_TYPE::
+handle_remove_edge(int source, int tag,
+ const msg_remove_edge_data& data,
+ trigger_receive_context)
+{
+ remove_local_edge(data, source, directed_selector());
+}
+
+}
+
+#endif // BOOST_GRAPH_DISTRIBUTED_ADJLIST_HANDLERS_HPP
+

Added: trunk/boost/graph/distributed/adjlist/initialize.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/adjlist/initialize.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,319 @@
+// Copyright (C) 2007 Douglas Gregor
+
+// 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)
+
+// This file contains code for the distributed adjacency list's
+// initializations. It should not be included directly by users.
+
+#ifndef BOOST_GRAPH_DISTRIBUTED_ADJLIST_INITIALIZE_HPP
+#define BOOST_GRAPH_DISTRIBUTED_ADJLIST_INITIALIZE_HPP
+
+#ifndef BOOST_GRAPH_USE_MPI
+#error "Parallel BGL files should not be included unless <boost/graph/use_mpi.hpp> has been included"
+#endif
+
+namespace boost {
+
+template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+template<typename EdgeIterator>
+void
+PBGL_DISTRIB_ADJLIST_TYPE::
+initialize(EdgeIterator first, EdgeIterator last,
+ vertices_size_type, const base_distribution_type& distribution,
+ vecS)
+{
+ process_id_type id = process_id(process_group_);
+ while (first != last) {
+ if ((process_id_type)distribution(first->first) == id) {
+ vertex_descriptor source(id, distribution.local(first->first));
+ vertex_descriptor target(distribution(first->second),
+ distribution.local(first->second));
+ add_edge(source, target, *this);
+ }
+ ++first;
+ }
+
+ synchronize(process_group_);
+}
+
+template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+template<typename EdgeIterator, typename EdgePropertyIterator>
+void
+PBGL_DISTRIB_ADJLIST_TYPE::
+initialize(EdgeIterator first, EdgeIterator last,
+ EdgePropertyIterator ep_iter,
+ vertices_size_type, const base_distribution_type& distribution,
+ vecS)
+{
+ process_id_type id = process_id(process_group_);
+ while (first != last) {
+ if (static_cast<process_id_type>(distribution(first->first)) == id) {
+ vertex_descriptor source(id, distribution.local(first->first));
+ vertex_descriptor target(distribution(first->second),
+ distribution.local(first->second));
+ add_edge(source, target, *ep_iter, *this);
+ }
+ ++first;
+ ++ep_iter;
+ }
+
+ synchronize(process_group_);
+}
+
+template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+template<typename EdgeIterator, typename EdgePropertyIterator,
+ typename VertexListS>
+void
+PBGL_DISTRIB_ADJLIST_TYPE::
+initialize(EdgeIterator first, EdgeIterator last,
+ EdgePropertyIterator ep_iter,
+ vertices_size_type n, const base_distribution_type& distribution,
+ VertexListS)
+{
+ using boost::parallel::inplace_all_to_all;
+
+ typedef vertices_size_type vertex_number_t;
+ typedef typename std::iterator_traits<EdgePropertyIterator>::value_type
+ edge_property_init_t;
+
+ typedef std::pair<vertex_descriptor, vertex_number_t>
+ st_pair;
+ typedef std::pair<st_pair, edge_property_init_t> delayed_edge_t;
+
+ process_group_type pg = process_group();
+ process_id_type id = process_id(pg);
+
+ // Vertex indices
+ std::vector<local_vertex_descriptor> index_to_vertex;
+ index_to_vertex.reserve(num_vertices(*this));
+ BGL_FORALL_VERTICES_T(v, base(), inherited)
+ index_to_vertex.push_back(v);
+
+ // The list of edges we can't add immediately.
+ std::vector<delayed_edge_t> delayed_edges;
+
+ std::vector<std::vector<vertex_number_t> > descriptor_requests;
+ descriptor_requests.resize(num_processes(pg));
+
+ // Add all of the edges we can, up to the point where we run
+ // into a descriptor we don't know.
+ while (first != last) {
+ if (distribution(first->first) == id) {
+ if (distribution(first->second) != id) break;
+ vertex_descriptor source
+ (id, index_to_vertex[distribution.local(first->first)]);
+ vertex_descriptor target
+ (distribution(first->second),
+ index_to_vertex[distribution.local(first->second)]);
+ add_edge(source, target, *ep_iter, *this);
+ }
+ ++first;
+ ++ep_iter;
+ }
+
+ // Queue all of the remaining edges and determine the set of
+ // descriptors we need to know about.
+ while (first != last) {
+ if (distribution(first->first) == id) {
+ vertex_descriptor source
+ (id, index_to_vertex[distribution.local(first->first)]);
+ process_id_type dest = distribution(first->second);
+ if (dest != id) {
+ descriptor_requests[dest]
+ .push_back(distribution.local(first->second));
+ // Compact request list if we need to
+ if (descriptor_requests[dest].size() >
+ distribution.block_size(dest, n)) {
+ std::sort(descriptor_requests[dest].begin(),
+ descriptor_requests[dest].end());
+ descriptor_requests[dest].erase(
+ std::unique(descriptor_requests[dest].begin(),
+ descriptor_requests[dest].end()),
+ descriptor_requests[dest].end());
+ }
+ }
+
+ // Save the edge for later
+ delayed_edges.push_back
+ (delayed_edge_t(st_pair(source, first->second), *ep_iter));
+ }
+ ++first;
+ ++ep_iter;
+ }
+
+ // Compact descriptor requests
+ for (process_id_type dest = 0; dest < num_processes(pg); ++dest) {
+ std::sort(descriptor_requests[dest].begin(),
+ descriptor_requests[dest].end());
+ descriptor_requests[dest].erase(
+ std::unique(descriptor_requests[dest].begin(),
+ descriptor_requests[dest].end()),
+ descriptor_requests[dest].end());
+ }
+
+ // Send out all of the descriptor requests
+ std::vector<std::vector<vertex_number_t> > in_descriptor_requests;
+ in_descriptor_requests.resize(num_processes(pg));
+ inplace_all_to_all(pg, descriptor_requests, in_descriptor_requests);
+
+ // Reply to all of the descriptor requests
+ std::vector<std::vector<local_vertex_descriptor> >
+ descriptor_responses;
+ descriptor_responses.resize(num_processes(pg));
+ for (process_id_type dest = 0; dest < num_processes(pg); ++dest) {
+ for (std::size_t i = 0; i < in_descriptor_requests[dest].size(); ++i) {
+ local_vertex_descriptor v =
+ index_to_vertex[in_descriptor_requests[dest][i]];
+ descriptor_responses[dest].push_back(v);
+ }
+ in_descriptor_requests[dest].clear();
+ }
+ in_descriptor_requests.clear();
+ inplace_all_to_all(pg, descriptor_responses);
+
+ // Add the queued edges
+ for(typename std::vector<delayed_edge_t>::iterator i
+ = delayed_edges.begin(); i != delayed_edges.end(); ++i) {
+ process_id_type dest = distribution(i->first.second);
+ local_vertex_descriptor tgt_local;
+ if (dest == id) {
+ tgt_local = index_to_vertex[distribution.local(i->first.second)];
+ } else {
+ std::vector<vertex_number_t>& requests = descriptor_requests[dest];
+ typename std::vector<vertex_number_t>::iterator pos =
+ std::lower_bound(requests.begin(), requests.end(),
+ distribution.local(i->first.second));
+ tgt_local = descriptor_responses[dest][pos - requests.begin()];
+ }
+ add_edge(i->first.first, vertex_descriptor(dest, tgt_local),
+ i->second, *this);
+ }
+ synchronize(process_group_);
+}
+
+template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+template<typename EdgeIterator, typename VertexListS>
+void
+PBGL_DISTRIB_ADJLIST_TYPE::
+initialize(EdgeIterator first, EdgeIterator last,
+ vertices_size_type n, const base_distribution_type& distribution,
+ VertexListS)
+{
+ using boost::parallel::inplace_all_to_all;
+
+ typedef vertices_size_type vertex_number_t;
+
+ typedef std::pair<vertex_descriptor, vertex_number_t> delayed_edge_t;
+
+ process_group_type pg = process_group();
+ process_id_type id = process_id(pg);
+
+ // Vertex indices
+ std::vector<local_vertex_descriptor> index_to_vertex;
+ index_to_vertex.reserve(num_vertices(*this));
+ BGL_FORALL_VERTICES_T(v, base(), inherited)
+ index_to_vertex.push_back(v);
+
+ // The list of edges we can't add immediately.
+ std::vector<delayed_edge_t> delayed_edges;
+
+ std::vector<std::vector<vertex_number_t> > descriptor_requests;
+ descriptor_requests.resize(num_processes(pg));
+
+ // Add all of the edges we can, up to the point where we run
+ // into a descriptor we don't know.
+ while (first != last) {
+ if (distribution(first->first) == id) {
+ if (distribution(first->second) != id) break;
+ vertex_descriptor source
+ (id, index_to_vertex[distribution.local(first->first)]);
+ vertex_descriptor target
+ (distribution(first->second),
+ index_to_vertex[distribution.local(first->second)]);
+ add_edge(source, target, *this);
+ }
+ ++first;
+ }
+
+ // Queue all of the remaining edges and determine the set of
+ // descriptors we need to know about.
+ while (first != last) {
+ if (distribution(first->first) == id) {
+ vertex_descriptor source
+ (id, index_to_vertex[distribution.local(first->first)]);
+ process_id_type dest = distribution(first->second);
+ if (dest != id) {
+ descriptor_requests[dest]
+ .push_back(distribution.local(first->second));
+ // Compact request list if we need to
+ if (descriptor_requests[dest].size() >
+ distribution.block_size(dest, n)) {
+ std::sort(descriptor_requests[dest].begin(),
+ descriptor_requests[dest].end());
+ descriptor_requests[dest].erase(
+ std::unique(descriptor_requests[dest].begin(),
+ descriptor_requests[dest].end()),
+ descriptor_requests[dest].end());
+ }
+ }
+
+ // Save the edge for later
+ delayed_edges.push_back(delayed_edge_t(source, first->second));
+ }
+ ++first;
+ }
+
+ // Compact descriptor requests
+ for (process_id_type dest = 0; dest < num_processes(pg); ++dest) {
+ std::sort(descriptor_requests[dest].begin(),
+ descriptor_requests[dest].end());
+ descriptor_requests[dest].erase(
+ std::unique(descriptor_requests[dest].begin(),
+ descriptor_requests[dest].end()),
+ descriptor_requests[dest].end());
+ }
+
+ // Send out all of the descriptor requests
+ std::vector<std::vector<vertex_number_t> > in_descriptor_requests;
+ in_descriptor_requests.resize(num_processes(pg));
+ inplace_all_to_all(pg, descriptor_requests, in_descriptor_requests);
+
+ // Reply to all of the descriptor requests
+ std::vector<std::vector<local_vertex_descriptor> >
+ descriptor_responses;
+ descriptor_responses.resize(num_processes(pg));
+ for (process_id_type dest = 0; dest < num_processes(pg); ++dest) {
+ for (std::size_t i = 0; i < in_descriptor_requests[dest].size(); ++i) {
+ local_vertex_descriptor v =
+ index_to_vertex[in_descriptor_requests[dest][i]];
+ descriptor_responses[dest].push_back(v);
+ }
+ in_descriptor_requests[dest].clear();
+ }
+ in_descriptor_requests.clear();
+ inplace_all_to_all(pg, descriptor_responses);
+
+ // Add the queued edges
+ for(typename std::vector<delayed_edge_t>::iterator i
+ = delayed_edges.begin(); i != delayed_edges.end(); ++i) {
+ process_id_type dest = distribution(i->second);
+ local_vertex_descriptor tgt_local;
+ if (dest == id) {
+ tgt_local = index_to_vertex[distribution.local(i->second)];
+ } else {
+ std::vector<vertex_number_t>& requests = descriptor_requests[dest];
+ typename std::vector<vertex_number_t>::iterator pos =
+ std::lower_bound(requests.begin(), requests.end(),
+ distribution.local(i->second));
+ tgt_local = descriptor_responses[dest][pos - requests.begin()];
+ }
+ add_edge(i->first, vertex_descriptor(dest, tgt_local), *this);
+ }
+ synchronize(process_group_);
+}
+
+} // end namespace boost
+
+#endif // BOOST_GRAPH_DISTRIBUTED_ADJLIST_INITIALIZE_HPP

Added: trunk/boost/graph/distributed/adjlist/redistribute.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/adjlist/redistribute.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,393 @@
+// Copyright (C) 2005-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
+
+//
+// Implements redistribution of vertices for a distributed adjacency
+// list. This file should not be included by users. It will be
+// included by the distributed adjacency list header.
+//
+
+#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/pending/container_traits.hpp>
+
+namespace boost { namespace detail { namespace parallel {
+
+/* This structure contains a (vertex or edge) descriptor that is being
+ moved from one processor to another. It contains the properties for
+ that descriptor (if any).
+ */
+template<typename Descriptor, typename DescriptorProperty>
+struct redistributed_descriptor : maybe_store_property<DescriptorProperty>
+{
+ typedef maybe_store_property<DescriptorProperty> inherited;
+
+ redistributed_descriptor() { }
+
+ redistributed_descriptor(const Descriptor& v, const DescriptorProperty& p)
+ : inherited(p), descriptor(v) { }
+
+ Descriptor descriptor;
+
+private:
+ friend class boost::serialization::access;
+
+ template<typename Archiver>
+ void serialize(Archiver& ar, unsigned int /*version*/)
+ {
+ ar & boost::serialization::base_object<inherited>(*this)
+ & unsafe_serialize(descriptor);
+ }
+};
+
+/* Predicate that returns true if the target has migrated. */
+template<typename VertexProcessorMap, typename Graph>
+struct target_migrated_t
+{
+ typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
+ typedef typename graph_traits<Graph>::edge_descriptor Edge;
+
+ target_migrated_t(VertexProcessorMap vertex_to_processor, const Graph& g)
+ : vertex_to_processor(vertex_to_processor), g(g) { }
+
+ bool operator()(Edge e) const
+ {
+ typedef global_descriptor<Vertex> DVertex;
+ processor_id_type owner = get(edge_target_processor_id, g, e);
+ return get(vertex_to_processor, DVertex(owner, target(e, g))) != owner;
+ }
+
+private:
+ VertexProcessorMap vertex_to_processor;
+ const Graph& g;
+};
+
+template<typename VertexProcessorMap, typename Graph>
+inline target_migrated_t<VertexProcessorMap, Graph>
+target_migrated(VertexProcessorMap vertex_to_processor, const Graph& g)
+{ return target_migrated_t<VertexProcessorMap, Graph>(vertex_to_processor, g); }
+
+/* Predicate that returns true if the source of an in-edge has migrated. */
+template<typename VertexProcessorMap, typename Graph>
+struct source_migrated_t
+{
+ typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
+ typedef typename graph_traits<Graph>::edge_descriptor Edge;
+
+ source_migrated_t(VertexProcessorMap vertex_to_processor, const Graph& g)
+ : vertex_to_processor(vertex_to_processor), g(g) { }
+
+ bool operator()(stored_in_edge<Edge> e) const
+ {
+ return get(vertex_to_processor, DVertex(e.source_processor, source(e.e, g)))
+ != e.source_processor;
+ }
+
+private:
+ VertexProcessorMap vertex_to_processor;
+ const Graph& g;
+};
+
+template<typename VertexProcessorMap, typename Graph>
+inline source_migrated_t<VertexProcessorMap, Graph>
+source_migrated(VertexProcessorMap vertex_to_processor, const Graph& g)
+{ return source_migrated_t<VertexProcessorMap, Graph>(vertex_to_processor, g); }
+
+/* Predicate that returns true if the target has migrated. */
+template<typename VertexProcessorMap, typename Graph>
+struct source_or_target_migrated_t
+{
+ typedef typename graph_traits<Graph>::edge_descriptor Edge;
+
+ source_or_target_migrated_t(VertexProcessorMap vertex_to_processor,
+ const Graph& g)
+ : vertex_to_processor(vertex_to_processor), g(g) { }
+
+ bool operator()(Edge e) const
+ {
+ return get(vertex_to_processor, source(e, g)) != source(e, g).owner
+ || get(vertex_to_processor, target(e, g)) != target(e, g).owner;
+ }
+
+private:
+ VertexProcessorMap vertex_to_processor;
+ const Graph& g;
+};
+
+template<typename VertexProcessorMap, typename Graph>
+inline source_or_target_migrated_t<VertexProcessorMap, Graph>
+source_or_target_migrated(VertexProcessorMap vertex_to_processor,
+const Graph& g)
+{
+ typedef source_or_target_migrated_t<VertexProcessorMap, Graph> result_type;
+ return result_type(vertex_to_processor, g);
+}
+
+} } // end of namespace detail::parallel
+
+template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+template<typename VertexProcessorMap>
+void
+PBGL_DISTRIB_ADJLIST_TYPE
+::request_in_neighbors(vertex_descriptor v,
+ VertexProcessorMap vertex_to_processor,
+ bidirectionalS)
+{
+ BGL_FORALL_INEDGES_T(v, e, *this, graph_type)
+ request(vertex_to_processor, source(e, *this));
+}
+
+template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+template<typename VertexProcessorMap>
+void
+PBGL_DISTRIB_ADJLIST_TYPE
+::remove_migrated_in_edges(vertex_descriptor v,
+ VertexProcessorMap vertex_to_processor,
+ bidirectionalS)
+{
+ graph_detail::erase_if(get(vertex_in_edges, base())[v.local],
+ source_migrated(vertex_to_processor, base()));
+}
+
+template<PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+template<typename VertexProcessorMap>
+void
+PBGL_DISTRIB_ADJLIST_TYPE
+::redistribute(VertexProcessorMap vertex_to_processor)
+{
+ using boost::parallel::inplace_all_to_all;
+
+ // When we have stable descriptors, we only move those descriptors
+ // that actually need to be moved. Otherwise, we essentially have to
+ // regenerate the entire graph.
+ const bool has_stable_descriptors =
+ is_same<typename config_type::vertex_list_selector, listS>::value
+ || is_same<typename config_type::vertex_list_selector, setS>::value
+ || is_same<typename config_type::vertex_list_selector, multisetS>::value;
+
+ typedef detail::parallel::redistributed_descriptor<vertex_descriptor,
+ vertex_property_type>
+ redistributed_vertex;
+ typedef detail::parallel::redistributed_descriptor<edge_descriptor,
+ edge_property_type>
+ redistributed_edge;
+ typedef std::pair<vertices_size_type, edges_size_type> num_relocated_pair;
+
+ vertex_iterator vi, vi_end;
+ edge_iterator ei, ei_end;
+
+ process_group_type pg = process_group();
+
+ // Initial synchronization makes sure that we have all of our ducks
+ // in a row. We don't want any outstanding add/remove messages
+ // coming in mid-redistribution!
+ synchronize(process_group_);
+
+ // We cannot cope with eviction of ghost cells
+ vertex_to_processor.set_max_ghost_cells(0);
+
+ process_id_type p = num_processes(pg);
+
+ // Send vertices and edges to the processor where they will
+ // actually reside. This requires O(|V| + |E|) communication
+ std::vector<std::vector<redistributed_vertex> > redistributed_vertices(p);
+ std::vector<std::vector<redistributed_edge> > redistributed_edges(p);
+
+ // Build the sets of relocated vertices for each process and then do
+ // an all-to-all transfer.
+ for (tie(vi, vi_end) = vertices(*this); vi != vi_end; ++vi) {
+ if (!has_stable_descriptors
+ || get(vertex_to_processor, *vi) != vi->owner) {
+ redistributed_vertices[get(vertex_to_processor, *vi)]
+ .push_back(redistributed_vertex(*vi, get(vertex_all_t(), base(),
+ vi->local)));
+ }
+
+ // When our descriptors are stable, we need to determine which
+ // adjacent descriptors are stable to determine which edges will
+ // be removed.
+ if (has_stable_descriptors) {
+ BGL_FORALL_OUTEDGES_T(*vi, e, *this, graph_type)
+ request(vertex_to_processor, target(e, *this));
+ request_in_neighbors(*vi, vertex_to_processor, directed_selector());
+ }
+ }
+
+ inplace_all_to_all(pg, redistributed_vertices);
+
+ // If we have stable descriptors, we need to know where our neighbor
+ // vertices are moving.
+ if (has_stable_descriptors)
+ synchronize(vertex_to_processor);
+
+ // Build the sets of relocated edges for each process and then do
+ // an all-to-all transfer.
+ for (tie(ei, ei_end) = edges(*this); ei != ei_end; ++ei) {
+ vertex_descriptor src = source(*ei, *this);
+ vertex_descriptor tgt = target(*ei, *this);
+ if (!has_stable_descriptors
+ || get(vertex_to_processor, src) != src.owner
+ || get(vertex_to_processor, tgt) != tgt.owner)
+ redistributed_edges[get(vertex_to_processor, source(*ei, *this))]
+ .push_back(redistributed_edge(*ei, get(edge_all_t(), base(),
+ ei->local)));
+ }
+ inplace_all_to_all(pg, redistributed_edges);
+
+ // A mapping from old vertex descriptors to new vertex
+ // descriptors. This is an STL map partly because I'm too lazy to
+ // build a real property map (which is hard in the general case) but
+ // also because it won't try to look in the graph itself, because
+ // the keys are all vertex descriptors that have been invalidated.
+ std::map<vertex_descriptor, vertex_descriptor> old_to_new_vertex_map;
+
+ if (has_stable_descriptors) {
+ // Clear out all vertices and edges that will have moved. There
+ // are several stages to this.
+
+ // First, eliminate all outgoing edges from the (local) vertices
+ // that have been moved or whose targets have been moved.
+ BGL_FORALL_VERTICES_T(v, *this, graph_type) {
+ if (get(vertex_to_processor, v) != v.owner) {
+ clear_out_edges(v.local, base());
+ clear_in_edges_local(v, directed_selector());
+ } else {
+ remove_out_edge_if(v.local,
+ target_migrated(vertex_to_processor, base()),
+ base());
+ remove_migrated_in_edges(v, vertex_to_processor, directed_selector());
+ }
+ }
+
+ // Next, eliminate locally-stored edges that have migrated (for
+ // undirected graphs).
+ graph_detail::erase_if(local_edges_,
+ source_or_target_migrated(vertex_to_processor, *this));
+
+ // Eliminate vertices that have migrated
+ for (tie(vi, vi_end) = vertices(*this); vi != vi_end; /* in loop */) {
+ if (get(vertex_to_processor, *vi) != vi->owner)
+ remove_vertex((*vi++).local, base());
+ else {
+ // Add the identity relation for vertices that have not migrated
+ old_to_new_vertex_map[*vi] = *vi;
+ ++vi;
+ }
+ }
+ } else {
+ // Clear out the local graph: the entire graph is in transit
+ clear();
+ }
+
+ // Add the new vertices to the graph. When we do so, update the old
+ // -> new vertex mapping both locally and for the owner of the "old"
+ // vertex.
+ {
+ typedef std::pair<vertex_descriptor, vertex_descriptor> mapping_pair;
+ std::vector<std::vector<mapping_pair> > mappings(p);
+
+ for (process_id_type src = 0; src < p; ++src) {
+ for (typename std::vector<redistributed_vertex>::iterator vi =
+ redistributed_vertices[src].begin();
+ vi != redistributed_vertices[src].end(); ++vi) {
+ vertex_descriptor new_vertex =
+ add_vertex(vi->get_property(), *this);
+ old_to_new_vertex_map[vi->descriptor] = new_vertex;
+ mappings[vi->descriptor.owner].push_back(mapping_pair(vi->descriptor,
+ new_vertex));
+ }
+
+ redistributed_vertices[src].clear();
+ }
+
+ inplace_all_to_all(pg, mappings);
+
+ // Add the mappings we were sent into the old->new map.
+ for (process_id_type src = 0; src < p; ++src)
+ old_to_new_vertex_map.insert(mappings[src].begin(), mappings[src].end());
+ }
+
+ // Get old->new vertex mappings for all of the vertices we need to
+ // know about.
+
+ // TBD: An optimization here might involve sending the
+ // request-response pairs without an explicit request step (for
+ // bidirectional and undirected graphs). However, it may not matter
+ // all that much given the cost of redistribution.
+ {
+ std::vector<std::vector<vertex_descriptor> > vertex_map_requests(p);
+ std::vector<std::vector<vertex_descriptor> > vertex_map_responses(p);
+
+ // We need to know about all of the vertices incident on edges
+ // that have been relocated to this processor. Tell each processor
+ // what each other processor needs to know.
+ for (process_id_type src = 0; src < p; ++src)
+ for (typename std::vector<redistributed_edge>::iterator ei =
+ redistributed_edges[src].begin();
+ ei != redistributed_edges[src].end(); ++ei) {
+ vertex_descriptor need_vertex = target(ei->descriptor, *this);
+ if (old_to_new_vertex_map.find(need_vertex)
+ == old_to_new_vertex_map.end())
+ {
+ old_to_new_vertex_map[need_vertex] = need_vertex;
+ vertex_map_requests[need_vertex.owner].push_back(need_vertex);
+ }
+ }
+ inplace_all_to_all(pg,
+ vertex_map_requests,
+ vertex_map_responses);
+
+ // Process the requests made for vertices we own. Then perform yet
+ // another all-to-all swap. This one matches the requests we've
+ // made to the responses we were given.
+ for (process_id_type src = 0; src < p; ++src)
+ for (typename std::vector<vertex_descriptor>::iterator vi =
+ vertex_map_responses[src].begin();
+ vi != vertex_map_responses[src].end(); ++vi)
+ *vi = old_to_new_vertex_map[*vi];
+ inplace_all_to_all(pg, vertex_map_responses);
+
+ // Matching the requests to the responses, update the old->new
+ // vertex map for all of the vertices we will need to know.
+ for (process_id_type src = 0; src < p; ++src) {
+ typedef typename std::vector<vertex_descriptor>::size_type size_type;
+ for (size_type i = 0; i < vertex_map_requests[src].size(); ++i) {
+ old_to_new_vertex_map[vertex_map_requests[src][i]] =
+ vertex_map_responses[src][i];
+ }
+ }
+ }
+
+ // Add edges to the graph by mapping the source and target.
+ for (process_id_type src = 0; src < p; ++src) {
+ for (typename std::vector<redistributed_edge>::iterator ei =
+ redistributed_edges[src].begin();
+ ei != redistributed_edges[src].end(); ++ei) {
+ add_edge(old_to_new_vertex_map[source(ei->descriptor, *this)],
+ old_to_new_vertex_map[target(ei->descriptor, *this)],
+ ei->get_property(),
+ *this);
+ }
+
+ redistributed_edges[src].clear();
+ }
+
+ // Be sure that edge-addition messages are received now, completing
+ // the graph.
+ synchronize(process_group_);
+
+ this->distribution().clear();
+
+ detail::parallel::maybe_initialize_vertex_indices(vertices(base()),
+ get(vertex_index, base()));
+}
+
+} // end namespace boost

Added: trunk/boost/graph/distributed/adjlist/serialization.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/adjlist/serialization.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,1007 @@
+// Copyright Daniel Wallin 2007. 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)
+
+#ifndef BOOST_GRAPH_DISTRIBUTED_ADJLIST_SERIALIZATION_070925_HPP
+#define BOOST_GRAPH_DISTRIBUTED_ADJLIST_SERIALIZATION_070925_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/lexical_cast.hpp>
+# include <boost/foreach.hpp>
+# include <boost/filesystem/path.hpp>
+# include <boost/filesystem/operations.hpp>
+# include <cctype>
+# include <fstream>
+
+namespace boost {
+
+namespace detail { namespace parallel
+{
+
+ // Wraps a local descriptor, making it serializable.
+ template <class Local>
+ struct serializable_local_descriptor
+ {
+ serializable_local_descriptor()
+ {}
+
+ serializable_local_descriptor(Local local)
+ : local(local)
+ {}
+
+ operator Local const&() const
+ {
+ return local;
+ }
+
+ bool operator==(serializable_local_descriptor const& other) const
+ {
+ return local == other.local;
+ }
+
+ bool operator<(serializable_local_descriptor const& other) const
+ {
+ return local < other.local;
+ }
+
+ template <class Archive>
+ void serialize(Archive& ar, const unsigned int /*version*/)
+ {
+ ar & unsafe_serialize(local);
+ }
+
+ Local local;
+ };
+
+ template <class Vertex, class Properties>
+ struct pending_edge
+ {
+ pending_edge(
+ Vertex source, Vertex target
+ , Properties properties, void* property_ptr
+ )
+ : source(source)
+ , target(target)
+ , properties(properties)
+ , property_ptr(property_ptr)
+ {}
+
+ Vertex source;
+ Vertex target;
+ Properties properties;
+ void* property_ptr;
+ };
+
+ inline bool is_digit(char c)
+ {
+ return std::isdigit(c);
+ }
+
+ inline std::vector<int>
+ available_process_files(std::string const& filename)
+ {
+ if (!filesystem::exists(filename))
+ return std::vector<int>();
+
+ std::vector<int> result;
+
+ for (filesystem::directory_iterator i(filename), end; i != end; ++i)
+ {
+ if (!filesystem::is_regular(*i))
+ boost::throw_exception(std::runtime_error("directory contains non-regular entries"));
+
+#if BOOST_VERSION >= 103600
+ std::string process_name = i->path().filename();
+#else
+ std::string process_name = i->leaf();
+#endif
+ for (std::string::size_type i = 0; i < process_name.size(); ++i)
+ if (!is_digit(process_name[i]))
+ boost::throw_exception(std::runtime_error("directory contains files with invalid names"));
+
+ result.push_back(boost::lexical_cast<int>(process_name));
+ }
+
+ return result;
+ }
+
+ template <class Archive, class Tag, class T, class Base>
+ void maybe_load_properties(
+ Archive& ar, char const* name, property<Tag, T, Base>& properties)
+ {
+ ar >> serialization::make_nvp(name, get_property_value(properties, Tag()));
+ maybe_load_properties(ar, name, static_cast<Base&>(properties));
+ }
+
+ template <class Archive>
+ void maybe_load_properties(
+ Archive&, char const*, no_property&)
+ {}
+
+ template <class Archive, typename Bundle>
+ void maybe_load_properties(
+ Archive& ar, char const* name, Bundle& bundle)
+ {
+ ar >> serialization::make_nvp(name, bundle);
+ no_property prop;
+ maybe_load_properties(ar, name, prop);
+ }
+
+
+
+
+
+
+ template <class Graph, class Archive, class VertexListS>
+ struct graph_loader
+ {
+ typedef typename Graph::vertex_descriptor vertex_descriptor;
+ typedef typename Graph::local_vertex_descriptor local_vertex_descriptor;
+ typedef typename Graph::vertex_property_type vertex_property_type;
+ typedef typename Graph::edge_descriptor edge_descriptor;
+ typedef typename Graph::local_edge_descriptor local_edge_descriptor;
+ typedef typename Graph::edge_property_type edge_property_type;
+ typedef typename Graph::process_group_type process_group_type;
+ typedef typename process_group_type::process_id_type process_id_type;
+ typedef typename Graph::directed_selector directed_selector;
+ typedef typename mpl::if_<
+ is_same<VertexListS, defaultS>, vecS, VertexListS
+ >::type vertex_list_selector;
+ typedef pending_edge<vertex_descriptor, edge_property_type>
+ pending_edge_type;
+ typedef serializable_local_descriptor<local_vertex_descriptor>
+ serializable_vertex_descriptor;
+
+ graph_loader(Graph& g, Archive& ar)
+ : m_g(g)
+ , m_ar(ar)
+ , m_pg(g.process_group())
+ , m_requested_vertices(num_processes(m_pg))
+ , m_remote_vertices(num_processes(m_pg))
+ , m_property_ptrs(num_processes(m_pg))
+ {
+ g.clear();
+ load_prefix();
+ load_vertices();
+ load_edges();
+ ar >> make_nvp("distribution", m_g.distribution());
+ }
+
+ private:
+ struct pending_in_edge
+ {
+ pending_in_edge(
+ vertex_descriptor u, vertex_descriptor v, void* property_ptr
+ )
+ : u(u)
+ , v(v)
+ , property_ptr(property_ptr)
+ {}
+
+ vertex_descriptor u;
+ vertex_descriptor v;
+ void* property_ptr;
+ };
+
+ bool is_root() const
+ {
+ return process_id(m_pg) == 0;
+ }
+
+ template <class T>
+ serialization::nvp<T> const make_nvp(char const* name, T& value) const
+ {
+ return serialization::nvp<T>(name, value);
+ }
+
+ void load_prefix();
+ void load_vertices();
+
+ template <class Anything>
+ void maybe_load_and_store_local_vertex(Anything);
+ void maybe_load_and_store_local_vertex(vecS);
+
+ void load_edges();
+ void load_in_edges(bidirectionalS);
+ void load_in_edges(directedS);
+ void add_pending_in_edge(
+ vertex_descriptor u, vertex_descriptor v, void* property_ptr, vecS);
+ template <class Anything>
+ void add_pending_in_edge(
+ vertex_descriptor u, vertex_descriptor v, void* property_ptr, Anything);
+ template <class Anything>
+ void add_edge(
+ vertex_descriptor u, vertex_descriptor v
+ , edge_property_type const& property, void* property_ptr, Anything);
+ void add_edge(
+ vertex_descriptor u, vertex_descriptor v
+ , edge_property_type const& property, void* property_ptr, vecS);
+ void add_remote_vertex_request(
+ vertex_descriptor u, vertex_descriptor v, directedS);
+ void add_remote_vertex_request(
+ vertex_descriptor u, vertex_descriptor v, bidirectionalS);
+ void add_in_edge(
+ edge_descriptor const&, void*, directedS);
+ void add_in_edge(
+ edge_descriptor const& edge, void* old_property_ptr, bidirectionalS);
+
+ void resolve_remote_vertices(directedS);
+ void resolve_remote_vertices(bidirectionalS);
+ vertex_descriptor resolve_remote_vertex(vertex_descriptor u) const;
+ vertex_descriptor resolve_remote_vertex(vertex_descriptor u, vecS) const;
+ template <class Anything>
+ vertex_descriptor resolve_remote_vertex(vertex_descriptor u, Anything) const;
+
+ void resolve_property_ptrs();
+
+ void commit_pending_edges(vecS);
+ template <class Anything>
+ void commit_pending_edges(Anything);
+ void commit_pending_in_edges(directedS);
+ void commit_pending_in_edges(bidirectionalS);
+
+ void* maybe_load_property_ptr(directedS) { return 0; }
+ void* maybe_load_property_ptr(bidirectionalS);
+
+ Graph& m_g;
+ Archive& m_ar;
+ process_group_type m_pg;
+
+ std::vector<process_id_type> m_id_mapping;
+
+ // Maps local vertices as loaded from the archive to
+ // the ones actually added to the graph. Only used
+ // when !vecS.
+ std::map<local_vertex_descriptor, local_vertex_descriptor> m_local_vertices;
+
+ // This is the list of remote vertex descriptors that we
+ // are going to receive from other processes. This is
+ // kept sorted so that we can determine the position of
+ // the matching vertex descriptor in m_remote_vertices.
+ std::vector<std::vector<serializable_vertex_descriptor> > m_requested_vertices;
+
+ // This is the list of remote vertex descriptors that
+ // we send and receive from other processes.
+ std::vector<std::vector<serializable_vertex_descriptor> > m_remote_vertices;
+
+ // ...
+ std::vector<pending_edge_type> m_pending_edges;
+
+ // The pending in-edges that will be added in the commit step, after
+ // the remote vertex descriptors has been resolved. Only used
+ // when bidirectionalS and !vecS.
+ std::vector<pending_in_edge> m_pending_in_edges;
+
+ std::vector<std::vector<unsafe_pair<void*,void*> > > m_property_ptrs;
+ };
+
+ template <class Graph, class Archive, class VertexListS>
+ void graph_loader<Graph, Archive, VertexListS>::load_prefix()
+ {
+ typename process_group_type::process_size_type num_processes_;
+ m_ar >> make_nvp("num_processes", num_processes_);
+
+ if (num_processes_ != num_processes(m_pg))
+ boost::throw_exception(std::runtime_error("number of processes mismatch"));
+
+ process_id_type old_id;
+ m_ar >> make_nvp("id", old_id);
+
+ std::vector<typename Graph::distribution_type::size_type> mapping;
+ m_ar >> make_nvp("mapping", mapping);
+
+ // Fetch all the old id's from the other processes.
+ std::vector<process_id_type> old_ids;
+ all_gather(m_pg, &old_id, &old_id+1, old_ids);
+
+ m_id_mapping.resize(num_processes(m_pg), -1);
+
+ for (process_id_type i = 0; i < num_processes(m_pg); ++i)
+ {
+# ifdef PBGL_SERIALIZE_DEBUG
+ if (is_root())
+ std::cout << i << " used to be " << old_ids[i] << "\n";
+# endif
+ assert(m_id_mapping[old_ids[i]] == -1);
+ m_id_mapping[old_ids[i]] = i;
+ }
+
+ std::vector<typename Graph::distribution_type::size_type> new_mapping(
+ mapping.size());
+
+ for (int i = 0; i < num_processes(m_pg); ++i)
+ {
+ new_mapping[mapping[old_ids[i]]] = i;
+ }
+
+ m_g.distribution().assign_mapping(
+ new_mapping.begin(), new_mapping.end());
+ }
+
+ template <class Graph, class Archive, class VertexListS>
+ void graph_loader<Graph, Archive, VertexListS>::load_vertices()
+ {
+ int V;
+ m_ar >> BOOST_SERIALIZATION_NVP(V);
+
+# ifdef PBGL_SERIALIZE_DEBUG
+ if (is_root())
+ std::cout << "Loading vertices\n";
+# endif
+
+ for (int i = 0; i < V; ++i)
+ {
+ maybe_load_and_store_local_vertex(vertex_list_selector());
+ }
+ }
+
+ template <class Graph, class Archive, class VertexListS>
+ template <class Anything>
+ void graph_loader<Graph, Archive, VertexListS>::maybe_load_and_store_local_vertex(Anything)
+ {
+ // Load the original vertex descriptor
+ local_vertex_descriptor local;
+ m_ar >> make_nvp("local", unsafe_serialize(local));
+
+ // Load the properties
+ vertex_property_type property;
+ detail::parallel::maybe_load_properties(m_ar, "vertex_property",
+ property);
+
+ // Add the vertex
+ vertex_descriptor v(process_id(m_pg), add_vertex(property, m_g.base()));
+
+ if (m_g.on_add_vertex)
+ m_g.on_add_vertex(v, m_g);
+
+ // Create the mapping from the "old" local descriptor to the new
+ // local descriptor.
+ m_local_vertices[local] = v.local;
+ }
+
+ template <class Graph, class Archive, class VertexListS>
+ void graph_loader<Graph, Archive, VertexListS>::maybe_load_and_store_local_vertex(vecS)
+ {
+ // Load the properties
+ vertex_property_type property;
+ detail::parallel::maybe_load_properties(m_ar, "vertex_property",
+ property);
+
+ // Add the vertex
+ vertex_descriptor v(process_id(m_pg),
+ add_vertex(m_g.build_vertex_property(property),
+ m_g.base()));
+
+ if (m_g.on_add_vertex)
+ m_g.on_add_vertex(v, m_g);
+ }
+
+ template <class Graph, class Archive, class VertexListS>
+ void graph_loader<Graph, Archive, VertexListS>::load_edges()
+ {
+ int E;
+ m_ar >> BOOST_SERIALIZATION_NVP(E);
+
+# ifdef PBGL_SERIALIZE_DEBUG
+ if (is_root())
+ std::cout << "Loading edges\n";
+# endif
+
+ for (int i = 0; i < E; ++i)
+ {
+ local_vertex_descriptor local_src;
+ process_id_type target_owner;
+ local_vertex_descriptor local_tgt;
+
+ m_ar >> make_nvp("source", unsafe_serialize(local_src));
+ m_ar >> make_nvp("target_owner", target_owner);
+ m_ar >> make_nvp("target", unsafe_serialize(local_tgt));
+
+ process_id_type new_src_owner = process_id(m_pg);
+ process_id_type new_tgt_owner = m_id_mapping[target_owner];
+
+ vertex_descriptor source(new_src_owner, local_src);
+ vertex_descriptor target(new_tgt_owner, local_tgt);
+
+ edge_property_type properties;
+ detail::parallel::maybe_load_properties(m_ar, "edge_property", properties);
+
+ void* property_ptr = maybe_load_property_ptr(directed_selector());
+ add_edge(source, target, properties, property_ptr, vertex_list_selector());
+ }
+
+ load_in_edges(directed_selector());
+ commit_pending_edges(vertex_list_selector());
+ }
+
+ template <class Graph, class Archive, class VertexListS>
+ void graph_loader<Graph, Archive, VertexListS>::load_in_edges(bidirectionalS)
+ {
+ std::size_t I;
+ m_ar >> BOOST_SERIALIZATION_NVP(I);
+
+# ifdef PBGL_SERIALIZE_DEBUG
+ if (is_root())
+ std::cout << "Loading in-edges\n";
+# endif
+
+ for (int i = 0; i < I; ++i)
+ {
+ process_id_type src_owner;
+ local_vertex_descriptor local_src;
+ local_vertex_descriptor local_target;
+ void* property_ptr;
+
+ m_ar >> make_nvp("src_owner", src_owner);
+ m_ar >> make_nvp("source", unsafe_serialize(local_src));
+ m_ar >> make_nvp("target", unsafe_serialize(local_target));
+ m_ar >> make_nvp("property_ptr", unsafe_serialize(property_ptr));
+
+ src_owner = m_id_mapping[src_owner];
+
+ vertex_descriptor u(src_owner, local_src);
+ vertex_descriptor v(process_id(m_pg), local_target);
+
+ add_pending_in_edge(u, v, property_ptr, vertex_list_selector());
+ }
+ }
+
+ template <class Graph, class Archive, class VertexListS>
+ void graph_loader<Graph, Archive, VertexListS>::load_in_edges(directedS)
+ {}
+
+ template <class Graph, class Archive, class VertexListS>
+ void graph_loader<Graph, Archive, VertexListS>::add_pending_in_edge(
+ vertex_descriptor u, vertex_descriptor v, void* property_ptr, vecS)
+ {
+ m_pending_in_edges.push_back(pending_in_edge(u,v,property_ptr));
+ }
+
+ template <class Graph, class Archive, class VertexListS>
+ template <class Anything>
+ void graph_loader<Graph, Archive, VertexListS>::add_pending_in_edge(
+ vertex_descriptor u, vertex_descriptor v, void* property_ptr, Anything)
+ {
+ // u and v represent the out-edge here, meaning v is local
+ // to us, and u is always remote.
+ m_pending_in_edges.push_back(pending_in_edge(u,v,property_ptr));
+ add_remote_vertex_request(v, u, bidirectionalS());
+ }
+
+ template <class Graph, class Archive, class VertexListS>
+ template <class Anything>
+ void graph_loader<Graph, Archive, VertexListS>::add_edge(
+ vertex_descriptor u, vertex_descriptor v
+ , edge_property_type const& property, void* property_ptr, Anything)
+ {
+ m_pending_edges.push_back(pending_edge_type(u, v, property, property_ptr));
+ add_remote_vertex_request(u, v, directed_selector());
+ }
+
+ template <class Graph, class Archive, class VertexListS>
+ void graph_loader<Graph, Archive, VertexListS>::add_remote_vertex_request(
+ vertex_descriptor u, vertex_descriptor v, directedS)
+ {
+ // We have to request the remote vertex.
+ m_requested_vertices[owner(v)].push_back(local(v));
+ }
+
+ template <class Graph, class Archive, class VertexListS>
+ void graph_loader<Graph, Archive, VertexListS>::add_remote_vertex_request(
+ vertex_descriptor u, vertex_descriptor v, bidirectionalS)
+ {
+ // If the edge spans to another process, we know
+ // that that process has a matching in-edge, so
+ // we can just send our vertex. No requests
+ // necessary.
+ if (owner(v) != m_g.processor())
+ {
+ m_remote_vertices[owner(v)].push_back(local(u));
+ m_requested_vertices[owner(v)].push_back(local(v));
+ }
+ }
+
+ template <class Graph, class Archive, class VertexListS>
+ void graph_loader<Graph, Archive, VertexListS>::add_edge(
+ vertex_descriptor u, vertex_descriptor v
+ , edge_property_type const& property, void* property_ptr, vecS)
+ {
+ std::pair<local_edge_descriptor, bool> inserted =
+ detail::parallel::add_local_edge(
+ local(u), local(v)
+ , m_g.build_edge_property(property), m_g.base());
+ assert(inserted.second);
+ put(edge_target_processor_id, m_g.base(), inserted.first, owner(v));
+
+ edge_descriptor e(owner(u), owner(v), true, inserted.first);
+
+ if (inserted.second && m_g.on_add_edge)
+ m_g.on_add_edge(e, m_g);
+
+ add_in_edge(e, property_ptr, directed_selector());
+ }
+
+ template <class Graph, class Archive, class VertexListS>
+ void graph_loader<Graph, Archive, VertexListS>::add_in_edge(
+ edge_descriptor const&, void*, directedS)
+ {}
+
+ template <class Graph, class Archive, class VertexListS>
+ void graph_loader<Graph, Archive, VertexListS>::add_in_edge(
+ edge_descriptor const& edge, void* old_property_ptr, bidirectionalS)
+ {
+ if (owner(target(edge, m_g)) == m_g.processor())
+ {
+ detail::parallel::stored_in_edge<local_edge_descriptor>
+ e(m_g.processor(), local(edge));
+ boost::graph_detail::push(get(
+ vertex_in_edges, m_g.base())[local(target(edge, m_g))], e);
+ }
+ else
+ {
+ // We send the (old,new) property pointer pair to
+ // the remote process. This could be optimized to
+ // only send the new one -- the ordering can be
+ // made implicit because the old pointer value is
+ // stored on the remote process.
+ //
+ // Doing that is a little bit more complicated, but
+ // in case it turns out it's important we can do it.
+ void* property_ptr = local(edge).get_property();
+ m_property_ptrs[owner(target(edge, m_g))].push_back(
+ unsafe_pair<void*,void*>(old_property_ptr, property_ptr));
+ }
+ }
+
+ template <class Graph, class Archive, class VertexListS>
+ void graph_loader<Graph, Archive, VertexListS>::resolve_property_ptrs()
+ {
+# ifdef PBGL_SERIALIZE_DEBUG
+ if (is_root())
+ std::cout << "Resolving property pointers\n";
+# endif
+
+ for (int i = 0; i < num_processes(m_pg); ++i)
+ {
+ std::sort(
+ m_property_ptrs[i].begin(), m_property_ptrs[i].end());
+ }
+
+ boost::parallel::inplace_all_to_all(m_pg, m_property_ptrs);
+ }
+
+ template <class Graph, class Archive, class VertexListS>
+ void graph_loader<Graph, Archive, VertexListS>::resolve_remote_vertices(directedS)
+ {
+ for (int i = 0; i < num_processes(m_pg); ++i)
+ {
+ std::sort(m_requested_vertices[i].begin(), m_requested_vertices[i].end());
+ }
+
+ boost::parallel::inplace_all_to_all(
+ m_pg, m_requested_vertices, m_remote_vertices);
+
+ for (int i = 0; i < num_processes(m_pg); ++i)
+ {
+ BOOST_FOREACH(serializable_vertex_descriptor& u, m_remote_vertices[i])
+ {
+ u = m_local_vertices[u];
+ }
+ }
+
+ boost::parallel::inplace_all_to_all(m_pg, m_remote_vertices);
+ }
+
+ template <class Graph, class Archive, class VertexListS>
+ void graph_loader<Graph, Archive, VertexListS>::resolve_remote_vertices(bidirectionalS)
+ {
+# ifdef PBGL_SERIALIZE_DEBUG
+ if (is_root())
+ std::cout << "Resolving remote vertices\n";
+# endif
+
+ for (int i = 0; i < num_processes(m_pg); ++i)
+ {
+ std::sort(m_requested_vertices[i].begin(), m_requested_vertices[i].end());
+ std::sort(m_remote_vertices[i].begin(), m_remote_vertices[i].end());
+
+ BOOST_FOREACH(serializable_vertex_descriptor& u, m_remote_vertices[i])
+ {
+ u = m_local_vertices[u];
+ }
+ }
+
+ boost::parallel::inplace_all_to_all(m_pg, m_remote_vertices);
+
+ for (int i = 0; i < num_processes(m_pg); ++i)
+ assert(m_remote_vertices[i].size() == m_requested_vertices[i].size());
+ }
+
+ template <class Graph, class Archive, class VertexListS>
+ void graph_loader<Graph, Archive, VertexListS>::commit_pending_edges(vecS)
+ {
+ commit_pending_in_edges(directed_selector());
+ }
+
+ template <class Graph, class Archive, class VertexListS>
+ template <class Anything>
+ void graph_loader<Graph, Archive, VertexListS>::commit_pending_edges(Anything)
+ {
+ resolve_remote_vertices(directed_selector());
+
+ BOOST_FOREACH(pending_edge_type const& e, m_pending_edges)
+ {
+ vertex_descriptor u = resolve_remote_vertex(e.source);
+ vertex_descriptor v = resolve_remote_vertex(e.target);
+ add_edge(u, v, e.properties, e.property_ptr, vecS());
+ }
+
+ commit_pending_in_edges(directed_selector());
+ }
+
+ template <class Graph, class Archive, class VertexListS>
+ void graph_loader<Graph, Archive, VertexListS>::commit_pending_in_edges(directedS)
+ {}
+
+ template <class Graph, class Archive, class VertexListS>
+ void graph_loader<Graph, Archive, VertexListS>::commit_pending_in_edges(bidirectionalS)
+ {
+ resolve_property_ptrs();
+
+ BOOST_FOREACH(pending_in_edge const& e, m_pending_in_edges)
+ {
+ vertex_descriptor u = resolve_remote_vertex(e.u, vertex_list_selector());
+ vertex_descriptor v = resolve_remote_vertex(e.v, vertex_list_selector());
+
+ typedef detail::parallel::stored_in_edge<local_edge_descriptor> stored_edge;
+
+ std::vector<unsafe_pair<void*,void*> >::iterator i = std::lower_bound(
+ m_property_ptrs[owner(u)].begin()
+ , m_property_ptrs[owner(u)].end()
+ , unsafe_pair<void*,void*>(e.property_ptr, 0)
+ );
+
+ if (i == m_property_ptrs[owner(u)].end()
+ || i->first != e.property_ptr)
+ {
+ assert(false);
+ }
+
+ local_edge_descriptor local_edge(local(u), local(v), i->second);
+ stored_edge edge(owner(u), local_edge);
+ boost::graph_detail::push(
+ get(vertex_in_edges, m_g.base())[local(v)], edge);
+ }
+ }
+
+ template <class Graph, class Archive, class VertexListS>
+ typename graph_loader<Graph, Archive, VertexListS>::vertex_descriptor
+ graph_loader<Graph, Archive, VertexListS>::resolve_remote_vertex(
+ vertex_descriptor u) const
+ {
+ if (owner(u) == process_id(m_pg))
+ {
+ return vertex_descriptor(
+ process_id(m_pg), m_local_vertices.find(local(u))->second);
+ }
+
+ typename std::vector<serializable_vertex_descriptor>::const_iterator
+ i = std::lower_bound(
+ m_requested_vertices[owner(u)].begin()
+ , m_requested_vertices[owner(u)].end()
+ , serializable_vertex_descriptor(local(u))
+ );
+
+ if (i == m_requested_vertices[owner(u)].end()
+ || *i != local(u))
+ {
+ assert(false);
+ }
+
+ local_vertex_descriptor local =
+ m_remote_vertices[owner(u)][m_requested_vertices[owner(u)].end() - i];
+ return vertex_descriptor(owner(u), local);
+ }
+
+ template <class Graph, class Archive, class VertexListS>
+ typename graph_loader<Graph, Archive, VertexListS>::vertex_descriptor
+ graph_loader<Graph, Archive, VertexListS>::resolve_remote_vertex(
+ vertex_descriptor u, vecS) const
+ {
+ return u;
+ }
+
+ template <class Graph, class Archive, class VertexListS>
+ template <class Anything>
+ typename graph_loader<Graph, Archive, VertexListS>::vertex_descriptor
+ graph_loader<Graph, Archive, VertexListS>::resolve_remote_vertex(
+ vertex_descriptor u, Anything) const
+ {
+ return resolve_remote_vertex(u);
+ }
+
+ template <class Graph, class Archive, class VertexListS>
+ void*
+ graph_loader<Graph, Archive, VertexListS>::maybe_load_property_ptr(bidirectionalS)
+ {
+ void* ptr;
+ m_ar >> make_nvp("property_ptr", unsafe_serialize(ptr));
+ return ptr;
+ }
+
+template <class Archive, class D>
+void maybe_save_local_descriptor(Archive& ar, D const&, vecS)
+{}
+
+template <class Archive, class D, class NotVecS>
+void maybe_save_local_descriptor(Archive& ar, D const& d, NotVecS)
+{
+ ar << serialization::make_nvp(
+ "local", unsafe_serialize(const_cast<D&>(d)));
+}
+
+template <class Archive>
+void maybe_save_properties(
+ Archive&, char const*, no_property const&)
+{}
+
+template <class Archive, class Tag, class T, class Base>
+void maybe_save_properties(
+ Archive& ar, char const* name, property<Tag, T, Base> const& properties)
+{
+ ar & serialization::make_nvp(name, get_property_value(properties, Tag()));
+ maybe_save_properties(ar, name, static_cast<Base const&>(properties));
+}
+
+template <class Archive, class Graph>
+void save_in_edges(Archive& ar, Graph const& g, directedS)
+{}
+
+// We need to save the edges in the base edge
+// list, and the in_edges that are stored in the
+// vertex_in_edges vertex property.
+template <class Archive, class Graph>
+void save_in_edges(Archive& ar, Graph const& g, bidirectionalS)
+{
+ typedef typename Graph::process_group_type
+ process_group_type;
+ typedef typename process_group_type::process_id_type
+ process_id_type;
+ typedef typename graph_traits<
+ Graph>::vertex_descriptor vertex_descriptor;
+ typedef typename vertex_descriptor::local_descriptor_type
+ local_vertex_descriptor;
+ typedef typename graph_traits<
+ Graph>::edge_descriptor edge_descriptor;
+
+ process_id_type id = g.processor();
+
+ typedef std::pair<local_vertex_descriptor, vertex_descriptor> in_edge;
+ std::vector<edge_descriptor> saved_in_edges;
+
+ BGL_FORALL_VERTICES_T(v, g, Graph)
+ {
+ BOOST_FOREACH(edge_descriptor const& e, in_edges(v, g))
+ {
+ // Only save the in_edges that isn't owned by this process.
+ if (owner(e) == id)
+ continue;
+
+ saved_in_edges.push_back(e);
+ }
+ }
+
+ std::size_t I = saved_in_edges.size();
+ ar << BOOST_SERIALIZATION_NVP(I);
+
+ BOOST_FOREACH(edge_descriptor const& e, saved_in_edges)
+ {
+ process_id_type src_owner = owner(source(e,g));
+ local_vertex_descriptor local_src = local(source(e,g));
+ local_vertex_descriptor local_target = local(target(e,g));
+ void* property_ptr = local(e).get_property();
+
+ using serialization::make_nvp;
+
+ ar << make_nvp("src_owner", src_owner);
+ ar << make_nvp("source", unsafe_serialize(local_src));
+ ar << make_nvp("target", unsafe_serialize(local_target));
+ ar << make_nvp("property_ptr", unsafe_serialize(property_ptr));
+ }
+}
+
+template <class Archive, class Edge>
+void maybe_save_property_ptr(Archive&, Edge const&, directedS)
+{}
+
+template <class Archive, class Edge>
+void maybe_save_property_ptr(Archive& ar, Edge const& e, bidirectionalS)
+{
+ void* ptr = local(e).get_property();
+ ar << serialization::make_nvp("property_ptr", unsafe_serialize(ptr));
+}
+
+template <class Archive, class Graph, class DirectedS>
+void save_edges(Archive& ar, Graph const& g, DirectedS)
+{
+ typedef typename Graph::process_group_type
+ process_group_type;
+ typedef typename process_group_type::process_id_type
+ process_id_type;
+ typedef typename graph_traits<
+ Graph>::vertex_descriptor vertex_descriptor;
+ typedef typename graph_traits<
+ Graph>::edge_descriptor edge_descriptor;
+
+ // We retag the property list here so that bundled properties are
+ // properly placed into property<edge_bundle_t, Bundle>.
+ typedef typename boost::detail::retag_property_list<
+ edge_bundle_t,
+ typename Graph::edge_property_type>::type
+ edge_property_type;
+
+ int E = num_edges(g);
+ ar << BOOST_SERIALIZATION_NVP(E);
+
+ // For *directed* graphs, we can just save
+ // the edge list and be done.
+ //
+ // For *bidirectional* graphs, we need to also
+ // save the "vertex_in_edges" property map,
+ // because it might contain in-edges that
+ // are not locally owned.
+ BGL_FORALL_EDGES_T(e, g, Graph)
+ {
+ vertex_descriptor src(source(e, g));
+ vertex_descriptor tgt(target(e, g));
+
+ typename vertex_descriptor::local_descriptor_type
+ local_u(local(src));
+ typename vertex_descriptor::local_descriptor_type
+ local_v(local(tgt));
+
+ process_id_type target_owner = owner(tgt);
+
+ using serialization::make_nvp;
+
+ ar << make_nvp("source", unsafe_serialize(local_u));
+ ar << make_nvp("target_owner", target_owner);
+ ar << make_nvp("target", unsafe_serialize(local_v));
+
+ maybe_save_properties(
+ ar, "edge_property"
+ , static_cast<edge_property_type const&>(get(edge_all_t(), g, e))
+ );
+
+ maybe_save_property_ptr(ar, e, DirectedS());
+ }
+
+ save_in_edges(ar, g, DirectedS());
+}
+
+}} // namespace detail::parallel
+
+template <PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+template <class IStreamConstructibleArchive>
+void PBGL_DISTRIB_ADJLIST_TYPE::load(std::string const& filename)
+{
+ typedef typename config_type::VertexListS vertex_list_selector;
+
+ process_group_type pg = process_group();
+ process_id_type id = process_id(pg);
+
+ synchronize(pg);
+
+ std::vector<int> disk_files = detail::parallel::available_process_files(filename);
+ std::sort(disk_files.begin(), disk_files.end());
+
+ // Negotiate which process gets which file. Serialized.
+ std::vector<int> consumed_files;
+ int picked_file = -1;
+
+ if (id > 0)
+ receive_oob(pg, id-1, 0, consumed_files);
+
+ std::sort(consumed_files.begin(), consumed_files.end());
+ std::vector<int> available_files;
+ std::set_difference(
+ disk_files.begin(), disk_files.end()
+ , consumed_files.begin(), consumed_files.end()
+ , std::back_inserter(available_files)
+ );
+
+ if (available_files.empty())
+ boost::throw_exception(std::runtime_error("no file available"));
+
+ // back() used for debug purposes. Making sure the
+ // ranks are shuffled.
+ picked_file = available_files.back();
+
+# ifdef PBGL_SERIALIZE_DEBUG
+ std::cout << id << " picked " << picked_file << "\n";
+# endif
+
+ consumed_files.push_back(picked_file);
+
+ if (id < num_processes(pg) - 1)
+ send_oob(pg, id+1, 0, consumed_files);
+
+ std::string local_filename = filename + "/" +
+ lexical_cast<std::string>(picked_file);
+
+ std::ifstream in(local_filename.c_str(), std::ios_base::binary);
+ IStreamConstructibleArchive ar(in);
+
+ detail::parallel::graph_loader<
+ graph_type, IStreamConstructibleArchive, InVertexListS
+ > loader(*this, ar);
+
+# ifdef PBGL_SERIALIZE_DEBUG
+ std::cout << "Process " << id << " done loading.\n";
+# endif
+
+ synchronize(pg);
+}
+
+template <PBGL_DISTRIB_ADJLIST_TEMPLATE_PARMS>
+template <class OStreamConstructibleArchive>
+void PBGL_DISTRIB_ADJLIST_TYPE::save(std::string const& filename) const
+{
+ // We retag the property list here so that bundled properties are
+ // properly placed into property<vertex_bundle_t, Bundle>.
+ typedef typename boost::detail::retag_property_list<
+ vertex_bundle_t, vertex_property_type
+ >::type vertex_property_type;
+
+ typedef typename config_type::VertexListS vertex_list_selector;
+
+ process_group_type pg = process_group();
+ process_id_type id = process_id(pg);
+
+ if (filesystem::exists(filename) && !filesystem::is_directory(filename))
+ boost::throw_exception(std::runtime_error("entry exists, but is not a directory"));
+
+ filesystem::remove_all(filename);
+ filesystem::create_directory(filename);
+
+ synchronize(pg);
+
+ std::string local_filename = filename + "/" +
+ lexical_cast<std::string>(id);
+
+ std::ofstream out(local_filename.c_str(), std::ios_base::binary);
+ OStreamConstructibleArchive ar(out);
+
+ using serialization::make_nvp;
+
+ typename process_group_type::process_size_type num_processes_ = num_processes(pg);
+ ar << make_nvp("num_processes", num_processes_);
+ ar << BOOST_SERIALIZATION_NVP(id);
+ ar << make_nvp("mapping", this->distribution().mapping());
+
+ int V = num_vertices(*this);
+ ar << BOOST_SERIALIZATION_NVP(V);
+
+ BGL_FORALL_VERTICES_T(v, *this, graph_type)
+ {
+ local_vertex_descriptor local_descriptor(local(v));
+ detail::parallel::maybe_save_local_descriptor(
+ ar, local_descriptor, vertex_list_selector());
+ detail::parallel::maybe_save_properties(
+ ar, "vertex_property"
+ , static_cast<vertex_property_type const&>(get(vertex_all_t(), *this, v))
+ );
+ }
+
+ detail::parallel::save_edges(ar, *this, directed_selector());
+
+ ar << make_nvp("distribution", this->distribution());
+}
+
+} // namespace boost
+
+#endif // BOOST_GRAPH_DISTRIBUTED_ADJLIST_SERIALIZATION_070925_HPP
+

Added: trunk/boost/graph/distributed/betweenness_centrality.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/betweenness_centrality.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,1718 @@
+// Copyright 2004 The Trustees of Indiana University.
+
+// Distributed under 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
+#ifndef BOOST_GRAPH_PARALLEL_BRANDES_BETWEENNESS_CENTRALITY_HPP
+#define BOOST_GRAPH_PARALLEL_BRANDES_BETWEENNESS_CENTRALITY_HPP
+
+#ifndef BOOST_GRAPH_USE_MPI
+#error "Parallel BGL files should not be included unless <boost/graph/use_mpi.hpp> has been included"
+#endif
+
+// #define COMPUTE_PATH_COUNTS_INLINE
+
+#include <boost/graph/betweenness_centrality.hpp>
+#include <boost/graph/overloading.hpp>
+#include <boost/graph/distributed/concepts.hpp>
+#include <boost/graph/graph_traits.hpp>
+#include <boost/config.hpp>
+
+// For additive_reducer
+#include <boost/graph/distributed/distributed_graph_utility.hpp>
+#include <boost/type_traits/is_convertible.hpp>
+#include <boost/type_traits/is_same.hpp>
+#include <boost/property_map/property_map.hpp>
+#include <boost/graph/named_function_params.hpp>
+
+#include <boost/property_map/parallel/distributed_property_map.hpp>
+#include <boost/graph/distributed/detail/dijkstra_shortest_paths.hpp>
+#include <boost/tuple/tuple.hpp>
+
+// NGE - Needed for minstd_rand at L807, should pass vertex list
+// or generator instead
+#include <boost/random/linear_congruential.hpp>
+
+#include <algorithm>
+#include <stack>
+#include <vector>
+
+// Appending reducer
+template <typename T>
+struct append_reducer {
+ BOOST_STATIC_CONSTANT(bool, non_default_resolver = true);
+
+ template<typename K>
+ T operator()(const K&) const { return T(); }
+
+ template<typename K>
+ T operator()(const K&, const T& x, const T& y) const
+ {
+ T z(x.begin(), x.end());
+ for (typename T::const_iterator iter = y.begin(); iter != y.end(); ++iter)
+ if (std::find(z.begin(), z.end(), *iter) == z.end())
+ z.push_back(*iter);
+
+ return z;
+ }
+};
+
+namespace boost {
+
+ namespace serialization {
+
+ // TODO(nge): Write generalized serialization for tuples
+ template<typename Archive, typename T1, typename T2, typename T3,
+ typename T4>
+ void serialize(Archive & ar,
+ boost::tuple<T1,T2,T3, T4>& t,
+ const unsigned int)
+ {
+ ar & get<0>(t);
+ ar & get<1>(t);
+ ar & get<2>(t);
+ ar & get<3>(t);
+ }
+
+ } // serialization
+
+ namespace graph { namespace distributed {
+
+ // HACKY: Overload get on a tuple to return the value at the key indicated
+ // by the first element of the tuple so that we can use tuples in a distributed queue
+ template<typename PropertyMap, typename Vertex, typename X, typename Y, typename Z>
+ inline
+ typename PropertyMap::value_type
+ get(PropertyMap& pm, boost::tuple<Vertex, X, Y, Z> const& t)
+ {
+ return get(pm, boost::tuples::get<0>(t));
+ }
+
+ // HACKY: Same as above for std::pair
+ template<typename PropertyMap, typename Vertex, typename X>
+ inline
+ typename PropertyMap::value_type
+ get(PropertyMap& pm, std::pair<Vertex, X> const& t)
+ {
+ return get(pm, t.first);
+ }
+
+ } } // graph::distributed
+
+ namespace graph { namespace parallel { namespace detail {
+
+ template<typename DistanceMap, typename IncomingMap>
+ class betweenness_centrality_msg_value
+ {
+ typedef typename property_traits<DistanceMap>::value_type distance_type;
+ typedef typename property_traits<IncomingMap>::value_type incoming_type;
+ typedef typename incoming_type::value_type incoming_value_type;
+
+ public:
+ typedef std::pair<distance_type, incoming_value_type> type;
+
+ static type create(distance_type dist, incoming_value_type source)
+ { return std::make_pair(dist, source); }
+ };
+
+
+ /************************************************************************/
+ /* Delta-stepping Betweenness Centrality */
+ /************************************************************************/
+
+ template<typename Graph, typename DistanceMap, typename IncomingMap,
+ typename EdgeWeightMap, typename PathCountMap
+#ifdef COMPUTE_PATH_COUNTS_INLINE
+ , typename IsSettledMap, typename VertexIndexMap
+#endif
+ >
+ class betweenness_centrality_delta_stepping_impl {
+ // Could inherit from delta_stepping_impl to get run() method
+ // but for the time being it's just reproduced here
+
+ 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 property_traits<IncomingMap>::value_type IncomingType;
+ 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 betweenness_centrality_msg_value<DistanceMap, IncomingMap>
+ 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:
+
+ // Must supply delta, ctor that guesses delta removed
+ betweenness_centrality_delta_stepping_impl(const Graph& g,
+ DistanceMap distance,
+ IncomingMap incoming,
+ EdgeWeightMap weight,
+ PathCountMap path_count,
+#ifdef COMPUTE_PATH_COUNTS_INLINE
+ IsSettledMap is_settled,
+ VertexIndexMap vertex_index,
+#endif
+ Dist delta);
+
+ 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()
+ {
+ using boost::graph::parallel::synchronize;
+ synchronize(pg);
+ }
+
+ // Setup triggers for msg_relax messages
+ void setup_triggers()
+ {
+ using boost::graph::parallel::simple_trigger;
+ simple_trigger(pg, msg_relax, this,
+ &betweenness_centrality_delta_stepping_impl::handle_msg_relax);
+ }
+
+ void handle_msg_relax(int /*source*/, int /*tag*/,
+ const std::pair<Vertex, typename MessageValue::type>& data,
+ trigger_receive_context)
+ { relax(data.second.second, data.first, data.second.first); }
+
+ const Graph& g;
+ IncomingMap incoming;
+ DistanceMap distance;
+ EdgeWeightMap weight;
+ PathCountMap path_count;
+#ifdef COMPUTE_PATH_COUNTS_INLINE
+ IsSettledMap is_settled;
+ VertexIndexMap vertex_index;
+#endif
+ 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 DistanceMap, typename IncomingMap,
+ typename EdgeWeightMap, typename PathCountMap
+#ifdef COMPUTE_PATH_COUNTS_INLINE
+ , typename IsSettledMap, typename VertexIndexMap
+#endif
+ >
+ betweenness_centrality_delta_stepping_impl<
+ Graph, DistanceMap, IncomingMap, EdgeWeightMap, PathCountMap
+#ifdef COMPUTE_PATH_COUNTS_INLINE
+ , IsSettledMap, VertexIndexMap
+#endif
+ >::
+ betweenness_centrality_delta_stepping_impl(const Graph& g,
+ DistanceMap distance,
+ IncomingMap incoming,
+ EdgeWeightMap weight,
+ PathCountMap path_count,
+#ifdef COMPUTE_PATH_COUNTS_INLINE
+ IsSettledMap is_settled,
+ VertexIndexMap vertex_index,
+#endif
+ Dist delta)
+ : g(g),
+ distance(distance),
+ incoming(incoming),
+ weight(weight),
+ path_count(path_count),
+#ifdef COMPUTE_PATH_COUNTS_INLINE
+ is_settled(is_settled),
+ vertex_index(vertex_index),
+#endif
+ 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 DistanceMap, typename IncomingMap,
+ typename EdgeWeightMap, typename PathCountMap
+#ifdef COMPUTE_PATH_COUNTS_INLINE
+ , typename IsSettledMap, typename VertexIndexMap
+#endif
+ >
+ void
+ betweenness_centrality_delta_stepping_impl<
+ Graph, DistanceMap, IncomingMap, EdgeWeightMap, PathCountMap
+#ifdef COMPUTE_PATH_COUNTS_INLINE
+ , IsSettledMap, VertexIndexMap
+#endif
+ >::
+ run(Vertex s)
+ {
+ typedef typename boost::graph::parallel::process_group_type<Graph>::type
+ process_group_type;
+ typename process_group_type::process_id_type id = process_id(pg);
+
+ 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) == id)
+ // 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);
+
+#ifdef COMPUTE_PATH_COUNTS_INLINE
+ // Synchronize here to deliver initial relaxation since we don't
+ // synchronize at the beginning of the inner loop any more
+ synchronize();
+
+ // Incoming edge count map is an implementation detail and should
+ // be freed as soon as possible so build it here
+ typedef typename graph_traits<Graph>::edges_size_type edges_size_type;
+
+ std::vector<edges_size_type> incoming_edge_countS(num_vertices(g));
+ iterator_property_map<typename std::vector<edges_size_type>::iterator, VertexIndexMap>
+ incoming_edge_count(incoming_edge_countS.begin(), vertex_index);
+#endif
+
+ BucketIndex max_bucket = (std::numeric_limits<BucketIndex>::max)();
+ BucketIndex current_bucket = 0;
+ do {
+#ifdef COMPUTE_PATH_COUNTS_INLINE
+ // We need to clear the outgoing map after every bucket so just build it here
+ std::vector<IncomingType> outgoingS(num_vertices(g));
+ IncomingMap outgoing(outgoingS.begin(), vertex_index);
+
+ outgoing.set_reduce(append_reducer<IncomingType>());
+#else
+ // Synchronize with all of the other processes.
+ synchronize();
+#endif
+
+ // 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;
+
+ // 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;
+
+ // 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();
+
+ // 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));
+
+#ifdef COMPUTE_PATH_COUNTS_INLINE
+ // Set outgoing paths
+ IncomingType in = get(incoming, u);
+ for (typename IncomingType::iterator pred = in.begin(); pred != in.end(); ++pred)
+ if (get(owner, *pred) == id) {
+ IncomingType x = get(outgoing, *pred);
+ if (std::find(x.begin(), x.end(), u) == x.end())
+ x.push_back(u);
+ put(outgoing, *pred, x);
+ } else {
+ IncomingType in;
+ in.push_back(u);
+ put(outgoing, *pred, in);
+ }
+
+ // Set incoming edge counts
+ put(incoming_edge_count, u, in.size());
+#endif
+ }
+
+#ifdef COMPUTE_PATH_COUNTS_INLINE
+ synchronize(); // Deliver heavy edge relaxations and outgoing paths
+
+ // Build Queue
+ typedef typename property_traits<PathCountMap>::value_type PathCountType;
+ typedef std::pair<Vertex, PathCountType> queue_value_type;
+ typedef typename property_map<Graph, vertex_owner_t>::const_type OwnerMap;
+
+ typedef boost::queue<queue_value_type> local_queue_type;
+ typedef boost::graph::distributed::distributed_queue<process_group_type,
+ OwnerMap,
+ local_queue_type> dist_queue_type;
+ dist_queue_type Q(pg, owner);
+
+ // Find sources to initialize queue
+ BGL_FORALL_VERTICES_T(v, g, Graph) {
+ if (get(is_settled, v) && !(get(outgoing, v).empty())) {
+ put(incoming_edge_count, v, 1);
+ Q.push(std::make_pair(v, 0)); // Push this vertex with no additional path count
+ }
+ }
+
+ // Set path counts for vertices in this bucket
+ while (!Q.empty()) {
+ queue_value_type t = Q.top(); Q.pop();
+ Vertex v = t.first;
+ PathCountType p = t.second;
+
+ put(path_count, v, get(path_count, v) + p);
+ put(incoming_edge_count, v, get(incoming_edge_count, v) - 1);
+
+ if (get(incoming_edge_count, v) == 0) {
+ IncomingType out = get(outgoing, v);
+ for (typename IncomingType::iterator iter = out.begin(); iter != out.end(); ++iter)
+ Q.push(std::make_pair(*iter, get(path_count, v)));
+ }
+ }
+
+ // Mark the vertices in this bucket settled
+ for (typename std::vector<Vertex>::iterator iter = deleted_vertices.begin();
+ iter != deleted_vertices.end(); ++iter)
+ put(is_settled, *iter, true);
+
+ // No need to clear path count map as it is never read/written remotely
+ // No need to clear outgoing map as it is re-alloced every bucket
+#endif
+
+ // 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 DistanceMap, typename IncomingMap,
+ typename EdgeWeightMap, typename PathCountMap
+#ifdef COMPUTE_PATH_COUNTS_INLINE
+ , typename IsSettledMap, typename VertexIndexMap
+#endif
+ >
+ void
+ betweenness_centrality_delta_stepping_impl<
+ Graph, DistanceMap, IncomingMap, EdgeWeightMap, PathCountMap
+#ifdef COMPUTE_PATH_COUNTS_INLINE
+ , IsSettledMap, VertexIndexMap
+#endif
+ >::
+ relax(Vertex u, Vertex v, Dist x)
+ {
+
+ if (x <= get(distance, v)) {
+
+ // We're relaxing the edge to vertex v.
+ if (get(owner, v) == process_id(pg)) {
+ if (x < get(distance, v)) {
+ // 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 tentative distance information and incoming, path_count
+ if (u != v) put(incoming, v, IncomingType(1, u));
+ put(distance, v, x);
+ } // u != v covers initial source relaxation and self-loops
+ else if (x == get(distance, v) && u != v) {
+
+ // Add incoming edge if it's not already in the list
+ IncomingType in = get(incoming, v);
+ if (std::find(in.begin(), in.end(), u) == in.end()) {
+ in.push_back(u);
+ put(incoming, v, in);
+ }
+ }
+ } else {
+ // 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);
+ }
+ }
+ }
+
+ /************************************************************************/
+ /* Shortest Paths function object for betweenness centrality */
+ /************************************************************************/
+
+ template<typename WeightMap>
+ struct brandes_shortest_paths {
+ typedef typename property_traits<WeightMap>::value_type weight_type;
+
+ brandes_shortest_paths()
+ : weight(1), delta(0) { }
+ brandes_shortest_paths(weight_type delta)
+ : weight(1), delta(delta) { }
+ brandes_shortest_paths(WeightMap w)
+ : weight(w), delta(0) { }
+ brandes_shortest_paths(WeightMap w, weight_type delta)
+ : weight(w), delta(delta) { }
+
+ template<typename Graph, typename IncomingMap, typename DistanceMap,
+ typename PathCountMap
+#ifdef COMPUTE_PATH_COUNTS_INLINE
+ , typename IsSettledMap, typename VertexIndexMap
+#endif
+
+ >
+ void
+ operator()(Graph& g,
+ typename graph_traits<Graph>::vertex_descriptor s,
+ IncomingMap incoming,
+ DistanceMap distance,
+ PathCountMap path_count
+#ifdef COMPUTE_PATH_COUNTS_INLINE
+ , IsSettledMap is_settled,
+ VertexIndexMap vertex_index
+#endif
+ )
+ {
+ typedef typename property_traits<DistanceMap>::value_type
+ distance_type;
+
+ typedef std::plus<distance_type> Combine;
+ typedef std::less<distance_type> Compare;
+
+ // The "distance" map needs to act like one, retrieving the default
+ // value of infinity.
+ set_property_map_role(vertex_distance, distance);
+
+ // Only calculate delta the first time operator() is called
+ // This presumes g is the same every time, but so does the fact
+ // that we're reusing the weight map
+ if (delta == 0)
+ set_delta(g);
+
+ // TODO (NGE): Restructure the code so we don't have to construct
+ // impl every time?
+ betweenness_centrality_delta_stepping_impl<
+ Graph, DistanceMap, IncomingMap, WeightMap, PathCountMap
+#ifdef COMPUTE_PATH_COUNTS_INLINE
+ , IsSettledMap, VertexIndexMap
+#endif
+ >
+ impl(g, distance, incoming, weight, path_count,
+#ifdef COMPUTE_PATH_COUNTS_INLINE
+ is_settled, vertex_index,
+#endif
+ delta);
+
+ impl.run(s);
+ }
+
+ private:
+
+ template <typename Graph>
+ void
+ set_delta(const Graph& g)
+ {
+ using boost::parallel::all_reduce;
+ using boost::parallel::maximum;
+ using std::max;
+
+ typedef typename graph_traits<Graph>::degree_size_type Degree;
+ typedef weight_type Dist;
+
+ // 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(process_group(g), max_edge_weight, maximum<Dist>());
+ max_degree = all_reduce(process_group(g), 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;
+ }
+
+ WeightMap weight;
+ weight_type delta;
+ };
+
+ // Perform a single SSSP from the specified vertex and update the centrality map(s)
+ template<typename Graph, typename CentralityMap, typename EdgeCentralityMap,
+ typename IncomingMap, typename DistanceMap, typename DependencyMap,
+ typename PathCountMap,
+#ifdef COMPUTE_PATH_COUNTS_INLINE
+ typename IsSettledMap,
+#endif
+ typename VertexIndexMap, typename ShortestPaths>
+ void
+ do_brandes_sssp(const Graph& g,
+ CentralityMap centrality,
+ EdgeCentralityMap edge_centrality_map,
+ IncomingMap incoming,
+ DistanceMap distance,
+ DependencyMap dependency,
+ PathCountMap path_count,
+#ifdef COMPUTE_PATH_COUNTS_INLINE
+ IsSettledMap is_settled,
+#endif
+ VertexIndexMap vertex_index,
+ ShortestPaths shortest_paths,
+ typename graph_traits<Graph>::vertex_descriptor s)
+ {
+ using boost::detail::graph::update_centrality;
+ using boost::graph::parallel::process_group;
+
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+ typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
+ typedef typename graph_traits<Graph>::edges_size_type edges_size_type;
+
+ typedef typename property_traits<IncomingMap>::value_type incoming_type;
+ typedef typename property_traits<DistanceMap>::value_type distance_type;
+ typedef typename property_traits<DependencyMap>::value_type dependency_type;
+ typedef typename property_traits<PathCountMap>::value_type path_count_type;
+
+ typedef typename incoming_type::iterator incoming_iterator;
+
+ typedef typename property_map<Graph, vertex_owner_t>::const_type OwnerMap;
+ OwnerMap owner = get(vertex_owner, g);
+
+ typedef typename boost::graph::parallel::process_group_type<Graph>::type
+ process_group_type;
+ process_group_type pg = process_group(g);
+ typename process_group_type::process_id_type id = process_id(pg);
+
+ // TODO: Is it faster not to clear some of these maps?
+ // Initialize for this iteration
+ distance.clear();
+ incoming.clear();
+ path_count.clear();
+ dependency.clear();
+ BGL_FORALL_VERTICES_T(v, g, Graph) {
+ put(path_count, v, 0);
+ put(dependency, v, 0);
+ }
+
+ if (get(owner, s) == id) {
+ put(incoming, s, incoming_type());
+#ifdef COMPUTE_PATH_COUNTS_INLINE
+ put(path_count, s, 1);
+ put(is_settled, s, true);
+#endif
+ }
+
+ // Execute the shortest paths algorithm. This will be either
+ // a weighted or unweighted customized breadth-first search,
+ shortest_paths(g, s, incoming, distance, path_count
+#ifdef COMPUTE_PATH_COUNTS_INLINE
+ , is_settled, vertex_index
+#endif
+ );
+
+#ifndef COMPUTE_PATH_COUNTS_INLINE
+
+ //
+ // TODO: Optimize case where source has no out-edges
+ //
+
+ // Count of incoming edges to tell when all incoming edges have been relaxed in
+ // the induced shortest paths DAG
+ std::vector<edges_size_type> incoming_edge_countS(num_vertices(g));
+ iterator_property_map<typename std::vector<edges_size_type>::iterator, VertexIndexMap>
+ incoming_edge_count(incoming_edge_countS.begin(), vertex_index);
+
+ BGL_FORALL_VERTICES_T(v, g, Graph) {
+ put(incoming_edge_count, v, get(incoming, v).size());
+ }
+
+ if (get(owner, s) == id) {
+ put(incoming_edge_count, s, 1);
+ put(incoming, s, incoming_type());
+ }
+
+ std::vector<incoming_type> outgoingS(num_vertices(g));
+ iterator_property_map<typename std::vector<incoming_type>::iterator, VertexIndexMap>
+ outgoing(outgoingS.begin(), vertex_index);
+
+ outgoing.set_reduce(append_reducer<incoming_type>());
+
+ // Mark forward adjacencies in DAG of shortest paths
+
+ // TODO: It's possible to do this using edge flags but it's not currently done this way
+ // because during traversal of the DAG we would have to examine all out edges
+ // which would lead to more memory accesses and a larger cache footprint.
+ //
+ // In the bidirectional graph case edge flags would be an excellent way of marking
+ // edges in the DAG of shortest paths
+ BGL_FORALL_VERTICES_T(v, g, Graph) {
+ incoming_type i = get(incoming, v);
+ for (typename incoming_type::iterator iter = i.begin(); iter != i.end(); ++iter) {
+ if (get(owner, *iter) == id) {
+ incoming_type x = get(outgoing, *iter);
+ if (std::find(x.begin(), x.end(), v) == x.end())
+ x.push_back(v);
+ put(outgoing, *iter, x);
+ } else {
+ incoming_type in;
+ in.push_back(v);
+ put(outgoing, *iter, in);
+ }
+ }
+ }
+
+ synchronize(pg);
+
+ // Traverse DAG induced by forward edges in dependency order and compute path counts
+ {
+ typedef std::pair<vertex_descriptor, path_count_type> queue_value_type;
+
+ typedef boost::queue<queue_value_type> local_queue_type;
+ typedef boost::graph::distributed::distributed_queue<process_group_type,
+ OwnerMap,
+ local_queue_type> dist_queue_type;
+ dist_queue_type Q(pg, owner);
+
+ if (get(owner, s) == id)
+ Q.push(std::make_pair(s, 1));
+
+ while (!Q.empty()) {
+ queue_value_type t = Q.top(); Q.pop();
+ vertex_descriptor v = t.first;
+ path_count_type p = t.second;
+
+ put(path_count, v, get(path_count, v) + p);
+ put(incoming_edge_count, v, get(incoming_edge_count, v) - 1);
+
+ if (get(incoming_edge_count, v) == 0) {
+ incoming_type out = get(outgoing, v);
+ for (typename incoming_type::iterator iter = out.begin(); iter != out.end(); ++iter)
+ Q.push(std::make_pair(*iter, get(path_count, v)));
+ }
+ }
+ }
+
+#endif // COMPUTE_PATH_COUNTS_INLINE
+
+ //
+ // Compute dependencies
+ //
+
+
+ // Build the distributed_queue
+ // Value type consists of 1) target of update 2) source of update
+ // 3) dependency of source 4) path count of source
+ typedef boost::tuple<vertex_descriptor, vertex_descriptor, dependency_type, path_count_type>
+ queue_value_type;
+
+ typedef boost::queue<queue_value_type> local_queue_type;
+ typedef boost::graph::distributed::distributed_queue<process_group_type,
+ OwnerMap,
+ local_queue_type> dist_queue_type;
+ dist_queue_type Q(pg, owner);
+
+ // Calculate number of vertices each vertex depends on, when a vertex has been pushed
+ // that number of times then we will update it
+ // AND Request path counts of sources of incoming edges
+ std::vector<dependency_type> dependency_countS(num_vertices(g), 0);
+ iterator_property_map<typename std::vector<dependency_type>::iterator, VertexIndexMap>
+ dependency_count(dependency_countS.begin(), vertex_index);
+
+ dependency_count.set_reduce(boost::graph::distributed::additive_reducer<dependency_type>());
+
+ path_count.set_max_ghost_cells(0);
+
+ BGL_FORALL_VERTICES_T(v, g, Graph) {
+ if (get(distance, v) < (std::numeric_limits<distance_type>::max)()) {
+ incoming_type el = get(incoming, v);
+ for (incoming_iterator vw = el.begin(); vw != el.end(); ++vw) {
+ if (get(owner, *vw) == id)
+ put(dependency_count, *vw, get(dependency_count, *vw) + 1);
+ else {
+ put(dependency_count, *vw, 1);
+
+ // Request path counts
+ get(path_count, *vw);
+ }
+
+ // request() doesn't work here, perhaps because we don't have a copy of this
+ // ghost cell already?
+ }
+ }
+ }
+
+ synchronize(pg);
+
+ // Push vertices with non-zero distance/path count and zero dependency count
+ BGL_FORALL_VERTICES_T(v, g, Graph) {
+ if (get(distance, v) < (std::numeric_limits<distance_type>::max)()
+ && get(dependency_count, v) == 0)
+ Q.push(boost::make_tuple(v, v, get(dependency, v), get(path_count, v)));
+ }
+
+ dependency.set_max_ghost_cells(0);
+ while(!Q.empty()) {
+
+ queue_value_type x = Q.top(); Q.pop();
+ vertex_descriptor w = get<0>(x);
+ vertex_descriptor source = get<1>(x);
+ dependency_type dep = get<2>(x);
+ path_count_type pc = get<3>(x);
+
+ cache(dependency, source, dep);
+ cache(path_count, source, pc);
+
+ if (get(dependency_count, w) != 0)
+ put(dependency_count, w, get(dependency_count, w) - 1);
+
+ if (get(dependency_count, w) == 0) {
+
+ // Update dependency and centrality of sources of incoming edges
+ incoming_type el = get(incoming, w);
+ for (incoming_iterator vw = el.begin(); vw != el.end(); ++vw) {
+ vertex_descriptor v = *vw;
+
+ assert(get(path_count, w) != 0);
+
+ dependency_type factor = dependency_type(get(path_count, v))
+ / dependency_type(get(path_count, w));
+ factor *= (dependency_type(1) + get(dependency, w));
+
+ if (get(owner, v) == id)
+ put(dependency, v, get(dependency, v) + factor);
+ else
+ put(dependency, v, factor);
+
+ update_centrality(edge_centrality_map, v, factor);
+ }
+
+ if (w != s)
+ update_centrality(centrality, w, get(dependency, w));
+
+ // Push sources of edges in incoming edge list
+ for (incoming_iterator vw = el.begin(); vw != el.end(); ++vw)
+ Q.push(boost::make_tuple(*vw, w, get(dependency, w), get(path_count, w)));
+ }
+ }
+ }
+
+ template<typename Graph, typename CentralityMap, typename EdgeCentralityMap,
+ typename IncomingMap, typename DistanceMap, typename DependencyMap,
+ typename PathCountMap, typename VertexIndexMap, typename ShortestPaths,
+ typename Buffer>
+ void
+ brandes_betweenness_centrality_impl(const Graph& g,
+ CentralityMap centrality,
+ EdgeCentralityMap edge_centrality_map,
+ IncomingMap incoming,
+ DistanceMap distance,
+ DependencyMap dependency,
+ PathCountMap path_count,
+ VertexIndexMap vertex_index,
+ ShortestPaths shortest_paths,
+ Buffer sources)
+ {
+ using boost::detail::graph::init_centrality_map;
+ using boost::detail::graph::divide_centrality_by_two;
+ using boost::graph::parallel::process_group;
+
+ typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
+ typedef typename graph_traits<Graph>::edge_iterator edge_iterator;
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+ typedef typename graph_traits<Graph>::vertices_size_type vertices_size_type;
+
+ typedef typename property_traits<DistanceMap>::value_type distance_type;
+ typedef typename property_traits<DependencyMap>::value_type dependency_type;
+
+ // Initialize centrality
+ init_centrality_map(vertices(g), centrality);
+ init_centrality_map(edges(g), edge_centrality_map);
+
+ // Set the reduction operation on the dependency map to be addition
+ dependency.set_reduce(boost::graph::distributed::additive_reducer<dependency_type>());
+ distance.set_reduce(boost::graph::distributed::choose_min_reducer<distance_type>());
+
+ // Don't allow remote procs to write incoming or path_count maps
+ // updating them is handled inside the betweenness_centrality_queue
+ incoming.set_consistency_model(0);
+ path_count.set_consistency_model(0);
+
+ typedef typename boost::graph::parallel::process_group_type<Graph>::type
+ process_group_type;
+ process_group_type pg = process_group(g);
+
+#ifdef COMPUTE_PATH_COUNTS_INLINE
+ // Build is_settled maps
+ std::vector<bool> is_settledS(num_vertices(g));
+ typedef iterator_property_map<std::vector<bool>::iterator, VertexIndexMap>
+ IsSettledMap;
+
+ IsSettledMap is_settled(is_settledS.begin(), vertex_index);
+#endif
+
+ if (!sources.empty()) {
+ // DO SSSPs
+ while (!sources.empty()) {
+ do_brandes_sssp(g, centrality, edge_centrality_map, incoming, distance,
+ dependency, path_count,
+#ifdef COMPUTE_PATH_COUNTS_INLINE
+ is_settled,
+#endif
+ vertex_index, shortest_paths, sources.top());
+ sources.pop();
+ }
+ } else { // Exact Betweenness Centrality
+ typedef typename graph_traits<Graph>::vertices_size_type vertices_size_type;
+ vertices_size_type n = num_vertices(g);
+ n = boost::parallel::all_reduce(pg, n, std::plus<vertices_size_type>());
+
+ for (int i = 0; i < n; ++i) {
+ vertex_descriptor v = vertex(i, g);
+
+ do_brandes_sssp(g, centrality, edge_centrality_map, incoming, distance,
+ dependency, path_count,
+#ifdef COMPUTE_PATH_COUNTS_INLINE
+ is_settled,
+#endif
+ vertex_index, shortest_paths, v);
+ }
+ }
+
+ typedef typename graph_traits<Graph>::directed_category directed_category;
+ const bool is_undirected =
+ is_convertible<directed_category*, undirected_tag*>::value;
+ if (is_undirected) {
+ divide_centrality_by_two(vertices(g), centrality);
+ divide_centrality_by_two(edges(g), edge_centrality_map);
+ }
+ }
+
+ template<typename Graph, typename CentralityMap, typename EdgeCentralityMap,
+ typename IncomingMap, typename DistanceMap, typename DependencyMap,
+ typename PathCountMap, typename VertexIndexMap, typename ShortestPaths,
+ typename Stack>
+ void
+ do_sequential_brandes_sssp(const Graph& g,
+ CentralityMap centrality,
+ EdgeCentralityMap edge_centrality_map,
+ IncomingMap incoming,
+ DistanceMap distance,
+ DependencyMap dependency,
+ PathCountMap path_count,
+ VertexIndexMap vertex_index,
+ ShortestPaths shortest_paths,
+ Stack& ordered_vertices,
+ typename graph_traits<Graph>::vertex_descriptor v)
+ {
+ using boost::detail::graph::update_centrality;
+
+ typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+
+ typedef typename property_traits<IncomingMap>::value_type incoming_type;
+
+ // Initialize for this iteration
+ BGL_FORALL_VERTICES_T(w, g, Graph) {
+ // put(path_count, w, 0);
+ incoming[w].clear();
+ put(dependency, w, 0);
+ }
+
+ put(path_count, v, 1);
+ incoming[v].clear();
+
+ // Execute the shortest paths algorithm. This will be either
+ // Dijkstra's algorithm or a customized breadth-first search,
+ // depending on whether the graph is weighted or unweighted.
+ shortest_paths(g, v, ordered_vertices, incoming, distance,
+ path_count, vertex_index);
+
+ while (!ordered_vertices.empty()) {
+ vertex_descriptor w = ordered_vertices.top();
+ ordered_vertices.pop();
+
+ typedef typename property_traits<IncomingMap>::value_type
+ incoming_type;
+ typedef typename incoming_type::iterator incoming_iterator;
+ typedef typename property_traits<DependencyMap>::value_type
+ dependency_type;
+
+ for (incoming_iterator vw = incoming[w].begin();
+ vw != incoming[w].end(); ++vw) {
+ vertex_descriptor v = source(*vw, g);
+ dependency_type factor = dependency_type(get(path_count, v))
+ / dependency_type(get(path_count, w));
+ factor *= (dependency_type(1) + get(dependency, w));
+ put(dependency, v, get(dependency, v) + factor);
+ update_centrality(edge_centrality_map, *vw, factor);
+ }
+
+ if (w != v) {
+ update_centrality(centrality, w, get(dependency, w));
+ }
+ }
+ }
+
+ // Betweenness Centrality variant that duplicates graph across processors
+ // and parallizes SSSPs
+ // This function expects a non-distributed graph and property-maps
+ template<typename ProcessGroup, typename Graph,
+ typename CentralityMap, typename EdgeCentralityMap,
+ typename IncomingMap, typename DistanceMap,
+ typename DependencyMap, typename PathCountMap,
+ typename VertexIndexMap, typename ShortestPaths>
+ void
+ non_distributed_brandes_betweenness_centrality_impl(const ProcessGroup& pg,
+ const Graph& g,
+ CentralityMap centrality,
+ EdgeCentralityMap edge_centrality_map,
+ IncomingMap incoming, // P
+ DistanceMap distance, // d
+ DependencyMap dependency, // delta
+ PathCountMap path_count, // sigma
+ VertexIndexMap vertex_index,
+ ShortestPaths shortest_paths,
+ typename graph_traits<Graph>::vertices_size_type num_sources = 0)
+ {
+ using boost::detail::graph::init_centrality_map;
+ using boost::detail::graph::divide_centrality_by_two;
+ using boost::graph::parallel::process_group;
+
+ typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
+ typedef typename graph_traits<Graph>::edge_iterator edge_iterator;
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+ typedef typename graph_traits<Graph>::vertices_size_type vertices_size_type;
+
+ typedef typename property_traits<DistanceMap>::value_type distance_type;
+
+ typedef ProcessGroup process_group_type;
+
+ typename process_group_type::process_id_type id = process_id(pg);
+ typename process_group_type::process_size_type p = num_processes(pg);
+
+ // Initialize centrality
+ init_centrality_map(vertices(g), centrality);
+ init_centrality_map(edges(g), edge_centrality_map);
+
+ std::stack<vertex_descriptor> ordered_vertices;
+
+ if (num_sources != 0) {
+
+ minstd_rand gen;
+ uniform_int<vertices_size_type> rand_vertex(0, num_vertices(g) - 1);
+ std::vector<vertex_descriptor> sources;
+ typename std::vector<vertex_descriptor>::iterator s, s_end;
+
+ for (int i = 0; i < num_sources; ++i) {
+ vertex_iterator iter = vertices(g).first;
+ std::advance(iter, rand_vertex(gen));
+
+ if (out_degree(*iter, g) != 0
+ && std::find(sources.begin(), sources.end(), *iter) == sources.end()) {
+ sources.push_back(*iter);
+ }
+ }
+
+ int N = sources.size();
+ s = sources.begin(), s_end = sources.end();
+ if (id != (p - 1)) {
+ s_end = s;
+ std::advance(s_end, (id + 1) * N/p);
+ }
+ std::advance(s, id * N/p);
+
+ for ( ; s != s_end; ++s)
+ do_sequential_brandes_sssp(g, centrality, edge_centrality_map, incoming,
+ distance, dependency, path_count, vertex_index,
+ shortest_paths, ordered_vertices, *s);
+ } else {
+ vertex_iterator s, s_end;
+
+ int N = num_vertices(g);
+ tie(s, s_end) = vertices(g);
+ if (id != (p - 1)) {
+ s_end = s;
+ std::advance(s_end, (id + 1) * N/p);
+ }
+ std::advance(s, id * N/p);
+
+ for ( ; s != s_end; ++s)
+ do_sequential_brandes_sssp(g, centrality, edge_centrality_map, incoming,
+ distance, dependency, path_count, vertex_index,
+ shortest_paths, ordered_vertices, *s);
+ }
+
+ typedef typename graph_traits<Graph>::directed_category directed_category;
+ const bool is_undirected =
+ is_convertible<directed_category*, undirected_tag*>::value;
+ if (is_undirected) {
+ divide_centrality_by_two(vertices(g), centrality);
+ divide_centrality_by_two(edges(g), edge_centrality_map);
+ }
+
+ // Merge the centrality maps by summing the values at each vertex)
+ // TODO(nge): this copy-out, reduce, copy-in is lame
+ typedef typename property_traits<CentralityMap>::value_type centrality_type;
+ typedef typename property_traits<EdgeCentralityMap>::value_type edge_centrality_type;
+
+ std::vector<centrality_type> centrality_v(num_vertices(g));
+ std::vector<edge_centrality_type> edge_centrality_v;
+ edge_centrality_v.reserve(num_edges(g));
+
+ BGL_FORALL_VERTICES_T(v, g, Graph) {
+ centrality_v[get(vertex_index, v)] = get(centrality, v);
+ }
+
+ // Skip when EdgeCentralityMap is a dummy_property_map
+ if (!is_same<EdgeCentralityMap, dummy_property_map>::value) {
+ BGL_FORALL_EDGES_T(e, g, Graph) {
+ edge_centrality_v.push_back(get(edge_centrality_map, e));
+ }
+ // NGE: If we trust that the order of elements in the vector isn't changed in the
+ // all_reduce below then this method avoids the need for an edge index map
+ }
+
+ using boost::parallel::all_reduce;
+
+ all_reduce(pg, &centrality_v[0], &centrality_v[centrality_v.size()],
+ &centrality_v[0], std::plus<centrality_type>());
+
+ if (edge_centrality_v.size())
+ all_reduce(pg, &edge_centrality_v[0], &edge_centrality_v[edge_centrality_v.size()],
+ &edge_centrality_v[0], std::plus<edge_centrality_type>());
+
+ BGL_FORALL_VERTICES_T(v, g, Graph) {
+ put(centrality, v, centrality_v[get(vertex_index, v)]);
+ }
+
+ // Skip when EdgeCentralityMap is a dummy_property_map
+ if (!is_same<EdgeCentralityMap, dummy_property_map>::value) {
+ int i = 0;
+ BGL_FORALL_EDGES_T(e, g, Graph) {
+ put(edge_centrality_map, e, edge_centrality_v[i]);
+ ++i;
+ }
+ }
+ }
+
+} } } // end namespace graph::parallel::detail
+
+template<typename Graph, typename CentralityMap, typename EdgeCentralityMap,
+ typename IncomingMap, typename DistanceMap, typename DependencyMap,
+ typename PathCountMap, typename VertexIndexMap, typename Buffer>
+void
+brandes_betweenness_centrality(const Graph& g,
+ CentralityMap centrality,
+ EdgeCentralityMap edge_centrality_map,
+ IncomingMap incoming,
+ DistanceMap distance,
+ DependencyMap dependency,
+ PathCountMap path_count,
+ VertexIndexMap vertex_index,
+ Buffer sources,
+ typename property_traits<DistanceMap>::value_type delta
+ BOOST_GRAPH_ENABLE_IF_MODELS_PARM(Graph,distributed_graph_tag))
+{
+ typedef typename property_traits<DistanceMap>::value_type distance_type;
+ typedef static_property_map<distance_type> WeightMap;
+
+ graph::parallel::detail::brandes_shortest_paths<WeightMap>
+ shortest_paths(delta);
+
+ graph::parallel::detail::brandes_betweenness_centrality_impl(g, centrality,
+ edge_centrality_map,
+ incoming, distance,
+ dependency, path_count,
+ vertex_index,
+ shortest_paths,
+ sources);
+}
+
+template<typename Graph, typename CentralityMap, typename EdgeCentralityMap,
+ typename IncomingMap, typename DistanceMap, typename DependencyMap,
+ typename PathCountMap, typename VertexIndexMap, typename WeightMap,
+ typename Buffer>
+void
+brandes_betweenness_centrality(const Graph& g,
+ CentralityMap centrality,
+ EdgeCentralityMap edge_centrality_map,
+ IncomingMap incoming,
+ DistanceMap distance,
+ DependencyMap dependency,
+ PathCountMap path_count,
+ VertexIndexMap vertex_index,
+ Buffer sources,
+ typename property_traits<WeightMap>::value_type delta,
+ WeightMap weight_map
+ BOOST_GRAPH_ENABLE_IF_MODELS_PARM(Graph,distributed_graph_tag))
+{
+ graph::parallel::detail::brandes_shortest_paths<WeightMap> shortest_paths(weight_map, delta);
+
+ graph::parallel::detail::brandes_betweenness_centrality_impl(g, centrality,
+ edge_centrality_map,
+ incoming, distance,
+ dependency, path_count,
+ vertex_index,
+ shortest_paths,
+ sources);
+}
+
+namespace graph { namespace parallel { namespace detail {
+ template<typename Graph, typename CentralityMap, typename EdgeCentralityMap,
+ typename WeightMap, typename VertexIndexMap, typename EdgeIndexMap,
+ typename Buffer>
+ void
+ brandes_betweenness_centrality_dispatch2(const Graph& g,
+ CentralityMap centrality,
+ EdgeCentralityMap edge_centrality_map,
+ WeightMap weight_map,
+ VertexIndexMap vertex_index,
+ EdgeIndexMap edge_index,
+ Buffer sources,
+ typename property_traits<WeightMap>::value_type delta)
+ {
+ typedef typename graph_traits<Graph>::degree_size_type degree_size_type;
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+ typedef typename mpl::if_c<(is_same<CentralityMap,
+ dummy_property_map>::value),
+ EdgeCentralityMap,
+ CentralityMap>::type a_centrality_map;
+ typedef typename property_traits<a_centrality_map>::value_type
+ centrality_type;
+
+ typename graph_traits<Graph>::vertices_size_type V = num_vertices(g);
+
+ std::vector<std::vector<vertex_descriptor> > incoming(V);
+ std::vector<centrality_type> distance(V);
+ std::vector<centrality_type> dependency(V);
+ std::vector<degree_size_type> path_count(V);
+
+ brandes_betweenness_centrality(
+ g, centrality, edge_centrality_map,
+ make_iterator_property_map(incoming.begin(), vertex_index),
+ make_iterator_property_map(distance.begin(), vertex_index),
+ make_iterator_property_map(dependency.begin(), vertex_index),
+ make_iterator_property_map(path_count.begin(), vertex_index),
+ vertex_index, sources.ref, delta,
+ weight_map);
+ }
+
+ // TODO: Should the type of the distance and dependency map depend on the
+ // value type of the centrality map?
+ template<typename Graph, typename CentralityMap, typename EdgeCentralityMap,
+ typename VertexIndexMap, typename EdgeIndexMap, typename Buffer>
+ void
+ brandes_betweenness_centrality_dispatch2(const Graph& g,
+ CentralityMap centrality,
+ EdgeCentralityMap edge_centrality_map,
+ VertexIndexMap vertex_index,
+ EdgeIndexMap edge_index,
+ Buffer sources,
+ typename graph_traits<Graph>::edges_size_type delta)
+ {
+ typedef typename graph_traits<Graph>::degree_size_type degree_size_type;
+ typedef typename graph_traits<Graph>::edges_size_type edges_size_type;
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+ typedef typename mpl::if_c<(is_same<CentralityMap,
+ dummy_property_map>::value),
+ EdgeCentralityMap,
+ CentralityMap>::type a_centrality_map;
+
+ typename graph_traits<Graph>::vertices_size_type V = num_vertices(g);
+
+ std::vector<std::vector<vertex_descriptor> > incoming(V);
+ std::vector<edges_size_type> distance(V);
+ std::vector<edges_size_type> dependency(V);
+ std::vector<degree_size_type> path_count(V);
+
+ brandes_betweenness_centrality(
+ g, centrality, edge_centrality_map,
+ make_iterator_property_map(incoming.begin(), vertex_index),
+ make_iterator_property_map(distance.begin(), vertex_index),
+ make_iterator_property_map(dependency.begin(), vertex_index),
+ make_iterator_property_map(path_count.begin(), vertex_index),
+ vertex_index, sources.ref, delta);
+ }
+
+ template<typename WeightMap>
+ struct brandes_betweenness_centrality_dispatch1
+ {
+ template<typename Graph, typename CentralityMap, typename EdgeCentralityMap,
+ typename VertexIndexMap, typename EdgeIndexMap, typename Buffer>
+ static void
+ run(const Graph& g, CentralityMap centrality, EdgeCentralityMap edge_centrality_map,
+ VertexIndexMap vertex_index, EdgeIndexMap edge_index, Buffer sources,
+ typename property_traits<WeightMap>::value_type delta, WeightMap weight_map)
+ {
+ boost::graph::parallel::detail::brandes_betweenness_centrality_dispatch2(
+ g, centrality, edge_centrality_map, weight_map, vertex_index, edge_index, sources, delta);
+ }
+ };
+
+ template<>
+ struct brandes_betweenness_centrality_dispatch1<boost::detail::error_property_not_found>
+ {
+ template<typename Graph, typename CentralityMap, typename EdgeCentralityMap,
+ typename VertexIndexMap, typename EdgeIndexMap, typename Buffer>
+ static void
+ run(const Graph& g, CentralityMap centrality, EdgeCentralityMap edge_centrality_map,
+ VertexIndexMap vertex_index, EdgeIndexMap edge_index, Buffer sources,
+ typename graph_traits<Graph>::edges_size_type delta,
+ boost::detail::error_property_not_found)
+ {
+ boost::graph::parallel::detail::brandes_betweenness_centrality_dispatch2(
+ g, centrality, edge_centrality_map, vertex_index, edge_index, sources, delta);
+ }
+ };
+
+} } } // end namespace graph::parallel::detail
+
+template<typename Graph, typename Param, typename Tag, typename Rest>
+void
+brandes_betweenness_centrality(const Graph& g,
+ const bgl_named_params<Param,Tag,Rest>& params
+ BOOST_GRAPH_ENABLE_IF_MODELS_PARM(Graph,distributed_graph_tag))
+{
+ typedef bgl_named_params<Param,Tag,Rest> named_params;
+
+ typedef queue<int> queue_t;
+ queue_t q;
+
+ typedef typename property_value<named_params, edge_weight_t>::type ew;
+ graph::parallel::detail::brandes_betweenness_centrality_dispatch1<ew>::run(
+ g,
+ choose_param(get_param(params, vertex_centrality),
+ dummy_property_map()),
+ choose_param(get_param(params, edge_centrality),
+ dummy_property_map()),
+ choose_const_pmap(get_param(params, vertex_index), g, vertex_index),
+ choose_const_pmap(get_param(params, edge_index), g, edge_index),
+ choose_param(get_param(params, buffer_param_t()),
+ detail::wrap_ref<queue_t>(q)),
+ choose_param(get_param(params, lookahead_t()), 0),
+ get_param(params, edge_weight));
+}
+
+template<typename Graph, typename CentralityMap>
+void
+brandes_betweenness_centrality(const Graph& g, CentralityMap centrality
+ BOOST_GRAPH_ENABLE_IF_MODELS_PARM(Graph,distributed_graph_tag))
+{
+ typedef queue<int> queue_t;
+ queue_t q;
+
+ boost::graph::parallel::detail::brandes_betweenness_centrality_dispatch2(
+ g, centrality, dummy_property_map(), get(vertex_index, g), get(edge_index, g),
+ detail::wrap_ref<queue_t>(q), 0);
+}
+
+template<typename Graph, typename CentralityMap, typename EdgeCentralityMap>
+void
+brandes_betweenness_centrality(const Graph& g, CentralityMap centrality,
+ EdgeCentralityMap edge_centrality_map
+ BOOST_GRAPH_ENABLE_IF_MODELS_PARM(Graph,distributed_graph_tag))
+{
+ typedef queue<int> queue_t;
+ queue_t q;
+
+ boost::graph::parallel::detail::brandes_betweenness_centrality_dispatch2(
+ g, centrality, edge_centrality_map, get(vertex_index, g), get(edge_index, g),
+ detail::wrap_ref<queue_t>(q), 0);
+}
+
+template<typename ProcessGroup, typename Graph,
+ typename CentralityMap, typename EdgeCentralityMap,
+ typename IncomingMap, typename DistanceMap,
+ typename DependencyMap, typename PathCountMap,
+ typename VertexIndexMap>
+void
+non_distributed_brandes_betweenness_centrality(const ProcessGroup& pg,
+ const Graph& g,
+ CentralityMap centrality,
+ EdgeCentralityMap edge_centrality_map,
+ IncomingMap incoming,
+ DistanceMap distance,
+ DependencyMap dependency,
+ PathCountMap path_count,
+ VertexIndexMap vertex_index,
+ typename graph_traits<Graph>::vertices_size_type num_sources)
+{
+ typedef typename property_traits<DistanceMap>::value_type distance_type;
+ typedef static_property_map<distance_type> WeightMap;
+
+ detail::graph::brandes_unweighted_shortest_paths shortest_paths;
+
+ graph::parallel::detail::non_distributed_brandes_betweenness_centrality_impl(pg, g, centrality,
+ edge_centrality_map,
+ incoming, distance,
+ dependency, path_count,
+ vertex_index,
+ shortest_paths,
+ num_sources);
+}
+
+template<typename ProcessGroup, typename Graph,
+ typename CentralityMap, typename EdgeCentralityMap,
+ typename IncomingMap, typename DistanceMap,
+ typename DependencyMap, typename PathCountMap,
+ typename VertexIndexMap, typename WeightMap>
+void
+non_distributed_brandes_betweenness_centrality(const ProcessGroup& pg,
+ const Graph& g,
+ CentralityMap centrality,
+ EdgeCentralityMap edge_centrality_map,
+ IncomingMap incoming,
+ DistanceMap distance,
+ DependencyMap dependency,
+ PathCountMap path_count,
+ VertexIndexMap vertex_index,
+ WeightMap weight_map,
+ typename graph_traits<Graph>::vertices_size_type num_sources)
+{
+ detail::graph::brandes_dijkstra_shortest_paths<WeightMap> shortest_paths(weight_map);
+
+ graph::parallel::detail::non_distributed_brandes_betweenness_centrality_impl(pg, g, centrality,
+ edge_centrality_map,
+ incoming, distance,
+ dependency, path_count,
+ vertex_index,
+ shortest_paths,
+ num_sources);
+}
+
+namespace detail { namespace graph {
+ template<typename ProcessGroup, typename Graph, typename CentralityMap,
+ typename EdgeCentralityMap, typename WeightMap, typename VertexIndexMap>
+ void
+ non_distributed_brandes_betweenness_centrality_dispatch2(const ProcessGroup& pg,
+ const Graph& g,
+ CentralityMap centrality,
+ EdgeCentralityMap edge_centrality_map,
+ WeightMap weight_map,
+ VertexIndexMap vertex_index,
+ typename graph_traits<Graph>::vertices_size_type num_sources)
+ {
+ typedef typename graph_traits<Graph>::degree_size_type degree_size_type;
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+ typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor;
+ typedef typename mpl::if_c<(is_same<CentralityMap,
+ dummy_property_map>::value),
+ EdgeCentralityMap,
+ CentralityMap>::type a_centrality_map;
+ typedef typename property_traits<a_centrality_map>::value_type
+ centrality_type;
+
+ typename graph_traits<Graph>::vertices_size_type V = num_vertices(g);
+
+ std::vector<std::vector<edge_descriptor> > incoming(V);
+ std::vector<centrality_type> distance(V);
+ std::vector<centrality_type> dependency(V);
+ std::vector<degree_size_type> path_count(V);
+
+ non_distributed_brandes_betweenness_centrality(
+ pg, g, centrality, edge_centrality_map,
+ make_iterator_property_map(incoming.begin(), vertex_index),
+ make_iterator_property_map(distance.begin(), vertex_index),
+ make_iterator_property_map(dependency.begin(), vertex_index),
+ make_iterator_property_map(path_count.begin(), vertex_index),
+ vertex_index,
+ weight_map, num_sources);
+ }
+
+
+ template<typename ProcessGroup, typename Graph, typename CentralityMap,
+ typename EdgeCentralityMap, typename VertexIndexMap>
+ void
+ non_distributed_brandes_betweenness_centrality_dispatch2(const ProcessGroup& pg,
+ const Graph& g,
+ CentralityMap centrality,
+ EdgeCentralityMap edge_centrality_map,
+ VertexIndexMap vertex_index,
+ typename graph_traits<Graph>::vertices_size_type num_sources)
+ {
+ typedef typename graph_traits<Graph>::degree_size_type degree_size_type;
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+ typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor;
+ typedef typename mpl::if_c<(is_same<CentralityMap,
+ dummy_property_map>::value),
+ EdgeCentralityMap,
+ CentralityMap>::type a_centrality_map;
+ typedef typename property_traits<a_centrality_map>::value_type
+ centrality_type;
+
+ typename graph_traits<Graph>::vertices_size_type V = num_vertices(g);
+
+ std::vector<std::vector<edge_descriptor> > incoming(V);
+ std::vector<centrality_type> distance(V);
+ std::vector<centrality_type> dependency(V);
+ std::vector<degree_size_type> path_count(V);
+
+ non_distributed_brandes_betweenness_centrality(
+ pg, g, centrality, edge_centrality_map,
+ make_iterator_property_map(incoming.begin(), vertex_index),
+ make_iterator_property_map(distance.begin(), vertex_index),
+ make_iterator_property_map(dependency.begin(), vertex_index),
+ make_iterator_property_map(path_count.begin(), vertex_index),
+ vertex_index, num_sources);
+ }
+
+ template<typename WeightMap>
+ struct non_distributed_brandes_betweenness_centrality_dispatch1
+ {
+ template<typename ProcessGroup, typename Graph, typename CentralityMap,
+ typename EdgeCentralityMap, typename VertexIndexMap>
+ static void
+ run(const ProcessGroup& pg, const Graph& g, CentralityMap centrality,
+ EdgeCentralityMap edge_centrality_map, VertexIndexMap vertex_index,
+ typename graph_traits<Graph>::vertices_size_type num_sources,
+ WeightMap weight_map)
+ {
+ non_distributed_brandes_betweenness_centrality_dispatch2(pg, g, centrality, edge_centrality_map,
+ weight_map, vertex_index, num_sources);
+ }
+ };
+
+ template<>
+ struct non_distributed_brandes_betweenness_centrality_dispatch1<detail::error_property_not_found>
+ {
+ template<typename ProcessGroup, typename Graph, typename CentralityMap,
+ typename EdgeCentralityMap, typename VertexIndexMap>
+ static void
+ run(const ProcessGroup& pg, const Graph& g, CentralityMap centrality,
+ EdgeCentralityMap edge_centrality_map, VertexIndexMap vertex_index,
+ typename graph_traits<Graph>::vertices_size_type num_sources,
+ detail::error_property_not_found)
+ {
+ non_distributed_brandes_betweenness_centrality_dispatch2(pg, g, centrality, edge_centrality_map,
+ vertex_index, num_sources);
+ }
+ };
+
+} } // end namespace detail::graph
+
+// TODO: Make named parameter variant work
+
+// template<typename ProcessGroup, typename Graph, typename Param, typename Tag, typename Rest>
+// void
+// non_distributed_brandes_betweenness_centrality(const ProcessGroup& pg, const Graph& g,
+// const bgl_named_params<Param,Tag,Rest>& params)
+// {
+// typedef bgl_named_params<Param,Tag,Rest> named_params;
+
+// typedef typename property_value<named_params, edge_weight_t>::type ew;
+// detail::graph::non_distributed_brandes_betweenness_centrality_dispatch1<ew>::run(
+// pg, g,
+// choose_param(get_param(params, vertex_centrality),
+// dummy_property_map()),
+// choose_param(get_param(params, edge_centrality),
+// dummy_property_map()),
+// choose_const_pmap(get_param(params, vertex_index), g, vertex_index),
+// get_param(params, edge_weight));
+// }
+
+template<typename ProcessGroup, typename Graph, typename CentralityMap>
+void
+non_distributed_brandes_betweenness_centrality(const ProcessGroup& pg, const Graph& g, CentralityMap centrality)
+{
+ detail::graph::non_distributed_brandes_betweenness_centrality_dispatch2(
+ pg, g, centrality, dummy_property_map(), get(vertex_index, g), 0);
+}
+
+template<typename ProcessGroup, typename Graph, typename CentralityMap>
+void
+non_distributed_brandes_betweenness_centrality(const ProcessGroup& pg, const Graph& g, CentralityMap centrality,
+ typename graph_traits<Graph>::vertices_size_type num_sources)
+{
+ detail::graph::non_distributed_brandes_betweenness_centrality_dispatch2(
+ pg, g, centrality, dummy_property_map(), get(vertex_index, g), num_sources);
+}
+
+template<typename ProcessGroup, typename Graph, typename CentralityMap,
+ typename EdgeCentralityMap>
+void
+non_distributed_brandes_betweenness_centrality(const ProcessGroup& pg, const Graph& g, CentralityMap centrality,
+ EdgeCentralityMap edge_centrality_map,
+ typename graph_traits<Graph>::vertices_size_type num_sources)
+{
+ detail::graph::non_distributed_brandes_betweenness_centrality_dispatch2(
+ pg, g, centrality, edge_centrality_map, get(vertex_index, g), num_sources);
+}
+
+// Compute the central point dominance of a graph.
+// TODO: Make sure central point dominance works in parallel case
+template<typename Graph, typename CentralityMap>
+typename property_traits<CentralityMap>::value_type
+central_point_dominance(const Graph& g, CentralityMap centrality
+ BOOST_GRAPH_ENABLE_IF_MODELS_PARM(Graph,distributed_graph_tag))
+{
+ using std::max;
+
+ typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
+ typedef typename property_traits<CentralityMap>::value_type centrality_type;
+ typedef typename graph_traits<Graph>::vertices_size_type vertices_size_type;
+
+ typedef typename boost::graph::parallel::process_group_type<Graph>::type
+ process_group_type;
+ process_group_type pg = boost::graph::parallel::process_group(g);
+
+ vertices_size_type n = num_vertices(g);
+
+ using boost::parallel::all_reduce;
+ n = all_reduce(pg, n, std::plus<vertices_size_type>());
+
+ // Find max centrality
+ centrality_type max_centrality(0);
+ vertex_iterator v, v_end;
+ for (tie(v, v_end) = vertices(g); v != v_end; ++v) {
+ max_centrality = (max)(max_centrality, get(centrality, *v));
+ }
+
+ // All reduce to get global max centrality
+ max_centrality = all_reduce(pg, max_centrality, boost::parallel::maximum<centrality_type>());
+
+ // Compute central point dominance
+ centrality_type sum(0);
+ for (tie(v, v_end) = vertices(g); v != v_end; ++v) {
+ sum += (max_centrality - get(centrality, *v));
+ }
+
+ sum = all_reduce(pg, sum, std::plus<centrality_type>());
+
+ return sum/(n-1);
+}
+
+} // end namespace boost
+
+#endif // BOOST_GRAPH_PARALLEL_BRANDES_BETWEENNESS_CENTRALITY_HPP

Added: trunk/boost/graph/distributed/boman_et_al_graph_coloring.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/boman_et_al_graph_coloring.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,372 @@
+// Copyright (C) 2005-2008 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
+#ifndef BOOST_GRAPH_DISTRIBUTED_BOMAN_ET_AL_GRAPH_COLORING_HPP
+#define BOOST_GRAPH_DISTRIBUTED_BOMAN_ET_AL_GRAPH_COLORING_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/graph/graph_traits.hpp>
+#include <boost/graph/parallel/algorithm.hpp>
+#include <boost/property_map/property_map.hpp>
+#include <boost/graph/parallel/process_group.hpp>
+#include <functional>
+#include <vector>
+#include <utility>
+#include <boost/graph/iteration_macros.hpp>
+#include <boost/optional.hpp>
+#include <cassert>
+#include <boost/graph/parallel/container_traits.hpp>
+#include <boost/graph/properties.hpp>
+
+#ifdef PBGL_ACCOUNTING
+# include <boost/graph/accounting.hpp>
+#endif // PBGL_ACCOUNTING
+
+namespace boost { namespace graph { namespace distributed {
+
+/**************************************************************************
+ * This source file implements the distributed graph coloring algorithm *
+ * by Boman et al in: *
+ * *
+ * Erik G. Boman, Doruk Bozdag, Umit Catalyurek, Assefaw H. Gebremedhin,*
+ * and Fredrik Manne. A Scalable Parallel Graph Coloring Algorithm for *
+ * Distributed Memory Computers. [unpublished preprint?] *
+ * *
+ **************************************************************************/
+
+#ifdef PBGL_ACCOUNTING
+struct boman_et_al_graph_coloring_stats_t
+{
+ /* The size of the blocks to step through (i.e., the parameter s). */
+ std::size_t block_size;
+
+ /* Total wall-clock time used by the algorithm.*/
+ accounting::time_type execution_time;
+
+ /* The number of conflicts that occurred during execution. */
+ std::size_t conflicts;
+
+ /* The number of supersteps. */
+ std::size_t supersteps;
+
+ /* The number of colors used. */
+ std::size_t num_colors;
+
+ template<typename OutputStream>
+ void print(OutputStream& out)
+ {
+ out << "Problem = \"Coloring\"\n"
+ << "Algorithm = \"Boman et al\"\n"
+ << "Function = boman_et_al_graph_coloring\n"
+ << "(P) Block size = " << block_size << "\n"
+ << "Wall clock time = " << accounting::print_time(execution_time)
+ << "\nConflicts = " << conflicts << "\n"
+ << "Supersteps = " << supersteps << "\n"
+ << "(R) Colors = " << num_colors << "\n";
+ }
+};
+
+static boman_et_al_graph_coloring_stats_t boman_et_al_graph_coloring_stats;
+#endif
+
+namespace detail {
+ template<typename T>
+ struct graph_coloring_reduce
+ {
+ BOOST_STATIC_CONSTANT(bool, non_default_resolver = true);
+
+ template<typename Key>
+ T operator()(const Key&) const { return (std::numeric_limits<T>::max)(); }
+
+ template<typename Key> T operator()(const Key&, T, T y) const { return y; }
+ };
+}
+
+template<typename Color>
+struct first_fit_color
+{
+ template<typename T>
+ Color operator()(const std::vector<T>& marked, T marked_true)
+ {
+ Color k = 0;
+ while (k < (Color)marked.size() && marked[k] == marked_true)
+ ++k;
+ return k;
+ }
+};
+
+template<typename DistributedGraph, typename ColorMap, typename ChooseColor,
+ typename VertexOrdering, typename VertexIndexMap>
+typename property_traits<ColorMap>::value_type
+boman_et_al_graph_coloring
+ (const DistributedGraph& g,
+ ColorMap color,
+ typename graph_traits<DistributedGraph>::vertices_size_type s,
+ ChooseColor choose_color,
+ VertexOrdering ordering, VertexIndexMap vertex_index)
+{
+ using namespace boost::graph::parallel;
+ using boost::parallel::all_reduce;
+
+ typename property_map<DistributedGraph, vertex_owner_t>::const_type
+ owner = get(vertex_owner, g);
+
+ typedef typename process_group_type<DistributedGraph>::type
+ process_group_type;
+ typedef typename process_group_type::process_id_type process_id_type;
+ typedef typename graph_traits<DistributedGraph>::vertex_descriptor Vertex;
+ typedef typename graph_traits<DistributedGraph>::edge_descriptor Edge;
+ typedef typename graph_traits<DistributedGraph>::vertices_size_type
+ vertices_size_type;
+ typedef typename property_traits<ColorMap>::value_type color_type;
+ typedef unsigned long long iterations_type;
+ typedef typename std::vector<Vertex>::iterator vertex_set_iterator;
+ typedef std::pair<Vertex, color_type> message_type;
+
+#ifdef PBGL_ACCOUNTING
+ boman_et_al_graph_coloring_stats.block_size = s;
+ boman_et_al_graph_coloring_stats.execution_time = accounting::get_time();
+ boman_et_al_graph_coloring_stats.conflicts = 0;
+ boman_et_al_graph_coloring_stats.supersteps = 0;
+#endif
+
+ // Initialize color map
+ color_type no_color = (std::numeric_limits<color_type>::max)();
+ BGL_FORALL_VERTICES_T(v, g, DistributedGraph)
+ put(color, v, no_color);
+ color.set_reduce(detail::graph_coloring_reduce<color_type>());
+
+ // Determine if we'll be using synchronous or asynchronous communication.
+ typedef typename process_group_type::communication_category
+ communication_category;
+ static const bool asynchronous =
+ is_convertible<communication_category, immediate_process_group_tag>::value;
+ process_group_type pg = process_group(g);
+
+ // U_i <- V_i
+ std::vector<Vertex> vertices_to_color(vertices(g).first, vertices(g).second);
+
+ iterations_type iter_num = 1, outer_iter_num = 1;
+ std::vector<iterations_type> marked;
+ std::vector<iterations_type> marked_conflicting(num_vertices(g), 0);
+ std::vector<bool> sent_to_processors;
+
+ std::size_t rounds = vertices_to_color.size() / s
+ + (vertices_to_color.size() % s == 0? 0 : 1);
+ rounds = all_reduce(pg, rounds, boost::parallel::maximum<std::size_t>());
+
+#ifdef PBGL_GRAPH_COLORING_DEBUG
+ std::cerr << "Number of rounds = " << rounds << std::endl;
+#endif
+
+ while (rounds > 0) {
+ if (!vertices_to_color.empty()) {
+ // Set of conflicting vertices
+ std::vector<Vertex> conflicting_vertices;
+
+ vertex_set_iterator first = vertices_to_color.begin();
+ while (first != vertices_to_color.end()) {
+ // For each subset of size s (or smaller for the last subset)
+ vertex_set_iterator start = first;
+ for (vertices_size_type counter = s;
+ first != vertices_to_color.end() && counter > 0;
+ ++first, --counter) {
+ // This vertex hasn't been sent to anyone yet
+ sent_to_processors.assign(num_processes(pg), false);
+ sent_to_processors[process_id(pg)] = true;
+
+ // Mark all of the colors that we see
+ BGL_FORALL_OUTEDGES_T(*first, e, g, DistributedGraph) {
+ color_type k = get(color, target(e, g));
+ if (k != no_color) {
+ if (k >= (color_type)marked.size()) marked.resize(k + 1, 0);
+ marked[k] = iter_num;
+ }
+ }
+
+ // Find a color for this vertex
+ put(color, *first, choose_color(marked, iter_num));
+
+#ifdef PBGL_GRAPH_COLORING_DEBUG
+ std::cerr << "Chose color " << get(color, *first) << " for vertex "
+ << *first << std::endl;
+#endif
+
+ // Send this vertex's color to the owner of the edge target.
+ BGL_FORALL_OUTEDGES_T(*first, e, g, DistributedGraph) {
+ if (!sent_to_processors[get(owner, target(e, g))]) {
+ send(pg, get(owner, target(e, g)), 17,
+ message_type(source(e, g), get(color, source(e, g))));
+ sent_to_processors[get(owner, target(e, g))] = true;
+ }
+ }
+
+ ++iter_num;
+ }
+
+ // Synchronize for non-immediate process groups.
+ if (!asynchronous) {
+ --rounds;
+ synchronize(pg);
+ }
+
+ // Receive boundary colors from other processors
+ while (optional<std::pair<process_id_type, int> > stp = probe(pg)) {
+ assert(stp->second == 17);
+ message_type msg;
+ receive(pg, stp->first, stp->second, msg);
+ cache(color, msg.first, msg.second);
+#ifdef PBGL_GRAPH_COLORING_DEBUG
+ std::cerr << "Cached color " << msg.second << " for vertex "
+ << msg.first << std::endl;
+#endif
+ }
+
+ // Compute the set of conflicting vertices
+ // [start, first) contains all vertices in this subset
+ for (vertex_set_iterator vi = start; vi != first; ++vi) {
+ Vertex v = *vi;
+ BGL_FORALL_OUTEDGES_T(v, e, g, DistributedGraph) {
+ Vertex w = target(e, g);
+ if (get(owner, w) != process_id(pg) // boundary vertex
+ && marked_conflicting[get(vertex_index, v)] != outer_iter_num
+ && get(color, v) == get(color, w)
+ && ordering(v, w)) {
+ conflicting_vertices.push_back(v);
+ marked_conflicting[get(vertex_index, v)] = outer_iter_num;
+ put(color, v, no_color);
+#ifdef PBGL_GRAPH_COLORING_DEBUG
+ std::cerr << "Vertex " << v << " has a conflict with vertex "
+ << w << std::endl;
+#endif
+ break;
+ }
+ }
+ }
+
+#ifdef PBGL_ACCOUNTING
+ boman_et_al_graph_coloring_stats.conflicts +=
+ conflicting_vertices.size();
+#endif
+ }
+
+ if (asynchronous) synchronize(pg);
+ else {
+ while (rounds > 0) {
+ synchronize(pg);
+ --rounds;
+ }
+ }
+ conflicting_vertices.swap(vertices_to_color);
+ ++outer_iter_num;
+ } else {
+ if (asynchronous) synchronize(pg);
+ else {
+ while (rounds > 0) {
+ synchronize(pg);
+ --rounds;
+ }
+ }
+ }
+
+ // Receive boundary colors from other processors
+ while (optional<std::pair<process_id_type, int> > stp = probe(pg)) {
+ assert(stp->second == 17);
+ message_type msg;
+ receive(pg, stp->first, stp->second, msg);
+ cache(color, msg.first, msg.second);
+ }
+
+ rounds = vertices_to_color.size() / s
+ + (vertices_to_color.size() % s == 0? 0 : 1);
+ rounds = all_reduce(pg, rounds, boost::parallel::maximum<std::size_t>());
+
+#ifdef PBGL_ACCOUNTING
+ ++boman_et_al_graph_coloring_stats.supersteps;
+#endif
+ }
+
+ // Determine the number of colors used.
+ color_type num_colors = 0;
+ BGL_FORALL_VERTICES_T(v, g, DistributedGraph) {
+ color_type k = get(color, v);
+ assert(k != no_color);
+ if (k != no_color) {
+ if (k >= (color_type)marked.size()) marked.resize(k + 1, 0); // TBD: perf?
+ if (marked[k] != iter_num) {
+ marked[k] = iter_num;
+ ++num_colors;
+ }
+ }
+ }
+
+ num_colors =
+ all_reduce(pg, num_colors, boost::parallel::maximum<color_type>());
+
+
+#ifdef PBGL_ACCOUNTING
+ boman_et_al_graph_coloring_stats.execution_time =
+ accounting::get_time() - boman_et_al_graph_coloring_stats.execution_time;
+
+ boman_et_al_graph_coloring_stats.conflicts =
+ all_reduce(pg, boman_et_al_graph_coloring_stats.conflicts,
+ std::plus<color_type>());
+ boman_et_al_graph_coloring_stats.num_colors = num_colors;
+#endif
+
+ return num_colors;
+}
+
+
+template<typename DistributedGraph, typename ColorMap, typename ChooseColor,
+ typename VertexOrdering>
+inline typename property_traits<ColorMap>::value_type
+boman_et_al_graph_coloring
+ (const DistributedGraph& g, ColorMap color,
+ typename graph_traits<DistributedGraph>::vertices_size_type s,
+ ChooseColor choose_color, VertexOrdering ordering)
+{
+ return boman_et_al_graph_coloring(g, color, s, choose_color, ordering,
+ get(vertex_index, g));
+}
+
+template<typename DistributedGraph, typename ColorMap, typename ChooseColor>
+inline typename property_traits<ColorMap>::value_type
+boman_et_al_graph_coloring
+ (const DistributedGraph& g,
+ ColorMap color,
+ typename graph_traits<DistributedGraph>::vertices_size_type s,
+ ChooseColor choose_color)
+{
+ typedef typename graph_traits<DistributedGraph>::vertex_descriptor
+ vertex_descriptor;
+ return boman_et_al_graph_coloring(g, color, s, choose_color,
+ std::less<vertex_descriptor>());
+}
+
+template<typename DistributedGraph, typename ColorMap>
+inline typename property_traits<ColorMap>::value_type
+boman_et_al_graph_coloring
+ (const DistributedGraph& g,
+ ColorMap color,
+ typename graph_traits<DistributedGraph>::vertices_size_type s = 100)
+{
+ typedef typename property_traits<ColorMap>::value_type Color;
+ return boman_et_al_graph_coloring(g, color, s, first_fit_color<Color>());
+}
+
+} } } // end namespace boost::graph::distributed
+
+namespace boost { namespace graph {
+using distributed::boman_et_al_graph_coloring;
+} } // end namespace boost::graph
+
+#endif // BOOST_GRAPH_DISTRIBUTED_BOMAN_ET_AL_GRAPH_COLORING_HPP

Added: trunk/boost/graph/distributed/breadth_first_search.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/breadth_first_search.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,164 @@
+// Copyright 2004 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
+#ifndef BOOST_GRAPH_PARALLEL_BFS_HPP
+#define BOOST_GRAPH_PARALLEL_BFS_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/graph/breadth_first_search.hpp>
+#include <boost/graph/overloading.hpp>
+#include <boost/graph/distributed/concepts.hpp>
+#include <boost/graph/distributed/detail/filtered_queue.hpp>
+#include <boost/graph/distributed/queue.hpp>
+#include <boost/dynamic_bitset.hpp>
+#include <boost/pending/queue.hpp>
+#include <boost/graph/parallel/properties.hpp>
+#include <boost/graph/parallel/container_traits.hpp>
+
+namespace boost {
+ namespace detail {
+ /** @brief A unary predicate that decides when to push into a
+ * breadth-first search queue.
+ *
+ * This predicate stores a color map that is used to determine
+ * when to push. If it is provided with a key for which the color
+ * is white, it darkens the color to gray and returns true (so
+ * that the value will be pushed appropriately); if the color is
+ * not white, it returns false so that the vertex will be
+ * ignored.
+ */
+ template<typename ColorMap>
+ struct darken_and_push
+ {
+ typedef typename property_traits<ColorMap>::key_type argument_type;
+ typedef bool result_type;
+
+ explicit darken_and_push(const ColorMap& color) : color(color) { }
+
+ bool operator()(const argument_type& value) const
+ {
+ typedef color_traits<typename property_traits<ColorMap>::value_type>
+ Color;
+ if (get(color, value) == Color::white()) {
+ put(color, value, Color::gray());
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ ColorMap color;
+ };
+
+ template<typename IndexMap>
+ struct has_not_been_seen
+ {
+ typedef bool result_type;
+
+ has_not_been_seen() { }
+
+ has_not_been_seen(std::size_t n, IndexMap index_map)
+ : seen(n), index_map(index_map) {}
+
+ template<typename Key>
+ result_type operator()(Key key)
+ {
+ bool result = seen[get(index_map, key)];
+ seen[get(index_map, key)] = true;
+ return !result;
+ }
+
+ void swap(has_not_been_seen& other)
+ {
+ using std::swap;
+ swap(seen, other.seen);
+ swap(index_map, other.index_map);
+ }
+
+ private:
+ dynamic_bitset<> seen;
+ IndexMap index_map;
+ };
+
+ template<typename IndexMap>
+ inline void
+ swap(has_not_been_seen<IndexMap>& x, has_not_been_seen<IndexMap>& y)
+ {
+ x.swap(y);
+ }
+
+ template <class DistributedGraph, class ColorMap, class BFSVisitor,
+ class BufferRef, class VertexIndexMap>
+ inline void
+ parallel_bfs_helper
+ (DistributedGraph& g,
+ typename graph_traits<DistributedGraph>::vertex_descriptor s,
+ ColorMap color,
+ BFSVisitor vis,
+ BufferRef Q,
+ VertexIndexMap)
+ {
+ set_property_map_role(vertex_color, color);
+ color.set_consistency_model(0);
+ breadth_first_search(g, s, Q.ref, vis, color);
+ }
+
+ template <class DistributedGraph, class ColorMap, class BFSVisitor,
+ class VertexIndexMap>
+ void parallel_bfs_helper
+ (DistributedGraph& g,
+ typename graph_traits<DistributedGraph>::vertex_descriptor s,
+ ColorMap color,
+ BFSVisitor vis,
+ error_property_not_found,
+ VertexIndexMap vertex_index)
+ {
+ using boost::graph::parallel::process_group;
+
+ typedef graph_traits<DistributedGraph> Traits;
+ typedef typename Traits::vertex_descriptor Vertex;
+ typedef typename boost::graph::parallel::process_group_type<DistributedGraph>::type
+ process_group_type;
+
+ set_property_map_role(vertex_color, color);
+ color.set_consistency_model(0);
+
+ // Buffer default
+ typedef typename property_map<DistributedGraph, vertex_owner_t>
+ ::const_type vertex_owner_map;
+ typedef boost::graph::distributed::distributed_queue<
+ process_group_type, vertex_owner_map, queue<Vertex>,
+ detail::darken_and_push<ColorMap> > queue_t;
+ queue_t Q(process_group(g),
+ get(vertex_owner, g),
+ detail::darken_and_push<ColorMap>(color));
+ breadth_first_search(g, s, Q, vis, color);
+ }
+
+ template <class DistributedGraph, class ColorMap, class BFSVisitor,
+ class P, class T, class R>
+ void bfs_helper
+ (DistributedGraph& g,
+ typename graph_traits<DistributedGraph>::vertex_descriptor s,
+ ColorMap color,
+ BFSVisitor vis,
+ const bgl_named_params<P, T, R>& params,
+ BOOST_GRAPH_ENABLE_IF_MODELS(DistributedGraph, distributed_graph_tag,
+ void)*)
+ {
+ parallel_bfs_helper
+ (g, s, color, vis, get_param(params, buffer_param_t()),
+ choose_const_pmap(get_param(params, vertex_index), g, vertex_index));
+ }
+ }
+}
+
+#endif // BOOST_GRAPH_PARALLEL_BFS_HPP

Added: trunk/boost/graph/distributed/compressed_sparse_row_graph.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/compressed_sparse_row_graph.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,1314 @@
+// Copyright (C) 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
+// Jeremiah Willcock
+// Andrew Lumsdaine
+
+// Distributed compressed sparse row graph type
+
+#ifndef BOOST_GRAPH_DISTRIBUTED_CSR_HPP
+#define BOOST_GRAPH_DISTRIBUTED_CSR_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/graph/compressed_sparse_row_graph.hpp>
+#include <boost/graph/distributed/selector.hpp>
+#include <boost/mpl/if.hpp>
+#include <boost/type_traits/is_same.hpp>
+#include <boost/graph/distributed/concepts.hpp>
+#include <boost/graph/parallel/properties.hpp>
+#include <boost/graph/parallel/distribution.hpp>
+#include <boost/property_map/parallel/local_property_map.hpp>
+#include <boost/property_map/parallel/distributed_property_map.hpp>
+
+namespace boost {
+
+// The number of bits we reserve for the processor ID.
+// DPG TBD: This is a hack. It will eventually be a run-time quantity.
+static const int processor_bits = 8;
+
+// Tag class for a distributed CSR graph
+struct distributed_csr_tag
+ : public virtual distributed_graph_tag,
+ public virtual distributed_vertex_list_graph_tag,
+ public virtual distributed_edge_list_graph_tag,
+ public virtual incidence_graph_tag,
+ public virtual adjacency_graph_tag {};
+
+template<typename Directed, typename VertexProperty, typename EdgeProperty,
+ typename GraphProperty, typename ProcessGroup, typename InVertex,
+ typename InDistribution, typename InEdgeIndex>
+class compressed_sparse_row_graph<
+ Directed, VertexProperty, EdgeProperty, GraphProperty,
+ distributedS<ProcessGroup, InVertex, InDistribution>,
+ InEdgeIndex>
+{
+ typedef compressed_sparse_row_graph self_type;
+
+ private:
+ /**
+ * Determine the type used to represent vertices in the graph. If
+ * the user has overridden the default, use the user's
+ * parameter. Otherwise, fall back to std::size_t.
+ */
+ typedef typename mpl::if_<is_same<InVertex, defaultS>,
+ std::size_t,
+ InVertex>::type Vertex;
+
+ /**
+ * Determine the type used to represent edges in the graph. If
+ * the user has overridden the default (which is to be the same as
+ * the distributed vertex selector type), use the user's
+ * parameter. Otherwise, fall back to the value of @c Vertex.
+ */
+ typedef typename mpl::if_<is_same<InEdgeIndex,
+ distributedS<ProcessGroup, InVertex,
+ InDistribution> >,
+ Vertex,
+ InEdgeIndex>::type EdgeIndex;
+
+ public:
+ /**
+ * The type of the CSR graph that will be stored locally.
+ */
+ typedef compressed_sparse_row_graph<Directed, VertexProperty, EdgeProperty,
+ GraphProperty, Vertex, EdgeIndex>
+ base_type;
+
+ // -----------------------------------------------------------------
+ // Graph concept requirements
+ typedef Vertex vertex_descriptor;
+ typedef typename graph_traits<base_type>::edge_descriptor edge_descriptor;
+ typedef directed_tag directed_category;
+ typedef allow_parallel_edge_tag edge_parallel_category;
+ typedef distributed_csr_tag traversal_category;
+ static vertex_descriptor null_vertex();
+
+ // -----------------------------------------------------------------
+ // Distributed Vertex List Graph concept requirements
+ typedef Vertex vertices_size_type;
+ class vertex_iterator;
+
+ // -----------------------------------------------------------------
+ // Distributed Edge List Graph concept requirements
+ typedef EdgeIndex edges_size_type;
+ class edge_iterator;
+
+ // -----------------------------------------------------------------
+ // Incidence Graph concept requirements
+ typedef typename graph_traits<base_type>::out_edge_iterator
+ out_edge_iterator;
+ typedef typename graph_traits<base_type>::degree_size_type
+ degree_size_type;
+
+ // -----------------------------------------------------------------
+ // Adjacency Graph concept requirements
+ typedef typename graph_traits<base_type>::adjacency_iterator
+ adjacency_iterator;
+
+ // Note: This graph type does not model Bidirectional Graph.
+ // However, this typedef is required to satisfy graph_traits.
+ typedef void in_edge_iterator;
+
+ // -----------------------------------------------------------------
+ // Distributed Container concept requirements
+ typedef ProcessGroup process_group_type;
+ typedef boost::parallel::variant_distribution<process_group_type, Vertex>
+ distribution_type;
+
+ // -----------------------------------------------------------------
+ // Workarounds
+ typedef no_property vertex_property_type;
+ typedef no_property edge_property_type;
+ typedef typename mpl::if_<is_void<VertexProperty>,
+ void****,
+ VertexProperty>::type vertex_bundled;
+ typedef typename mpl::if_<is_void<EdgeProperty>,
+ void****,
+ EdgeProperty>::type edge_bundled;
+
+ // -----------------------------------------------------------------
+ // Useful types
+ typedef typename ProcessGroup::process_id_type process_id_type;
+
+ // -----------------------------------------------------------------
+ // Graph constructors
+ compressed_sparse_row_graph(const ProcessGroup& pg = ProcessGroup())
+ : m_process_group(pg), m_distribution(parallel::block(pg, 0)) {}
+
+ template<typename InputIterator>
+ compressed_sparse_row_graph(InputIterator edge_begin, InputIterator edge_end,
+ vertices_size_type numverts,
+ const ProcessGroup& pg = ProcessGroup(),
+ const GraphProperty& prop = GraphProperty());
+
+ template<typename InputIterator, typename EdgePropertyIterator>
+ compressed_sparse_row_graph(InputIterator edge_begin, InputIterator edge_end,
+ EdgePropertyIterator ep_iter,
+ vertices_size_type numverts,
+ const ProcessGroup& pg = ProcessGroup(),
+ const GraphProperty& prop = GraphProperty());
+
+ template<typename InputIterator, typename Distribution>
+ compressed_sparse_row_graph(InputIterator edge_begin, InputIterator edge_end,
+ vertices_size_type numverts,
+ const ProcessGroup& pg,
+ const Distribution& dist,
+ const GraphProperty& prop = GraphProperty());
+
+ template<typename InputIterator, typename EdgePropertyIterator,
+ typename Distribution>
+ compressed_sparse_row_graph(InputIterator edge_begin, InputIterator edge_end,
+ EdgePropertyIterator ep_iter,
+ vertices_size_type numverts,
+ const ProcessGroup& pg,
+ const Distribution& dist,
+ const GraphProperty& prop = GraphProperty());
+
+ base_type& base() { return m_base; }
+ const base_type& base() const { return m_base; }
+
+ process_group_type process_group() const { return m_process_group.base(); }
+
+ distribution_type& distribution() { return m_distribution; }
+ const distribution_type& distribution() const { return m_distribution; }
+
+ // Directly access a vertex or edge bundle
+ vertex_bundled& operator[](vertex_descriptor v)
+ {
+ std::pair<process_id_type, vertex_descriptor> locator
+ = get(vertex_global, *this, v);
+ assert(locator.first == process_id(m_process_group));
+ return base().m_vertex_properties[locator.second];
+ }
+
+ const vertex_bundled& operator[](vertex_descriptor v) const
+ {
+ std::pair<process_id_type, vertex_descriptor> locator
+ = get(vertex_global, *this, v);
+ assert(locator.first == process_id(m_process_group));
+ return base().m_process_group[locator.second];
+ }
+
+ edge_bundled& operator[](edge_descriptor e)
+ {
+ assert(get(vertex_owner, *this, e.src) == process_id(m_process_group));
+ return base().m_edge_properties[e.idx];
+ }
+
+ const edge_bundled& operator[](edge_descriptor e) const
+ {
+ assert(get(vertex_owner, *this, e.src) == process_id(m_process_group));
+ return base().m_edge_properties[e.idx];
+ }
+
+ // Create a vertex descriptor from a process ID and a local index.
+ vertex_descriptor
+ make_vertex_descriptor(process_id_type p, vertex_descriptor v) const
+ {
+ vertex_descriptor vertex_local_index_bits =
+ sizeof(vertex_descriptor) * CHAR_BIT - processor_bits;
+ return v | ((vertex_descriptor)p << vertex_local_index_bits);
+ }
+
+ // Convert a local vertex descriptor into a global vertex descriptor
+ vertex_descriptor local_to_global_vertex(vertex_descriptor v) const
+ {
+ return make_vertex_descriptor(process_id(m_process_group), v);
+ }
+
+ protected:
+ ProcessGroup m_process_group;
+ distribution_type m_distribution;
+ base_type m_base;
+};
+
+/** @brief Helper macro containing the template parameters for the
+ * distributed CSR graph.
+ *
+ * This macro contains all of the template parameters needed for the
+ * distributed compressed_sparse_row graph type. It is used to reduce
+ * the amount of typing required to declare free functions for this
+ * graph type.
+ */
+#define BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS \
+ typename Directed, typename VertexProperty, typename EdgeProperty, \
+ typename GraphProperty, typename ProcessGroup, typename InVertex, \
+ typename InDistribution, typename InEdgeIndex
+
+/** @brief Helper macro containing the typical instantiation of the
+ * distributed CSR graph.
+ *
+ * This macro contains an instantiation of the distributed CSR graph
+ * type using the typical template parameters names (e.g., those
+ * provided by the macro @c
+ * BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS). It is used to reduce
+ * the amount of typing required to declare free functions for this
+ * graph type.
+ */
+#define BOOST_DISTRIB_CSR_GRAPH_TYPE \
+ compressed_sparse_row_graph< \
+ Directed, VertexProperty, EdgeProperty, GraphProperty, \
+ distributedS<ProcessGroup, InVertex, InDistribution>, \
+ InEdgeIndex>
+
+// -----------------------------------------------------------------
+// Graph concept operations
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor
+BOOST_DISTRIB_CSR_GRAPH_TYPE::null_vertex()
+{
+ return graph_traits<base_type>::null_vertex();
+}
+
+// -----------------------------------------------------------------
+// Incidence Graph concept operations
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor
+source(typename BOOST_DISTRIB_CSR_GRAPH_TYPE::edge_descriptor e,
+ const BOOST_DISTRIB_CSR_GRAPH_TYPE&)
+{ return e.src; }
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor
+target(typename BOOST_DISTRIB_CSR_GRAPH_TYPE::edge_descriptor e,
+ const BOOST_DISTRIB_CSR_GRAPH_TYPE& g)
+{ return target(e, g.base()); }
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline std::pair<typename BOOST_DISTRIB_CSR_GRAPH_TYPE::out_edge_iterator,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::out_edge_iterator>
+out_edges(typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor u,
+ const BOOST_DISTRIB_CSR_GRAPH_TYPE& g)
+{
+ typedef typename BOOST_DISTRIB_CSR_GRAPH_TYPE::edges_size_type
+ edges_size_type;
+ typedef typename BOOST_DISTRIB_CSR_GRAPH_TYPE::edge_descriptor ed;
+ typedef typename BOOST_DISTRIB_CSR_GRAPH_TYPE::out_edge_iterator it;
+ edges_size_type u_local = get(vertex_local, g, u);
+ edges_size_type u_row_start = g.base().m_rowstart[u_local];
+ edges_size_type next_row_start = g.base().m_rowstart[u_local + 1];
+ return std::make_pair(it(ed(u, u_row_start)),
+ it(ed(u, (std::max)(u_row_start, next_row_start))));
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline typename BOOST_DISTRIB_CSR_GRAPH_TYPE::degree_size_type
+out_degree(typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor u,
+ const BOOST_DISTRIB_CSR_GRAPH_TYPE& g)
+{
+ return out_degree(get(vertex_local, g, u), g.base());
+}
+
+// -----------------------------------------------------------------
+// DistributedGraph concept requirements
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+void synchronize(const BOOST_DISTRIB_CSR_GRAPH_TYPE& g)
+{
+ typedef BOOST_DISTRIB_CSR_GRAPH_TYPE graph_type;
+ synchronize(g.process_group());
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+ProcessGroup
+process_group(const BOOST_DISTRIB_CSR_GRAPH_TYPE& g)
+{ return g.process_group(); }
+
+
+// -----------------------------------------------------------------
+// Adjacency Graph concept requirements
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline std::pair<typename BOOST_DISTRIB_CSR_GRAPH_TYPE::adjacency_iterator,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::adjacency_iterator>
+adjacent_vertices(typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor u,
+ const BOOST_DISTRIB_CSR_GRAPH_TYPE& g)
+{
+ return adjacent_vertices(get(vertex_local, g, u), g.base());
+}
+
+// -----------------------------------------------------------------
+// Distributed Vertex List Graph concept operations
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+class BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_iterator
+ : public iterator_adaptor<vertex_iterator,
+ counting_iterator<Vertex>,
+ Vertex,
+ random_access_traversal_tag,
+ Vertex>
+{
+ typedef iterator_adaptor<vertex_iterator,
+ counting_iterator<Vertex>,
+ Vertex,
+ random_access_traversal_tag,
+ Vertex> inherited;
+ public:
+ vertex_iterator() {}
+
+ explicit vertex_iterator(Vertex v, const self_type* graph)
+ : inherited(counting_iterator<Vertex>(v)), graph(graph) { }
+
+ Vertex dereference() const
+ {
+ return graph->local_to_global_vertex(*(this->base_reference()));
+ }
+
+ friend class iterator_core_access;
+
+ private:
+ const self_type* graph;
+};
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline typename BOOST_DISTRIB_CSR_GRAPH_TYPE::degree_size_type
+num_vertices(const BOOST_DISTRIB_CSR_GRAPH_TYPE& g)
+{
+ return num_vertices(g.base());
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline std::pair<typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_iterator,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_iterator>
+vertices(const BOOST_DISTRIB_CSR_GRAPH_TYPE& g)
+{
+ typedef typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_iterator
+ vertex_iterator;
+ return std::make_pair(vertex_iterator(0, &g),
+ vertex_iterator(num_vertices(g), &g));
+}
+
+// -----------------------------------------------------------------
+// Distributed Edge List Graph concept operations
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+class BOOST_DISTRIB_CSR_GRAPH_TYPE::edge_iterator
+{
+ public:
+ typedef std::forward_iterator_tag iterator_category;
+ typedef edge_descriptor value_type;
+
+ typedef const edge_descriptor* pointer;
+
+ typedef edge_descriptor reference;
+ typedef typename int_t<CHAR_BIT * sizeof(EdgeIndex)>::fast difference_type;
+
+ edge_iterator() : graph(0), current_edge(), end_of_this_vertex(0) {}
+
+ edge_iterator(const compressed_sparse_row_graph& graph,
+ edge_descriptor current_edge,
+ EdgeIndex end_of_this_vertex)
+ : graph(&graph), local_src(current_edge.src), current_edge(current_edge),
+ end_of_this_vertex(end_of_this_vertex)
+ {
+ // The edge that comes in has a local source vertex. Make it global.
+ current_edge.src = graph.local_to_global_vertex(current_edge.src);
+ }
+
+ // From InputIterator
+ reference operator*() const { return current_edge; }
+ pointer operator->() const { return &current_edge; }
+
+ bool operator==(const edge_iterator& o) const {
+ return current_edge == o.current_edge;
+ }
+ bool operator!=(const edge_iterator& o) const {
+ return current_edge != o.current_edge;
+ }
+
+ edge_iterator& operator++()
+ {
+ ++current_edge.idx;
+ while (current_edge.idx == end_of_this_vertex && local_src < num_vertices(*graph)-1) {
+ ++local_src;
+ current_edge.src = graph->local_to_global_vertex(local_src);
+ end_of_this_vertex = graph->base().m_rowstart[local_src + 1];
+ }
+ return *this;
+ }
+
+ edge_iterator operator++(int) {
+ edge_iterator temp = *this;
+ ++*this;
+ return temp;
+ }
+
+ private:
+ const compressed_sparse_row_graph* graph;
+ EdgeIndex local_src;
+ edge_descriptor current_edge;
+ EdgeIndex end_of_this_vertex;
+};
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline typename BOOST_DISTRIB_CSR_GRAPH_TYPE::edges_size_type
+num_edges(const BOOST_DISTRIB_CSR_GRAPH_TYPE& g)
+{
+ return g.base().m_column.size();
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+std::pair<typename BOOST_DISTRIB_CSR_GRAPH_TYPE::edge_iterator,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::edge_iterator>
+edges(const BOOST_DISTRIB_CSR_GRAPH_TYPE& g)
+{
+ typedef typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor Vertex;
+ typedef typename BOOST_DISTRIB_CSR_GRAPH_TYPE::edge_iterator ei;
+ typedef typename BOOST_DISTRIB_CSR_GRAPH_TYPE::edge_descriptor edgedesc;
+ if (g.base().m_rowstart.size() == 1 || g.base().m_column.empty()) {
+ return std::make_pair(ei(), ei());
+ } else {
+ // Find the first vertex that has outgoing edges
+ Vertex src = 0;
+ while (g.base().m_rowstart[src + 1] == 0) ++src;
+ return std::make_pair(ei(g, edgedesc(src, 0), g.base().m_rowstart[src + 1]),
+ ei(g, edgedesc(num_vertices(g), g.base().m_column.size()), 0));
+ }
+}
+
+// -----------------------------------------------------------------
+// Graph constructors
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+template<typename InputIterator>
+BOOST_DISTRIB_CSR_GRAPH_TYPE::
+compressed_sparse_row_graph(InputIterator edge_begin, InputIterator edge_end,
+ vertices_size_type numverts,
+ const ProcessGroup& pg,
+ const GraphProperty& prop)
+ : m_process_group(pg),
+ m_distribution(parallel::block(m_process_group, numverts)),
+ m_base(m_distribution.block_size(process_id(m_process_group), numverts))
+{
+ parallel::block dist(m_process_group, numverts);
+
+ // Allows us to add edges
+ m_base.m_last_source = 0;
+
+ typename ProcessGroup::process_id_type id = process_id(m_process_group);
+
+ while (edge_begin != edge_end) {
+ vertex_descriptor src = edge_begin->first;
+ if (static_cast<process_id_type>(dist(src)) == id) {
+ vertex_descriptor tgt =
+ make_vertex_descriptor(dist(edge_begin->second),
+ dist.local(edge_begin->second));
+ add_edge(dist.local(src), tgt, m_base);
+ }
+ ++edge_begin;
+ }
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+template<typename InputIterator, typename EdgePropertyIterator>
+BOOST_DISTRIB_CSR_GRAPH_TYPE::
+compressed_sparse_row_graph(InputIterator edge_begin, InputIterator edge_end,
+ EdgePropertyIterator ep_iter,
+ vertices_size_type numverts,
+ const ProcessGroup& pg,
+ const GraphProperty& prop)
+ : m_process_group(pg),
+ m_distribution(parallel::block(m_process_group, numverts)),
+ m_base(m_distribution.block_size(process_id(m_process_group), numverts))
+{
+ parallel::block dist(m_process_group, numverts);
+
+ // Allows us to add edges
+ m_base.m_last_source = 0;
+
+ typename ProcessGroup::process_id_type id = process_id(m_process_group);
+
+ while (edge_begin != edge_end) {
+ EdgeIndex src = edge_begin->first;
+ if (static_cast<process_id_type>(dist(src)) == id) {
+ EdgeIndex tgt =
+ make_vertex_descriptor(dist(edge_begin->second),
+ dist.local(edge_begin->second));
+
+ add_edge(dist.local(src), tgt, *ep_iter, m_base);
+ }
+ ++edge_begin;
+ ++ep_iter;
+ }
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+template<typename InputIterator, typename Distribution>
+BOOST_DISTRIB_CSR_GRAPH_TYPE::
+compressed_sparse_row_graph(InputIterator edge_begin, InputIterator edge_end,
+ vertices_size_type numverts,
+ const ProcessGroup& pg,
+ const Distribution& dist,
+ const GraphProperty& prop)
+ : m_process_group(pg),
+ m_distribution(dist),
+ m_base(dist.block_size(process_id(m_process_group), numverts))
+{
+ // Allows us to add edges
+ m_base.m_last_source = 0;
+
+ typename ProcessGroup::process_id_type id = process_id(m_process_group);
+
+ while (edge_begin != edge_end) {
+ vertex_descriptor src = edge_begin->first;
+ if (static_cast<process_id_type>(dist(src)) == id) {
+ vertex_descriptor tgt =
+ make_vertex_descriptor(dist(edge_begin->second),
+ dist.local(edge_begin->second));
+ assert(get(vertex_owner, *this, tgt) == dist(edge_begin->second));
+ assert(get(vertex_local, *this, tgt) == dist.local(edge_begin->second));
+ add_edge(dist.local(src), tgt, m_base);
+ }
+ ++edge_begin;
+ }
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+template<typename InputIterator, typename EdgePropertyIterator,
+ typename Distribution>
+BOOST_DISTRIB_CSR_GRAPH_TYPE::
+compressed_sparse_row_graph(InputIterator edge_begin, InputIterator edge_end,
+ EdgePropertyIterator ep_iter,
+ vertices_size_type numverts,
+ const ProcessGroup& pg,
+ const Distribution& dist,
+ const GraphProperty& prop)
+ : m_process_group(pg),
+ m_distribution(dist),
+ m_base(dist.block_size(process_id(m_process_group), numverts))
+{
+ // Allows us to add edges
+ m_base.m_last_source = 0;
+
+ typename ProcessGroup::process_id_type id = process_id(m_process_group);
+
+ while (edge_begin != edge_end) {
+ EdgeIndex src = edge_begin->first;
+ if (static_cast<process_id_type>(dist(src)) == id) {
+ EdgeIndex tgt =
+ make_vertex_descriptor(dist(edge_begin->second),
+ dist.local(edge_begin->second));
+ add_edge(dist.local(src), tgt, *ep_iter, m_base);
+ }
+ ++edge_begin;
+ ++ep_iter;
+ }
+}
+
+// -----------------------------------------------------------------
+// Vertex Global Property Map
+template<typename ProcessID, typename Key>
+class csr_vertex_global_map
+{
+ public:
+ // -----------------------------------------------------------------
+ // Readable Property Map concept requirements
+ typedef std::pair<ProcessID, Key> value_type;
+ typedef value_type reference;
+ typedef Key key_type;
+ typedef readable_property_map_tag category;
+};
+
+template<typename ProcessID, typename Key>
+inline std::pair<ProcessID, Key>
+get(csr_vertex_global_map<ProcessID, Key>,
+ typename csr_vertex_global_map<ProcessID, Key>::key_type k)
+{
+ const int local_index_bits = sizeof(Key) * CHAR_BIT - processor_bits;
+ const Key local_index_mask = Key(-1) >> processor_bits;
+
+ return std::pair<ProcessID, Key>(k >> local_index_bits,
+ k & local_index_mask);
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+class property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, vertex_global_t>
+{
+ public:
+ typedef csr_vertex_global_map<
+ typename ProcessGroup::process_id_type,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor> type;
+ typedef type const_type;
+};
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline
+typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, vertex_global_t>::type
+get(vertex_global_t, BOOST_DISTRIB_CSR_GRAPH_TYPE& g)
+{
+ typedef typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, vertex_global_t>
+ ::type result_type;
+ return result_type();
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline
+std::pair<typename ProcessGroup::process_id_type,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor>
+get(vertex_global_t, BOOST_DISTRIB_CSR_GRAPH_TYPE& g,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor k)
+{
+ return get(vertex_global,
+ const_cast<const BOOST_DISTRIB_CSR_GRAPH_TYPE&>(g),
+ k);
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline
+typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, vertex_global_t>::const_type
+get(vertex_global_t, const BOOST_DISTRIB_CSR_GRAPH_TYPE& g)
+{
+ typedef typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, vertex_global_t>
+ ::const_type result_type;
+ return result_type();
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline
+std::pair<typename ProcessGroup::process_id_type,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor>
+get(vertex_global_t, const BOOST_DISTRIB_CSR_GRAPH_TYPE& g,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor k)
+{
+ typedef typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor
+ vertex_descriptor;
+ typedef std::pair<typename ProcessGroup::process_id_type, vertex_descriptor>
+ result_type;
+ const int local_index_bits =
+ sizeof(vertex_descriptor) * CHAR_BIT - processor_bits;
+ const vertex_descriptor local_index_mask =
+ vertex_descriptor(-1) >> processor_bits;
+
+ return result_type(k >> local_index_bits, k & local_index_mask);
+}
+
+// -----------------------------------------------------------------
+// Extra, common functions
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor
+vertex(typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertices_size_type i,
+ const BOOST_DISTRIB_CSR_GRAPH_TYPE& g)
+{
+ return g.make_vertex_descriptor(g.distribution()(i),
+ g.distribution().local(i));
+}
+
+// Unlike for an adjacency_matrix, edge_range and edge take lg(out_degree(i))
+// time
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline std::pair<typename BOOST_DISTRIB_CSR_GRAPH_TYPE::out_edge_iterator,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::out_edge_iterator>
+edge_range(typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor i,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor j,
+ const BOOST_DISTRIB_CSR_GRAPH_TYPE& g)
+{
+ typedef typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor Vertex;
+ typedef typename BOOST_DISTRIB_CSR_GRAPH_TYPE::edges_size_type EdgeIndex;
+ typedef typename std::vector<Vertex>::const_iterator adj_iter;
+ typedef typename BOOST_DISTRIB_CSR_GRAPH_TYPE::out_edge_iterator out_edge_iter;
+ typedef typename BOOST_DISTRIB_CSR_GRAPH_TYPE::edge_descriptor edge_desc;
+ std::pair<adj_iter, adj_iter> raw_adjacencies = adjacent_vertices(i, g);
+ std::pair<adj_iter, adj_iter> adjacencies =
+ std::equal_range(raw_adjacencies.first, raw_adjacencies.second, j);
+ EdgeIndex idx_begin = adjacencies.first - g.base().m_column.begin();
+ EdgeIndex idx_end = adjacencies.second - g.base().m_column.begin();
+ return std::make_pair(out_edge_iter(edge_desc(i, idx_begin)),
+ out_edge_iter(edge_desc(i, idx_end)));
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline std::pair<typename BOOST_DISTRIB_CSR_GRAPH_TYPE::edge_descriptor, bool>
+edge(typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor i,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor j,
+ const BOOST_DISTRIB_CSR_GRAPH_TYPE& g)
+{
+ typedef typename BOOST_DISTRIB_CSR_GRAPH_TYPE::out_edge_iterator out_edge_iter;
+ std::pair<out_edge_iter, out_edge_iter> range = edge_range(i, j, g);
+ if (range.first == range.second)
+ return std::make_pair(typename BOOST_DISTRIB_CSR_GRAPH_TYPE::edge_descriptor(),
+ false);
+ else
+ return std::make_pair(*range.first, true);
+}
+
+// A helper that turns requests for property maps for const graphs
+// into property maps for non-const graphs.
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS, typename Property>
+class property_map<const BOOST_DISTRIB_CSR_GRAPH_TYPE, Property>
+{
+ public:
+ typedef typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, Property>
+ ::const_type type;
+ typedef type const_type;
+};
+
+// -----------------------------------------------------------------
+// Vertex Owner Property Map
+template<typename ProcessID, typename Key>
+class csr_vertex_owner_map
+{
+ public:
+ // -----------------------------------------------------------------
+ // Readable Property Map concept requirements
+ typedef ProcessID value_type;
+ typedef value_type reference;
+ typedef Key key_type;
+ typedef readable_property_map_tag category;
+};
+
+template<typename ProcessID, typename Key>
+inline ProcessID
+get(csr_vertex_owner_map<ProcessID, Key> pm,
+ typename csr_vertex_owner_map<ProcessID, Key>::key_type k)
+{
+ const int local_index_bits = sizeof(Key) * CHAR_BIT - processor_bits;
+ return k >> local_index_bits;
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+class property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, vertex_owner_t>
+{
+ public:
+ typedef csr_vertex_owner_map<
+ typename ProcessGroup::process_id_type,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor> type;
+ typedef type const_type;
+};
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline
+typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, vertex_owner_t>::type
+get(vertex_owner_t, BOOST_DISTRIB_CSR_GRAPH_TYPE& g)
+{
+ typedef typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, vertex_owner_t>
+ ::type result_type;
+ return result_type();
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline typename ProcessGroup::process_id_type
+get(vertex_owner_t, BOOST_DISTRIB_CSR_GRAPH_TYPE& g,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor k)
+{
+ return get(vertex_owner,
+ const_cast<const BOOST_DISTRIB_CSR_GRAPH_TYPE&>(g),
+ k);
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline
+typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, vertex_owner_t>::const_type
+get(vertex_owner_t, const BOOST_DISTRIB_CSR_GRAPH_TYPE& g)
+{
+ typedef typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, vertex_owner_t>
+ ::const_type result_type;
+ return result_type();
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline typename ProcessGroup::process_id_type
+get(vertex_owner_t, const BOOST_DISTRIB_CSR_GRAPH_TYPE& g,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor k)
+{
+ typedef typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor
+ vertex_descriptor;
+ const int local_index_bits =
+ sizeof(vertex_descriptor) * CHAR_BIT - processor_bits;
+ return k >> local_index_bits;
+}
+
+// -----------------------------------------------------------------
+// Vertex Local Property Map
+template<typename Key>
+class csr_vertex_local_map
+{
+ public:
+ // -----------------------------------------------------------------
+ // Readable Property Map concept requirements
+ typedef Key value_type;
+ typedef value_type reference;
+ typedef Key key_type;
+ typedef readable_property_map_tag category;
+};
+
+template<typename Key>
+inline Key
+get(csr_vertex_local_map<Key> pm,
+ typename csr_vertex_local_map<Key>::key_type k)
+{
+ const Key local_index_mask = Key(-1) >> processor_bits;
+ return k & local_index_mask;
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+class property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, vertex_local_t>
+{
+ public:
+ typedef csr_vertex_local_map<
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor> type;
+ typedef type const_type;
+};
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline
+typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, vertex_local_t>::type
+get(vertex_local_t, BOOST_DISTRIB_CSR_GRAPH_TYPE& g)
+{
+ typedef typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, vertex_local_t>
+ ::type result_type;
+ return result_type();
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor
+get(vertex_local_t, BOOST_DISTRIB_CSR_GRAPH_TYPE& g,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor k)
+{
+ return get(vertex_local,
+ const_cast<const BOOST_DISTRIB_CSR_GRAPH_TYPE&>(g),
+ k);
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline
+typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, vertex_local_t>::const_type
+get(vertex_local_t, const BOOST_DISTRIB_CSR_GRAPH_TYPE& g)
+{
+ typedef typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, vertex_local_t>
+ ::const_type result_type;
+ return result_type();
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor
+get(vertex_local_t, const BOOST_DISTRIB_CSR_GRAPH_TYPE& g,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor k)
+{
+ typedef typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor
+ vertex_descriptor;
+ const vertex_descriptor local_index_mask =
+ vertex_descriptor(-1) >> processor_bits;
+ return k & local_index_mask;
+}
+
+// -----------------------------------------------------------------
+// Vertex Index Property Map
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+class property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, vertex_index_t>
+{
+ typedef typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE,
+ vertex_global_t>::const_type
+ global_map;
+ typedef typename BOOST_DISTRIB_CSR_GRAPH_TYPE::process_group_type
+ process_group_type;
+
+ typedef property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, vertex_local_t> local;
+
+public:
+ typedef local_property_map<process_group_type,
+ global_map,
+ typename local::type> type;
+ typedef local_property_map<process_group_type,
+ global_map,
+ typename local::const_type> const_type;
+};
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline
+typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, vertex_index_t>::type
+get(vertex_index_t, BOOST_DISTRIB_CSR_GRAPH_TYPE& g)
+{
+ typedef typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, vertex_index_t>
+ ::type result_type;
+
+ return result_type(g.process_group(), get(vertex_global, g),
+ get(vertex_local, g));
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertices_size_type
+get(vertex_index_t, BOOST_DISTRIB_CSR_GRAPH_TYPE& g,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor k)
+{
+ return get(vertex_local, g, k);
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline
+typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, vertex_index_t>::const_type
+get(vertex_index_t, const BOOST_DISTRIB_CSR_GRAPH_TYPE& g)
+{
+ typedef typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, vertex_index_t>
+ ::const_type result_type;
+ return result_type(g.process_group(), get(vertex_global, g),
+ get(vertex_local, g));
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertices_size_type
+get(vertex_index_t, const BOOST_DISTRIB_CSR_GRAPH_TYPE& g,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor k)
+{
+ return get(vertex_local, g, k);
+}
+
+// -----------------------------------------------------------------
+// Vertex Local Index Property Map
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+class property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, vertex_local_index_t>
+ : public property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, vertex_local_t> { };
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline
+typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, vertex_local_index_t>::type
+get(vertex_local_index_t, BOOST_DISTRIB_CSR_GRAPH_TYPE& g)
+{
+ return get(vertex_local, g);
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertices_size_type
+get(vertex_local_index_t, BOOST_DISTRIB_CSR_GRAPH_TYPE& g,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor k)
+{
+ return get(vertex_local, g, k);
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline
+typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, vertex_local_index_t>::const_type
+get(vertex_local_index_t, const BOOST_DISTRIB_CSR_GRAPH_TYPE& g)
+{
+ return get(vertex_local, g);
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertices_size_type
+get(vertex_local_index_t, const BOOST_DISTRIB_CSR_GRAPH_TYPE& g,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor k)
+{
+ return get(vertex_local, g, k);
+}
+
+// -----------------------------------------------------------------
+// Edge Global Property Map
+template<typename ProcessID, typename Vertex, typename EdgeIndex>
+class csr_edge_global_map
+{
+ public:
+ // -----------------------------------------------------------------
+ // Readable Property Map concept requirements
+ typedef std::pair<ProcessID, EdgeIndex> value_type;
+ typedef value_type reference;
+ typedef csr_edge_descriptor<Vertex, EdgeIndex> key_type;
+ typedef readable_property_map_tag category;
+};
+
+template<typename ProcessID, typename Vertex, typename EdgeIndex>
+inline std::pair<ProcessID, EdgeIndex>
+get(csr_edge_global_map<ProcessID, Vertex, EdgeIndex> pm,
+ typename csr_edge_global_map<ProcessID, Vertex, EdgeIndex>::key_type k)
+{
+ const int local_index_bits = sizeof(Vertex) * CHAR_BIT - processor_bits;
+ return std::pair<ProcessID, EdgeIndex>(k.src >> local_index_bits, k.idx);
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+class property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, edge_global_t>
+{
+ public:
+ typedef csr_edge_global_map<
+ typename ProcessGroup::process_id_type,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::edges_size_type> type;
+ typedef type const_type;
+};
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline
+typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, edge_global_t>::type
+get(edge_global_t, BOOST_DISTRIB_CSR_GRAPH_TYPE& g)
+{
+ typedef typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, edge_global_t>
+ ::type result_type;
+ return result_type();
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline
+std::pair<typename ProcessGroup::process_id_type,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::edges_size_type>
+get(edge_global_t, BOOST_DISTRIB_CSR_GRAPH_TYPE& g,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::edge_descriptor k)
+{
+ return get(edge_global,
+ const_cast<const BOOST_DISTRIB_CSR_GRAPH_TYPE&>(g),
+ k);
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline
+typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, edge_global_t>::const_type
+get(edge_global_t, const BOOST_DISTRIB_CSR_GRAPH_TYPE& g)
+{
+ typedef typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, edge_global_t>
+ ::const_type result_type;
+ return result_type();
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline
+std::pair<typename ProcessGroup::process_id_type,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::edges_size_type>
+get(edge_global_t, const BOOST_DISTRIB_CSR_GRAPH_TYPE& g,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::edge_descriptor k)
+{
+ typedef typename BOOST_DISTRIB_CSR_GRAPH_TYPE::vertex_descriptor
+ vertex_descriptor;
+
+ const int local_index_bits =
+ sizeof(vertex_descriptor) * CHAR_BIT - processor_bits;
+
+ typedef std::pair<typename ProcessGroup::process_id_type,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::edges_size_type>
+ result_type;
+
+ return result_type(k.src >> local_index_bits, k.idx);
+}
+
+// -----------------------------------------------------------------
+// Edge Index Property Map
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+class property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, edge_index_t>
+{
+ typedef typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, edge_global_t>
+ ::type global_map;
+
+ public:
+ typedef local_property_map<
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::process_group_type,
+ global_map,
+ identity_property_map> type;
+ typedef type const_type;
+};
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline
+typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, edge_index_t>::type
+get(edge_index_t, BOOST_DISTRIB_CSR_GRAPH_TYPE& g)
+{
+ typedef typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, edge_index_t>
+ ::type result_type;
+ return result_type(g.process_group(), get(edge_global, g),
+ identity_property_map());
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline typename BOOST_DISTRIB_CSR_GRAPH_TYPE::edges_size_type
+get(edge_index_t, BOOST_DISTRIB_CSR_GRAPH_TYPE& g,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::edge_descriptor k)
+{
+ return k.idx;
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline
+typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, edge_index_t>::const_type
+get(edge_index_t, const BOOST_DISTRIB_CSR_GRAPH_TYPE& g)
+{
+ typedef typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, edge_index_t>
+ ::const_type result_type;
+ return result_type(g.process_group(), get(edge_global, g),
+ identity_property_map());
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS>
+inline typename BOOST_DISTRIB_CSR_GRAPH_TYPE::edges_size_type
+get(edge_index_t, const BOOST_DISTRIB_CSR_GRAPH_TYPE& g,
+ typename BOOST_DISTRIB_CSR_GRAPH_TYPE::edge_descriptor k)
+{
+ return k.idx;
+}
+
+// -----------------------------------------------------------------
+// Bundled Properties
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS, typename T, typename Bundle>
+class property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, T Bundle::*>
+{
+ typedef BOOST_DISTRIB_CSR_GRAPH_TYPE Graph;
+ typedef typename Graph::process_group_type process_group_type;
+
+ // Determine which locator map to use (vertex or edge)
+ typedef typename mpl::if_<detail::is_vertex_bundle<VertexProperty,
+ EdgeProperty,
+ Bundle>,
+ vertex_global_t, edge_global_t>::type global_t;
+
+ // Extract the global property map for our key type.
+ typedef typename property_map<Graph, global_t>::const_type global_map;
+ typedef typename property_traits<global_map>::value_type locator;
+
+ // Determine which bundle type we are using
+ typedef typename mpl::if_<detail::is_vertex_bundle<VertexProperty,
+ EdgeProperty,
+ Bundle>,
+ VertexProperty, EdgeProperty>::type bundle_t;
+
+public:
+ // Build the local property map
+ typedef bundle_property_map<std::vector<bundle_t>,
+ typename locator::second_type,
+ bundle_t,
+ T> local_pmap;
+
+ // Build the local const property map
+ typedef bundle_property_map<const std::vector<bundle_t>,
+ typename locator::second_type,
+ bundle_t,
+ const T> local_const_pmap;
+ typedef ::boost::parallel::distributed_property_map<
+ process_group_type, global_map, local_pmap> type;
+
+ typedef ::boost::parallel::distributed_property_map<
+ process_group_type, global_map, local_const_pmap> const_type;
+};
+
+namespace detail {
+ // Retrieve the local bundle_property_map corresponding to a
+ // non-const vertex property.
+ template<typename Graph, typename T, typename Bundle>
+ inline bundle_property_map<std::vector<typename Graph::vertex_bundled>,
+ typename Graph::vertex_descriptor,
+ typename Graph::vertex_bundled, T>
+ get_distrib_csr_bundle(T Bundle::* p, Graph& g, mpl::true_)
+ {
+ typedef bundle_property_map<std::vector<typename Graph::vertex_bundled>,
+ typename Graph::vertex_descriptor,
+ typename Graph::vertex_bundled, T> result_type;
+ return result_type(&g.base().vertex_properties().m_vertex_properties, p);
+ }
+
+ // Retrieve the local bundle_property_map corresponding to a
+ // const vertex property.
+ template<typename Graph, typename T, typename Bundle>
+ inline bundle_property_map<const std::vector<typename Graph::vertex_bundled>,
+ typename Graph::vertex_descriptor,
+ typename Graph::vertex_bundled, const T>
+ get_distrib_csr_bundle(T Bundle::* p, const Graph& g, mpl::true_)
+ {
+ typedef bundle_property_map<
+ const std::vector<typename Graph::vertex_bundled>,
+ typename Graph::vertex_descriptor,
+ typename Graph::vertex_bundled, const T> result_type;
+ return result_type(&g.base().vertex_properties().m_vertex_properties, p);
+ }
+
+ // Retrieve the local bundle_property_map corresponding to a
+ // non-const edge property.
+ template<typename Graph, typename T, typename Bundle>
+ inline bundle_property_map<std::vector<typename Graph::edge_bundled>,
+ typename Graph::edges_size_type,
+ typename Graph::edge_bundled, T>
+ get_distrib_csr_bundle(T Bundle::* p, Graph& g, mpl::false_)
+ {
+ typedef bundle_property_map<std::vector<typename Graph::edge_bundled>,
+ typename Graph::edges_size_type,
+ typename Graph::edge_bundled, T> result_type;
+ return result_type(&g.base().edge_properties().m_edge_properties, p);
+ }
+
+ // Retrieve the local bundle_property_map corresponding to a
+ // const edge property.
+ template<typename Graph, typename T, typename Bundle>
+ inline bundle_property_map<const std::vector<typename Graph::edge_bundled>,
+ typename Graph::edges_size_type,
+ typename Graph::edge_bundled, const T>
+ get_distrib_csr_bundle(T Bundle::* p, const Graph& g, mpl::false_)
+ {
+ typedef bundle_property_map<
+ const std::vector<typename Graph::edge_bundled>,
+ typename Graph::edges_size_type,
+ typename Graph::edge_bundled, const T> result_type;
+ return result_type(&g.base().edge_properties().m_edge_properties, p);
+ }
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS, typename T, typename Bundle>
+typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, T Bundle::*>::type
+get(T Bundle::* p, BOOST_DISTRIB_CSR_GRAPH_TYPE& g)
+{
+ typedef BOOST_DISTRIB_CSR_GRAPH_TYPE Graph;
+ typedef typename property_map<Graph, T Bundle::*>::type result_type;
+ typedef typename property_map<Graph, T Bundle::*>::local_pmap local_pmap;
+
+ // Resolver
+ typedef typename property_traits<result_type>::value_type value_type;
+ typedef typename property_reduce<T Bundle::*>::template apply<value_type>
+ reduce;
+
+ typedef typename property_traits<result_type>::key_type descriptor;
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+ typedef typename mpl::if_<is_same<descriptor, vertex_descriptor>,
+ vertex_global_t, edge_global_t>::type
+ global_map_t;
+
+ return result_type(g.process_group(), get(global_map_t(), g),
+ detail::get_distrib_csr_bundle
+ (p, g, mpl::bool_<is_same<descriptor,
+ vertex_descriptor>::value>()),
+ reduce());
+}
+
+template<BOOST_DISTRIB_CSR_GRAPH_TEMPLATE_PARMS, typename T, typename Bundle>
+typename property_map<BOOST_DISTRIB_CSR_GRAPH_TYPE, T Bundle::*>::const_type
+get(T Bundle::* p, const BOOST_DISTRIB_CSR_GRAPH_TYPE& g)
+{
+ typedef BOOST_DISTRIB_CSR_GRAPH_TYPE Graph;
+ typedef typename property_map<Graph, T Bundle::*>::const_type result_type;
+ typedef typename property_map<Graph, T Bundle::*>::local_const_pmap
+ local_pmap;
+
+ // Resolver
+ typedef typename property_traits<result_type>::value_type value_type;
+ typedef typename property_reduce<T Bundle::*>::template apply<value_type>
+ reduce;
+
+ typedef typename property_traits<result_type>::key_type descriptor;
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+ typedef typename mpl::if_<is_same<descriptor, vertex_descriptor>,
+ vertex_global_t, edge_global_t>::type
+ global_map_t;
+
+ return result_type(g.process_group(), get(global_map_t(), g),
+ detail::get_distrib_csr_bundle
+ (p, g, mpl::bool_<is_same<descriptor,
+ vertex_descriptor>::value>()),
+ reduce());
+}
+
+namespace mpi {
+ template<typename Vertex, typename EdgeIndex>
+ struct is_mpi_datatype<csr_edge_descriptor<Vertex, EdgeIndex> >
+ : mpl::true_ { };
+}
+
+namespace serialization {
+ template<typename Vertex, typename EdgeIndex>
+ struct is_bitwise_serializable<csr_edge_descriptor<Vertex, EdgeIndex> >
+ : mpl::true_ { };
+
+ template<typename Vertex, typename EdgeIndex>
+ struct implementation_level<csr_edge_descriptor<Vertex, EdgeIndex> >
+ : mpl::int_<object_serializable> {} ;
+
+ template<typename Vertex, typename EdgeIndex>
+ struct tracking_level<csr_edge_descriptor<Vertex, EdgeIndex> >
+ : mpl::int_<track_never> {} ;
+
+}
+
+} // end namespace boost
+
+#endif // BOOST_GRAPH_DISTRIBUTED_CSR_HPP

Added: trunk/boost/graph/distributed/concepts.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/concepts.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,216 @@
+// 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
+
+//
+// Distributed graph concepts and helpers
+//
+
+#ifndef BOOST_GRAPH_DISTRIBUTED_CONCEPTS_HPP
+#define BOOST_GRAPH_DISTRIBUTED_CONCEPTS_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/version.hpp>
+#include <boost/graph/graph_traits.hpp>
+#include <boost/graph/graph_concepts.hpp>
+
+#if BOOST_VERSION >= 103500
+# include <boost/concept/detail/concept_def.hpp>
+#endif
+
+namespace boost {
+
+class distributed_graph_tag { };
+class distributed_vertex_list_graph_tag { };
+class distributed_edge_list_graph_tag { };
+
+#if BOOST_VERSION >= 103500
+ namespace concepts {
+#endif
+
+#if BOOST_VERSION < 103500
+
+template <class G>
+struct DistributedVertexListGraphConcept
+{
+ typedef typename graph_traits<G>::vertex_iterator vertex_iterator;
+ typedef typename graph_traits<G>::vertices_size_type vertices_size_type;
+ typedef typename graph_traits<G>::traversal_category
+ traversal_category;
+ void constraints() {
+ function_requires< GraphConcept<G> >();
+ function_requires< MultiPassInputIteratorConcept<vertex_iterator> >();
+ function_requires< ConvertibleConcept<traversal_category,
+ distributed_vertex_list_graph_tag> >();
+
+#ifdef BOOST_VECTOR_AS_GRAPH_GRAPH_ADL_HACK
+ // dwa 2003/7/11 -- This clearly shouldn't be necessary, but if
+ // you want to use vector_as_graph, it is! I'm sure the graph
+ // library leaves these out all over the place. Probably a
+ // redesign involving specializing a template with a static
+ // member function is in order :(
+ using boost::vertices;
+#endif
+ p = vertices(g);
+ v = *p.first;
+ const_constraints(g);
+ }
+ void const_constraints(const G& cg) {
+#ifdef BOOST_VECTOR_AS_GRAPH_GRAPH_ADL_HACK
+ // dwa 2003/7/11 -- This clearly shouldn't be necessary, but if
+ // you want to use vector_as_graph, it is! I'm sure the graph
+ // library leaves these out all over the place. Probably a
+ // redesign involving specializing a template with a static
+ // member function is in order :(
+ using boost::vertices;
+#endif
+
+ p = vertices(cg);
+ v = *p.first;
+ V = num_vertices(cg);
+ }
+ std::pair<vertex_iterator,vertex_iterator> p;
+ typename graph_traits<G>::vertex_descriptor v;
+ G g;
+ vertices_size_type V;
+};
+
+template <class G>
+struct DistributedEdgeListGraphConcept
+{
+ typedef typename graph_traits<G>::edge_descriptor edge_descriptor;
+ typedef typename graph_traits<G>::edge_iterator edge_iterator;
+ typedef typename graph_traits<G>::edges_size_type edges_size_type;
+ typedef typename graph_traits<G>::traversal_category
+ traversal_category;
+ void constraints() {
+ function_requires< GraphConcept<G> >();
+ function_requires< MultiPassInputIteratorConcept<edge_iterator> >();
+ function_requires< DefaultConstructibleConcept<edge_descriptor> >();
+ function_requires< EqualityComparableConcept<edge_descriptor> >();
+ function_requires< AssignableConcept<edge_descriptor> >();
+ function_requires< ConvertibleConcept<traversal_category,
+ distributed_edge_list_graph_tag> >();
+
+ p = edges(g);
+ e = *p.first;
+ u = source(e, g);
+ v = target(e, g);
+ const_constraints(g);
+ }
+ void const_constraints(const G& cg) {
+ p = edges(cg);
+ E = num_edges(cg);
+ e = *p.first;
+ u = source(e, cg);
+ v = target(e, cg);
+ }
+ std::pair<edge_iterator,edge_iterator> p;
+ typename graph_traits<G>::vertex_descriptor u, v;
+ typename graph_traits<G>::edge_descriptor e;
+ edges_size_type E;
+ G g;
+};
+#else
+ BOOST_concept(DistributedVertexListGraph,(G))
+ : Graph<G>
+ {
+ typedef typename graph_traits<G>::vertex_iterator vertex_iterator;
+ typedef typename graph_traits<G>::vertices_size_type vertices_size_type;
+ typedef typename graph_traits<G>::traversal_category
+ traversal_category;
+ ~DistributedVertexListGraph() {
+ BOOST_CONCEPT_ASSERT((MultiPassInputIterator<vertex_iterator>));
+ BOOST_CONCEPT_ASSERT((Convertible<traversal_category,
+ distributed_vertex_list_graph_tag>));
+
+#ifdef BOOST_VECTOR_AS_GRAPH_GRAPH_ADL_HACK
+ // dwa 2003/7/11 -- This clearly shouldn't be necessary, but if
+ // you want to use vector_as_graph, it is! I'm sure the graph
+ // library leaves these out all over the place. Probably a
+ // redesign involving specializing a template with a static
+ // member function is in order :(
+ using boost::vertices;
+#endif
+ p = vertices(g);
+ v = *p.first;
+ const_constraints(g);
+ }
+ void const_constraints(const G& cg) {
+#ifdef BOOST_VECTOR_AS_GRAPH_GRAPH_ADL_HACK
+ // dwa 2003/7/11 -- This clearly shouldn't be necessary, but if
+ // you want to use vector_as_graph, it is! I'm sure the graph
+ // library leaves these out all over the place. Probably a
+ // redesign involving specializing a template with a static
+ // member function is in order :(
+ using boost::vertices;
+#endif
+
+ p = vertices(cg);
+ v = *p.first;
+ V = num_vertices(cg);
+ }
+ std::pair<vertex_iterator,vertex_iterator> p;
+ typename graph_traits<G>::vertex_descriptor v;
+ G g;
+ vertices_size_type V;
+ };
+
+ BOOST_concept(DistributedEdgeListGraph,(G))
+ : Graph<G>
+ {
+ typedef typename graph_traits<G>::edge_descriptor edge_descriptor;
+ typedef typename graph_traits<G>::edge_iterator edge_iterator;
+ typedef typename graph_traits<G>::edges_size_type edges_size_type;
+ typedef typename graph_traits<G>::traversal_category
+ traversal_category;
+ ~DistributedEdgeListGraph() {
+ BOOST_CONCEPT_ASSERT((MultiPassInputIterator<edge_iterator>));
+ BOOST_CONCEPT_ASSERT((DefaultConstructible<edge_descriptor>));
+ BOOST_CONCEPT_ASSERT((EqualityComparable<edge_descriptor>));
+ BOOST_CONCEPT_ASSERT((Assignable<edge_descriptor>));
+ BOOST_CONCEPT_ASSERT((Convertible<traversal_category,
+ distributed_edge_list_graph_tag>));
+
+ p = edges(g);
+ e = *p.first;
+ u = source(e, g);
+ v = target(e, g);
+ const_constraints(g);
+ }
+ void const_constraints(const G& cg) {
+ p = edges(cg);
+ E = num_edges(cg);
+ e = *p.first;
+ u = source(e, cg);
+ v = target(e, cg);
+ }
+ std::pair<edge_iterator,edge_iterator> p;
+ typename graph_traits<G>::vertex_descriptor u, v;
+ typename graph_traits<G>::edge_descriptor e;
+ edges_size_type E;
+ G g;
+ };
+#endif
+
+#if BOOST_VERSION >= 103500
+ } // end namespace concepts
+
+ using concepts::DistributedVertexListGraphConcept;
+ using concepts::DistributedEdgeListGraphConcept;
+#endif
+} // end namespace boost
+
+#if BOOST_VERSION >= 103500
+# include <boost/concept/detail/concept_undef.hpp>
+#endif
+
+#endif // BOOST_GRAPH_DISTRIBUTED_CONCEPTS_HPP

Added: trunk/boost/graph/distributed/connected_components.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/connected_components.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,767 @@
+// 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: Nick Edmonds
+// Douglas Gregor
+// Andrew Lumsdaine
+#ifndef BOOST_GRAPH_PARALLEL_CC_HPP
+#define BOOST_GRAPH_PARALLEL_CC_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/property_map/property_map.hpp>
+#include <boost/property_map/parallel/caching_property_map.hpp>
+#include <boost/graph/parallel/algorithm.hpp>
+#include <boost/pending/indirect_cmp.hpp>
+#include <boost/graph/graph_traits.hpp>
+#include <boost/graph/overloading.hpp>
+#include <boost/graph/distributed/concepts.hpp>
+#include <boost/graph/parallel/properties.hpp>
+#include <boost/graph/distributed/local_subgraph.hpp>
+#include <boost/graph/connected_components.hpp>
+#include <boost/graph/named_function_params.hpp>
+#include <boost/graph/parallel/process_group.hpp>
+#include <boost/optional.hpp>
+#include <algorithm>
+#include <vector>
+#include <list>
+#include <boost/graph/parallel/container_traits.hpp>
+#include <boost/graph/iteration_macros.hpp>
+
+#define PBGL_IN_PLACE_MERGE /* In place merge instead of sorting */
+//#define PBGL_SORT_ASSERT /* Assert sorted for in place merge */
+
+/* Explicit sychronization in pointer doubling step? */
+#define PBGL_EXPLICIT_SYNCH
+//#define PBGL_CONSTRUCT_METAGRAPH
+#ifdef PBGL_CONSTRUCT_METAGRAPH
+# define MAX_VERTICES_IN_METAGRAPH 10000
+#endif
+
+namespace boost { namespace graph { namespace distributed {
+ namespace cc_detail {
+ enum connected_components_message {
+ edges_msg, req_parents_msg, parents_msg, root_adj_msg
+ };
+
+ template <typename Vertex>
+ struct metaVertex {
+ metaVertex() {}
+ metaVertex(const Vertex& v) : name(v) {}
+
+ template<typename Archiver>
+ void serialize(Archiver& ar, const unsigned int /*version*/)
+ {
+ ar & name;
+ }
+
+ Vertex name;
+ };
+
+#ifdef PBGL_CONSTRUCT_METAGRAPH
+ // Build meta-graph on result of local connected components
+ template <typename Graph, typename ParentMap, typename RootIterator,
+ typename AdjacencyMap>
+ void
+ build_local_metagraph(const Graph& g, ParentMap p, RootIterator r,
+ RootIterator r_end, AdjacencyMap& adj)
+ {
+ // TODO: Static assert that AdjacencyMap::value_type is std::vector<vertex_descriptor>
+
+ typedef typename boost::graph::parallel::process_group_type<Graph>::type
+ process_group_type;
+ typedef typename process_group_type::process_id_type process_id_type;
+
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+
+ BOOST_STATIC_ASSERT((is_same<typename AdjacencyMap::mapped_type,
+ std::vector<vertex_descriptor> >::value));
+
+ using boost::graph::parallel::process_group;
+
+ process_group_type pg = process_group(g);
+ process_id_type id = process_id(pg);
+
+ if (id != 0) {
+
+ // Send component roots and their associated edges to P0
+ for ( ; r != r_end; ++r ) {
+ std::vector<vertex_descriptor> adjs(1, *r); // Root
+ adjs.reserve(adjs.size() + adj[*r].size());
+ for (typename std::vector<vertex_descriptor>::iterator iter = adj[*r].begin();
+ iter != adj[*r].end(); ++iter)
+ adjs.push_back(get(p, *iter)); // Adjacencies
+
+ send(pg, 0, root_adj_msg, adjs);
+ }
+ }
+
+ synchronize(pg);
+
+ if (id == 0) {
+ typedef metaVertex<vertex_descriptor> VertexProperties;
+
+ typedef boost::adjacency_list<vecS, vecS, undirectedS,
+ VertexProperties> metaGraph;
+ typedef typename graph_traits<metaGraph>::vertex_descriptor
+ meta_vertex_descriptor;
+
+ std::map<vertex_descriptor, meta_vertex_descriptor> vertex_map;
+ std::vector<std::pair<vertex_descriptor, vertex_descriptor> > edges;
+
+ // Receive remote roots and edges
+ while (optional<std::pair<process_id_type, int> > m = probe(pg)) {
+ assert(m->second == root_adj_msg);
+
+ std::vector<vertex_descriptor> adjs;
+ receive(pg, m->first, m->second, adjs);
+
+ vertex_map[adjs[0]] = graph_traits<metaGraph>::null_vertex();
+ for (typename std::vector<vertex_descriptor>::iterator iter
+ = ++adjs.begin(); iter != adjs.end(); ++iter)
+ edges.push_back(std::make_pair(adjs[0], *iter));
+ }
+
+ // Add local roots and edges
+ for ( ; r != r_end; ++r ) {
+ vertex_map[*r] = graph_traits<metaGraph>::null_vertex();
+ edges.reserve(edges.size() + adj[*r].size());
+ for (typename std::vector<vertex_descriptor>::iterator iter = adj[*r].begin();
+ iter != adj[*r].end(); ++iter)
+ edges.push_back(std::make_pair(*r, get(p, *iter)));
+ }
+
+ // Build local meta-graph
+ metaGraph mg;
+
+ // Add vertices with property to map back to distributed graph vertex
+ for (typename std::map<vertex_descriptor, meta_vertex_descriptor>::iterator
+ iter = vertex_map.begin(); iter != vertex_map.end(); ++iter)
+ vertex_map[iter->first]
+ = add_vertex(metaVertex<vertex_descriptor>(iter->first), mg);
+
+ // Build meta-vertex map
+ typename property_map<metaGraph, vertex_descriptor VertexProperties::*>::type
+ metaVertexMap = get(&VertexProperties::name, mg);
+
+ typename std::vector<std::pair<vertex_descriptor, vertex_descriptor> >
+ ::iterator edge_iter = edges.begin();
+ for ( ; edge_iter != edges.end(); ++edge_iter)
+ add_edge(vertex_map[edge_iter->first], vertex_map[edge_iter->second], mg);
+
+ edges.clear();
+
+ // Call connected_components on it
+ typedef typename property_map<metaGraph, vertex_index_t>::type
+ meta_index_map_type;
+ meta_index_map_type meta_index = get(vertex_index, mg);
+
+ std::vector<std::size_t> mg_component_vec(num_vertices(mg));
+ typedef iterator_property_map<std::vector<std::size_t>::iterator,
+ meta_index_map_type>
+ meta_components_map_type;
+ meta_components_map_type mg_component(mg_component_vec.begin(),
+ meta_index);
+ std::size_t num_comp = connected_components(mg, mg_component);
+
+ // Update Parent pointers
+ std::vector<meta_vertex_descriptor> roots(num_comp, graph_traits<metaGraph>::null_vertex());
+
+ BGL_FORALL_VERTICES_T(v, mg, metaGraph) {
+ size_t component = get(mg_component, v);
+ if (roots[component] == graph_traits<metaGraph>::null_vertex() ||
+ get(meta_index, v) < get(meta_index, roots[component]))
+ roots[component] = v;
+ }
+
+ // Set all the local parent pointers
+ BGL_FORALL_VERTICES_T(v, mg, metaGraph) {
+ // Problem in value being put (3rd parameter)
+ put(p, get(metaVertexMap, v), get(metaVertexMap, roots[get(mg_component, v)]));
+ }
+ }
+
+ synchronize(p);
+ }
+#endif
+
+ /* Function object used to remove internal vertices and vertices >
+ the current vertex from the adjacent vertex lists at each
+ root */
+ template <typename Vertex, typename ParentMap>
+ class cull_adjacency_list
+ {
+ public:
+ cull_adjacency_list(const Vertex v, const ParentMap p) : v(v), p(p) {}
+ bool operator() (const Vertex x) { return (get(p, x) == v || x == v); }
+
+ private:
+ const Vertex v;
+ const ParentMap p;
+ };
+
+ /* Comparison operator used to choose targets for hooking s.t. vertices
+ that are hooked to are evenly distributed across processors */
+ template <typename OwnerMap, typename LocalMap>
+ class hashed_vertex_compare
+ {
+ public:
+ hashed_vertex_compare (const OwnerMap& o, const LocalMap& l)
+ : owner(o), local(l) { }
+
+ template <typename Vertex>
+ bool operator() (const Vertex x, const Vertex y)
+ {
+ if (get(local, x) < get(local, y))
+ return true;
+ else if (get(local, x) == get(local, y))
+ return (get(owner, x) < get(owner, y));
+ return false;
+ }
+
+ private:
+ OwnerMap owner;
+ LocalMap local;
+ };
+
+#ifdef PBGL_EXPLICIT_SYNCH
+ template <typename Graph, typename ParentMap, typename VertexList>
+ void
+ request_parent_map_entries(const Graph& g, ParentMap p,
+ std::vector<VertexList>& parent_requests)
+ {
+ typedef typename boost::graph::parallel::process_group_type<Graph>
+ ::type process_group_type;
+ typedef typename process_group_type::process_id_type process_id_type;
+
+ typedef typename graph_traits<Graph>::vertex_descriptor
+ vertex_descriptor;
+
+ process_group_type pg = process_group(g);
+
+ /*
+ This should probably be send_oob_with_reply, especially when Dave
+ finishes prefetch-batching
+ */
+
+ // Send root requests
+ for (process_id_type i = 0; i < num_processes(pg); ++i) {
+ if (!parent_requests[i].empty()) {
+ std::vector<vertex_descriptor> reqs(parent_requests[i].begin(),
+ parent_requests[i].end());
+ send(pg, i, req_parents_msg, reqs);
+ }
+ }
+
+ synchronize(pg);
+
+ // Receive root requests and reply to them
+ while (optional<std::pair<process_id_type, int> > m = probe(pg)) {
+ std::vector<vertex_descriptor> requests;
+ receive(pg, m->first, m->second, requests);
+ for (std::size_t i = 0; i < requests.size(); ++i)
+ requests[i] = get(p, requests[i]);
+ send(pg, m->first, parents_msg, requests);
+ }
+
+ synchronize(pg);
+
+ // Receive requested parents
+ std::vector<vertex_descriptor> responses;
+ for (process_id_type i = 0; i < num_processes(pg); ++i) {
+ if (!parent_requests[i].empty()) {
+ receive(pg, i, parents_msg, responses);
+ std::size_t parent_idx = 0;
+ for (typename VertexList::iterator v = parent_requests[i].begin();
+ v != parent_requests[i].end(); ++v, ++parent_idx)
+ put(p, *v, responses[parent_idx]);
+ }
+ }
+ }
+#endif
+
+ template<typename DistributedGraph, typename ParentMap>
+ void
+ parallel_connected_components(DistributedGraph& g, ParentMap p)
+ {
+ using boost::connected_components;
+
+ typedef typename graph_traits<DistributedGraph>::adjacency_iterator
+ adjacency_iterator;
+ typedef typename graph_traits<DistributedGraph>::out_edge_iterator
+ out_edge_iterator;
+ typedef typename graph_traits<DistributedGraph>::edge_iterator
+ edge_iterator;
+ typedef typename graph_traits<DistributedGraph>::vertex_descriptor
+ vertex_descriptor;
+ typedef typename graph_traits<DistributedGraph>::edge_descriptor
+ edge_descriptor;
+
+ typedef typename boost::graph::parallel::process_group_type<DistributedGraph>
+ ::type process_group_type;
+ typedef typename process_group_type::process_id_type process_id_type;
+
+ using boost::graph::parallel::process_group;
+
+ process_group_type pg = process_group(g);
+ process_id_type id = process_id(pg);
+
+ // TODO (NGE): Should old_roots, roots, and completed_roots be std::list
+ adjacency_iterator av1, av2;
+ std::vector<vertex_descriptor> old_roots;
+ typename std::vector<vertex_descriptor>::iterator liter;
+ typename std::vector<vertex_descriptor>::iterator aliter;
+ typename std::map<vertex_descriptor,
+ std::vector<vertex_descriptor> > adj;
+
+ typedef typename property_map<DistributedGraph, vertex_owner_t>::const_type
+ OwnerMap;
+ OwnerMap owner = get(vertex_owner, g);
+ typedef typename property_map<DistributedGraph, vertex_local_t>::const_type
+ LocalMap;
+ LocalMap local = get(vertex_local, g);
+
+ // We need to hold on to all of the parent pointers
+ p.set_max_ghost_cells(0);
+
+ //
+ // STAGE 1 : Compute local components
+ //
+ local_subgraph<const DistributedGraph> ls(g);
+ typedef typename property_map<local_subgraph<const DistributedGraph>,
+ vertex_index_t>::type local_index_map_type;
+ local_index_map_type local_index = get(vertex_index, ls);
+
+ // Compute local connected components
+ std::vector<std::size_t> ls_components_vec(num_vertices(ls));
+ typedef iterator_property_map<std::vector<std::size_t>::iterator,
+ local_index_map_type>
+ ls_components_map_type;
+ ls_components_map_type ls_component(ls_components_vec.begin(),
+ local_index);
+ std::size_t num_comp = connected_components(ls, ls_component);
+
+ std::vector<vertex_descriptor>
+ roots(num_comp, graph_traits<DistributedGraph>::null_vertex());
+
+ BGL_FORALL_VERTICES_T(v, g, DistributedGraph) {
+ size_t component = get(ls_component, v);
+ if (roots[component] == graph_traits<DistributedGraph>::null_vertex() ||
+ get(local_index, v) < get(local_index, roots[component]))
+ roots[component] = v;
+ }
+
+ // Set all the local parent pointers
+ BGL_FORALL_VERTICES_T(v, g, DistributedGraph) {
+ put(p, v, roots[get(ls_component, v)]);
+ }
+
+ if (num_processes(pg) == 1) return;
+
+ // Build adjacency list for all roots
+ BGL_FORALL_VERTICES_T(v, g, DistributedGraph) {
+ std::vector<vertex_descriptor>& my_adj = adj[get(p, v)];
+ for (tie(av1, av2) = adjacent_vertices(v, g);
+ av1 != av2; ++av1) {
+ if (get(owner, *av1) != id) my_adj.push_back(*av1);
+ }
+ }
+
+ // For all vertices adjacent to a local vertex get p(v)
+ for ( liter = roots.begin(); liter != roots.end(); ++liter ) {
+ std::vector<vertex_descriptor>& my_adj = adj[*liter];
+ for ( aliter = my_adj.begin(); aliter != my_adj.end(); ++aliter )
+ request(p, *aliter);
+ }
+ synchronize(p);
+
+ // Update adjacency list at root to make sure all adjacent
+ // vertices are roots of remote components
+ for ( liter = roots.begin(); liter != roots.end(); ++liter )
+ {
+ std::vector<vertex_descriptor>& my_adj = adj[*liter];
+ for ( aliter = my_adj.begin(); aliter != my_adj.end(); ++aliter )
+ *aliter = get(p, *aliter);
+
+ my_adj.erase
+ (remove_if(my_adj.begin(), my_adj.end(),
+ cull_adjacency_list<vertex_descriptor,
+ ParentMap>(*liter, p) ),
+ my_adj.end());
+ // This sort needs to be here to make sure the initial
+ // adjacency list is sorted
+ sort(my_adj.begin(), my_adj.end(), std::less<vertex_descriptor>());
+ my_adj.erase(unique(my_adj.begin(), my_adj.end()), my_adj.end());
+ }
+
+ // Get p(v) for the new adjacent roots
+ p.clear();
+ for ( liter = roots.begin(); liter != roots.end(); ++liter ) {
+ std::vector<vertex_descriptor>& my_adj = adj[*liter];
+ for ( aliter = my_adj.begin(); aliter != my_adj.end(); ++aliter )
+ request(p, *aliter);
+ }
+#ifdef PBGL_EXPLICIT_SYNCH
+ synchronize(p);
+#endif
+ size_t lone_vertex_count = roots.size();
+
+ // Lastly, remove roots with no adjacent vertices, this is
+ // unnecessary but will speed up sparse graphs
+ for ( liter = roots.begin(); liter != roots.end(); /*in loop*/)
+ {
+ if ( adj[*liter].empty() )
+ liter = roots.erase(liter);
+ else
+ ++liter;
+ }
+
+#ifdef PBGL_CONSTRUCT_METAGRAPH
+ /* TODO: If the number of roots is sufficiently small, we can
+ use a 'problem folding' approach like we do in MST
+ to gather all the roots and their adjacencies on one proc
+ and solve for the connected components of the meta-graph */
+ using boost::parallel::all_reduce;
+ std::size_t num_roots = all_reduce(pg, roots.size(), std::plus<std::size_t>());
+ if (num_roots < MAX_VERTICES_IN_METAGRAPH) {
+ build_local_metagraph(g, p, roots.begin(), roots.end(), adj);
+
+ // For each vertex in g, p(v) = p(p(v)), assign parent of leaf
+ // vertices from first step to final parent
+ BGL_FORALL_VERTICES_T(v, g, DistributedGraph) {
+ put(p, v, get(p, get(p, v)));
+ }
+
+ synchronize(p);
+
+ return;
+ }
+#endif
+
+ //
+ // Parallel Phase
+ //
+
+ std::vector<vertex_descriptor> completed_roots;
+ hashed_vertex_compare<OwnerMap, LocalMap> v_compare(owner, local);
+ bool any_hooked;
+ vertex_descriptor new_root;
+
+ std::size_t steps = 0;
+
+ do {
+ ++steps;
+
+ // Pull in new parents for hooking phase
+ synchronize(p);
+
+ //
+ // Hooking
+ //
+ bool hooked = false;
+ completed_roots.clear();
+ for ( liter = roots.begin(); liter != roots.end(); )
+ {
+ new_root = graph_traits<DistributedGraph>::null_vertex();
+ std::vector<vertex_descriptor>& my_adj = adj[*liter];
+ for ( aliter = my_adj.begin(); aliter != my_adj.end(); ++aliter )
+ // try to hook to better adjacent vertex
+ if ( v_compare( get(p, *aliter), *liter ) )
+ new_root = get(p, *aliter);
+
+ if ( new_root != graph_traits<DistributedGraph>::null_vertex() )
+ {
+ hooked = true;
+ put(p, *liter, new_root);
+ old_roots.push_back(*liter);
+ completed_roots.push_back(*liter);
+ liter = roots.erase(liter);
+ }
+ else
+ ++liter;
+ }
+
+ //
+ // Pointer jumping, perform until new roots determined
+ //
+
+ // TODO: Implement cycle reduction rules to reduce this from
+ // O(n) to O(log n) [n = cycle length]
+ bool all_done;
+ std::size_t parent_root_count;
+
+ std::size_t double_steps = 0;
+
+ do {
+ ++double_steps;
+#ifndef PBGL_EXPLICIT_SYNCH
+ // Get p(p(v)) for all old roots, and p(v) for all current roots
+ for ( liter = old_roots.begin(); liter != old_roots.end(); ++liter )
+ request(p, get(p, *liter));
+
+ synchronize(p);
+#else
+ // Build root requests
+ typedef std::set<vertex_descriptor> VertexSet;
+ std::vector<VertexSet> parent_requests(num_processes(pg));
+ for ( liter = old_roots.begin(); liter != old_roots.end(); ++liter )
+ {
+ vertex_descriptor p1 = *liter;
+ if (get(owner, p1) != id) parent_requests[get(owner, p1)].insert(p1);
+ vertex_descriptor p2 = get(p, p1);
+ if (get(owner, p2) != id) parent_requests[get(owner, p2)].insert(p2);
+ }
+
+ request_parent_map_entries(g, p, parent_requests);
+#endif
+ // Perform a pointer jumping step on all old roots
+ for ( liter = old_roots.begin(); liter != old_roots.end(); ++liter )
+ put(p, *liter, get(p, get(p, *liter)));
+
+ // make sure the parent of all old roots is itself a root
+ parent_root_count = 0;
+ for ( liter = old_roots.begin(); liter != old_roots.end(); ++liter )
+ if ( get(p, *liter) == get(p, get(p, *liter)) )
+ parent_root_count++;
+
+ bool done = parent_root_count == old_roots.size();
+
+ all_reduce(pg, &done, &done+1, &all_done,
+ std::logical_and<bool>());
+ } while ( !all_done );
+#ifdef PARALLEL_BGL_DEBUG
+ if (id == 0) std::cerr << double_steps << " doubling steps.\n";
+#endif
+ //
+ // Add adjacent vertices of just completed roots to adjacent
+ // vertex list at new parent
+ //
+ typename std::vector<vertex_descriptor> outgoing_edges;
+ for ( liter = completed_roots.begin(); liter != completed_roots.end();
+ ++liter )
+ {
+ vertex_descriptor new_parent = get(p, *liter);
+
+ if ( get(owner, new_parent) == id )
+ {
+ std::vector<vertex_descriptor>& my_adj = adj[new_parent];
+ my_adj.reserve(my_adj.size() + adj[*liter].size());
+ my_adj.insert( my_adj.end(),
+ adj[*liter].begin(), adj[*liter].end() );
+#ifdef PBGL_IN_PLACE_MERGE
+#ifdef PBGL_SORT_ASSERT
+ assert(__gnu_cxx::is_sorted(my_adj.begin(),
+ my_adj.end() - adj[*liter].size(),
+ std::less<vertex_descriptor>()));
+ assert(__gnu_cxx::is_sorted(my_adj.end() - adj[*liter].size(),
+ my_adj.end(),
+ std::less<vertex_descriptor>()));
+#endif
+ std::inplace_merge(my_adj.begin(),
+ my_adj.end() - adj[*liter].size(),
+ my_adj.end(),
+ std::less<vertex_descriptor>());
+#endif
+
+
+ }
+ else if ( adj[*liter].begin() != adj[*liter].end() )
+ {
+ outgoing_edges.clear();
+ outgoing_edges.reserve(adj[*liter].size() + 1);
+ // First element is the destination of the adjacency list
+ outgoing_edges.push_back(new_parent);
+ outgoing_edges.insert(outgoing_edges.end(),
+ adj[*liter].begin(), adj[*liter].end() );
+ send(pg, get(owner, new_parent), edges_msg, outgoing_edges);
+ adj[*liter].clear();
+ }
+ }
+ synchronize(pg);
+
+ // Receive edges sent by remote nodes and add them to the
+ // indicated vertex's adjacency list
+ while (optional<std::pair<process_id_type, int> > m
+ = probe(pg))
+ {
+ std::vector<vertex_descriptor> incoming_edges;
+ receive(pg, m->first, edges_msg, incoming_edges);
+ typename std::vector<vertex_descriptor>::iterator aviter
+ = incoming_edges.begin();
+ ++aviter;
+
+ std::vector<vertex_descriptor>& my_adj = adj[incoming_edges[0]];
+
+ my_adj.reserve(my_adj.size() + incoming_edges.size() - 1);
+ my_adj.insert( my_adj.end(), aviter, incoming_edges.end() );
+
+#ifdef PBGL_IN_PLACE_MERGE
+ std::size_t num_incoming_edges = incoming_edges.size();
+#ifdef PBGL_SORT_ASSERT
+ assert(__gnu_cxx::is_sorted(my_adj.begin(),
+ my_adj.end() - (num_incoming_edges-1),
+ std::less<vertex_descriptor>()));
+ assert(__gnu_cxx::is_sorted(my_adj.end() - (num_incoming_edges-1),
+ my_adj.end(),
+ std::less<vertex_descriptor>()));
+#endif
+ std::inplace_merge(my_adj.begin(),
+ my_adj.end() - (num_incoming_edges - 1),
+ my_adj.end(),
+ std::less<vertex_descriptor>());
+#endif
+
+ }
+
+
+ // Remove any adjacent vertices that are in the same component
+ // as a root from that root's list
+ for ( liter = roots.begin(); liter != roots.end(); ++liter )
+ {
+ // We can probably get away without sorting and removing
+ // duplicates Though sorting *may* cause root
+ // determination to occur faster by choosing the root with
+ // the most potential to hook to at each step
+ std::vector<vertex_descriptor>& my_adj = adj[*liter];
+ my_adj.erase
+ (remove_if(my_adj.begin(), my_adj.end(),
+ cull_adjacency_list<vertex_descriptor,
+ ParentMap>(*liter, p) ),
+ my_adj.end());
+#ifndef PBGL_IN_PLACE_MERGE
+ sort(my_adj.begin(), my_adj.end(),
+ std::less<vertex_descriptor>() );
+#endif
+ my_adj.erase(unique(my_adj.begin(), my_adj.end()), my_adj.end());
+ }
+
+ // Reduce result of empty root list test
+ all_reduce(pg, &hooked, &hooked+1, &any_hooked,
+ std::logical_or<bool>());
+ } while ( any_hooked );
+#ifdef PARALLEL_BGL_DEBUG
+ if (id == 0) std::cerr << steps << " iterations.\n";
+#endif
+ //
+ // Finalize
+ //
+
+ // For each vertex in g, p(v) = p(p(v)), assign parent of leaf
+ // vertices from first step to final parent
+ BGL_FORALL_VERTICES_T(v, g, DistributedGraph) {
+ put(p, v, get(p, get(p, v)));
+ }
+
+ synchronize(p);
+ }
+
+ } // end namespace cc_detail
+
+ template<typename Graph, typename ParentMap, typename ComponentMap>
+ typename property_traits<ComponentMap>::value_type
+ number_components_from_parents(const Graph& g, ParentMap p, ComponentMap c)
+ {
+ typedef typename graph_traits<Graph>::vertex_descriptor
+ vertex_descriptor;
+ typedef typename boost::graph::parallel::process_group_type<Graph>::type
+ process_group_type;
+ typedef typename property_traits<ComponentMap>::value_type
+ ComponentMapType;
+
+ process_group_type pg = process_group(g);
+
+ /* Build list of roots */
+ std::vector<vertex_descriptor> my_roots, all_roots;
+
+ BGL_FORALL_VERTICES_T(v, g, Graph) {
+ if( find( my_roots.begin(), my_roots.end(), get(p, v) )
+ == my_roots.end() )
+ my_roots.push_back( get(p, v) );
+ }
+
+ all_gather(pg, my_roots.begin(), my_roots.end(), all_roots);
+
+ /* Number components */
+ std::map<vertex_descriptor, ComponentMapType> comp_numbers;
+ ComponentMapType c_num = 0;
+
+ // Compute component numbers
+ for (std::size_t i = 0; i < all_roots.size(); i++ )
+ if ( comp_numbers.count(all_roots[i]) == 0 )
+ comp_numbers[all_roots[i]] = c_num++;
+
+ // Broadcast component numbers
+ BGL_FORALL_VERTICES_T(v, g, Graph) {
+ put( c, v, comp_numbers[get(p, v)] );
+ }
+
+ // Broadcast number of components
+ if (process_id(pg) == 0) {
+ typedef typename process_group_type::process_size_type
+ process_size_type;
+ for (process_size_type dest = 1, n = num_processes(pg);
+ dest != n; ++dest)
+ send(pg, dest, 0, c_num);
+ }
+ synchronize(pg);
+
+ if (process_id(pg) != 0) receive(pg, 0, 0, c_num);
+
+ synchronize(c);
+
+ return c_num;
+ }
+
+ template<typename Graph, typename ParentMap>
+ int
+ number_components_from_parents(const Graph& g, ParentMap p,
+ dummy_property_map)
+ {
+ using boost::parallel::all_reduce;
+
+ // Count local roots.
+ int num_roots = 0;
+ BGL_FORALL_VERTICES_T(v, g, Graph)
+ if (get(p, v) == v) ++num_roots;
+ return all_reduce(g.process_group(), num_roots, std::plus<int>());
+ }
+
+ template<typename Graph, typename ComponentMap, typename ParentMap>
+ typename property_traits<ComponentMap>::value_type
+ connected_components
+ (const Graph& g, ComponentMap c, ParentMap p
+ BOOST_GRAPH_ENABLE_IF_MODELS_PARM(Graph, distributed_graph_tag))
+ {
+ cc_detail::parallel_connected_components(g, p);
+ return number_components_from_parents(g, p, c);
+ }
+
+ /* Construct ParentMap by default */
+ template<typename Graph, typename ComponentMap>
+ typename property_traits<ComponentMap>::value_type
+ connected_components
+ ( const Graph& g, ComponentMap c
+ BOOST_GRAPH_ENABLE_IF_MODELS_PARM(Graph, distributed_graph_tag) )
+ {
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+
+ std::vector<vertex_descriptor> x(num_vertices(g));
+
+ return connected_components
+ (g, c,
+ make_iterator_property_map(x.begin(), get(vertex_index, g)));
+ }
+} // end namespace distributed
+
+using distributed::connected_components;
+} // end namespace graph
+
+using graph::distributed::connected_components;
+} // end namespace boost
+
+#endif // BOOST_GRAPH_PARALLEL_CC_HPP

Added: trunk/boost/graph/distributed/connected_components_parallel_search.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/connected_components_parallel_search.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,408 @@
+// 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: Brian Barrett
+// Douglas Gregor
+// Andrew Lumsdaine
+#ifndef BOOST_GRAPH_PARALLEL_CC_PS_HPP
+#define BOOST_GRAPH_PARALLEL_CC_PS_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/property_map/property_map.hpp>
+#include <boost/graph/parallel/algorithm.hpp>
+#include <boost/pending/indirect_cmp.hpp>
+#include <boost/graph/graph_traits.hpp>
+#include <boost/graph/overloading.hpp>
+#include <boost/graph/distributed/concepts.hpp>
+#include <boost/graph/parallel/properties.hpp>
+#include <boost/graph/parallel/process_group.hpp>
+#include <boost/optional.hpp>
+#include <algorithm>
+#include <vector>
+#include <queue>
+#include <limits>
+#include <map>
+#include <boost/graph/parallel/container_traits.hpp>
+#include <boost/graph/iteration_macros.hpp>
+
+
+// Connected components algorithm based on a parallel search.
+//
+// Every N nodes starts a parallel search from the first vertex in
+// their local vertex list during the first superstep (the other nodes
+// remain idle during the first superstep to reduce the number of
+// conflicts in numbering the components). At each superstep, all new
+// component mappings from remote nodes are handled. If there is no
+// work from remote updates, a new vertex is removed from the local
+// list and added to the work queue.
+//
+// Components are allocated from the component_value_allocator object,
+// which ensures that a given component number is unique in the
+// system, currently by using the rank and number of processes to
+// stride allocations.
+//
+// When two components are discovered to actually be the same
+// component, a mapping is created in the collisions object. The
+// lower component number is prefered in the resolution, so component
+// numbering resolution is consistent. After the search has exhausted
+// all vertices in the graph, the mapping is shared with all
+// processes, and they independently resolve the comonent mapping (so
+// O((N * NP) + (V * NP)) work, in O(N + V) time, where N is the
+// number of mappings and V is the number of local vertices). This
+// phase can likely be significantly sped up if a clever algorithm for
+// the reduction can be found.
+namespace boost { namespace graph { namespace distributed {
+ namespace cc_ps_detail {
+ // Local object for allocating component numbers. There are two
+ // places this happens in the code, and I was getting sick of them
+ // getting out of sync. Components are not tightly packed in
+ // numbering, but are numbered to ensure each rank has its own
+ // independent sets of numberings.
+ template<typename component_value_type>
+ class component_value_allocator {
+ public:
+ component_value_allocator(int num, int size) :
+ last(0), num(num), size(size)
+ {
+ }
+
+ component_value_type allocate(void)
+ {
+ component_value_type ret = num + (last * size);
+ last++;
+ return ret;
+ }
+
+ private:
+ component_value_type last;
+ int num;
+ int size;
+ };
+
+
+ // Map of the "collisions" between component names in the global
+ // component mapping. TO make cleanup easier, component numbers
+ // are added, pointing to themselves, when a new component is
+ // found. In order to make the results deterministic, the lower
+ // component number is always taken. The resolver will drill
+ // through the map until it finds a component entry that points to
+ // itself as the next value, allowing some cleanup to happen at
+ // update() time. Attempts are also made to update the mapping
+ // when new entries are created.
+ //
+ // Note that there's an assumption that the entire mapping is
+ // shared during the end of the algorithm, but before component
+ // name resolution.
+ template<typename component_value_type>
+ class collision_map {
+ public:
+ collision_map() : num_unique(0)
+ {
+ }
+
+ // add new component mapping first time component is used. Own
+ // function only so that we can sanity check there isn't already
+ // a mapping for that component number (which would be bad)
+ void add(const component_value_type &a)
+ {
+ assert(collisions.count(a) == 0);
+ collisions[a] = a;
+ }
+
+ // add a mapping between component values saying they're the
+ // same component
+ void add(const component_value_type &a, const component_value_type &b)
+ {
+ component_value_type high, low, tmp;
+ if (a > b) {
+ high = a;
+ low = b;
+ } else {
+ high = b;
+ low = a;
+ }
+
+ if (collisions.count(high) != 0 && collisions[high] != low) {
+ tmp = collisions[high];
+ if (tmp > low) {
+ collisions[tmp] = low;
+ collisions[high] = low;
+ } else {
+ collisions[low] = tmp;
+ collisions[high] = tmp;
+ }
+ } else {
+ collisions[high] = low;
+ }
+
+ }
+
+ // get the "real" component number for the given component.
+ // Used to resolve mapping at end of run.
+ component_value_type update(component_value_type a)
+ {
+ assert(num_unique > 0);
+ assert(collisions.count(a) != 0);
+ return collisions[a];
+ }
+
+ // collapse the collisions tree, so that update is a one lookup
+ // operation. Count unique components at the same time.
+ void uniqify(void)
+ {
+ typename std::map<component_value_type, component_value_type>::iterator i, end;
+
+ end = collisions.end();
+ for (i = collisions.begin() ; i != end ; ++i) {
+ if (i->first == i->second) {
+ num_unique++;
+ } else {
+ i->second = collisions[i->second];
+ }
+ }
+ }
+
+ // get the number of component entries that have an associated
+ // component number of themselves, which are the real components
+ // used in the final mapping. This is the number of unique
+ // components in the graph.
+ int unique(void)
+ {
+ assert(num_unique > 0);
+ return num_unique;
+ }
+
+ // "serialize" into a vector for communication.
+ std::vector<component_value_type> serialize(void)
+ {
+ std::vector<component_value_type> ret;
+ typename std::map<component_value_type, component_value_type>::iterator i, end;
+
+ end = collisions.end();
+ for (i = collisions.begin() ; i != end ; ++i) {
+ ret.push_back(i->first);
+ ret.push_back(i->second);
+ }
+
+ return ret;
+ }
+
+ private:
+ std::map<component_value_type, component_value_type> collisions;
+ int num_unique;
+ };
+
+
+ // resolver to handle remote updates. The resolver will add
+ // entries into the collisions map if required, and if it is the
+ // first time the vertex has been touched, it will add the vertex
+ // to the remote queue. Note that local updates are handled
+ // differently, in the main loop (below).
+
+ // BWB - FIX ME - don't need graph anymore - can pull from key value of Component Map.
+ template<typename ComponentMap, typename work_queue>
+ struct update_reducer {
+ BOOST_STATIC_CONSTANT(bool, non_default_resolver = false);
+
+ typedef typename property_traits<ComponentMap>::value_type component_value_type;
+ typedef typename property_traits<ComponentMap>::key_type vertex_descriptor;
+
+ update_reducer(work_queue *q,
+ cc_ps_detail::collision_map<component_value_type> *collisions,
+ processor_id_type pg_id) :
+ q(q), collisions(collisions), pg_id(pg_id)
+ {
+ }
+
+ // ghost cell initialization routine. This should never be
+ // called in this imlementation.
+ template<typename K>
+ component_value_type operator()(const K&) const
+ {
+ return component_value_type(0);
+ }
+
+ // resolver for remote updates. I'm not entirely sure why, but
+ // I decided to not change the value of the vertex if it's
+ // already non-infinite. It doesn't matter in the end, as we'll
+ // touch every vertex in the cleanup phase anyway. If the
+ // component is currently infinite, set to the new component
+ // number and add the vertex to the work queue. If it's not
+ // infinite, we've touched it already so don't add it to the
+ // work queue. Do add a collision entry so that we know the two
+ // components are the same.
+ component_value_type operator()(const vertex_descriptor &v,
+ const component_value_type& current,
+ const component_value_type& update) const
+ {
+ const component_value_type max = (std::numeric_limits<component_value_type>::max)();
+ component_value_type ret = current;
+
+ if (max == current) {
+ q->push(v);
+ ret = update;
+ } else if (current != update) {
+ collisions->add(current, update);
+ }
+
+ return ret;
+ }
+
+ // So for whatever reason, the property map can in theory call
+ // the resolver with a local descriptor in addition to the
+ // standard global descriptor. As far as I can tell, this code
+ // path is never taken in this implementation, but I need to
+ // have this code here to make it compile. We just make a
+ // global descriptor and call the "real" operator().
+ template<typename K>
+ component_value_type operator()(const K& v,
+ const component_value_type& current,
+ const component_value_type& update) const
+ {
+ return (*this)(vertex_descriptor(pg_id, v), current, update);
+ }
+
+ private:
+ work_queue *q;
+ collision_map<component_value_type> *collisions;
+ boost::processor_id_type pg_id;
+ };
+
+ } // namespace cc_ps_detail
+
+
+ template<typename Graph, typename ComponentMap>
+ typename property_traits<ComponentMap>::value_type
+ connected_components_ps(const Graph& g, ComponentMap c)
+ {
+ using boost::graph::parallel::process_group;
+
+ typedef typename property_traits<ComponentMap>::value_type component_value_type;
+ typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+ typedef typename boost::graph::parallel::process_group_type<Graph>
+ ::type process_group_type;
+ typedef typename process_group_type::process_id_type process_id_type;
+ typedef typename property_map<Graph, vertex_owner_t>
+ ::const_type vertex_owner_map;
+ typedef std::queue<vertex_descriptor> work_queue;
+
+ static const component_value_type max_component =
+ (std::numeric_limits<component_value_type>::max)();
+ typename property_map<Graph, vertex_owner_t>::const_type
+ owner = get(vertex_owner, g);
+
+ // standard who am i? stuff
+ process_group_type pg = process_group(g);
+ process_id_type id = process_id(pg);
+
+ // Initialize every vertex to have infinite component number
+ BGL_FORALL_VERTICES_T(v, g, Graph) put(c, v, max_component);
+
+ vertex_iterator current, end;
+ tie(current, end) = vertices(g);
+
+ cc_ps_detail::component_value_allocator<component_value_type> cva(process_id(pg), num_processes(pg));
+ cc_ps_detail::collision_map<component_value_type> collisions;
+ work_queue q; // this is intentionally a local data structure
+ c.set_reduce(cc_ps_detail::update_reducer<ComponentMap, work_queue>(&q, &collisions, id));
+
+ // add starting work
+ while (true) {
+ bool useful_found = false;
+ component_value_type val = cva.allocate();
+ put(c, *current, val);
+ collisions.add(val);
+ q.push(*current);
+ if (0 != out_degree(*current, g)) useful_found = true;
+ ++current;
+ if (useful_found) break;
+ }
+
+ // Run the loop until everyone in the system is done
+ bool global_done = false;
+ while (!global_done) {
+
+ // drain queue of work for this superstep
+ while (!q.empty()) {
+ vertex_descriptor v = q.front();
+ q.pop();
+ // iterate through outedges of the vertex currently being
+ // examined, setting their component to our component. There
+ // is no way to end up in the queue without having a component
+ // number already.
+
+ BGL_FORALL_ADJ_T(v, peer, g, Graph) {
+ component_value_type my_component = get(c, v);
+
+ // update other vertex with our component information.
+ // Resolver will handle remote collisions as well as whether
+ // to put the vertex on the work queue or not. We have to
+ // handle local collisions and work queue management
+ if (id == get(owner, peer)) {
+ if (max_component == get(c, peer)) {
+ put(c, peer, my_component);
+ q.push(peer);
+ } else if (my_component != get(c, peer)) {
+ collisions.add(my_component, get(c, peer));
+ }
+ } else {
+ put(c, peer, my_component);
+ }
+ }
+ }
+
+ // synchronize / start a new superstep.
+ synchronize(pg);
+ global_done = all_reduce(pg, (q.empty() && (current == end)), boost::parallel::minimum<bool>());
+
+ // If the queue is currently empty, add something to do to start
+ // the current superstep (supersteps start at the sync, not at
+ // the top of the while loop as one might expect). Down at the
+ // bottom of the while loop so that not everyone starts the
+ // algorithm with something to do, to try to reduce component
+ // name conflicts
+ if (q.empty()) {
+ bool useful_found = false;
+ for ( ; current != end && !useful_found ; ++current) {
+ if (max_component == get(c, *current)) {
+ component_value_type val = cva.allocate();
+ put(c, *current, val);
+ collisions.add(val);
+ q.push(*current);
+ if (0 != out_degree(*current, g)) useful_found = true;
+ }
+ }
+ }
+ }
+
+ // share component mappings
+ std::vector<component_value_type> global;
+ std::vector<component_value_type> mine = collisions.serialize();
+ all_gather(pg, mine.begin(), mine.end(), global);
+ for (size_t i = 0 ; i < global.size() ; i += 2) {
+ collisions.add(global[i], global[i + 1]);
+ }
+ collisions.uniqify();
+
+ // update the component mappings
+ BGL_FORALL_VERTICES_T(v, g, Graph) {
+ put(c, v, collisions.update(get(c, v)));
+ }
+
+ return collisions.unique();
+ }
+
+} // end namespace distributed
+
+} // end namespace graph
+
+} // end namespace boost
+
+#endif // BOOST_GRAPH_PARALLEL_CC_HPP

Added: trunk/boost/graph/distributed/crauser_et_al_shortest_paths.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/crauser_et_al_shortest_paths.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,664 @@
+// 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 <boost/graph/use_mpi.hpp> has been included"
+#endif
+
+#include <boost/graph/distributed/detail/dijkstra_shortest_paths.hpp>
+#include <boost/graph/parallel/algorithm.hpp>
+#include <functional>
+#include <boost/graph/iteration_macros.hpp>
+#include <boost/property_map/property_map_iterator.hpp>
+#include <boost/type_traits/is_same.hpp>
+#include <algorithm>
+#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>
+
+#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 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<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 = \"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<typename Vertex, typename DistanceMap, typename MinInWeightMap,
+ typename Combine, typename Compare>
+ struct min_in_distance_compare
+ : std::binary_function<Vertex, Vertex, bool>
+ {
+ 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<typename Vertex, typename DistanceMap, typename MinOutWeightMap,
+ typename Combine, typename Compare>
+ struct min_out_distance_compare
+ : std::binary_function<Vertex, Vertex, bool>
+ {
+ 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<typename Graph, typename Combine,
+ typename Compare, typename VertexIndexMap, typename DistanceMap,
+ typename PredecessorMap, typename MinOutWeightMap,
+ typename MinInWeightMap>
+ 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<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 crauser_et_al_dijkstra_queue self_type;
+ typedef dijkstra_msg_value<DistanceMap, PredecessorMap> msg_value_creator;
+ typedef typename msg_value_creator::type msg_value_type;
+ typedef typename graph_traits<Graph>::vertices_size_type
+ vertices_size_type;
+ typedef typename property_map<Graph, vertex_owner_t>::const_type
+ OwnerPropertyMap;
+ typedef typename boost::graph::parallel::process_group_type<Graph>::type
+ process_group_type;
+ 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> dist_queue_compare_type;
+
+ typedef typename property_traits<DistanceMap>::value_type distance_type;
+
+#ifdef MUTABLE_QUEUE
+ typedef mutable_queue<vertex_descriptor, std::vector<vertex_descriptor>,
+ dist_queue_compare_type, VertexIndexMap> dist_queue_type;
+
+#else
+ typedef relaxed_heap<vertex_descriptor, dist_queue_compare_type,
+ VertexIndexMap> dist_queue_type;
+#endif // MUTABLE_QUEUE
+
+ // Priority queue for OUT criteria
+ typedef min_out_distance_compare<vertex_descriptor, DistanceMap,
+ MinOutWeightMap, Combine, Compare>
+ out_queue_compare_type;
+
+#ifdef MUTABLE_QUEUE
+ typedef mutable_queue<vertex_descriptor, std::vector<vertex_descriptor>,
+ out_queue_compare_type, VertexIndexMap> out_queue_type;
+
+#else
+ typedef relaxed_heap<vertex_descriptor, out_queue_compare_type,
+ VertexIndexMap> out_queue_type;
+#endif // MUTABLE_QUEUE
+
+ // Priority queue for IN criteria
+ typedef min_in_distance_compare<vertex_descriptor, DistanceMap,
+ MinInWeightMap, Combine, Compare>
+ in_queue_compare_type;
+
+#ifdef MUTABLE_QUEUE
+ typedef mutable_queue<vertex_descriptor, std::vector<vertex_descriptor>,
+ in_queue_compare_type, VertexIndexMap> in_queue_type;
+
+#else
+ typedef relaxed_heap<vertex_descriptor, in_queue_compare_type,
+ VertexIndexMap> 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<distance_type>() as the function object.
+ distance_type local_distances[2];
+ local_distances[0] =
+ dist_queue.empty()? (std::numeric_limits<distance_type>::max)()
+ : get(distance_map, dist_queue.top());
+
+ local_distances[1] =
+ out_queue.empty()? (std::numeric_limits<distance_type>::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<distance_type>());
+ 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<std::size_t>());
+ if (process_id(this->process_group) == 0) {
+ crauser_et_al_shortest_paths_stats.deleted_vertices.push_back(deletions);
+ }
+ local_deletions = 0;
+ assert(deletions > 0);
+#endif
+
+ return min_distance == (std::numeric_limits<distance_type>::max)();
+ }
+
+ 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
+ {
+ 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<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:
+ 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<typename Graph, typename MinInWeightMap, typename WeightMap,
+ typename Inf, typename Compare>
+ 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<typename Graph, typename MinInWeightMap, typename WeightMap,
+ typename Inf, typename Compare>
+ 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<Graph, vertex_local_t>::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<typename Graph, typename MinInWeightMap, typename WeightMap,
+ typename Inf, typename Compare>
+ 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<typename Graph, typename MinOutWeightMap, typename WeightMap,
+ typename Compare>
+ void
+ initialize_min_out_weights(const Graph& g, MinOutWeightMap min_out_weight,
+ WeightMap weight, Compare compare)
+ {
+ typedef typename property_traits<WeightMap>::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<typename DistributedGraph, typename DijkstraVisitor,
+ typename PredecessorMap, typename DistanceMap, typename WeightMap,
+ typename IndexMap, typename ColorMap, typename Compare,
+ typename Combine, typename DistInf, typename DistZero>
+void
+crauser_et_al_shortest_paths
+ (const DistributedGraph& g,
+ typename graph_traits<DistributedGraph>::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)
+{
+ typedef typename boost::graph::parallel::process_group_type<DistributedGraph>::type
+ process_group_type;
+ typedef typename process_group_type::process_id_type process_id_type;
+ typedef typename graph_traits<DistributedGraph>::vertex_descriptor
+ Vertex;
+ typedef typename graph_traits<DistributedGraph>::vertices_size_type
+ vertices_size_type;
+
+#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<WeightMap>::value_type weight_type;
+ typedef iterator_property_map<weight_type*, IndexMap> MinOutWeightMap;
+ std::vector<weight_type> 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<DistributedGraph>::directed_category
+ directed_category;
+ const bool is_undirected =
+ is_same<directed_category, undirected_tag>::value;
+ typedef MinOutWeightMap MinInWeightMap;
+ std::vector<weight_type>
+ 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<DistributedGraph>::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<DistributedGraph>::vertex_iterator ui, ui_end;
+ for (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
+ <DistributedGraph, Combine, Compare, IndexMap, DistanceMap,
+ PredecessorMap, MinOutWeightMap, MinInWeightMap>
+ 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<PredecessorMap>,
+ boost::parallel::caching_property_map<DistanceMap>, 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<typename DistributedGraph, typename PredecessorMap,
+ typename DistanceMap, typename WeightMap>
+void
+crauser_et_al_shortest_paths
+ (const DistributedGraph& g,
+ typename graph_traits<DistributedGraph>::vertex_descriptor s,
+ PredecessorMap predecessor, DistanceMap distance, WeightMap weight)
+{
+ typedef typename property_traits<DistanceMap>::value_type distance_type;
+
+ std::vector<default_color_type> 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<distance_type>(),
+ closed_plus<distance_type>(),
+ (std::numeric_limits<distance_type>::max)(),
+ distance_type(),
+ dijkstra_visitor<>());
+}
+
+template<typename DistributedGraph, typename PredecessorMap,
+ typename DistanceMap>
+void
+crauser_et_al_shortest_paths
+ (const DistributedGraph& g,
+ typename graph_traits<DistributedGraph>::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

Added: trunk/boost/graph/distributed/dehne_gotz_min_spanning_tree.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/dehne_gotz_min_spanning_tree.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,938 @@
+// 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 header implements four distributed algorithms to compute
+ * the minimum spanning tree (actually, minimum spanning forest) of a
+ * graph. All of the algorithms were implemented as specified in the
+ * paper by Dehne and Gotz:
+ *
+ * Frank Dehne and Silvia Gotz. Practical Parallel Algorithms for Minimum
+ * Spanning Trees. In Symposium on Reliable Distributed Systems,
+ * pages 366--371, 1998.
+ *
+ * There are four algorithm variants implemented.
+ */
+
+#ifndef BOOST_DEHNE_GOTZ_MIN_SPANNING_TREE_HPP
+#define BOOST_DEHNE_GOTZ_MIN_SPANNING_TREE_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/graph/graph_traits.hpp>
+#include <boost/property_map/property_map.hpp>
+#include <vector>
+#include <boost/graph/parallel/algorithm.hpp>
+#include <boost/limits.hpp>
+#include <utility>
+#include <boost/pending/disjoint_sets.hpp>
+#include <boost/pending/indirect_cmp.hpp>
+#include <boost/property_map/parallel/caching_property_map.hpp>
+#include <boost/graph/vertex_and_edge_range.hpp>
+#include <boost/graph/kruskal_min_spanning_tree.hpp>
+#include <boost/iterator/counting_iterator.hpp>
+#include <boost/iterator/transform_iterator.hpp>
+#include <boost/graph/parallel/container_traits.hpp>
+#include <boost/graph/parallel/detail/untracked_pair.hpp>
+#include <cmath>
+
+namespace boost { namespace graph { namespace distributed {
+
+namespace detail {
+ /**
+ * Binary function object type that selects the (edge, weight) pair
+ * with the minimum weight. Used within a Boruvka merge step to select
+ * the candidate edges incident to each supervertex.
+ */
+ struct smaller_weighted_edge
+ {
+ template<typename Edge, typename Weight>
+ std::pair<Edge, Weight>
+ operator()(const std::pair<Edge, Weight>& x,
+ const std::pair<Edge, Weight>& y) const
+ { return x.second < y.second? x : y; }
+ };
+
+ /**
+ * Unary predicate that determines if the source and target vertices
+ * of the given edge have the same representative within a disjoint
+ * sets data structure. Used to indicate when an edge is now a
+ * self-loop because of supervertex merging in Boruvka's algorithm.
+ */
+ template<typename DisjointSets, typename Graph>
+ class do_has_same_supervertex
+ {
+ public:
+ typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor;
+
+ do_has_same_supervertex(DisjointSets& dset, const Graph& g)
+ : dset(dset), g(g) { }
+
+ bool operator()(edge_descriptor e)
+ { return dset.find_set(source(e, g)) == dset.find_set(target(e, g)); }
+
+ private:
+ DisjointSets& dset;
+ const Graph& g;
+ };
+
+ /**
+ * Build a @ref do_has_same_supervertex object.
+ */
+ template<typename DisjointSets, typename Graph>
+ inline do_has_same_supervertex<DisjointSets, Graph>
+ has_same_supervertex(DisjointSets& dset, const Graph& g)
+ { return do_has_same_supervertex<DisjointSets, Graph>(dset, g); }
+
+ /** \brief A single distributed Boruvka merge step.
+ *
+ * A distributed Boruvka merge step involves computing (globally)
+ * the minimum weight edges incident on each supervertex and then
+ * merging supervertices along these edges. Once supervertices are
+ * merged, self-loops are eliminated.
+ *
+ * The set of parameters passed to this algorithm is large, and
+ * considering this algorithm in isolation there are several
+ * redundancies. However, the more asymptotically efficient
+ * distributed MSF algorithms require mixing Boruvka steps with the
+ * merging of local MSFs (implemented in
+ * merge_local_minimum_spanning_trees_step): the interaction of the
+ * two algorithms mandates the addition of these parameters.
+ *
+ * \param pg The process group over which communication should be
+ * performed. Within the distributed Boruvka algorithm, this will be
+ * equivalent to \code process_group(g); however, in the context of
+ * the mixed MSF algorithms, the process group @p pg will be a
+ * (non-strict) process subgroup of \code process_group(g).
+ *
+ * \param g The underlying graph on which the MSF is being
+ * computed. The type of @p g must model DistributedGraph, but there
+ * are no other requirements because the edge and (super)vertex
+ * lists are passed separately.
+ *
+ * \param weight_map Property map containing the weights of each
+ * edge. The type of this property map must model
+ * ReadablePropertyMap and must support caching.
+ *
+ * \param out An output iterator that will be written with the set
+ * of edges selected to build the MSF. Every process within the
+ * process group @p pg will receive all edges in the MSF.
+ *
+ * \param dset Disjoint sets data structure mapping from vertices in
+ * the graph @p g to their representative supervertex.
+ *
+ * \param supervertex_map Mapping from supervertex descriptors to
+ * indices.
+ *
+ * \param supervertices A vector containing all of the
+ * supervertices. Will be modified to include only the remaining
+ * supervertices after merging occurs.
+ *
+ * \param edge_list The list of edges that remain in the graph. This
+ * list will be pruned to remove self-loops once MSF edges have been
+ * found.
+ */
+ template<typename ProcessGroup, typename Graph, typename WeightMap,
+ typename OutputIterator, typename RankMap, typename ParentMap,
+ typename SupervertexMap, typename Vertex, typename EdgeList>
+ OutputIterator
+ boruvka_merge_step(ProcessGroup pg, const Graph& g, WeightMap weight_map,
+ OutputIterator out,
+ disjoint_sets<RankMap, ParentMap>& dset,
+ SupervertexMap supervertex_map,
+ std::vector<Vertex>& supervertices,
+ EdgeList& edge_list)
+ {
+ typedef typename graph_traits<Graph>::vertex_descriptor
+ vertex_descriptor;
+ typedef typename graph_traits<Graph>::vertices_size_type
+ vertices_size_type;
+ typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor;
+ typedef typename EdgeList::iterator edge_iterator;
+ typedef typename property_traits<WeightMap>::value_type
+ weight_type;
+ typedef boost::parallel::detail::untracked_pair<edge_descriptor,
+ weight_type> w_edge;
+ typedef typename property_traits<SupervertexMap>::value_type
+ supervertex_index;
+
+ smaller_weighted_edge min_edge;
+ weight_type inf = (std::numeric_limits<weight_type>::max)();
+
+ // Renumber the supervertices
+ for (std::size_t i = 0; i < supervertices.size(); ++i)
+ put(supervertex_map, supervertices[i], i);
+
+ // BSP-B1: Find local minimum-weight edges for each supervertex
+ std::vector<w_edge> candidate_edges(supervertices.size(),
+ w_edge(edge_descriptor(), inf));
+ for (edge_iterator ei = edge_list.begin(); ei != edge_list.end(); ++ei) {
+ weight_type w = get(weight_map, *ei);
+ supervertex_index u =
+ get(supervertex_map, dset.find_set(source(*ei, g)));
+ supervertex_index v =
+ get(supervertex_map, dset.find_set(target(*ei, g)));
+
+ if (u != v) {
+ candidate_edges[u] = min_edge(candidate_edges[u], w_edge(*ei, w));
+ candidate_edges[v] = min_edge(candidate_edges[v], w_edge(*ei, w));
+ }
+ }
+
+ // BSP-B2 (a): Compute global minimum edges for each supervertex
+ all_reduce(pg,
+ &candidate_edges[0],
+ &candidate_edges[0] + candidate_edges.size(),
+ &candidate_edges[0], min_edge);
+
+ // BSP-B2 (b): Use the edges to compute sequentially the new
+ // connected components and emit the edges.
+ for (vertices_size_type i = 0; i < candidate_edges.size(); ++i) {
+ if (candidate_edges[i].second != inf) {
+ edge_descriptor e = candidate_edges[i].first;
+ vertex_descriptor u = dset.find_set(source(e, g));
+ vertex_descriptor v = dset.find_set(target(e, g));
+ if (u != v) {
+ // Emit the edge, but cache the weight so everyone knows it
+ cache(weight_map, e, candidate_edges[i].second);
+ *out++ = e;
+
+ // Link the two supervertices
+ dset.link(u, v);
+
+ // Whichever vertex was reparented will be removed from the
+ // list of supervertices.
+ vertex_descriptor victim = u;
+ if (dset.find_set(u) == u) victim = v;
+ supervertices[get(supervertex_map, victim)] =
+ graph_traits<Graph>::null_vertex();
+ }
+ }
+ }
+
+ // BSP-B3: Eliminate self-loops
+ edge_list.erase(std::remove_if(edge_list.begin(), edge_list.end(),
+ has_same_supervertex(dset, g)),
+ edge_list.end());
+
+ // TBD: might also eliminate multiple edges between supervertices
+ // when the edges do not have the best weight, but this is not
+ // strictly necessary.
+
+ // Eliminate supervertices that have been absorbed
+ supervertices.erase(std::remove(supervertices.begin(),
+ supervertices.end(),
+ graph_traits<Graph>::null_vertex()),
+ supervertices.end());
+
+ return out;
+ }
+
+ /**
+ * An edge descriptor adaptor that reroutes the source and target
+ * edges to different vertices, but retains the original edge
+ * descriptor for, e.g., property maps. This is used when we want to
+ * turn a set of edges in the overall graph into a set of edges
+ * between supervertices.
+ */
+ template<typename Graph>
+ struct supervertex_edge_descriptor
+ {
+ typedef supervertex_edge_descriptor self_type;
+ typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
+ typedef typename graph_traits<Graph>::edge_descriptor Edge;
+
+ Vertex source;
+ Vertex target;
+ Edge e;
+
+ operator Edge() const { return e; }
+
+ friend inline bool operator==(const self_type& x, const self_type& y)
+ { return x.e == y.e; }
+
+ friend inline bool operator!=(const self_type& x, const self_type& y)
+ { return x.e != y.e; }
+ };
+
+ template<typename Graph>
+ inline typename supervertex_edge_descriptor<Graph>::Vertex
+ source(supervertex_edge_descriptor<Graph> se, const Graph&)
+ { return se.source; }
+
+ template<typename Graph>
+ inline typename supervertex_edge_descriptor<Graph>::Vertex
+ target(supervertex_edge_descriptor<Graph> se, const Graph&)
+ { return se.target; }
+
+ /**
+ * Build a supervertex edge descriptor from a normal edge descriptor
+ * using the given disjoint sets data structure to identify
+ * supervertex representatives.
+ */
+ template<typename Graph, typename DisjointSets>
+ struct build_supervertex_edge_descriptor
+ {
+ typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
+ typedef typename graph_traits<Graph>::edge_descriptor Edge;
+
+ typedef Edge argument_type;
+ typedef supervertex_edge_descriptor<Graph> result_type;
+
+ build_supervertex_edge_descriptor() : g(0), dsets(0) { }
+
+ build_supervertex_edge_descriptor(const Graph& g, DisjointSets& dsets)
+ : g(&g), dsets(&dsets) { }
+
+ result_type operator()(argument_type e) const
+ {
+ result_type result;
+ result.source = dsets->find_set(source(e, *g));
+ result.target = dsets->find_set(target(e, *g));
+ result.e = e;
+ return result;
+ }
+
+ private:
+ const Graph* g;
+ DisjointSets* dsets;
+ };
+
+ template<typename Graph, typename DisjointSets>
+ inline build_supervertex_edge_descriptor<Graph, DisjointSets>
+ make_supervertex_edge_descriptor(const Graph& g, DisjointSets& dsets)
+ { return build_supervertex_edge_descriptor<Graph, DisjointSets>(g, dsets); }
+
+ template<typename T>
+ struct identity_function
+ {
+ typedef T argument_type;
+ typedef T result_type;
+
+ result_type operator()(argument_type x) const { return x; }
+ };
+
+ template<typename Graph, typename DisjointSets, typename EdgeMapper>
+ class is_not_msf_edge
+ {
+ typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
+ typedef typename graph_traits<Graph>::edge_descriptor Edge;
+
+ public:
+ is_not_msf_edge(const Graph& g, DisjointSets dset, EdgeMapper edge_mapper)
+ : g(g), dset(dset), edge_mapper(edge_mapper) { }
+
+ bool operator()(Edge e)
+ {
+ Vertex u = dset.find_set(source(edge_mapper(e), g));
+ Vertex v = dset.find_set(target(edge_mapper(e), g));
+ if (u == v) return true;
+ else {
+ dset.link(u, v);
+ return false;
+ }
+ }
+
+ private:
+ const Graph& g;
+ DisjointSets dset;
+ EdgeMapper edge_mapper;
+ };
+
+ template<typename Graph, typename ForwardIterator, typename EdgeList,
+ typename EdgeMapper, typename RankMap, typename ParentMap>
+ void
+ sorted_mutating_kruskal(const Graph& g,
+ ForwardIterator first_vertex,
+ ForwardIterator last_vertex,
+ EdgeList& edge_list, EdgeMapper edge_mapper,
+ RankMap rank_map, ParentMap parent_map)
+ {
+ typedef disjoint_sets<RankMap, ParentMap> DisjointSets;
+
+ // Build and initialize disjoint-sets data structure
+ DisjointSets dset(rank_map, parent_map);
+ for (ForwardIterator v = first_vertex; v != last_vertex; ++v)
+ dset.make_set(*v);
+
+ is_not_msf_edge<Graph, DisjointSets, EdgeMapper>
+ remove_non_msf_edges(g, dset, edge_mapper);
+ edge_list.erase(std::remove_if(edge_list.begin(), edge_list.end(),
+ remove_non_msf_edges),
+ edge_list.end());
+ }
+
+ /**
+ * Merge local minimum spanning forests from p processes into
+ * minimum spanning forests on p/D processes (where D is the tree
+ * factor, currently fixed at 3), eliminating unnecessary edges in
+ * the process.
+ *
+ * As with @ref boruvka_merge_step, this routine has many
+ * parameters, not all of which make sense within the limited
+ * context of this routine. The parameters are required for the
+ * Boruvka and local MSF merging steps to interoperate.
+ *
+ * \param pg The process group on which local minimum spanning
+ * forests should be merged. The top (D-1)p/D processes will be
+ * eliminated, and a new process subgroup containing p/D processors
+ * will be returned. The value D is a constant factor that is
+ * currently fixed to 3.
+ *
+ * \param g The underlying graph whose MSF is being computed. It must model
+ * the DistributedGraph concept.
+ *
+ * \param first_vertex Iterator to the first vertex in the graph
+ * that should be considered. While the local MSF merging algorithm
+ * typically operates on the entire vertex set, within the hybrid
+ * distributed MSF algorithms this will refer to the first
+ * supervertex.
+ *
+ * \param last_vertex The past-the-end iterator for the vertex list.
+ *
+ * \param edge_list The list of local edges that will be
+ * considered. For the p/D processes that remain, this list will
+ * contain edges in the MSF known to the vertex after other
+ * processes' edge lists have been merged. The edge list must be
+ * sorted in order of increasing weight.
+ *
+ * \param weight Property map containing the weights of each
+ * edge. The type of this property map must model
+ * ReadablePropertyMap and must support caching.
+ *
+ * \param global_index Mapping from vertex descriptors to a global
+ * index. The type must model ReadablePropertyMap.
+ *
+ * \param edge_mapper A function object that can remap edge descriptors
+ * in the edge list to any alternative edge descriptor. This
+ * function object will be the identity function when a pure merging
+ * of local MSFs is required, but may be a mapping to a supervertex
+ * edge when the local MSF merging occurs on a supervertex
+ * graph. This function object saves us the trouble of having to
+ * build a supervertex graph adaptor.
+ *
+ * \param already_local_msf True when the edge list already
+ * constitutes a local MSF. If false, Kruskal's algorithm will first
+ * be applied to the local edge list to select MSF edges.
+ *
+ * \returns The process subgroup containing the remaining p/D
+ * processes. If the size of this process group is greater than one,
+ * the MSF edges contained in the edge list do not constitute an MSF
+ * for the entire graph.
+ */
+ template<typename ProcessGroup, typename Graph, typename ForwardIterator,
+ typename EdgeList, typename WeightMap, typename GlobalIndexMap,
+ typename EdgeMapper>
+ ProcessGroup
+ merge_local_minimum_spanning_trees_step(ProcessGroup pg,
+ const Graph& g,
+ ForwardIterator first_vertex,
+ ForwardIterator last_vertex,
+ EdgeList& edge_list,
+ WeightMap weight,
+ GlobalIndexMap global_index,
+ EdgeMapper edge_mapper,
+ bool already_local_msf)
+ {
+ typedef typename ProcessGroup::process_id_type process_id_type;
+ typedef typename EdgeList::value_type edge_descriptor;
+ typedef typename property_traits<WeightMap>::value_type weight_type;
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+
+ // The tree factor, often called "D"
+ process_id_type const tree_factor = 3;
+ process_id_type num_procs = num_processes(pg);
+ process_id_type id = process_id(pg);
+ process_id_type procs_left = (num_procs + tree_factor - 1) / tree_factor;
+ std::size_t n = std::size_t(last_vertex - first_vertex);
+
+ if (!already_local_msf) {
+ // Compute local minimum spanning forest. We only care about the
+ // edges in the MSF, because only edges in the local MSF can be in
+ // the global MSF.
+ std::vector<std::size_t> ranks(n);
+ std::vector<vertex_descriptor> parents(n);
+ detail::sorted_mutating_kruskal
+ (g, first_vertex, last_vertex,
+ edge_list, edge_mapper,
+ make_iterator_property_map(ranks.begin(), global_index),
+ make_iterator_property_map(parents.begin(), global_index));
+ }
+
+ typedef std::pair<edge_descriptor, weight_type> w_edge;
+
+ // Order edges based on their weights.
+ indirect_cmp<WeightMap, std::less<weight_type> > cmp_edge_weight(weight);
+
+ if (id < procs_left) {
+ // The p/D processes that remain will receive local MSF edges from
+ // D-1 other processes.
+ synchronize(pg);
+ for (process_id_type from_id = procs_left + id; from_id < num_procs;
+ from_id += procs_left) {
+ std::size_t num_incoming_edges;
+ receive(pg, from_id, 0, num_incoming_edges);
+ if (num_incoming_edges > 0) {
+ std::vector<w_edge> incoming_edges(num_incoming_edges);
+ receive(pg, from_id, 1, &incoming_edges[0], num_incoming_edges);
+
+ edge_list.reserve(edge_list.size() + num_incoming_edges);
+ for (std::size_t i = 0; i < num_incoming_edges; ++i) {
+ cache(weight, incoming_edges[i].first, incoming_edges[i].second);
+ edge_list.push_back(incoming_edges[i].first);
+ }
+ std::inplace_merge(edge_list.begin(),
+ edge_list.end() - num_incoming_edges,
+ edge_list.end(),
+ cmp_edge_weight);
+ }
+ }
+
+ // Compute the local MSF from union of the edges in the MSFs of
+ // all children.
+ std::vector<std::size_t> ranks(n);
+ std::vector<vertex_descriptor> parents(n);
+ detail::sorted_mutating_kruskal
+ (g, first_vertex, last_vertex,
+ edge_list, edge_mapper,
+ make_iterator_property_map(ranks.begin(), global_index),
+ make_iterator_property_map(parents.begin(), global_index));
+ } else {
+ // The (D-1)p/D processes that are dropping out of further
+ // computations merely send their MSF edges to their parent
+ // process in the process tree.
+ send(pg, id % procs_left, 0, edge_list.size());
+ if (edge_list.size() > 0) {
+ std::vector<w_edge> outgoing_edges;
+ outgoing_edges.reserve(edge_list.size());
+ for (std::size_t i = 0; i < edge_list.size(); ++i) {
+ outgoing_edges.push_back(std::make_pair(edge_list[i],
+ get(weight, edge_list[i])));
+ }
+ send(pg, id % procs_left, 1, &outgoing_edges[0],
+ outgoing_edges.size());
+ }
+ synchronize(pg);
+ }
+
+ // Return a process subgroup containing the p/D parent processes
+ return process_subgroup(pg,
+ make_counting_iterator(process_id_type(0)),
+ make_counting_iterator(procs_left));
+ }
+} // end namespace detail
+
+// ---------------------------------------------------------------------
+// Dense Boruvka MSF algorithm
+// ---------------------------------------------------------------------
+template<typename Graph, typename WeightMap, typename OutputIterator,
+ typename VertexIndexMap, typename RankMap, typename ParentMap,
+ typename SupervertexMap>
+OutputIterator
+dense_boruvka_minimum_spanning_tree(const Graph& g, WeightMap weight_map,
+ OutputIterator out,
+ VertexIndexMap index_map,
+ RankMap rank_map, ParentMap parent_map,
+ SupervertexMap supervertex_map)
+{
+ using boost::graph::parallel::process_group;
+
+ typedef typename graph_traits<Graph>::traversal_category traversal_category;
+
+ BOOST_STATIC_ASSERT((is_convertible<traversal_category*,
+ vertex_list_graph_tag*>::value));
+
+ typedef typename graph_traits<Graph>::vertices_size_type vertices_size_type;
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+ typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
+ typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor;
+
+ // Don't throw away cached edge weights
+ weight_map.set_max_ghost_cells(0);
+
+ // Initialize the disjoint sets structures
+ disjoint_sets<RankMap, ParentMap> dset(rank_map, parent_map);
+ vertex_iterator vi, vi_end;
+ for (tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi)
+ dset.make_set(*vi);
+
+ std::vector<vertex_descriptor> supervertices;
+ supervertices.assign(vertices(g).first, vertices(g).second);
+
+ // Use Kruskal's algorithm to find the minimum spanning forest
+ // considering only the local edges. The resulting edges are not
+ // necessarily going to be in the final minimum spanning
+ // forest. However, any edge not part of the local MSF cannot be a
+ // part of the global MSF, so we should have eliminated some edges
+ // from consideration.
+ std::vector<edge_descriptor> edge_list;
+ kruskal_minimum_spanning_tree
+ (make_vertex_and_edge_range(g, vertices(g).first, vertices(g).second,
+ edges(g).first, edges(g).second),
+ std::back_inserter(edge_list),
+ boost::weight_map(weight_map).
+ vertex_index_map(index_map));
+
+ // While the number of supervertices is decreasing, keep executing
+ // Boruvka steps to identify additional MSF edges. This loop will
+ // execute log |V| times.
+ vertices_size_type old_num_supervertices;
+ do {
+ old_num_supervertices = supervertices.size();
+ out = detail::boruvka_merge_step(process_group(g), g,
+ weight_map, out,
+ dset, supervertex_map, supervertices,
+ edge_list);
+ } while (supervertices.size() < old_num_supervertices);
+
+ return out;
+}
+
+template<typename Graph, typename WeightMap, typename OutputIterator,
+ typename VertexIndex>
+OutputIterator
+dense_boruvka_minimum_spanning_tree(const Graph& g, WeightMap weight_map,
+ OutputIterator out, VertexIndex i_map)
+{
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+
+ std::vector<std::size_t> ranks(num_vertices(g));
+ std::vector<vertex_descriptor> parents(num_vertices(g));
+ std::vector<std::size_t> supervertices(num_vertices(g));
+
+ return dense_boruvka_minimum_spanning_tree
+ (g, weight_map, out, i_map,
+ make_iterator_property_map(ranks.begin(), i_map),
+ make_iterator_property_map(parents.begin(), i_map),
+ make_iterator_property_map(supervertices.begin(), i_map));
+}
+
+template<typename Graph, typename WeightMap, typename OutputIterator>
+OutputIterator
+dense_boruvka_minimum_spanning_tree(const Graph& g, WeightMap weight_map,
+ OutputIterator out)
+{
+ return dense_boruvka_minimum_spanning_tree(g, weight_map, out,
+ get(vertex_index, g));
+}
+
+// ---------------------------------------------------------------------
+// Merge local MSFs MSF algorithm
+// ---------------------------------------------------------------------
+template<typename Graph, typename WeightMap, typename OutputIterator,
+ typename GlobalIndexMap>
+OutputIterator
+merge_local_minimum_spanning_trees(const Graph& g, WeightMap weight,
+ OutputIterator out,
+ GlobalIndexMap global_index)
+{
+ using boost::graph::parallel::process_group_type;
+ using boost::graph::parallel::process_group;
+
+ typedef typename graph_traits<Graph>::traversal_category traversal_category;
+
+ BOOST_STATIC_ASSERT((is_convertible<traversal_category*,
+ vertex_list_graph_tag*>::value));
+
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+ typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor;
+
+ // Don't throw away cached edge weights
+ weight.set_max_ghost_cells(0);
+
+ // Compute the initial local minimum spanning forests
+ std::vector<edge_descriptor> edge_list;
+ kruskal_minimum_spanning_tree
+ (make_vertex_and_edge_range(g, vertices(g).first, vertices(g).second,
+ edges(g).first, edges(g).second),
+ std::back_inserter(edge_list),
+ boost::weight_map(weight).vertex_index_map(global_index));
+
+ // Merge the local MSFs from p processes into p/D processes,
+ // reducing the number of processes in each step. Continue looping
+ // until either (a) the current process drops out or (b) only one
+ // process remains in the group. This loop will execute log_D p
+ // times.
+ typename process_group_type<Graph>::type pg = process_group(g);
+ while (pg && num_processes(pg) > 1) {
+ pg = detail::merge_local_minimum_spanning_trees_step
+ (pg, g, vertices(g).first, vertices(g).second,
+ edge_list, weight, global_index,
+ detail::identity_function<edge_descriptor>(), true);
+ }
+
+ // Only process 0 has the entire edge list, so emit it to the output
+ // iterator.
+ if (pg && process_id(pg) == 0) {
+ out = std::copy(edge_list.begin(), edge_list.end(), out);
+ }
+
+ synchronize(process_group(g));
+ return out;
+}
+
+template<typename Graph, typename WeightMap, typename OutputIterator>
+inline OutputIterator
+merge_local_minimum_spanning_trees(const Graph& g, WeightMap weight,
+ OutputIterator out)
+{
+ return merge_local_minimum_spanning_trees(g, weight, out,
+ get(vertex_index, g));
+}
+
+// ---------------------------------------------------------------------
+// Boruvka-then-merge MSF algorithm
+// ---------------------------------------------------------------------
+template<typename Graph, typename WeightMap, typename OutputIterator,
+ typename GlobalIndexMap, typename RankMap, typename ParentMap,
+ typename SupervertexMap>
+OutputIterator
+boruvka_then_merge(const Graph& g, WeightMap weight, OutputIterator out,
+ GlobalIndexMap index, RankMap rank_map,
+ ParentMap parent_map, SupervertexMap supervertex_map)
+{
+ using std::log;
+ using boost::graph::parallel::process_group_type;
+ using boost::graph::parallel::process_group;
+
+ typedef typename graph_traits<Graph>::traversal_category traversal_category;
+
+ BOOST_STATIC_ASSERT((is_convertible<traversal_category*,
+ vertex_list_graph_tag*>::value));
+
+ typedef typename graph_traits<Graph>::vertices_size_type vertices_size_type;
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+ typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
+ typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor;
+
+ // Don't throw away cached edge weights
+ weight.set_max_ghost_cells(0);
+
+ // Compute the initial local minimum spanning forests
+ std::vector<edge_descriptor> edge_list;
+ kruskal_minimum_spanning_tree
+ (make_vertex_and_edge_range(g, vertices(g).first, vertices(g).second,
+ edges(g).first, edges(g).second),
+ std::back_inserter(edge_list),
+ boost::weight_map(weight).
+ vertex_index_map(index));
+
+ // Initialize the disjoint sets structures for Boruvka steps
+ disjoint_sets<RankMap, ParentMap> dset(rank_map, parent_map);
+ vertex_iterator vi, vi_end;
+ for (tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi)
+ dset.make_set(*vi);
+
+ // Construct the initial set of supervertices (all vertices)
+ std::vector<vertex_descriptor> supervertices;
+ supervertices.assign(vertices(g).first, vertices(g).second);
+
+ // Continue performing Boruvka merge steps until the number of
+ // supervertices reaches |V| / (log_D p)^2.
+ const std::size_t tree_factor = 3; // TBD: same as above! should be param
+ double log_d_p = log((double)num_processes(process_group(g)))
+ / log((double)tree_factor);
+ vertices_size_type target_supervertices =
+ vertices_size_type(num_vertices(g) / (log_d_p * log_d_p));
+ vertices_size_type old_num_supervertices;
+ while (supervertices.size() > target_supervertices) {
+ old_num_supervertices = supervertices.size();
+ out = detail::boruvka_merge_step(process_group(g), g,
+ weight, out, dset,
+ supervertex_map, supervertices,
+ edge_list);
+ if (supervertices.size() == old_num_supervertices)
+ return out;
+ }
+
+ // Renumber the supervertices
+ for (std::size_t i = 0; i < supervertices.size(); ++i)
+ put(supervertex_map, supervertices[i], i);
+
+ // Merge local MSFs on the supervertices. (D-1)p/D processors drop
+ // out each iteration, so this loop executes log_D p times.
+ typename process_group_type<Graph>::type pg = process_group(g);
+ bool have_msf = false;
+ while (pg && num_processes(pg) > 1) {
+ pg = detail::merge_local_minimum_spanning_trees_step
+ (pg, g, supervertices.begin(), supervertices.end(),
+ edge_list, weight, supervertex_map,
+ detail::make_supervertex_edge_descriptor(g, dset),
+ have_msf);
+ have_msf = true;
+ }
+
+ // Only process 0 has the complete list of _supervertex_ MST edges,
+ // so emit those to the output iterator. This is not the complete
+ // list of edges in the MSF, however: the Boruvka steps in the
+ // beginning of the algorithm emitted any edges used to merge
+ // supervertices.
+ if (pg && process_id(pg) == 0)
+ out = std::copy(edge_list.begin(), edge_list.end(), out);
+
+ synchronize(process_group(g));
+ return out;
+}
+
+template<typename Graph, typename WeightMap, typename OutputIterator,
+ typename GlobalIndexMap>
+inline OutputIterator
+boruvka_then_merge(const Graph& g, WeightMap weight, OutputIterator out,
+ GlobalIndexMap index)
+{
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+ typedef typename graph_traits<Graph>::vertices_size_type vertices_size_type;
+ std::vector<vertices_size_type> ranks(num_vertices(g));
+ std::vector<vertex_descriptor> parents(num_vertices(g));
+ std::vector<vertices_size_type> supervertex_indices(num_vertices(g));
+
+ return boruvka_then_merge
+ (g, weight, out, index,
+ make_iterator_property_map(ranks.begin(), index),
+ make_iterator_property_map(parents.begin(), index),
+ make_iterator_property_map(supervertex_indices.begin(), index));
+}
+
+template<typename Graph, typename WeightMap, typename OutputIterator>
+inline OutputIterator
+boruvka_then_merge(const Graph& g, WeightMap weight, OutputIterator out)
+{ return boruvka_then_merge(g, weight, out, get(vertex_index, g)); }
+
+// ---------------------------------------------------------------------
+// Boruvka-mixed-merge MSF algorithm
+// ---------------------------------------------------------------------
+template<typename Graph, typename WeightMap, typename OutputIterator,
+ typename GlobalIndexMap, typename RankMap, typename ParentMap,
+ typename SupervertexMap>
+OutputIterator
+boruvka_mixed_merge(const Graph& g, WeightMap weight, OutputIterator out,
+ GlobalIndexMap index, RankMap rank_map,
+ ParentMap parent_map, SupervertexMap supervertex_map)
+{
+ using boost::graph::parallel::process_group_type;
+ using boost::graph::parallel::process_group;
+
+ typedef typename graph_traits<Graph>::traversal_category traversal_category;
+
+ BOOST_STATIC_ASSERT((is_convertible<traversal_category*,
+ vertex_list_graph_tag*>::value));
+
+ typedef typename graph_traits<Graph>::vertices_size_type vertices_size_type;
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+ typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
+ typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor;
+
+ // Don't throw away cached edge weights
+ weight.set_max_ghost_cells(0);
+
+ // Initialize the disjoint sets structures for Boruvka steps
+ disjoint_sets<RankMap, ParentMap> dset(rank_map, parent_map);
+ vertex_iterator vi, vi_end;
+ for (tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi)
+ dset.make_set(*vi);
+
+ // Construct the initial set of supervertices (all vertices)
+ std::vector<vertex_descriptor> supervertices;
+ supervertices.assign(vertices(g).first, vertices(g).second);
+
+ // Compute the initial local minimum spanning forests
+ std::vector<edge_descriptor> edge_list;
+ kruskal_minimum_spanning_tree
+ (make_vertex_and_edge_range(g, vertices(g).first, vertices(g).second,
+ edges(g).first, edges(g).second),
+ std::back_inserter(edge_list),
+ boost::weight_map(weight).
+ vertex_index_map(index));
+
+ if (num_processes(process_group(g)) == 1) {
+ return std::copy(edge_list.begin(), edge_list.end(), out);
+ }
+
+ // Like the merging local MSFs algorithm and the Boruvka-then-merge
+ // algorithm, each iteration of this loop reduces the number of
+ // processes by a constant factor D, and therefore we require log_D
+ // p iterations. Note also that the number of edges in the edge list
+ // decreases geometrically, giving us an efficient distributed MSF
+ // algorithm.
+ typename process_group_type<Graph>::type pg = process_group(g);
+ vertices_size_type old_num_supervertices;
+ while (pg && num_processes(pg) > 1) {
+ // A single Boruvka step. If this doesn't change anything, we're done
+ old_num_supervertices = supervertices.size();
+ out = detail::boruvka_merge_step(pg, g, weight, out, dset,
+ supervertex_map, supervertices,
+ edge_list);
+ if (old_num_supervertices == supervertices.size()) {
+ edge_list.clear();
+ break;
+ }
+
+ // Renumber the supervertices
+ for (std::size_t i = 0; i < supervertices.size(); ++i)
+ put(supervertex_map, supervertices[i], i);
+
+ // A single merging of local MSTs, which reduces the number of
+ // processes we're using by a constant factor D.
+ pg = detail::merge_local_minimum_spanning_trees_step
+ (pg, g, supervertices.begin(), supervertices.end(),
+ edge_list, weight, supervertex_map,
+ detail::make_supervertex_edge_descriptor(g, dset),
+ true);
+
+ }
+
+ // Only process 0 has the complete edge list, so emit it for the
+ // user. Note that list edge list only contains the MSF edges in the
+ // final supervertex graph: all of the other edges were used to
+ // merge supervertices and have been emitted by the Boruvka steps,
+ // although only process 0 has received the complete set.
+ if (pg && process_id(pg) == 0)
+ out = std::copy(edge_list.begin(), edge_list.end(), out);
+
+ synchronize(process_group(g));
+ return out;
+}
+
+template<typename Graph, typename WeightMap, typename OutputIterator,
+ typename GlobalIndexMap>
+inline OutputIterator
+boruvka_mixed_merge(const Graph& g, WeightMap weight, OutputIterator out,
+ GlobalIndexMap index)
+{
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+ typedef typename graph_traits<Graph>::vertices_size_type vertices_size_type;
+ std::vector<vertices_size_type> ranks(num_vertices(g));
+ std::vector<vertex_descriptor> parents(num_vertices(g));
+ std::vector<vertices_size_type> supervertex_indices(num_vertices(g));
+
+ return boruvka_mixed_merge
+ (g, weight, out, index,
+ make_iterator_property_map(ranks.begin(), index),
+ make_iterator_property_map(parents.begin(), index),
+ make_iterator_property_map(supervertex_indices.begin(), index));
+}
+
+template<typename Graph, typename WeightMap, typename OutputIterator>
+inline OutputIterator
+boruvka_mixed_merge(const Graph& g, WeightMap weight, OutputIterator out)
+{ return boruvka_mixed_merge(g, weight, out, get(vertex_index, g)); }
+
+} // end namespace distributed
+
+using distributed::dense_boruvka_minimum_spanning_tree;
+using distributed::merge_local_minimum_spanning_trees;
+using distributed::boruvka_then_merge;
+using distributed::boruvka_mixed_merge;
+
+} } // end namespace boost::graph
+
+
+#endif // BOOST_DEHNE_GOTZ_MIN_SPANNING_TREE_HPP

Added: trunk/boost/graph/distributed/delta_stepping_shortest_paths.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/delta_stepping_shortest_paths.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,513 @@
+// 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/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::graph::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

Added: trunk/boost/graph/distributed/depth_first_search.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/depth_first_search.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,306 @@
+// Copyright (C) 2004-2008 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
+#ifndef BOOST_GRAPH_DISTRIBUTED_DFS_HPP
+#define BOOST_GRAPH_DISTRIBUTED_DFS_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/graph/graph_traits.hpp>
+#include <boost/property_map/property_map.hpp>
+#include <boost/graph/overloading.hpp>
+#include <boost/graph/properties.hpp>
+#include <boost/graph/distributed/concepts.hpp>
+#include <boost/static_assert.hpp>
+#include <boost/graph/parallel/process_group.hpp>
+#include <boost/graph/parallel/container_traits.hpp>
+
+namespace boost {
+ namespace graph { namespace distributed { namespace detail {
+ template<typename DistributedGraph, typename ColorMap, typename ParentMap,
+ typename ExploreMap, typename VertexIndexMap, typename DFSVisitor>
+ class parallel_dfs
+ {
+ typedef typename graph_traits<DistributedGraph>::vertex_iterator
+ vertex_iterator;
+ typedef typename graph_traits<DistributedGraph>::vertex_descriptor
+ vertex_descriptor;
+ typedef typename graph_traits<DistributedGraph>::out_edge_iterator
+ out_edge_iterator;
+
+ typedef typename boost::graph::parallel::process_group_type<DistributedGraph>
+ ::type process_group_type;
+ typedef typename process_group_type::process_id_type process_id_type;
+
+ /**
+ * The first vertex in the pair is the local node (i) and the
+ * second vertex in the pair is the (possibly remote) node (j).
+ */
+ typedef boost::parallel::detail::untracked_pair<vertex_descriptor, vertex_descriptor> vertex_pair;
+
+ typedef typename property_traits<ColorMap>::value_type color_type;
+ typedef color_traits<color_type> Color;
+
+ // Message types
+ enum { discover_msg = 10, return_msg = 50, visited_msg = 100 , done_msg = 150};
+
+
+ public:
+ parallel_dfs(const DistributedGraph& g, ColorMap color,
+ ParentMap parent, ExploreMap explore,
+ VertexIndexMap index_map, DFSVisitor vis)
+ : g(g), color(color), parent(parent), explore(explore),
+ index_map(index_map), vis(vis), pg(process_group(g)),
+ owner(get(vertex_owner, g)), next_out_edge(num_vertices(g))
+ { }
+
+ void run(vertex_descriptor s)
+ {
+ vertex_iterator vi, vi_end;
+ for (tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) {
+ put(color, *vi, Color::white());
+ put(parent, *vi, *vi);
+ put(explore, *vi, *vi);
+ next_out_edge[get(index_map, *vi)] = out_edges(*vi, g).first;
+ vis.initialize_vertex(*vi, g);
+ }
+
+ vis.start_vertex(s, g);
+
+ if (get(owner, s) == process_id(pg)) {
+ send_oob(pg, get(owner, s), discover_msg, vertex_pair(s, s));
+ }
+
+ bool done = false;
+ while (!done) {
+ std::pair<process_id_type, int> msg = *pg.poll(true);
+
+ switch (msg.second) {
+ case discover_msg:
+ {
+ vertex_pair p;
+ receive_oob(pg, msg.first, msg.second, p);
+
+ if (p.first != p.second) {
+ // delete j from nomessage(j)
+ if (get(color, p.second) != Color::black())
+ local_put(color, p.second, Color::gray());
+
+ if (recover(p)) break;
+ }
+
+ if (get(color, p.first) == Color::white()) {
+ put(color, p.first, Color::gray());
+ put(parent, p.first, p.second);
+
+ vis.discover_vertex(p.first, g);
+
+ if (shift_center_of_activity(p.first)) break;
+
+ out_edge_iterator ei, ei_end;
+ for (tie(ei,ei_end) = out_edges(p.first, g); ei != ei_end; ++ei)
+ {
+ // Notify everyone who may not know that the source
+ // vertex has been visited. They can then mark the
+ // corresponding color map entry gray.
+ if (get(parent, p.first) != target(*ei, g)
+ && get(explore, p.first) != target(*ei, g)) {
+ vertex_pair visit(target(*ei, g), p.first);
+
+ send_oob(pg, get(owner, target(*ei, g)), visited_msg, visit);
+ }
+ }
+ }
+ }
+ break;
+
+ case visited_msg:
+ {
+ vertex_pair p;
+ receive_oob(pg, msg.first, msg.second, p);
+
+ // delete j from nomessage(j)
+ if (get(color, p.second) != Color::black())
+ local_put(color, p.second, Color::gray());
+
+ recover(p);
+ }
+ break;
+
+ case return_msg:
+ {
+ vertex_pair p;
+ receive_oob(pg, msg.first, msg.second, p);
+
+ // delete j from nomessage(i)
+ local_put(color, p.second, Color::black());
+
+ shift_center_of_activity(p.first);
+ }
+ break;
+
+ case done_msg:
+ {
+ receive_oob(pg, msg.first, msg.second, done);
+
+ // Propagate done message downward in tree
+ done = true;
+ process_id_type id = process_id(pg);
+ process_id_type left = 2*id + 1;
+ process_id_type right = left + 1;
+ if (left < num_processes(pg))
+ send_oob(pg, left, done_msg, done);
+ if (right < num_processes(pg))
+ send_oob(pg, right, done_msg, done);
+ }
+ break;
+
+ default:
+ assert(false);
+ }
+ }
+ }
+
+ private:
+ bool recover(const vertex_pair& p)
+ {
+ if (get(explore, p.first) == p.second) {
+ return shift_center_of_activity(p.first);
+ }
+ else
+ return false;
+ }
+
+ bool shift_center_of_activity(vertex_descriptor i)
+ {
+ for (out_edge_iterator ei = next_out_edge[get(index_map, i)],
+ ei_end = out_edges(i, g).second;
+ ei != ei_end; ++ei) {
+ vis.examine_edge(*ei, g);
+
+ vertex_descriptor k = target(*ei, g);
+ color_type target_color = get(color, k);
+ if (target_color == Color::black()) vis.forward_or_cross_edge(*ei, g);
+ else if (target_color == Color::gray()) vis.back_edge(*ei, g);
+ else {
+ put(explore, i, k);
+ vis.tree_edge(*ei, g);
+ vertex_pair p(k, i);
+ send_oob(pg, get(owner, k), discover_msg, p);
+ next_out_edge[get(index_map, i)] = ++ei;
+ return false;
+ }
+ }
+
+ next_out_edge[get(index_map, i)] = out_edges(i, g).second;
+ put(explore, i, i);
+ put(color, i, Color::black());
+ vis.finish_vertex(i, g);
+
+ if (get(parent, i) == i) {
+ send_oob(pg, 0, done_msg, true);
+ return true;
+ }
+ else {
+ vertex_pair ret(get(parent, i), i);
+ send_oob(pg, get(owner, ret.first), return_msg, ret);
+ }
+ return false;
+ }
+
+ const DistributedGraph& g;
+ ColorMap color;
+ ParentMap parent;
+ ExploreMap explore;
+ VertexIndexMap index_map;
+ DFSVisitor vis;
+ process_group_type pg;
+ typename property_map<DistributedGraph, vertex_owner_t>::const_type owner;
+ std::vector<out_edge_iterator> next_out_edge;
+ };
+ } // end namespace detail
+
+ template<typename DistributedGraph, typename ColorMap, typename ParentMap,
+ typename ExploreMap, typename VertexIndexMap, typename DFSVisitor>
+ void
+ tsin_depth_first_visit
+ (const DistributedGraph& g,
+ typename graph_traits<DistributedGraph>::vertex_descriptor s,
+ DFSVisitor vis, ColorMap color, ParentMap parent, ExploreMap explore,
+ VertexIndexMap index_map)
+ {
+ typedef typename graph_traits<DistributedGraph>::directed_category
+ directed_category;
+ BOOST_STATIC_ASSERT(
+ (is_convertible<directed_category, undirected_tag>::value));
+
+ set_property_map_role(vertex_color, color);
+ graph::distributed::detail::parallel_dfs
+ <DistributedGraph, ColorMap, ParentMap, ExploreMap, VertexIndexMap,
+ DFSVisitor> do_dfs(g, color, parent, explore, index_map, vis);
+ do_dfs.run(s);
+
+ using boost::graph::parallel::process_group;
+ synchronize(process_group(g));
+ }
+
+ template<typename DistributedGraph, typename DFSVisitor,
+ typename VertexIndexMap>
+ void
+ tsin_depth_first_visit
+ (const DistributedGraph& g,
+ typename graph_traits<DistributedGraph>::vertex_descriptor s,
+ DFSVisitor vis,
+ VertexIndexMap index_map)
+ {
+ typedef typename graph_traits<DistributedGraph>::vertex_descriptor
+ vertex_descriptor;
+
+ std::vector<default_color_type> colors(num_vertices(g));
+ std::vector<vertex_descriptor> parent(num_vertices(g));
+ std::vector<vertex_descriptor> explore(num_vertices(g));
+ tsin_depth_first_visit
+ (g, s,
+ vis,
+ make_iterator_property_map(colors.begin(), index_map),
+ make_iterator_property_map(parent.begin(), index_map),
+ make_iterator_property_map(explore.begin(), index_map),
+ index_map);
+ }
+
+ template<typename DistributedGraph, typename DFSVisitor,
+ typename VertexIndexMap>
+ void
+ tsin_depth_first_visit
+ (const DistributedGraph& g,
+ typename graph_traits<DistributedGraph>::vertex_descriptor s,
+ DFSVisitor vis)
+ {
+ tsin_depth_first_visit(g, s, vis, get(vertex_index, g));
+ }
+} // end namespace distributed
+
+using distributed::tsin_depth_first_visit;
+
+} // end namespace graph
+
+template<typename DistributedGraph, typename DFSVisitor>
+void
+depth_first_visit
+ (const DistributedGraph& g,
+ typename graph_traits<DistributedGraph>::vertex_descriptor s,
+ DFSVisitor vis)
+{
+ graph::tsin_depth_first_visit(g, s, vis, get(vertex_index, g));
+}
+
+} // end namespace boost
+
+#endif // BOOST_GRAPH_DISTRIBUTED_DFS_HPP

Added: trunk/boost/graph/distributed/detail/dijkstra_shortest_paths.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/detail/dijkstra_shortest_paths.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,50 @@
+// 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
+#ifndef BOOST_GRAPH_PARALLEL_DIJKSTRA_DETAIL_HPP
+#define BOOST_GRAPH_PARALLEL_DIJKSTRA_DETAIL_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/property_map/property_map.hpp>
+
+namespace boost { namespace graph { namespace distributed { namespace detail {
+
+/**********************************************************************
+ * Dijkstra queue message data *
+ **********************************************************************/
+template<typename DistanceMap, typename PredecessorMap>
+class dijkstra_msg_value
+{
+ typedef typename property_traits<DistanceMap>::value_type distance_type;
+ typedef typename property_traits<PredecessorMap>::value_type
+ predecessor_type;
+
+public:
+ typedef std::pair<distance_type, predecessor_type> type;
+
+ static type create(distance_type dist, predecessor_type pred)
+ { return std::make_pair(dist, pred); }
+};
+
+template<typename DistanceMap>
+class dijkstra_msg_value<DistanceMap, dummy_property_map>
+{
+ typedef typename property_traits<DistanceMap>::key_type vertex_descriptor;
+public:
+ typedef typename property_traits<DistanceMap>::value_type type;
+
+ static type create(type dist, vertex_descriptor) { return dist; }
+};
+/**********************************************************************/
+
+} } } } // end namespace boost::graph::distributed::detail
+
+#endif // BOOST_GRAPH_PARALLEL_DIJKSTRA_DETAIL_HPP

Added: trunk/boost/graph/distributed/detail/filtered_queue.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/detail/filtered_queue.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,108 @@
+// 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
+#ifndef BOOST_FILTERED_QUEUE_HPP
+#define BOOST_FILTERED_QUEUE_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 <algorithm>
+
+namespace boost {
+
+/** Queue adaptor that filters elements pushed into the queue
+ * according to some predicate.
+ */
+template<typename Buffer, typename Predicate>
+class filtered_queue
+{
+ public:
+ typedef Buffer buffer_type;
+ typedef Predicate predicate_type;
+ typedef typename Buffer::value_type value_type;
+ typedef typename Buffer::size_type size_type;
+
+ /**
+ * Constructs a new filtered queue with an initial buffer and a
+ * predicate.
+ *
+ * @param buffer the initial buffer
+ * @param pred the predicate
+ */
+ explicit
+ filtered_queue(const buffer_type& buffer = buffer_type(),
+ const predicate_type& pred = predicate_type())
+ : buffer(buffer), pred(pred) {}
+
+ /** Push a value into the queue.
+ *
+ * If the predicate returns @c true for @p x, pushes @p x into the
+ * buffer.
+ */
+ void push(const value_type& x) { if (pred(x)) buffer.push(x); }
+
+ /** Pop the front element off the buffer.
+ *
+ * @pre @c !empty()
+ */
+ void pop() { buffer.pop(); }
+
+ /** Retrieve the front (top) element in the buffer.
+ *
+ * @pre @c !empty()
+ */
+ value_type& top() { return buffer.top(); }
+
+ /**
+ * \overload
+ */
+ const value_type& top() const { return buffer.top(); }
+
+ /** Determine the number of elements in the buffer. */
+ size_type size() const { return buffer.size(); }
+
+ /** Determine if the buffer is empty. */
+ bool empty() const { return buffer.empty(); }
+
+ /** Get a reference to the underlying buffer. */
+ buffer_type& base() { return buffer; }
+ const buffer_type& base() const { return buffer; }
+
+ /** Swap the contents of this with @p other. */
+ void swap(filtered_queue& other)
+ {
+ using std::swap;
+ swap(buffer, other.buffer);
+ swap(pred, other.pred);
+ }
+
+ private:
+ buffer_type buffer;
+ predicate_type pred;
+};
+
+/** Create a filtered queue. */
+template<typename Buffer, typename Predicate>
+inline filtered_queue<Buffer, Predicate>
+make_filtered_queue(const Buffer& buffer, const Predicate& pred)
+{ return filtered_queue<Buffer, Predicate>(buffer, pred); }
+
+/** Swap a filtered_queue. */
+template<typename Buffer, typename Predicate>
+inline void
+swap(filtered_queue<Buffer, Predicate>& x,
+ filtered_queue<Buffer, Predicate>& y)
+{
+ x.swap(y);
+}
+
+} // end namespace boost
+
+#endif // BOOST_FILTERED_QUEUE_HPP

Added: trunk/boost/graph/distributed/detail/mpi_process_group.tpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/detail/mpi_process_group.tpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,1006 @@
+// -*- C++ -*-
+
+// Copyright (C) 2004-2008 The Trustees of Indiana University.
+// Copyright (C) 2007 Douglas Gregor <doug.gregor_at_[hidden]>
+// Copyright (C) 2007 Matthias Troyer <troyer_at_[hidden]>
+
+// 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
+// Matthias Troyer
+
+//#define PBGL_PROCESS_GROUP_DEBUG
+
+#ifndef BOOST_GRAPH_USE_MPI
+#error "Parallel BGL files should not be included unless <boost/graph/use_mpi.hpp> has been included"
+#endif
+
+#include <cassert>
+#include <algorithm>
+#include <boost/graph/parallel/detail/untracked_pair.hpp>
+#include <numeric>
+#include <iterator>
+#include <functional>
+#include <vector>
+#include <queue>
+#include <stack>
+#include <boost/graph/distributed/detail/tag_allocator.hpp>
+
+// #define PBGL_PROCESS_GROUP_DEBUG
+
+#ifdef PBGL_PROCESS_GROUP_DEBUG
+# include <iostream>
+#endif
+
+namespace boost { namespace graph { namespace distributed {
+
+struct mpi_process_group::impl
+{
+
+ typedef mpi_process_group::message_header message_header;
+ typedef mpi_process_group::outgoing_messages outgoing_messages;
+
+ /**
+ * Stores the incoming messages from a particular processor.
+ *
+ * @todo Evaluate whether we should use a deque instance, which
+ * would reduce could reduce the cost of "receiving" messages and
+ allow us to deallocate memory earlier, but increases the time
+ spent in the synchronization step.
+ */
+ struct incoming_messages {
+ incoming_messages();
+ ~incoming_messages() {}
+
+ std::vector<message_header> headers;
+ buffer_type buffer;
+ std::vector<std::vector<message_header>::iterator> next_header;
+ };
+
+ struct batch_request {
+ MPI_Request request;
+ buffer_type buffer;
+ };
+
+ // send once we have a certain number of messages or bytes in the buffer
+ // these numbers need to be tuned, we keep them small at first for testing
+ std::size_t batch_header_number;
+ std::size_t batch_buffer_size;
+ std::size_t batch_message_size;
+
+ /**
+ * The actual MPI communicator used to transmit data.
+ */
+ boost::mpi::communicator comm;
+
+ /**
+ * The MPI communicator used to transmit out-of-band replies.
+ */
+ boost::mpi::communicator oob_reply_comm;
+
+ /// Outgoing message information, indexed by destination processor.
+ std::vector<outgoing_messages> outgoing;
+
+ /// Incoming message information, indexed by source processor.
+ std::vector<incoming_messages> incoming;
+
+ /// The numbers of processors that have entered a synchronization stage
+ std::vector<int> processors_synchronizing_stage;
+
+ /// The synchronization stage of a processor
+ std::vector<int> synchronizing_stage;
+
+ /// Number of processors still sending messages
+ std::vector<int> synchronizing_unfinished;
+
+ /// Number of batches sent since last synchronization stage
+ std::vector<int> number_sent_batches;
+
+ /// Number of batches received minus number of expected batches
+ std::vector<int> number_received_batches;
+
+
+ /// The context of the currently-executing trigger, or @c trc_none
+ /// if no trigger is executing.
+ trigger_receive_context trigger_context;
+
+ /// Non-zero indicates that we're processing batches
+ /// Increment this when processing patches,
+ /// decrement it when you're done.
+ int processing_batches;
+
+ /**
+ * Contains all of the active blocks corresponding to attached
+ * distributed data structures.
+ */
+ blocks_type blocks;
+
+ /// Whether we are currently synchronizing
+ bool synchronizing;
+
+ /// The MPI requests for posted sends of oob messages
+ std::vector<MPI_Request> requests;
+
+ /// The MPI buffers for posted irecvs of oob messages
+ std::map<int,buffer_type> buffers;
+
+ /// Queue for message batches received while already processing messages
+ std::queue<std::pair<int,outgoing_messages> > new_batches;
+ /// Maximum encountered size of the new_batches queue
+ std::size_t max_received;
+
+ /// The MPI requests and buffers for batchess being sent
+ std::list<batch_request> sent_batches;
+ /// Maximum encountered size of the sent_batches list
+ std::size_t max_sent;
+
+ /// Pre-allocated requests in a pool
+ std::vector<batch_request> batch_pool;
+ /// A stack controlling which batches are available
+ std::stack<std::size_t> free_batches;
+
+ void free_sent_batches();
+
+ // Tag allocator
+ detail::tag_allocator allocated_tags;
+
+ impl(std::size_t num_headers, std::size_t buffers_size,
+ communicator_type parent_comm);
+ ~impl();
+
+private:
+ void set_batch_size(std::size_t header_num, std::size_t buffer_sz);
+};
+
+inline trigger_receive_context mpi_process_group::trigger_context() const
+{
+ return impl_->trigger_context;
+}
+
+template<typename T>
+void
+mpi_process_group::send_impl(int dest, int tag, const T& value,
+ mpl::true_ /*is_mpi_datatype*/) const
+{
+ assert(tag < msg_reserved_first || tag > msg_reserved_last);
+
+ impl::outgoing_messages& outgoing = impl_->outgoing[dest];
+
+ // Start constructing the message header
+ impl::message_header header;
+ header.source = process_id(*this);
+ header.tag = tag;
+ header.offset = outgoing.buffer.size();
+
+ boost::mpi::packed_oarchive oa(impl_->comm, outgoing.buffer);
+ oa << value;
+
+#ifdef PBGL_PROCESS_GROUP_DEBUG
+ std::cerr << "SEND: " << process_id(*this) << " -> " << dest << ", tag = "
+ << tag << ", bytes = " << packed_size << std::endl;
+#endif
+
+ // Store the header
+ header.bytes = outgoing.buffer.size() - header.offset;
+ outgoing.headers.push_back(header);
+
+ maybe_send_batch(dest);
+}
+
+
+template<typename T>
+void
+mpi_process_group::send_impl(int dest, int tag, const T& value,
+ mpl::false_ /*is_mpi_datatype*/) const
+{
+ assert(tag < msg_reserved_first || tag > msg_reserved_last);
+
+ impl::outgoing_messages& outgoing = impl_->outgoing[dest];
+
+ // Start constructing the message header
+ impl::message_header header;
+ header.source = process_id(*this);
+ header.tag = tag;
+ header.offset = outgoing.buffer.size();
+
+ // Serialize into the buffer
+ boost::mpi::packed_oarchive out(impl_->comm, outgoing.buffer);
+ out << value;
+
+ // Store the header
+ header.bytes = outgoing.buffer.size() - header.offset;
+ outgoing.headers.push_back(header);
+ maybe_send_batch(dest);
+
+#ifdef PBGL_PROCESS_GROUP_DEBUG
+ std::cerr << "SEND: " << process_id(*this) << " -> " << dest << ", tag = "
+ << tag << ", bytes = " << header.bytes << std::endl;
+#endif
+}
+
+template<typename T>
+inline void
+send(const mpi_process_group& pg, mpi_process_group::process_id_type dest,
+ int tag, const T& value)
+{
+ pg.send_impl(dest, pg.encode_tag(pg.my_block_number(), tag), value,
+ boost::mpi::is_mpi_datatype<T>());
+}
+
+template<typename T>
+typename enable_if<boost::mpi::is_mpi_datatype<T>, void>::type
+send(const mpi_process_group& pg, mpi_process_group::process_id_type dest,
+ int tag, const T values[], std::size_t n)
+{
+ pg.send_impl(dest, pg.encode_tag(pg.my_block_number(), tag),
+ boost::serialization::make_array(values,n),
+ boost::mpl::true_());
+}
+
+template<typename T>
+typename disable_if<boost::mpi::is_mpi_datatype<T>, void>::type
+mpi_process_group::
+array_send_impl(int dest, int tag, const T values[], std::size_t n) const
+{
+ assert(tag < msg_reserved_first || tag > msg_reserved_last);
+
+ impl::outgoing_messages& outgoing = impl_->outgoing[dest];
+
+ // Start constructing the message header
+ impl::message_header header;
+ header.source = process_id(*this);
+ header.tag = tag;
+ header.offset = outgoing.buffer.size();
+
+ // Serialize into the buffer
+ boost::mpi::packed_oarchive out(impl_->comm, outgoing.buffer);
+ out << n;
+
+ for (std::size_t i = 0; i < n; ++i)
+ out << values[i];
+
+ // Store the header
+ header.bytes = outgoing.buffer.size() - header.offset;
+ outgoing.headers.push_back(header);
+ maybe_send_batch(dest);
+
+#ifdef PBGL_PROCESS_GROUP_DEBUG
+ std::cerr << "SEND: " << process_id(*this) << " -> " << dest << ", tag = "
+ << tag << ", bytes = " << header.bytes << std::endl;
+#endif
+}
+
+template<typename T>
+typename disable_if<boost::mpi::is_mpi_datatype<T>, void>::type
+send(const mpi_process_group& pg, mpi_process_group::process_id_type dest,
+ int tag, const T values[], std::size_t n)
+{
+ pg.array_send_impl(dest, pg.encode_tag(pg.my_block_number(), tag),
+ values, n);
+}
+
+template<typename InputIterator>
+void
+send(const mpi_process_group& pg, mpi_process_group::process_id_type dest,
+ int tag, InputIterator first, InputIterator last)
+{
+ typedef typename std::iterator_traits<InputIterator>::value_type value_type;
+ std::vector<value_type> values(first, last);
+ if (values.empty()) send(pg, dest, tag, static_cast<value_type*>(0), 0);
+ else send(pg, dest, tag, &values[0], values.size());
+}
+
+template<typename T>
+bool
+mpi_process_group::receive_impl(int source, int tag, T& value,
+ mpl::true_ /*is_mpi_datatype*/) const
+{
+#ifdef PBGL_PROCESS_GROUP_DEBUG
+ std::cerr << "RECV: " << process_id(*this) << " <- " << source << ", tag = "
+ << tag << std::endl;
+#endif
+
+ impl::incoming_messages& incoming = impl_->incoming[source];
+
+ // Find the next header with the right tag
+ std::vector<impl::message_header>::iterator header =
+ incoming.next_header[my_block_number()];
+ while (header != incoming.headers.end() && header->tag != tag) ++header;
+
+ // If no header is found, notify the caller
+ if (header == incoming.headers.end()) return false;
+
+ // Unpack the data
+ if (header->bytes > 0) {
+ boost::mpi::packed_iarchive ia(impl_->comm, incoming.buffer,
+ archive::no_header, header->offset);
+ ia >> value;
+ }
+
+ // Mark this message as received
+ header->tag = -1;
+
+ // Move the "next header" indicator to the next unreceived message
+ while (incoming.next_header[my_block_number()] != incoming.headers.end()
+ && incoming.next_header[my_block_number()]->tag == -1)
+ ++incoming.next_header[my_block_number()];
+
+ if (incoming.next_header[my_block_number()] == incoming.headers.end()) {
+ bool finished = true;
+ for (std::size_t i = 0; i < incoming.next_header.size() && finished; ++i) {
+ if (incoming.next_header[i] != incoming.headers.end()) finished = false;
+ }
+
+ if (finished) {
+ std::vector<impl::message_header> no_headers;
+ incoming.headers.swap(no_headers);
+ buffer_type empty_buffer;
+ incoming.buffer.swap(empty_buffer);
+ for (std::size_t i = 0; i < incoming.next_header.size(); ++i)
+ incoming.next_header[i] = incoming.headers.end();
+ }
+ }
+
+ return true;
+}
+
+template<typename T>
+bool
+mpi_process_group::receive_impl(int source, int tag, T& value,
+ mpl::false_ /*is_mpi_datatype*/) const
+{
+ impl::incoming_messages& incoming = impl_->incoming[source];
+
+ // Find the next header with the right tag
+ std::vector<impl::message_header>::iterator header =
+ incoming.next_header[my_block_number()];
+ while (header != incoming.headers.end() && header->tag != tag) ++header;
+
+ // If no header is found, notify the caller
+ if (header == incoming.headers.end()) return false;
+
+ // Deserialize the data
+ boost::mpi::packed_iarchive in(impl_->comm, incoming.buffer,
+ archive::no_header, header->offset);
+ in >> value;
+
+ // Mark this message as received
+ header->tag = -1;
+
+ // Move the "next header" indicator to the next unreceived message
+ while (incoming.next_header[my_block_number()] != incoming.headers.end()
+ && incoming.next_header[my_block_number()]->tag == -1)
+ ++incoming.next_header[my_block_number()];
+
+ if (incoming.next_header[my_block_number()] == incoming.headers.end()) {
+ bool finished = true;
+ for (std::size_t i = 0; i < incoming.next_header.size() && finished; ++i) {
+ if (incoming.next_header[i] != incoming.headers.end()) finished = false;
+ }
+
+ if (finished) {
+ std::vector<impl::message_header> no_headers;
+ incoming.headers.swap(no_headers);
+ buffer_type empty_buffer;
+ incoming.buffer.swap(empty_buffer);
+ for (std::size_t i = 0; i < incoming.next_header.size(); ++i)
+ incoming.next_header[i] = incoming.headers.end();
+ }
+ }
+
+ return true;
+}
+
+template<typename T>
+typename disable_if<boost::mpi::is_mpi_datatype<T>, bool>::type
+mpi_process_group::
+array_receive_impl(int source, int tag, T* values, std::size_t& n) const
+{
+ impl::incoming_messages& incoming = impl_->incoming[source];
+
+ // Find the next header with the right tag
+ std::vector<impl::message_header>::iterator header =
+ incoming.next_header[my_block_number()];
+ while (header != incoming.headers.end() && header->tag != tag) ++header;
+
+ // If no header is found, notify the caller
+ if (header == incoming.headers.end()) return false;
+
+ // Deserialize the data
+ boost::mpi::packed_iarchive in(impl_->comm, incoming.buffer,
+ archive::no_header, header->offset);
+ std::size_t num_sent;
+ in >> num_sent;
+ if (num_sent > n)
+ std::cerr << "ERROR: Have " << num_sent << " items but only space for "
+ << n << " items\n";
+
+ for (std::size_t i = 0; i < num_sent; ++i)
+ in >> values[i];
+ n = num_sent;
+
+ // Mark this message as received
+ header->tag = -1;
+
+ // Move the "next header" indicator to the next unreceived message
+ while (incoming.next_header[my_block_number()] != incoming.headers.end()
+ && incoming.next_header[my_block_number()]->tag == -1)
+ ++incoming.next_header[my_block_number()];
+
+ if (incoming.next_header[my_block_number()] == incoming.headers.end()) {
+ bool finished = true;
+ for (std::size_t i = 0; i < incoming.next_header.size() && finished; ++i) {
+ if (incoming.next_header[i] != incoming.headers.end()) finished = false;
+ }
+
+ if (finished) {
+ std::vector<impl::message_header> no_headers;
+ incoming.headers.swap(no_headers);
+ buffer_type empty_buffer;
+ incoming.buffer.swap(empty_buffer);
+ for (std::size_t i = 0; i < incoming.next_header.size(); ++i)
+ incoming.next_header[i] = incoming.headers.end();
+ }
+ }
+
+ return true;
+}
+
+// Construct triggers
+template<typename Type, typename Handler>
+void mpi_process_group::trigger(int tag, const Handler& handler)
+{
+ assert(block_num);
+ install_trigger(tag,my_block_number(),shared_ptr<trigger_base>(
+ new trigger_launcher<Type, Handler>(*this, tag, handler)));
+}
+
+template<typename Type, typename Handler>
+void mpi_process_group::trigger_with_reply(int tag, const Handler& handler)
+{
+ assert(block_num);
+ install_trigger(tag,my_block_number(),shared_ptr<trigger_base>(
+ new reply_trigger_launcher<Type, Handler>(*this, tag, handler)));
+}
+
+template<typename Type, typename Handler>
+void mpi_process_group::global_trigger(int tag, const Handler& handler,
+ std::size_t sz)
+{
+ if (sz==0) // normal trigger
+ install_trigger(tag,0,shared_ptr<trigger_base>(
+ new global_trigger_launcher<Type, Handler>(*this, tag, handler)));
+ else // trigger with irecv
+ install_trigger(tag,0,shared_ptr<trigger_base>(
+ new global_irecv_trigger_launcher<Type, Handler>(*this, tag, handler,sz)));
+
+}
+
+namespace detail {
+
+template<typename Type>
+void do_oob_receive(mpi_process_group const& self,
+ int source, int tag, Type& data, mpl::true_ /*is_mpi_datatype*/)
+{
+ using boost::mpi::get_mpi_datatype;
+
+ //self.impl_->comm.recv(source,tag,data);
+ MPI_Recv(&data, 1, get_mpi_datatype<Type>(data), source, tag, self.impl_->comm,
+ MPI_STATUS_IGNORE);
+}
+
+template<typename Type>
+void do_oob_receive(mpi_process_group const& self,
+ int source, int tag, Type& data, mpl::false_ /*is_mpi_datatype*/)
+{
+ // self.impl_->comm.recv(source,tag,data);
+ // Receive the size of the data packet
+ boost::mpi::status status;
+ status = self.impl_->comm.probe(source, tag);
+
+#if BOOST_VERSION >= 103600
+ int size = status.count<boost::mpi::packed>().get();
+#else
+ int size;
+ MPI_Status& mpi_status = status;
+ MPI_Get_count(&mpi_status, MPI_PACKED, &size);
+#endif
+
+ // Receive the data packed itself
+ boost::mpi::packed_iarchive in(self.impl_->comm);
+ in.resize(size);
+ MPI_Recv(in.address(), size, MPI_PACKED, source, tag, self.impl_->comm,
+ MPI_STATUS_IGNORE);
+
+ // Deserialize the data
+ in >> data;
+}
+
+template<typename Type>
+void do_oob_receive(mpi_process_group const& self, int source, int tag, Type& data)
+{
+ do_oob_receive(self, source, tag, data,
+ boost::mpi::is_mpi_datatype<Type>());
+}
+
+
+} // namespace detail
+
+
+template<typename Type, typename Handler>
+void
+mpi_process_group::trigger_launcher<Type, Handler>::
+receive(mpi_process_group const&, int source, int tag,
+ trigger_receive_context context, int block) const
+{
+#ifdef PBGL_PROCESS_GROUP_DEBUG
+ std::cerr << (out_of_band? "OOB trigger" : "Trigger")
+ << " receive from source " << source << " and tag " << tag
+ << " in block " << (block == -1 ? self.my_block_number() : block) << std::endl;
+#endif
+
+ Type data;
+
+ if (context == trc_out_of_band) {
+ // Receive the message directly off the wire
+ int realtag = self.encode_tag(
+ block == -1 ? self.my_block_number() : block, tag);
+ detail::do_oob_receive(self,source,realtag,data);
+ }
+ else
+ // Receive the message out of the local buffer
+ boost::graph::distributed::receive(self, source, tag, data);
+
+ // Pass the message off to the handler
+ handler(source, tag, data, context);
+}
+
+template<typename Type, typename Handler>
+void
+mpi_process_group::reply_trigger_launcher<Type, Handler>::
+receive(mpi_process_group const&, int source, int tag,
+ trigger_receive_context context, int block) const
+{
+#ifdef PBGL_PROCESS_GROUP_DEBUG
+ std::cerr << (out_of_band? "OOB reply trigger" : "Reply trigger")
+ << " receive from source " << source << " and tag " << tag
+ << " in block " << (block == -1 ? self.my_block_number() : block) << std::endl;
+#endif
+ assert(context == trc_out_of_band);
+
+ boost::parallel::detail::untracked_pair<int, Type> data;
+
+ // Receive the message directly off the wire
+ int realtag = self.encode_tag(block == -1 ? self.my_block_number() : block,
+ tag);
+ detail::do_oob_receive(self, source, realtag, data);
+
+ // Pass the message off to the handler and send the result back to
+ // the source.
+ send_oob(self, source, data.first,
+ handler(source, tag, data.second, context), -2);
+}
+
+template<typename Type, typename Handler>
+void
+mpi_process_group::global_trigger_launcher<Type, Handler>::
+receive(mpi_process_group const& self, int source, int tag,
+ trigger_receive_context context, int block) const
+{
+#ifdef PBGL_PROCESS_GROUP_DEBUG
+ std::cerr << (out_of_band? "OOB trigger" : "Trigger")
+ << " receive from source " << source << " and tag " << tag
+ << " in block " << (block == -1 ? self.my_block_number() : block) << std::endl;
+#endif
+
+ Type data;
+
+ if (context == trc_out_of_band) {
+ // Receive the message directly off the wire
+ int realtag = self.encode_tag(
+ block == -1 ? self.my_block_number() : block, tag);
+ detail::do_oob_receive(self,source,realtag,data);
+ }
+ else
+ // Receive the message out of the local buffer
+ boost::graph::distributed::receive(self, source, tag, data);
+
+ // Pass the message off to the handler
+ handler(self, source, tag, data, context);
+}
+
+
+template<typename Type, typename Handler>
+void
+mpi_process_group::global_irecv_trigger_launcher<Type, Handler>::
+receive(mpi_process_group const& self, int source, int tag,
+ trigger_receive_context context, int block) const
+{
+#ifdef PBGL_PROCESS_GROUP_DEBUG
+ std::cerr << (out_of_band? "OOB trigger" : "Trigger")
+ << " receive from source " << source << " and tag " << tag
+ << " in block " << (block == -1 ? self.my_block_number() : block) << std::endl;
+#endif
+
+ Type data;
+
+ if (context == trc_out_of_band) {
+ return;
+ }
+ assert (context == trc_irecv_out_of_band);
+
+ // force posting of new MPI_Irecv, even though buffer is already allocated
+ boost::mpi::packed_iarchive ia(self.impl_->comm,self.impl_->buffers[tag]);
+ ia >> data;
+ // Start a new receive
+ prepare_receive(self,tag,true);
+ // Pass the message off to the handler
+ handler(self, source, tag, data, context);
+}
+
+
+template<typename Type, typename Handler>
+void
+mpi_process_group::global_irecv_trigger_launcher<Type, Handler>::
+prepare_receive(mpi_process_group const& self, int tag, bool force) const
+{
+#ifdef PBGL_PROCESS_GROUP_DEBUG
+ std::cerr << ("Posting Irecv for trigger")
+ << " receive with tag " << tag << std::endl;
+#endif
+ if (self.impl_->buffers.find(tag) == self.impl_->buffers.end()) {
+ self.impl_->buffers[tag].resize(buffer_size);
+ force = true;
+ }
+ assert(static_cast<int>(self.impl_->buffers[tag].size()) >= buffer_size);
+
+ //BOOST_MPL_ASSERT(mpl::not_<is_mpi_datatype<Type> >);
+ if (force) {
+ self.impl_->requests.push_back(MPI_Request());
+ MPI_Request* request = &self.impl_->requests.back();
+ MPI_Irecv(&self.impl_->buffers[tag].front(),buffer_size,
+ MPI_PACKED,MPI_ANY_SOURCE,tag,self.impl_->comm,request);
+ }
+}
+
+
+template<typename T>
+inline mpi_process_group::process_id_type
+receive(const mpi_process_group& pg, int tag, T& value)
+{
+ for (std::size_t source = 0; source < pg.impl_->incoming.size(); ++source) {
+ if (pg.receive_impl(source, pg.encode_tag(pg.my_block_number(), tag),
+ value, boost::mpi::is_mpi_datatype<T>()))
+ return source;
+ }
+ assert (false);
+}
+
+template<typename T>
+typename
+ enable_if<boost::mpi::is_mpi_datatype<T>,
+ std::pair<mpi_process_group::process_id_type, std::size_t> >::type
+receive(const mpi_process_group& pg, int tag, T values[], std::size_t n)
+{
+ for (std::size_t source = 0; source < pg.impl_->incoming.size(); ++source) {
+ bool result =
+ pg.receive_impl(source, pg.encode_tag(pg.my_block_number(), tag),
+ boost::serialization::make_array(values,n),
+ boost::mpl::true_());
+ if (result)
+ return std::make_pair(source, n);
+ }
+ assert(false);
+}
+
+template<typename T>
+typename
+ disable_if<boost::mpi::is_mpi_datatype<T>,
+ std::pair<mpi_process_group::process_id_type, std::size_t> >::type
+receive(const mpi_process_group& pg, int tag, T values[], std::size_t n)
+{
+ for (std::size_t source = 0; source < pg.impl_->incoming.size(); ++source) {
+ if (pg.array_receive_impl(source, pg.encode_tag(pg.my_block_number(), tag),
+ values, n))
+ return std::make_pair(source, n);
+ }
+ assert(false);
+}
+
+template<typename T>
+mpi_process_group::process_id_type
+receive(const mpi_process_group& pg,
+ mpi_process_group::process_id_type source, int tag, T& value)
+{
+ if (pg.receive_impl(source, pg.encode_tag(pg.my_block_number(), tag),
+ value, boost::mpi::is_mpi_datatype<T>()))
+ return source;
+ else {
+ fprintf(stderr,
+ "Process %d failed to receive a message from process %d with tag %d in block %d.\n",
+ process_id(pg), source, tag, pg.my_block_number());
+
+ assert(false);
+ exit(1);
+ }
+}
+
+template<typename T>
+typename
+ enable_if<boost::mpi::is_mpi_datatype<T>,
+ std::pair<mpi_process_group::process_id_type, std::size_t> >::type
+receive(const mpi_process_group& pg, int source, int tag, T values[],
+ std::size_t n)
+{
+ if (pg.receive_impl(source, pg.encode_tag(pg.my_block_number(), tag),
+ boost::serialization::make_array(values,n),
+ boost::mpl::true_()))
+ return std::make_pair(source,n);
+ else {
+ fprintf(stderr,
+ "Process %d failed to receive a message from process %d with tag %d in block %d.\n",
+ process_id(pg), source, tag, pg.my_block_number());
+
+ assert(false);
+ exit(1);
+ }
+}
+
+template<typename T>
+typename
+ disable_if<boost::mpi::is_mpi_datatype<T>,
+ std::pair<mpi_process_group::process_id_type, std::size_t> >::type
+receive(const mpi_process_group& pg, int source, int tag, T values[],
+ std::size_t n)
+{
+ pg.array_receive_impl(source, pg.encode_tag(pg.my_block_number(), tag),
+ values, n);
+
+ return std::make_pair(source, n);
+}
+
+template<typename T, typename BinaryOperation>
+T*
+all_reduce(const mpi_process_group& pg, T* first, T* last, T* out,
+ BinaryOperation bin_op)
+{
+ synchronize(pg);
+
+ bool inplace = first == out;
+
+ if (inplace) out = new T [last-first];
+
+ boost::mpi::all_reduce(boost::mpi::communicator(communicator(pg),
+ boost::mpi::comm_attach),
+ first, last-first, out, bin_op);
+
+ if (inplace) {
+ std::copy(out, out + (last-first), first);
+ delete [] out;
+ return last;
+ }
+
+ return out;
+}
+
+template<typename T>
+void
+broadcast(const mpi_process_group& pg, T& val,
+ mpi_process_group::process_id_type root)
+{
+ // broadcast the seed
+ boost::mpi::communicator comm(communicator(pg),boost::mpi::comm_attach);
+ boost::mpi::broadcast(comm,val,root);
+}
+
+
+template<typename T, typename BinaryOperation>
+T*
+scan(const mpi_process_group& pg, T* first, T* last, T* out,
+ BinaryOperation bin_op)
+{
+ synchronize(pg);
+
+ bool inplace = first == out;
+
+ if (inplace) out = new T [last-first];
+
+ boost::mpi::scan(communicator(pg), first, last-first, out, bin_op);
+
+ if (inplace) {
+ std::copy(out, out + (last-first), first);
+ delete [] out;
+ return last;
+ }
+
+ return out;
+}
+
+
+template<typename InputIterator, typename T>
+void
+all_gather(const mpi_process_group& pg, InputIterator first,
+ InputIterator last, std::vector<T>& out)
+{
+ synchronize(pg);
+
+ // Stick a copy of the local values into a vector, so we can broadcast it
+ std::vector<T> local_values(first, last);
+
+ // Collect the number of vertices stored in each process
+ int size = local_values.size();
+ std::vector<int> sizes(num_processes(pg));
+ int result = MPI_Allgather(&size, 1, MPI_INT,
+ &sizes[0], 1, MPI_INT,
+ communicator(pg));
+ assert(result == MPI_SUCCESS);
+
+ // Adjust sizes based on the number of bytes
+ std::transform(sizes.begin(), sizes.end(), sizes.begin(),
+ std::bind2nd(std::multiplies<int>(), sizeof(T)));
+
+ // Compute displacements
+ std::vector<int> displacements;
+ displacements.reserve(sizes.size() + 1);
+ displacements.push_back(0);
+ std::partial_sum(sizes.begin(), sizes.end(),
+ std::back_inserter(displacements));
+
+ // Gather all of the values
+ out.resize(displacements.back() / sizeof(T));
+ if (!out.empty()) {
+ result = MPI_Allgatherv(local_values.empty()? (void*)&local_values
+ /* local results */: (void*)&local_values[0],
+ local_values.size() * sizeof(T),
+ MPI_BYTE,
+ &out[0], &sizes[0], &displacements[0], MPI_BYTE,
+ communicator(pg));
+ }
+ assert(result == MPI_SUCCESS);
+}
+
+template<typename InputIterator>
+mpi_process_group
+process_subgroup(const mpi_process_group& pg,
+ InputIterator first, InputIterator last)
+{
+/*
+ boost::mpi::group current_group = communicator(pg).group();
+ boost::mpi::group new_group = current_group.include(first,last);
+ boost::mpi::communicator new_comm(communicator(pg),new_group);
+ return mpi_process_group(new_comm);
+*/
+ std::vector<int> ranks(first, last);
+
+ MPI_Group current_group;
+ int result = MPI_Comm_group(communicator(pg), &current_group);
+ assert(result == MPI_SUCCESS);
+
+ MPI_Group new_group;
+ result = MPI_Group_incl(current_group, ranks.size(), &ranks[0], &new_group);
+ assert(result == MPI_SUCCESS);
+
+ MPI_Comm new_comm;
+ result = MPI_Comm_create(communicator(pg), new_group, &new_comm);
+ assert(result == MPI_SUCCESS);
+
+ result = MPI_Group_free(&new_group);
+ assert(result == MPI_SUCCESS);
+ result = MPI_Group_free(&current_group);
+ assert(result == MPI_SUCCESS);
+
+ if (new_comm != MPI_COMM_NULL) {
+ mpi_process_group result_pg(boost::mpi::communicator(new_comm,boost::mpi::comm_attach));
+ result = MPI_Comm_free(&new_comm);
+ assert(result == 0);
+ return result_pg;
+ } else {
+ return mpi_process_group(mpi_process_group::create_empty());
+ }
+
+}
+
+
+template<typename Receiver>
+Receiver* mpi_process_group::get_receiver()
+{
+ return impl_->blocks[my_block_number()]->on_receive
+ .template target<Receiver>();
+}
+
+template<typename T>
+typename enable_if<boost::mpi::is_mpi_datatype<T> >::type
+receive_oob(const mpi_process_group& pg,
+ mpi_process_group::process_id_type source, int tag, T& value, int block)
+{
+ using boost::mpi::get_mpi_datatype;
+
+ // Determine the actual message we expect to receive, and which
+ // communicator it will come by.
+ std::pair<boost::mpi::communicator, int> actual
+ = pg.actual_communicator_and_tag(tag, block);
+
+ // Post a non-blocking receive that waits until we complete this request.
+ MPI_Request request;
+ MPI_Irecv(&value, 1, get_mpi_datatype<T>(value),
+ source, actual.second, actual.first, &request);
+
+ int done = 0;
+ do {
+ MPI_Test(&request, &done, MPI_STATUS_IGNORE);
+ if (!done)
+ pg.poll(/*wait=*/false, block);
+ } while (!done);
+}
+
+template<typename T>
+typename disable_if<boost::mpi::is_mpi_datatype<T> >::type
+receive_oob(const mpi_process_group& pg,
+ mpi_process_group::process_id_type source, int tag, T& value, int block)
+{
+ // Determine the actual message we expect to receive, and which
+ // communicator it will come by.
+ std::pair<boost::mpi::communicator, int> actual
+ = pg.actual_communicator_and_tag(tag, block);
+
+ boost::optional<boost::mpi::status> status;
+ do {
+ status = actual.first.iprobe(source, actual.second);
+ if (!status)
+ pg.poll();
+ } while (!status);
+
+ //actual.first.recv(status->source(), status->tag(),value);
+
+ // Allocate the receive buffer
+ boost::mpi::packed_iarchive in(actual.first);
+
+#if BOOST_VERSION >= 103600
+ in.resize(status->count<boost::mpi::packed>().get());
+#else
+ int size;
+ MPI_Status mpi_status = *status;
+ MPI_Get_count(&mpi_status, MPI_PACKED, &size);
+ in.resize(size);
+#endif
+
+ // Receive the message data
+ MPI_Recv(in.address(), in.size(), MPI_PACKED,
+ status->source(), status->tag(), actual.first, MPI_STATUS_IGNORE);
+
+ // Unpack the message data
+ in >> value;
+}
+
+
+template<typename SendT, typename ReplyT>
+typename enable_if<boost::mpi::is_mpi_datatype<ReplyT> >::type
+send_oob_with_reply(const mpi_process_group& pg,
+ mpi_process_group::process_id_type dest,
+ int tag, const SendT& send_value, ReplyT& reply_value,
+ int block)
+{
+ detail::tag_allocator::token reply_tag = pg.impl_->allocated_tags.get_tag();
+ send_oob(pg, dest, tag, boost::parallel::detail::make_untracked_pair(
+ (int)reply_tag, send_value), block);
+ receive_oob(pg, dest, reply_tag, reply_value);
+}
+
+template<typename SendT, typename ReplyT>
+typename disable_if<boost::mpi::is_mpi_datatype<ReplyT> >::type
+send_oob_with_reply(const mpi_process_group& pg,
+ mpi_process_group::process_id_type dest,
+ int tag, const SendT& send_value, ReplyT& reply_value,
+ int block)
+{
+ detail::tag_allocator::token reply_tag = pg.impl_->allocated_tags.get_tag();
+ send_oob(pg, dest, tag,
+ boost::parallel::detail::make_untracked_pair((int)reply_tag,
+ send_value), block);
+ receive_oob(pg, dest, reply_tag, reply_value);
+}
+
+} } } // end namespace boost::graph::distributed

Added: trunk/boost/graph/distributed/detail/queue.cpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/detail/queue.cpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,177 @@
+// 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
+#include <boost/optional.hpp>
+#include <cassert>
+#include <boost/graph/parallel/algorithm.hpp>
+#include <boost/graph/parallel/process_group.hpp>
+#include <functional>
+#include <algorithm>
+#include <boost/graph/parallel/simple_trigger.hpp>
+
+#ifndef BOOST_GRAPH_USE_MPI
+#error "Parallel BGL files should not be included unless <boost/graph/use_mpi.hpp> has been included"
+#endif
+
+namespace boost { namespace graph { namespace distributed {
+
+template<BOOST_DISTRIBUTED_QUEUE_PARMS>
+BOOST_DISTRIBUTED_QUEUE_TYPE::
+distributed_queue(const ProcessGroup& process_group, const OwnerMap& owner,
+ const Buffer& buffer, bool polling)
+ : process_group(process_group, attach_distributed_object()),
+ owner(owner),
+ buffer(buffer),
+ polling(polling)
+{
+ if (!polling)
+ outgoing_buffers.reset(
+ new outgoing_buffers_t(num_processes(process_group)));
+
+ setup_triggers();
+}
+
+template<BOOST_DISTRIBUTED_QUEUE_PARMS>
+BOOST_DISTRIBUTED_QUEUE_TYPE::
+distributed_queue(const ProcessGroup& process_group, const OwnerMap& owner,
+ const Buffer& buffer, const UnaryPredicate& pred,
+ bool polling)
+ : process_group(process_group, attach_distributed_object()),
+ owner(owner),
+ buffer(buffer),
+ pred(pred),
+ polling(polling)
+{
+ if (!polling)
+ outgoing_buffers.reset(
+ new outgoing_buffers_t(num_processes(process_group)));
+
+ setup_triggers();
+}
+
+template<BOOST_DISTRIBUTED_QUEUE_PARMS>
+BOOST_DISTRIBUTED_QUEUE_TYPE::
+distributed_queue(const ProcessGroup& process_group, const OwnerMap& owner,
+ const UnaryPredicate& pred, bool polling)
+ : process_group(process_group, attach_distributed_object()),
+ owner(owner),
+ pred(pred),
+ polling(polling)
+{
+ if (!polling)
+ outgoing_buffers.reset(
+ new outgoing_buffers_t(num_processes(process_group)));
+
+ setup_triggers();
+}
+
+template<BOOST_DISTRIBUTED_QUEUE_PARMS>
+void
+BOOST_DISTRIBUTED_QUEUE_TYPE::push(const value_type& x)
+{
+ typename ProcessGroup::process_id_type dest = get(owner, x);
+ if (outgoing_buffers)
+ outgoing_buffers->at(dest).push_back(x);
+ else if (dest == process_id(process_group))
+ buffer.push(x);
+ else
+ send(process_group, get(owner, x), msg_push, x);
+}
+
+template<BOOST_DISTRIBUTED_QUEUE_PARMS>
+bool
+BOOST_DISTRIBUTED_QUEUE_TYPE::empty() const
+{
+ /* Processes will stay here until the buffer is nonempty or
+ synchronization with the other processes indicates that all local
+ buffers are empty (and no messages are in transit).
+ */
+ while (buffer.empty() && !do_synchronize()) ;
+
+ return buffer.empty();
+}
+
+template<BOOST_DISTRIBUTED_QUEUE_PARMS>
+typename BOOST_DISTRIBUTED_QUEUE_TYPE::size_type
+BOOST_DISTRIBUTED_QUEUE_TYPE::size() const
+{
+ empty();
+ return buffer.size();
+}
+
+template<BOOST_DISTRIBUTED_QUEUE_PARMS>
+void BOOST_DISTRIBUTED_QUEUE_TYPE::setup_triggers()
+{
+ using boost::graph::parallel::simple_trigger;
+
+ simple_trigger(process_group, msg_push, this,
+ &distributed_queue::handle_push);
+ simple_trigger(process_group, msg_multipush, this,
+ &distributed_queue::handle_multipush);
+}
+
+template<BOOST_DISTRIBUTED_QUEUE_PARMS>
+void
+BOOST_DISTRIBUTED_QUEUE_TYPE::
+handle_push(int /*source*/, int /*tag*/, const value_type& value,
+ trigger_receive_context)
+{
+ if (pred(value)) buffer.push(value);
+}
+
+template<BOOST_DISTRIBUTED_QUEUE_PARMS>
+void
+BOOST_DISTRIBUTED_QUEUE_TYPE::
+handle_multipush(int /*source*/, int /*tag*/,
+ const std::vector<value_type>& values,
+ trigger_receive_context)
+{
+ for (std::size_t i = 0; i < values.size(); ++i)
+ if (pred(values[i])) buffer.push(values[i]);
+}
+
+template<BOOST_DISTRIBUTED_QUEUE_PARMS>
+bool
+BOOST_DISTRIBUTED_QUEUE_TYPE::do_synchronize() const
+{
+#ifdef PBGL_ACCOUNTING
+ ++num_synchronizations;
+#endif
+
+ using boost::parallel::all_reduce;
+ using std::swap;
+
+ typedef typename ProcessGroup::process_id_type process_id_type;
+
+ if (outgoing_buffers) {
+ // Transfer all of the push requests
+ process_id_type id = process_id(process_group);
+ process_id_type np = num_processes(process_group);
+ for (process_id_type dest = 0; dest < np; ++dest) {
+ outgoing_buffer_t& outgoing = outgoing_buffers->at(dest);
+ std::size_t size = outgoing.size();
+ if (size != 0) {
+ if (dest != id) {
+ send(process_group, dest, msg_multipush, outgoing);
+ } else {
+ for (std::size_t i = 0; i < size; ++i)
+ buffer.push(outgoing[i]);
+ }
+ outgoing.clear();
+ }
+ }
+ }
+ synchronize(process_group);
+
+ unsigned local_size = buffer.size();
+ unsigned global_size =
+ all_reduce(process_group, local_size, std::plus<unsigned>());
+ return global_size == 0;
+}
+
+} } } // end namespace boost::graph::distributed

Added: trunk/boost/graph/distributed/detail/remote_update_set.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/detail/remote_update_set.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,259 @@
+// Copyright (C) 2005-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
+#ifndef BOOST_GRAPH_DETAIL_REMOTE_UPDATE_SET_HPP
+#define BOOST_GRAPH_DETAIL_REMOTE_UPDATE_SET_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/graph/parallel/process_group.hpp>
+#include <boost/type_traits/is_convertible.hpp>
+#include <vector>
+#include <cassert>
+#include <boost/optional.hpp>
+#include <queue>
+
+namespace boost { namespace graph { namespace detail {
+
+template<typename ProcessGroup>
+void do_synchronize(ProcessGroup& pg)
+{
+ using boost::parallel::synchronize;
+ synchronize(pg);
+}
+
+struct remote_set_queued {};
+struct remote_set_immediate {};
+
+template<typename ProcessGroup>
+class remote_set_semantics
+{
+ BOOST_STATIC_CONSTANT
+ (bool,
+ queued = (is_convertible<
+ typename ProcessGroup::communication_category,
+ parallel::bsp_process_group_tag>::value));
+
+ public:
+ typedef typename mpl::if_c<queued,
+ remote_set_queued,
+ remote_set_immediate>::type type;
+};
+
+
+template<typename Derived, typename ProcessGroup, typename Value,
+ typename OwnerMap,
+ typename Semantics = typename remote_set_semantics<ProcessGroup>::type>
+class remote_update_set;
+
+/**********************************************************************
+ * Remote updating set that queues messages until synchronization *
+ **********************************************************************/
+template<typename Derived, typename ProcessGroup, typename Value,
+ typename OwnerMap>
+class remote_update_set<Derived, ProcessGroup, Value, OwnerMap,
+ remote_set_queued>
+{
+ typedef typename property_traits<OwnerMap>::key_type Key;
+ typedef std::vector<std::pair<Key, Value> > Updates;
+ typedef typename Updates::size_type updates_size_type;
+ typedef typename Updates::value_type updates_pair_type;
+
+public:
+
+private:
+ typedef typename ProcessGroup::process_id_type process_id_type;
+
+ enum message_kind {
+ /** Message containing the number of updates that will be sent in
+ * a msg_updates message that will immediately follow. This
+ * message will contain a single value of type
+ * updates_size_type.
+ */
+ msg_num_updates,
+
+ /** Contains (key, value) pairs with all of the updates from a
+ * particular source. The number of updates is variable, but will
+ * be provided in a msg_num_updates message that immediately
+ * preceeds this message.
+ *
+ */
+ msg_updates
+ };
+
+ struct handle_messages
+ {
+ explicit
+ handle_messages(remote_update_set* self, const ProcessGroup& pg)
+ : self(self), update_sizes(num_processes(pg), 0) { }
+
+ void operator()(process_id_type source, int tag)
+ {
+ switch(tag) {
+ case msg_num_updates:
+ {
+ // Receive the # of updates
+ updates_size_type num_updates;
+ receive(self->process_group, source, tag, num_updates);
+
+ update_sizes[source] = num_updates;
+ }
+ break;
+
+ case msg_updates:
+ {
+ updates_size_type num_updates = update_sizes[source];
+ assert(num_updates);
+
+ // Receive the actual updates
+ std::vector<updates_pair_type> updates(num_updates);
+ receive(self->process_group, source, msg_updates, &updates[0],
+ num_updates);
+
+ // Send updates to derived "receive_update" member
+ Derived* derived = static_cast<Derived*>(self);
+ for (updates_size_type u = 0; u < num_updates; ++u)
+ derived->receive_update(source, updates[u].first, updates[u].second);
+
+ update_sizes[source] = 0;
+ }
+ break;
+ };
+ }
+
+ private:
+ remote_update_set* self;
+ std::vector<updates_size_type> update_sizes;
+ };
+ friend struct handle_messages;
+
+ protected:
+ remote_update_set(const ProcessGroup& pg, const OwnerMap& owner)
+ : process_group(pg, handle_messages(this, pg)),
+ updates(num_processes(pg)), owner(owner) {
+ }
+
+
+ void update(const Key& key, const Value& value)
+ {
+ if (get(owner, key) == process_id(process_group)) {
+ Derived* derived = static_cast<Derived*>(this);
+ derived->receive_update(get(owner, key), key, value);
+ }
+ else {
+ updates[get(owner, key)].push_back(std::make_pair(key, value));
+ }
+ }
+
+ void collect() { }
+
+ void synchronize()
+ {
+ // Emit all updates and then remove them
+ process_id_type num_processes = updates.size();
+ for (process_id_type p = 0; p < num_processes; ++p) {
+ if (!updates[p].empty()) {
+ send(process_group, p, msg_num_updates, updates[p].size());
+ send(process_group, p, msg_updates,
+ &updates[p].front(), updates[p].size());
+ updates[p].clear();
+ }
+ }
+
+ do_synchronize(process_group);
+ }
+
+ ProcessGroup process_group;
+
+ private:
+ std::vector<Updates> updates;
+ OwnerMap owner;
+};
+
+/**********************************************************************
+ * Remote updating set that sends messages immediately *
+ **********************************************************************/
+template<typename Derived, typename ProcessGroup, typename Value,
+ typename OwnerMap>
+class remote_update_set<Derived, ProcessGroup, Value, OwnerMap,
+ remote_set_immediate>
+{
+ typedef typename property_traits<OwnerMap>::key_type Key;
+ typedef std::pair<Key, Value> update_pair_type;
+ typedef typename std::vector<update_pair_type>::size_type updates_size_type;
+
+public:
+ typedef typename ProcessGroup::process_id_type process_id_type;
+
+private:
+ enum message_kind {
+ /** Contains a (key, value) pair that will be updated. */
+ msg_update
+ };
+
+ struct handle_messages
+ {
+ explicit handle_messages(remote_update_set* self, const ProcessGroup& pg)
+ : self(self)
+ { update_sizes.resize(num_processes(pg), 0); }
+
+ void operator()(process_id_type source, int tag)
+ {
+ // Receive the # of updates
+ assert(tag == msg_update);
+ update_pair_type update;
+ receive(self->process_group, source, tag, update);
+
+ // Send update to derived "receive_update" member
+ Derived* derived = static_cast<Derived*>(self);
+ derived->receive_update(source, update.first, update.second);
+ }
+
+ private:
+ std::vector<updates_size_type> update_sizes;
+ remote_update_set* self;
+ };
+ friend struct handle_messages;
+
+ protected:
+ remote_update_set(const ProcessGroup& pg, const OwnerMap& owner)
+ : process_group(pg, handle_messages(this, pg)), owner(owner) { }
+
+ void update(const Key& key, const Value& value)
+ {
+ if (get(owner, key) == process_id(process_group)) {
+ Derived* derived = static_cast<Derived*>(this);
+ derived->receive_update(get(owner, key), key, value);
+ }
+ else
+ send(process_group, get(owner, key), msg_update,
+ update_pair_type(key, value));
+ }
+
+ void collect()
+ {
+ typedef std::pair<process_id_type, int> probe_type;
+ handle_messages handler(this, process_group);
+ while (optional<probe_type> stp = probe(process_group))
+ if (stp->second == msg_update) handler(stp->first, stp->second);
+ }
+
+ void synchronize()
+ {
+ do_synchronize(process_group);
+ }
+
+ ProcessGroup process_group;
+ OwnerMap owner;
+};
+
+} } } // end namespace boost::graph::detail
+
+#endif // BOOST_GRAPH_DETAIL_REMOTE_UPDATE_SET_HPP

Added: trunk/boost/graph/distributed/detail/tag_allocator.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/detail/tag_allocator.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,84 @@
+// -*- C++ -*-
+
+// Copyright (C) 2007 Douglas Gregor <doug.gregor_at_[hidden]>
+
+// 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)
+#ifndef BOOST_GRAPH_DISTRIBUTED_TAG_ALLOCATOR_HPP
+#define BOOST_GRAPH_DISTRIBUTED_TAG_ALLOCATOR_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 <vector>
+
+namespace boost { namespace graph { namespace distributed { namespace detail {
+
+/**
+ * \brief The tag allocator allows clients to request unique tags that
+ * can be used for one-time communications.
+ *
+ * The tag allocator hands out tag values from a predefined maximum
+ * (given in the constructor) moving downward. Tags are provided one
+ * at a time via a @c token. When the @c token goes out of scope, the
+ * tag is returned and may be reallocated. These tags should be used,
+ * for example, for one-time communication of values.
+ */
+class tag_allocator {
+public:
+ class token;
+ friend class token;
+
+ /**
+ * Construct a new tag allocator that provides unique tags starting
+ * with the value @p top_tag and moving lower, as necessary.
+ */
+ explicit tag_allocator(int top_tag) : bottom(top_tag) { }
+
+ /**
+ * Retrieve a new tag. The token itself holds onto the tag, which
+ * will be released when the token is destroyed.
+ */
+ token get_tag();
+
+private:
+ int bottom;
+ std::vector<int> freed;
+};
+
+/**
+ * A token used to represent an allocated tag.
+ */
+class tag_allocator::token {
+public:
+ /// Transfer ownership of the tag from @p other.
+ token(const token& other);
+
+ /// De-allocate the tag, if this token still owns it.
+ ~token();
+
+ /// Retrieve the tag allocated for this task.
+ operator int() const { return tag_; }
+
+private:
+ /// Create a token with a specific tag from the given tag_allocator
+ token(tag_allocator* allocator, int tag)
+ : allocator(allocator), tag_(tag) { }
+
+ /// Undefined: tokens are not copy-assignable
+ token& operator=(const token&);
+
+ /// The allocator from which this tag was allocated.
+ tag_allocator* allocator;
+
+ /// The stored tag flag. If -1, this token does not own the tag.
+ mutable int tag_;
+
+ friend class tag_allocator;
+};
+
+} } } } // end namespace boost::graph::distributed::detail
+
+#endif // BOOST_GRAPH_DISTRIBUTED_TAG_ALLOCATOR_HPP

Added: trunk/boost/graph/distributed/dijkstra_shortest_paths.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/dijkstra_shortest_paths.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,205 @@
+// 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
+#ifndef BOOST_GRAPH_PARALLEL_DIJKSTRA_HPP
+#define BOOST_GRAPH_PARALLEL_DIJKSTRA_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/graph/dijkstra_shortest_paths.hpp>
+#include <boost/graph/overloading.hpp>
+#include <boost/graph/distributed/concepts.hpp>
+#include <boost/graph/parallel/properties.hpp>
+#include <boost/graph/distributed/crauser_et_al_shortest_paths.hpp>
+#include <boost/graph/distributed/eager_dijkstra_shortest_paths.hpp>
+
+namespace boost {
+
+ namespace graph { namespace detail {
+
+
+ template<typename Lookahead>
+ struct parallel_dijkstra_impl2
+ {
+ template<typename DistributedGraph, typename DijkstraVisitor,
+ typename PredecessorMap, typename DistanceMap,
+ typename WeightMap, typename IndexMap, typename ColorMap,
+ typename Compare, typename Combine, typename DistInf,
+ typename DistZero>
+ static void
+ run(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)
+ {
+ eager_dijkstra_shortest_paths(g, s, predecessor, distance, lookahead,
+ weight, index_map, color_map, compare,
+ combine, inf, zero, vis);
+ }
+ };
+
+ template<>
+ struct parallel_dijkstra_impl2< ::boost::detail::error_property_not_found >
+ {
+ template<typename DistributedGraph, typename DijkstraVisitor,
+ typename PredecessorMap, typename DistanceMap,
+ typename WeightMap, typename IndexMap, typename ColorMap,
+ typename Compare, typename Combine, typename DistInf,
+ typename DistZero>
+ static void
+ run(const DistributedGraph& g,
+ typename graph_traits<DistributedGraph>::vertex_descriptor s,
+ PredecessorMap predecessor, DistanceMap distance,
+ ::boost::detail::error_property_not_found,
+ WeightMap weight, IndexMap index_map, ColorMap color_map,
+ Compare compare, Combine combine, DistInf inf, DistZero zero,
+ DijkstraVisitor vis)
+ {
+ crauser_et_al_shortest_paths(g, s, predecessor, distance, weight,
+ index_map, color_map, compare, combine,
+ inf, zero, vis);
+ }
+ };
+
+ template<typename ColorMap>
+ struct parallel_dijkstra_impl
+ {
+ template<typename DistributedGraph, typename DijkstraVisitor,
+ typename PredecessorMap, typename DistanceMap,
+ typename Lookahead, typename WeightMap, typename IndexMap,
+ typename Compare, typename Combine,
+ typename DistInf, typename DistZero>
+ static void
+ run(const DistributedGraph& g,
+ typename graph_traits<DistributedGraph>::vertex_descriptor s,
+ PredecessorMap predecessor, DistanceMap distance,
+ Lookahead lookahead,
+ WeightMap weight, IndexMap index_map, ColorMap color_map,
+ Compare compare, Combine combine, DistInf inf, DistZero zero,
+ DijkstraVisitor vis)
+ {
+ graph::detail::parallel_dijkstra_impl2<Lookahead>
+ ::run(g, s, predecessor, distance, lookahead, weight, index_map,
+ color_map, compare, combine, inf, zero, vis);
+ }
+ };
+
+ template<>
+ struct parallel_dijkstra_impl< ::boost::detail::error_property_not_found >
+ {
+ private:
+ template<typename DistributedGraph, typename DijkstraVisitor,
+ typename PredecessorMap, typename DistanceMap,
+ typename Lookahead, typename WeightMap, typename IndexMap,
+ typename ColorMap, typename Compare, typename Combine,
+ typename DistInf, typename DistZero>
+ static void
+ run_impl(const DistributedGraph& g,
+ typename graph_traits<DistributedGraph>::vertex_descriptor s,
+ PredecessorMap predecessor, DistanceMap distance,
+ Lookahead lookahead, WeightMap weight, IndexMap index_map,
+ ColorMap color_map, Compare compare, Combine combine,
+ DistInf inf, DistZero zero, DijkstraVisitor vis)
+ {
+ BGL_FORALL_VERTICES_T(u, g, DistributedGraph)
+ BGL_FORALL_OUTEDGES_T(u, e, g, DistributedGraph)
+ local_put(color_map, target(e, g), white_color);
+
+ graph::detail::parallel_dijkstra_impl2<Lookahead>
+ ::run(g, s, predecessor, distance, lookahead, weight, index_map,
+ color_map, compare, combine, inf, zero, vis);
+ }
+
+ public:
+ template<typename DistributedGraph, typename DijkstraVisitor,
+ typename PredecessorMap, typename DistanceMap,
+ typename Lookahead, typename WeightMap, typename IndexMap,
+ typename Compare, typename Combine,
+ typename DistInf, typename DistZero>
+ static void
+ run(const DistributedGraph& g,
+ typename graph_traits<DistributedGraph>::vertex_descriptor s,
+ PredecessorMap predecessor, DistanceMap distance,
+ Lookahead lookahead, WeightMap weight, IndexMap index_map,
+ ::boost::detail::error_property_not_found,
+ Compare compare, Combine combine, DistInf inf, DistZero zero,
+ DijkstraVisitor vis)
+ {
+ typedef typename graph_traits<DistributedGraph>::vertices_size_type
+ vertices_size_type;
+
+ vertices_size_type n = num_vertices(g);
+ std::vector<default_color_type> colors(n, white_color);
+
+ run_impl(g, s, predecessor, distance, lookahead, weight, index_map,
+ make_iterator_property_map(colors.begin(), index_map),
+ compare, combine, inf, zero, vis);
+ }
+ };
+ } } // end namespace graph::detail
+
+
+ /** Dijkstra's single-source shortest paths algorithm for distributed
+ * graphs.
+ *
+ * Also implements the heuristics of:
+ *
+ * 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.
+ */
+ template<typename DistributedGraph, typename DijkstraVisitor,
+ typename PredecessorMap, typename DistanceMap,
+ typename WeightMap, typename IndexMap, typename Compare,
+ typename Combine, typename DistInf, typename DistZero,
+ typename T, typename Tag, typename Base>
+ inline
+ void
+ dijkstra_shortest_paths
+ (const DistributedGraph& g,
+ typename graph_traits<DistributedGraph>::vertex_descriptor s,
+ PredecessorMap predecessor, DistanceMap distance, WeightMap weight,
+ IndexMap index_map,
+ Compare compare, Combine combine, DistInf inf, DistZero zero,
+ DijkstraVisitor vis,
+ const bgl_named_params<T, Tag, Base>& params
+ BOOST_GRAPH_ENABLE_IF_MODELS_PARM(DistributedGraph,distributed_graph_tag))
+ {
+ typedef typename graph_traits<DistributedGraph>::vertices_size_type
+ vertices_size_type;
+
+ // Build a distributed property map for vertex colors, if we need it
+ bool use_default_color_map
+ = is_default_param(get_param(params, vertex_color));
+ vertices_size_type n = use_default_color_map? num_vertices(g) : 1;
+ std::vector<default_color_type> color(n, white_color);
+ typedef iterator_property_map<std::vector<default_color_type>::iterator,
+ IndexMap> DefColorMap;
+ DefColorMap color_map(color.begin(), index_map);
+
+ typedef typename property_value< bgl_named_params<T, Tag, Base>,
+ vertex_color_t>::type color_map_type;
+
+ graph::detail::parallel_dijkstra_impl<color_map_type>
+ ::run(g, s, predecessor, distance,
+ get_param(params, lookahead_t()),
+ weight, index_map,
+ get_param(params, vertex_color),
+ compare, combine, inf, zero, vis);
+ }
+} // end namespace boost
+
+#endif // BOOST_GRAPH_PARALLEL_DIJKSTRA_HPP

Added: trunk/boost/graph/distributed/distributed_graph_utility.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/distributed_graph_utility.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,154 @@
+// Copyright (C) 2005-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: Peter Gottschling
+// Douglas Gregor
+// Andrew Lumsdaine
+
+#include <boost/graph/iteration_macros.hpp>
+#include <boost/property_map/parallel/global_index_map.hpp>
+
+#ifndef BOOST_GRAPH_DISTRIBUTED_GRAPH_UTILITY_INCLUDE
+#define BOOST_GRAPH_DISTRIBUTED_GRAPH_UTILITY_INCLUDE
+
+#ifndef BOOST_GRAPH_USE_MPI
+#error "Parallel BGL files should not be included unless <boost/graph/use_mpi.hpp> has been included"
+#endif
+
+namespace boost { namespace graph {
+
+ template <class Property, class Graph>
+ void property_on_inedges(Property p, const Graph& g)
+ {
+ BGL_FORALL_VERTICES_T(u, g, Graph)
+ BGL_FORALL_INEDGES_T(u, e, g, Graph)
+ request(p, e);
+ synchronize(p);
+ }
+
+ // For reverse graphs
+ template <class Property, class Graph>
+ void property_on_outedges(Property p, const Graph& g)
+ {
+ BGL_FORALL_VERTICES_T(u, g, Graph)
+ BGL_FORALL_OUTEDGES_T(u, e, g, Graph)
+ request(p, e);
+ synchronize(p);
+ }
+
+ template <class Property, class Graph>
+ void property_on_successors(Property p, const Graph& g)
+ {
+ BGL_FORALL_VERTICES_T(u, g, Graph)
+ BGL_FORALL_OUTEDGES_T(u, e, g, Graph)
+ request(p, target(e, g));
+ synchronize(p);
+ }
+
+ template <class Property, class Graph>
+ void property_on_predecessors(Property p, const Graph& g)
+ {
+ BGL_FORALL_VERTICES_T(u, g, Graph)
+ BGL_FORALL_INEDGES_T(u, e, g, Graph)
+ request(p, source(e, g));
+ synchronize(p);
+ }
+
+ // Like successors and predecessors but saves one synchronize (and a call)
+ template <class Property, class Graph>
+ void property_on_adjacents(Property p, const Graph& g)
+ {
+ BGL_FORALL_VERTICES_T(u, g, Graph) {
+ BGL_FORALL_OUTEDGES_T(u, e, g, Graph)
+ request(p, target(e, g));
+ BGL_FORALL_INEDGES_T(u, e, g, Graph)
+ request(p, source(e, g));
+ }
+ synchronize(p);
+ }
+
+ template <class PropertyIn, class PropertyOut, class Graph>
+ void copy_vertex_property(PropertyIn p_in, PropertyOut p_out, Graph& g)
+ {
+ BGL_FORALL_VERTICES_T(u, g, Graph)
+ put(p_out, u, get(p_in, g));
+ }
+
+ template <class PropertyIn, class PropertyOut, class Graph>
+ void copy_edge_property(PropertyIn p_in, PropertyOut p_out, Graph& g)
+ {
+ BGL_FORALL_EDGES_T(e, g, Graph)
+ put(p_out, e, get(p_in, g));
+ }
+
+
+ namespace distributed {
+
+ // Define global_index<Graph> global(graph);
+ // Then global(v) returns global index of v
+ template <typename Graph>
+ struct global_index
+ {
+ typedef typename property_map<Graph, vertex_index_t>::const_type
+ VertexIndexMap;
+ typedef typename property_map<Graph, vertex_global_t>::const_type
+ VertexGlobalMap;
+
+ explicit global_index(Graph const& g)
+ : global_index_map(process_group(g), num_vertices(g), get(vertex_index, g),
+ get(vertex_global, g)) {}
+
+ int operator() (typename graph_traits<Graph>::vertex_descriptor v)
+ { return get(global_index_map, v); }
+
+ protected:
+ boost::parallel::global_index_map<VertexIndexMap, VertexGlobalMap>
+ global_index_map;
+ };
+
+ template<typename T>
+ struct additive_reducer {
+ BOOST_STATIC_CONSTANT(bool, non_default_resolver = true);
+
+ template<typename K>
+ T operator()(const K&) const { return T(0); }
+
+ template<typename K>
+ T operator()(const K&, const T& local, const T& remote) const { return local + remote; }
+ };
+
+ template <typename T>
+ struct choose_min_reducer {
+ BOOST_STATIC_CONSTANT(bool, non_default_resolver = true);
+
+ template<typename K>
+ T operator()(const K&) const { return (std::numeric_limits<T>::max)(); }
+
+ template<typename K>
+ T operator()(const K&, const T& x, const T& y) const
+ { return x < y ? x : y; }
+ };
+
+ // To use a property map syntactically like a function
+ template <typename PropertyMap>
+ struct property_map_reader
+ {
+ explicit property_map_reader(PropertyMap pm) : pm(pm) {}
+
+ template <typename T>
+ typename PropertyMap::value_type
+ operator() (const T& v)
+ {
+ return get(pm, v);
+ }
+ private:
+ PropertyMap pm;
+ };
+
+ } // namespace distributed
+
+}} // namespace boost::graph
+
+#endif // BOOST_GRAPH_DISTRIBUTED_GRAPH_UTILITY_INCLUDE

Added: trunk/boost/graph/distributed/eager_dijkstra_shortest_paths.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/eager_dijkstra_shortest_paths.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,446 @@
+// 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/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>
+
+#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;
+ 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)
+{
+ typedef typename boost::graph::parallel::process_group_type<DistributedGraph>::type
+ process_group_type;
+ typedef typename graph_traits<DistributedGraph>::vertex_descriptor
+ Vertex;
+ typedef typename graph_traits<DistributedGraph>::vertices_size_type
+ vertices_size_type;
+
+#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 (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

Added: trunk/boost/graph/distributed/filtered_graph.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/filtered_graph.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,51 @@
+// Copyright (C) 2004-2008 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: Nick Edmonds
+// Douglas Gregor
+// Andrew Lumsdaine
+#ifndef BOOST_DISTRIBUTED_FILTERED_GRAPH_HPP
+#define BOOST_DISTRIBUTED_FILTERED_GRAPH_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/graph/parallel/process_group.hpp>
+#include <boost/graph/filtered_graph.hpp>
+
+namespace boost {
+ namespace graph {
+ namespace parallel {
+ /// Retrieve the process group from a filtered graph
+ template<typename Graph, typename EdgePredicate, typename VertexPredicate>
+ struct process_group_type<filtered_graph<Graph, EdgePredicate, VertexPredicate> >
+ : process_group_type<Graph> { };
+
+ template<typename Graph, typename EdgePredicate, typename VertexPredicate>
+ struct process_group_type<const filtered_graph<Graph, EdgePredicate, VertexPredicate> >
+ : process_group_type<Graph> { };
+ }
+
+ }
+
+ /// Retrieve the process group from a filtered graph
+ template<typename Graph, typename EdgePredicate, typename VertexPredicate>
+ inline typename graph::parallel::process_group_type<Graph>::type
+ process_group(filtered_graph<Graph, EdgePredicate, VertexPredicate> const& g) {
+ return process_group(g.m_g);
+ }
+
+ /// Forward vertex() to vertex() of the base graph
+ template <typename Graph, typename EdgePredicate, typename VertexPredicate>
+ typename graph_traits<Graph>::vertex_descriptor
+ vertex(typename graph_traits<Graph>::vertices_size_type i,
+ filtered_graph<Graph, EdgePredicate, VertexPredicate> const& g)
+ { return vertex(i, g.m_g); }
+
+}
+
+#endif // BOOST_DISTRIBUTED_FILTERED_GRAPH_HPP

Added: trunk/boost/graph/distributed/fruchterman_reingold.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/fruchterman_reingold.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,384 @@
+// Copyright (C) 2005-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
+#ifndef BOOST_GRAPH_DISTRIBUTED_FRUCHTERMAN_REINGOLD_HPP
+#define BOOST_GRAPH_DISTRIBUTED_FRUCHTERMAN_REINGOLD_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/graph/fruchterman_reingold.hpp>
+
+namespace boost { namespace graph { namespace distributed {
+
+class simple_tiling
+{
+ public:
+ simple_tiling(int columns, int rows, bool flip = true)
+ : columns(columns), rows(rows), flip(flip)
+ {
+ }
+
+ // Convert from a position (x, y) in the tiled display into a
+ // processor ID number
+ int operator()(int x, int y) const
+ {
+ return flip? (rows - y - 1) * columns + x : y * columns + x;
+ }
+
+ // Convert from a process ID to a position (x, y) in the tiled
+ // display
+ std::pair<int, int> operator()(int id)
+ {
+ int my_col = id % columns;
+ int my_row = flip? rows - (id / columns) - 1 : id / columns;
+ return std::make_pair(my_col, my_row);
+ }
+
+ int columns, rows;
+
+ private:
+ bool flip;
+};
+
+// Force pairs function object that does nothing
+struct no_force_pairs
+{
+ template<typename Graph, typename ApplyForce>
+ void operator()(const Graph&, const ApplyForce&)
+ {
+ }
+};
+
+// Computes force pairs in the distributed case.
+template<typename PositionMap, typename DisplacementMap, typename LocalForces,
+ typename NonLocalForces = no_force_pairs>
+class distributed_force_pairs_proxy
+{
+ public:
+ distributed_force_pairs_proxy(const PositionMap& position,
+ const DisplacementMap& displacement,
+ const LocalForces& local_forces,
+ const NonLocalForces& nonlocal_forces = NonLocalForces())
+ : position(position), displacement(displacement),
+ local_forces(local_forces), nonlocal_forces(nonlocal_forces)
+ {
+ }
+
+ template<typename Graph, typename ApplyForce>
+ void operator()(const Graph& g, ApplyForce apply_force)
+ {
+ // Flush remote displacements
+ displacement.flush();
+
+ // Receive updated positions for all of our neighbors
+ synchronize(position);
+
+ // Reset remote displacements
+ displacement.reset();
+
+ // Compute local repulsive forces
+ local_forces(g, apply_force);
+
+ // Compute neighbor repulsive forces
+ nonlocal_forces(g, apply_force);
+ }
+
+ protected:
+ PositionMap position;
+ DisplacementMap displacement;
+ LocalForces local_forces;
+ NonLocalForces nonlocal_forces;
+};
+
+template<typename PositionMap, typename DisplacementMap, typename LocalForces>
+inline
+distributed_force_pairs_proxy<PositionMap, DisplacementMap, LocalForces>
+make_distributed_force_pairs(const PositionMap& position,
+ const DisplacementMap& displacement,
+ const LocalForces& local_forces)
+{
+ typedef
+ distributed_force_pairs_proxy<PositionMap, DisplacementMap, LocalForces>
+ result_type;
+ return result_type(position, displacement, local_forces);
+}
+
+template<typename PositionMap, typename DisplacementMap, typename LocalForces,
+ typename NonLocalForces>
+inline
+distributed_force_pairs_proxy<PositionMap, DisplacementMap, LocalForces,
+ NonLocalForces>
+make_distributed_force_pairs(const PositionMap& position,
+ const DisplacementMap& displacement,
+ const LocalForces& local_forces,
+ const NonLocalForces& nonlocal_forces)
+{
+ typedef
+ distributed_force_pairs_proxy<PositionMap, DisplacementMap, LocalForces,
+ NonLocalForces>
+ result_type;
+ return result_type(position, displacement, local_forces, nonlocal_forces);
+}
+
+// Compute nonlocal force pairs based on the shared borders with
+// adjacent tiles.
+template<typename PositionMap>
+class neighboring_tiles_force_pairs
+{
+ public:
+ typedef typename property_traits<PositionMap>::value_type Point;
+ typedef typename point_traits<Point>::component_type Dim;
+
+ enum bucket_position { left, top, right, bottom, end_position };
+
+ neighboring_tiles_force_pairs(PositionMap position, Point origin,
+ Point extent, simple_tiling tiling)
+ : position(position), origin(origin), extent(extent), tiling(tiling)
+ {
+ }
+
+ template<typename Graph, typename ApplyForce>
+ void operator()(const Graph& g, ApplyForce apply_force)
+ {
+ // TBD: Do this some smarter way
+ if (tiling.columns == 1 && tiling.rows == 1)
+ return;
+
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+#ifndef BOOST_NO_STDC_NAMESPACE
+ using std::sqrt;
+#endif // BOOST_NO_STDC_NAMESPACE
+
+ // TBD: num_vertices(g) should be the global number of vertices?
+ Dim two_k = Dim(2) * sqrt(extent[0] * extent[1] / num_vertices(g));
+
+ std::vector<vertex_descriptor> my_vertices[4];
+ std::vector<vertex_descriptor> neighbor_vertices[4];
+
+ // Compute cutoff positions
+ Dim cutoffs[4];
+ cutoffs[left] = origin[0] + two_k;
+ cutoffs[top] = origin[1] + two_k;
+ cutoffs[right] = origin[0] + extent[0] - two_k;
+ cutoffs[bottom] = origin[1] + extent[1] - two_k;
+
+ // Compute neighbors
+ typename PositionMap::process_group_type pg = position.process_group();
+ std::pair<int, int> my_tile = tiling(process_id(pg));
+ int neighbors[4] = { -1, -1, -1, -1 } ;
+ if (my_tile.first > 0)
+ neighbors[left] = tiling(my_tile.first - 1, my_tile.second);
+ if (my_tile.second > 0)
+ neighbors[top] = tiling(my_tile.first, my_tile.second - 1);
+ if (my_tile.first < tiling.columns - 1)
+ neighbors[right] = tiling(my_tile.first + 1, my_tile.second);
+ if (my_tile.second < tiling.rows - 1)
+ neighbors[bottom] = tiling(my_tile.first, my_tile.second + 1);
+
+ // Sort vertices along the edges into buckets
+ BGL_FORALL_VERTICES_T(v, g, Graph) {
+ if (position[v][0] <= cutoffs[left]) my_vertices[left].push_back(v);
+ if (position[v][1] <= cutoffs[top]) my_vertices[top].push_back(v);
+ if (position[v][0] >= cutoffs[right]) my_vertices[right].push_back(v);
+ if (position[v][1] >= cutoffs[bottom]) my_vertices[bottom].push_back(v);
+ }
+
+ // Send vertices to neighbors, and gather our neighbors' vertices
+ bucket_position pos;
+ for (pos = left; pos < end_position; pos = bucket_position(pos + 1)) {
+ if (neighbors[pos] != -1) {
+ send(pg, neighbors[pos], 0, my_vertices[pos].size());
+ if (!my_vertices[pos].empty())
+ send(pg, neighbors[pos], 1,
+ &my_vertices[pos].front(), my_vertices[pos].size());
+ }
+ }
+
+ // Pass messages around
+ synchronize(pg);
+
+ // Receive neighboring vertices
+ for (pos = left; pos < end_position; pos = bucket_position(pos + 1)) {
+ if (neighbors[pos] != -1) {
+ std::size_t incoming_vertices;
+ receive(pg, neighbors[pos], 0, incoming_vertices);
+
+ if (incoming_vertices) {
+ neighbor_vertices[pos].resize(incoming_vertices);
+ receive(pg, neighbors[pos], 1, &neighbor_vertices[pos].front(),
+ incoming_vertices);
+ }
+ }
+ }
+
+ // For each neighboring vertex, we need to get its current position
+ for (pos = left; pos < end_position; pos = bucket_position(pos + 1))
+ for (typename std::vector<vertex_descriptor>::iterator i =
+ neighbor_vertices[pos].begin();
+ i != neighbor_vertices[pos].end();
+ ++i)
+ request(position, *i);
+ synchronize(position);
+
+ // Apply forces in adjacent bins. This is O(n^2) in the worst
+ // case. Oh well.
+ for (pos = left; pos < end_position; pos = bucket_position(pos + 1)) {
+ for (typename std::vector<vertex_descriptor>::iterator i =
+ my_vertices[pos].begin();
+ i != my_vertices[pos].end();
+ ++i)
+ for (typename std::vector<vertex_descriptor>::iterator j =
+ neighbor_vertices[pos].begin();
+ j != neighbor_vertices[pos].end();
+ ++j)
+ apply_force(*i, *j);
+ }
+ }
+
+ protected:
+ PositionMap position;
+ Point origin;
+ Point extent;
+ simple_tiling tiling;
+};
+
+template<typename PositionMap>
+inline neighboring_tiles_force_pairs<PositionMap>
+make_neighboring_tiles_force_pairs
+ (PositionMap position,
+ typename property_traits<PositionMap>::value_type origin,
+ typename property_traits<PositionMap>::value_type extent,
+ simple_tiling tiling)
+{
+ return neighboring_tiles_force_pairs<PositionMap>(position, origin, extent,
+ tiling);
+}
+
+template<typename DisplacementMap, typename Cooling>
+class distributed_cooling_proxy
+{
+ public:
+ typedef typename Cooling::result_type result_type;
+
+ distributed_cooling_proxy(const DisplacementMap& displacement,
+ const Cooling& cooling)
+ : displacement(displacement), cooling(cooling)
+ {
+ }
+
+ result_type operator()()
+ {
+ // Accumulate displacements computed on each processor
+ synchronize(displacement.data->process_group);
+
+ // Allow the underlying cooling to occur
+ return cooling();
+ }
+
+ protected:
+ DisplacementMap displacement;
+ Cooling cooling;
+};
+
+template<typename DisplacementMap, typename Cooling>
+inline distributed_cooling_proxy<DisplacementMap, Cooling>
+make_distributed_cooling(const DisplacementMap& displacement,
+ const Cooling& cooling)
+{
+ typedef distributed_cooling_proxy<DisplacementMap, Cooling> result_type;
+ return result_type(displacement, cooling);
+}
+
+template<typename Point>
+struct point_accumulating_reducer {
+ BOOST_STATIC_CONSTANT(bool, non_default_resolver = true);
+
+ template<typename K>
+ Point operator()(const K&) const { return Point(); }
+
+ template<typename K>
+ Point operator()(const K&, const Point& p1, const Point& p2) const
+ { return Point(p1[0] + p2[0], p1[1] + p2[1]); }
+};
+
+template<typename Graph, typename PositionMap,
+ typename AttractiveForce, typename RepulsiveForce,
+ typename ForcePairs, typename Cooling, typename DisplacementMap>
+void
+fruchterman_reingold_force_directed_layout
+ (const Graph& g,
+ PositionMap position,
+ typename property_traits<PositionMap>::value_type const& origin,
+ typename property_traits<PositionMap>::value_type const& extent,
+ AttractiveForce attractive_force,
+ RepulsiveForce repulsive_force,
+ ForcePairs force_pairs,
+ Cooling cool,
+ DisplacementMap displacement)
+{
+ typedef typename property_traits<PositionMap>::value_type Point;
+
+ // Reduction in the displacement map involves summing the forces
+ displacement.set_reduce(point_accumulating_reducer<Point>());
+
+ // We need to track the positions of all of our neighbors
+ BGL_FORALL_VERTICES_T(u, g, Graph)
+ BGL_FORALL_ADJ_T(u, v, g, Graph)
+ request(position, v);
+
+ // Invoke the "sequential" Fruchterman-Reingold implementation
+ boost::fruchterman_reingold_force_directed_layout
+ (g, position, origin, extent,
+ attractive_force, repulsive_force,
+ make_distributed_force_pairs(position, displacement, force_pairs),
+ make_distributed_cooling(displacement, cool),
+ displacement);
+}
+
+template<typename Graph, typename PositionMap,
+ typename AttractiveForce, typename RepulsiveForce,
+ typename ForcePairs, typename Cooling, typename DisplacementMap>
+void
+fruchterman_reingold_force_directed_layout
+ (const Graph& g,
+ PositionMap position,
+ typename property_traits<PositionMap>::value_type const& origin,
+ typename property_traits<PositionMap>::value_type const& extent,
+ AttractiveForce attractive_force,
+ RepulsiveForce repulsive_force,
+ ForcePairs force_pairs,
+ Cooling cool,
+ DisplacementMap displacement,
+ simple_tiling tiling)
+{
+ typedef typename property_traits<PositionMap>::value_type Point;
+
+ // Reduction in the displacement map involves summing the forces
+ displacement.set_reduce(point_accumulating_reducer<Point>());
+
+ // We need to track the positions of all of our neighbors
+ BGL_FORALL_VERTICES_T(u, g, Graph)
+ BGL_FORALL_ADJ_T(u, v, g, Graph)
+ request(position, v);
+
+ // Invoke the "sequential" Fruchterman-Reingold implementation
+ boost::fruchterman_reingold_force_directed_layout
+ (g, position, origin, extent,
+ attractive_force, repulsive_force,
+ make_distributed_force_pairs
+ (position, displacement, force_pairs,
+ make_neighboring_tiles_force_pairs(position, origin, extent, tiling)),
+ make_distributed_cooling(displacement, cool),
+ displacement);
+}
+
+} } } // end namespace boost::graph::distributed
+
+#endif // BOOST_GRAPH_DISTRIBUTED_FRUCHTERMAN_REINGOLD_HPP

Added: trunk/boost/graph/distributed/graphviz.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/graphviz.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,294 @@
+// 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
+#ifndef BOOST_GRAPH_PARALLEL_GRAPHVIZ_HPP
+#define BOOST_GRAPH_PARALLEL_GRAPHVIZ_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/graph/graph_traits.hpp>
+#include <boost/graph/distributed/concepts.hpp>
+#include <boost/property_map/property_map.hpp>
+#include <boost/graph/graphviz.hpp>
+#include <boost/type_traits/is_base_and_derived.hpp>
+#include <boost/type_traits/is_same.hpp>
+#include <fstream>
+#include <sstream>
+#include <iostream>
+#include <string>
+#include <boost/graph/parallel/container_traits.hpp>
+#include <boost/graph/parallel/process_group.hpp>
+#include <boost/property_map/parallel/global_index_map.hpp>
+
+namespace boost {
+
+template<typename Graph>
+struct graph_id_writer
+{
+ explicit graph_id_writer(const Graph& g) : g(g) { }
+
+ void operator()(std::ostream& out)
+ {
+ out << " label=\"p" << process_id(g.process_group()) << "\";\n";
+ }
+
+ private:
+ const Graph& g;
+};
+
+template<typename NumberMap>
+struct paint_by_number_writer
+{
+ explicit paint_by_number_writer(NumberMap number) : number(number) { }
+
+ template<typename Descriptor>
+ void operator()(std::ostream& out, Descriptor k)
+ {
+ static const char* color_names[] = {
+ "blue",
+ "brown",
+ "cyan",
+ "darkgreen",
+ "darkorchid",
+ "darksalmon",
+ "darkviolet",
+ "deeppink",
+ "gold3",
+ "green",
+ "magenta",
+ "navy",
+ "red",
+ "yellow",
+ "palegreen",
+ "gray65",
+ "gray21",
+ "bisque2",
+ "greenyellow",
+ "indianred4",
+ "lightblue2",
+ "mediumspringgreen",
+ "orangered",
+ "orange"
+ };
+ const int colors = sizeof(color_names) / sizeof(color_names[0]);
+ if (get(number, k) < colors) {
+ out << " [ style=\"filled\", fillcolor=\"" << color_names[get(number, k)]
+ << "\" ]";
+ } else {
+ out << " [ label=\"(" << get(number, k) << ")\" ]";
+ }
+ }
+
+ private:
+ NumberMap number;
+};
+
+template<typename NumberMap>
+inline paint_by_number_writer<NumberMap>
+paint_by_number(NumberMap number)
+{ return paint_by_number_writer<NumberMap>(number); }
+
+template<typename Graph, typename VertexPropertiesWriter,
+ typename EdgePropertiesWriter, typename GraphPropertiesWriter>
+void
+write_graphviz(std::ostream& out,
+ const Graph& g,
+ VertexPropertiesWriter vpw,
+ EdgePropertiesWriter epw,
+ GraphPropertiesWriter gpw
+ BOOST_GRAPH_ENABLE_IF_MODELS_PARM(Graph,distributed_graph_tag))
+{
+ typedef typename graph_traits<Graph>::directed_category directed_category;
+ typedef typename graph_traits<Graph>::vertices_size_type vertices_size_type;
+ typedef typename boost::graph::parallel::process_group_type<Graph>::type
+ process_group_type;
+ typedef typename process_group_type::process_id_type process_id_type;
+ typedef typename property_map<Graph, vertex_index_t>::const_type
+ VertexIndexMap;
+ typedef typename property_map<Graph, vertex_global_t>::const_type
+ VertexGlobalMap;
+
+ static const bool is_undirected
+ = (is_base_and_derived<undirected_tag, directed_category>::value
+ || is_same<undirected_tag, directed_category>::value);
+ static const char* graph_kind = is_undirected? "graph" : "digraph";
+ static const char* edge_kind = is_undirected? "--" : "->";
+
+ using boost::graph::parallel::process_group;
+ process_group_type pg = process_group(g);
+
+ parallel::global_index_map<VertexIndexMap, VertexGlobalMap>
+ global_index(pg, num_vertices(g), get(vertex_index, g),
+ get(vertex_global, g));
+
+ std::ostringstream local_graph_out;
+
+ local_graph_out << " subgraph cluster_" << process_id(pg) << " {\n";
+ gpw(local_graph_out);
+
+ typename graph_traits<Graph>::vertex_iterator vi, vi_end;
+ for (tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) {
+
+ int global_idx = get(global_index, *vi);
+ local_graph_out << " n" << global_idx;
+ vpw(local_graph_out, *vi);
+ local_graph_out << ";\n";
+ }
+ local_graph_out << " }\n\n";
+
+
+ typename graph_traits<Graph>::edge_iterator ei, ei_end;
+ for (tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) {
+ int source_idx = get(global_index, source(*ei, g));
+ int target_idx = get(global_index, target(*ei, g));
+ local_graph_out << " n" << source_idx << " " << edge_kind << " n"
+ << target_idx;
+ epw(local_graph_out, *ei);
+ local_graph_out << ";\n";
+ }
+
+ if (process_id(pg) == 0) {
+ out << graph_kind << " g {\n";
+ out << local_graph_out.str();
+
+ synchronize(pg);
+ for (int i = 1; i < num_processes(pg); ++i) {
+ int len;
+ receive(pg, i, 0, len);
+ char* data = new char [len+1];
+ data[len] = 0;
+ receive(pg, i, 1, data, len);
+ out << std::endl << data;
+ delete [] data;
+ }
+ out << "}\n";
+ } else {
+ std::string result_str = local_graph_out.str();
+ const char* data = result_str.c_str();
+
+ int len = result_str.length();
+ send(pg, 0, 0, len);
+ send(pg, 0, 1, data, len);
+ synchronize(pg);
+ }
+ synchronize(pg);
+ synchronize(pg);
+ synchronize(pg);
+}
+
+template<typename Graph, typename VertexPropertiesWriter,
+ typename EdgePropertiesWriter>
+inline void
+write_graphviz(std::ostream& out,
+ const Graph& g,
+ VertexPropertiesWriter vpw,
+ EdgePropertiesWriter epw
+ BOOST_GRAPH_ENABLE_IF_MODELS_PARM(Graph,distributed_graph_tag))
+{
+ write_graphviz(out, g, vpw, epw, graph_id_writer<Graph>(g));
+}
+
+template<typename Graph, typename VertexPropertiesWriter>
+inline void
+write_graphviz(std::ostream& out,
+ const Graph& g,
+ VertexPropertiesWriter vpw
+ BOOST_GRAPH_ENABLE_IF_MODELS_PARM(Graph,distributed_graph_tag))
+{
+ write_graphviz(out, g, vpw, default_writer());
+}
+
+template<typename Graph>
+inline void
+write_graphviz(std::ostream& out, const Graph& g
+ BOOST_GRAPH_ENABLE_IF_MODELS_PARM(Graph,distributed_graph_tag))
+{
+ write_graphviz(out, g, default_writer());
+}
+
+template<typename Graph, typename VertexPropertiesWriter,
+ typename EdgePropertiesWriter, typename GraphPropertiesWriter>
+void
+write_graphviz(const std::string& filename,
+ const Graph& g,
+ VertexPropertiesWriter vpw,
+ EdgePropertiesWriter epw,
+ GraphPropertiesWriter gpw
+ BOOST_GRAPH_ENABLE_IF_MODELS_PARM(Graph,distributed_graph_tag))
+{
+ if (process_id(g.process_group()) == 0) {
+ std::ofstream out(filename.c_str());
+ write_graphviz(out, g, vpw, epw, gpw);
+ } else {
+ write_graphviz(std::cout, g, vpw, epw, gpw);
+ }
+}
+
+template<typename Graph, typename VertexPropertiesWriter,
+ typename EdgePropertiesWriter>
+void
+write_graphviz(const std::string& filename,
+ const Graph& g,
+ VertexPropertiesWriter vpw,
+ EdgePropertiesWriter epw
+ BOOST_GRAPH_ENABLE_IF_MODELS_PARM(Graph,distributed_graph_tag))
+{
+ if (process_id(g.process_group()) == 0) {
+ std::ofstream out(filename.c_str());
+ write_graphviz(out, g, vpw, epw);
+ } else {
+ write_graphviz(std::cout, g, vpw, epw);
+ }
+}
+
+template<typename Graph, typename VertexPropertiesWriter>
+void
+write_graphviz(const std::string& filename,
+ const Graph& g,
+ VertexPropertiesWriter vpw
+ BOOST_GRAPH_ENABLE_IF_MODELS_PARM(Graph,distributed_graph_tag))
+{
+ if (process_id(g.process_group()) == 0) {
+ std::ofstream out(filename.c_str());
+ write_graphviz(out, g, vpw);
+ } else {
+ write_graphviz(std::cout, g, vpw);
+ }
+}
+
+template<typename Graph>
+void
+write_graphviz(const std::string& filename, const Graph& g
+ BOOST_GRAPH_ENABLE_IF_MODELS_PARM(Graph,distributed_graph_tag))
+{
+ if (process_id(g.process_group()) == 0) {
+ std::ofstream out(filename.c_str());
+ write_graphviz(out, g);
+ } else {
+ write_graphviz(std::cout, g);
+ }
+}
+
+template<typename Graph>
+void
+write_graphviz(std::ostream& out, const Graph& g,
+ const dynamic_properties& dp,
+ const std::string& node_id = "node_id"
+ BOOST_GRAPH_ENABLE_IF_MODELS_PARM(Graph,distributed_graph_tag))
+{
+ write_graphviz
+ (out, g,
+ /*vertex_writer=*/dynamic_vertex_properties_writer(dp, node_id),
+ /*edge_writer=*/dynamic_properties_writer(dp));
+}
+
+} // end namespace boost
+
+#endif // BOOST_GRAPH_PARALLEL_GRAPHVIZ_HPP

Added: trunk/boost/graph/distributed/hohberg_biconnected_components.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/hohberg_biconnected_components.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,1129 @@
+// Copyright 2005 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
+
+// An implementation of Walter Hohberg's distributed biconnected
+// components algorithm, from:
+//
+// Walter Hohberg. How to Find Biconnected Components in Distributed
+// Networks. J. Parallel Distrib. Comput., 9(4):374-386, 1990.
+//
+#ifndef BOOST_GRAPH_DISTRIBUTED_HOHBERG_BICONNECTED_COMPONENTS_HPP
+#define BOOST_GRAPH_DISTRIBUTED_HOHBERG_BICONNECTED_COMPONENTS_HPP
+
+#ifndef BOOST_GRAPH_USE_MPI
+#error "Parallel BGL files should not be included unless <boost/graph/use_mpi.hpp> has been included"
+#endif
+
+/* You can define PBGL_HOHBERG_DEBUG to an integer value (1, 2, or 3)
+ * to enable debugging information. 1 includes only the phases of the
+ * algorithm and messages as their are received. 2 and 3 add
+ * additional levels of detail about internal data structures related
+ * to the algorithm itself.
+ *
+ * #define PBGL_HOHBERG_DEBUG 1
+*/
+
+#include <boost/graph/graph_traits.hpp>
+#include <boost/graph/parallel/container_traits.hpp>
+#include <boost/graph/parallel/process_group.hpp>
+#include <boost/static_assert.hpp>
+#include <boost/mpi/operations.hpp>
+#include <boost/type_traits/is_convertible.hpp>
+#include <boost/graph/graph_concepts.hpp>
+#include <boost/graph/iteration_macros.hpp>
+#include <boost/optional.hpp>
+#include <utility> // for std::pair
+#include <cassert>
+#include <algorithm> // for std::find, std::mismatch
+#include <vector>
+#include <boost/graph/parallel/algorithm.hpp>
+#include <boost/graph/distributed/connected_components.hpp>
+
+namespace boost { namespace graph { namespace distributed {
+
+namespace hohberg_detail {
+ enum message_kind {
+ /* A header for the PATH message, stating which edge the message
+ is coming on and how many vertices will be following. The data
+ structure communicated will be a path_header. */
+ msg_path_header,
+ /* A message containing the vertices that make up a path. It will
+ always follow a msg_path_header and will contain vertex
+ descriptors, only. */
+ msg_path_vertices,
+ /* A header for the TREE message, stating the value of gamma and
+ the number of vertices to come in the following
+ msg_tree_vertices. */
+ msg_tree_header,
+ /* A message containing the vertices that make up the set of
+ vertices in the same bicomponent as the sender. It will always
+ follow a msg_tree_header and will contain vertex descriptors,
+ only. */
+ msg_tree_vertices,
+ /* Provides a name for the biconnected component of the edge. */
+ msg_name
+ };
+
+ // Payload for a msg_path_header message.
+ template<typename EdgeDescriptor>
+ struct path_header
+ {
+ // The edge over which the path is being communicated
+ EdgeDescriptor edge;
+
+ // The length of the path, i.e., the number of vertex descriptors
+ // that will be coming in the next msg_path_vertices message.
+ std::size_t path_length;
+
+ template<typename Archiver>
+ void serialize(Archiver& ar, const unsigned int /*version*/)
+ {
+ ar & edge & path_length;
+ }
+ };
+
+ // Payload for a msg_tree_header message.
+ template<typename Vertex, typename Edge>
+ struct tree_header
+ {
+ // The edge over which the tree is being communicated
+ Edge edge;
+
+ // Gamma, which is the eta of the sender.
+ Vertex gamma;
+
+ // The length of the list of vertices in the bicomponent, i.e.,
+ // the number of vertex descriptors that will be coming in the
+ // next msg_tree_vertices message.
+ std::size_t bicomp_length;
+
+ template<typename Archiver>
+ void serialize(Archiver& ar, const unsigned int /*version*/)
+ {
+ ar & edge & gamma & bicomp_length;
+ }
+ };
+
+ // Payload for the msg_name message.
+ template<typename EdgeDescriptor>
+ struct name_header
+ {
+ // The edge being communicated and named.
+ EdgeDescriptor edge;
+
+ // The 0-based name of the component
+ std::size_t name;
+
+ template<typename Archiver>
+ void serialize(Archiver& ar, const unsigned int /*version*/)
+ {
+ ar & edge & name;
+ }
+ };
+
+ /* Computes the branch point between two paths. The branch point is
+ the last position at which both paths are equivalent, beyond
+ which the paths diverge. Both paths must have length > 0 and the
+ initial elements of the paths must be equal. This is guaranteed
+ in Hohberg's algorithm because all paths start at the
+ leader. Returns the value at the branch point. */
+ template<typename T>
+ T branch_point(const std::vector<T>& p1, const std::vector<T>& p2)
+ {
+ assert(!p1.empty());
+ assert(!p2.empty());
+ assert(p1.front() == p2.front());
+
+ typedef typename std::vector<T>::const_iterator iterator;
+
+ iterator mismatch_pos;
+ if (p1.size() <= p2.size())
+ mismatch_pos = std::mismatch(p1.begin(), p1.end(), p2.begin()).first;
+ else
+ mismatch_pos = std::mismatch(p2.begin(), p2.end(), p1.begin()).first;
+ --mismatch_pos;
+ return *mismatch_pos;
+ }
+
+ /* Computes the infimum of vertices a and b in the given path. The
+ infimum is the largest element that is on the paths from a to the
+ root and from b to the root. */
+ template<typename T>
+ T infimum(const std::vector<T>& parent_path, T a, T b)
+ {
+ using std::swap;
+
+ typedef typename std::vector<T>::const_iterator iterator;
+ iterator first = parent_path.begin(), last = parent_path.end();
+
+#if defined(PBGL_HOHBERG_DEBUG) and PBGL_HOHBERG_DEBUG > 2
+ std::cerr << "infimum(";
+ for (iterator i = first; i != last; ++i) {
+ if (i != first) std::cerr << ' ';
+ std::cerr << local(*i) << '@' << owner(*i);
+ }
+ std::cerr << ", " << local(a) << '@' << owner(a) << ", "
+ << local(b) << '@' << owner(b) << ") = ";
+#endif
+
+ if (a == b) {
+#if defined(PBGL_HOHBERG_DEBUG) and PBGL_HOHBERG_DEBUG > 2
+ std::cerr << local(a) << '@' << owner(a) << std::endl;
+#endif
+ return a;
+ }
+
+ // Try to find a or b, whichever is closest to the end
+ --last;
+ while (*last != a) {
+ // If we match b, swap the two so we'll be looking for b later.
+ if (*last == b) { swap(a,b); break; }
+
+ if (last == first) {
+#if defined(PBGL_HOHBERG_DEBUG) and PBGL_HOHBERG_DEBUG > 2
+ std::cerr << local(*first) << '@' << owner(*first) << std::endl;
+#endif
+ return *first;
+ }
+ else --last;
+ }
+
+ // Try to find b (which may originally have been a)
+ while (*last != b) {
+ if (last == first) {
+#if defined(PBGL_HOHBERG_DEBUG) and PBGL_HOHBERG_DEBUG > 2
+ std::cerr << local(*first) << '@' << owner(*first) << std::endl;
+#endif
+ return *first;
+ }
+ else --last;
+ }
+
+#if defined(PBGL_HOHBERG_DEBUG) and PBGL_HOHBERG_DEBUG > 2
+ std::cerr << local(*last) << '@' << owner(*last) << std::endl;
+#endif
+ // We've found b; it's the infimum.
+ return *last;
+ }
+} // end namespace hohberg_detail
+
+/* A message that will be stored for each edge by Hohberg's algorithm. */
+template<typename Graph>
+struct hohberg_message
+{
+ typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
+ typedef typename graph_traits<Graph>::edge_descriptor Edge;
+
+ // Assign from a path message
+ void assign(const std::vector<Vertex>& path)
+ {
+ gamma = graph_traits<Graph>::null_vertex();
+ path_or_bicomp = path;
+ }
+
+ // Assign from a tree message
+ void assign(Vertex gamma, const std::vector<Vertex>& in_same_bicomponent)
+ {
+ this->gamma = gamma;
+ path_or_bicomp = in_same_bicomponent;
+ }
+
+ bool is_path() const { return gamma == graph_traits<Graph>::null_vertex(); }
+ bool is_tree() const { return gamma != graph_traits<Graph>::null_vertex(); }
+
+ /// The "gamma" of a tree message, or null_vertex() for a path message
+ Vertex gamma;
+
+ // Either the path for a path message or the in_same_bicomponent
+ std::vector<Vertex> path_or_bicomp;
+};
+
+
+/* An abstraction of a vertex processor in Hohberg's algorithm. The
+ hohberg_vertex_processor class is responsible for processing
+ messages transmitted to it via its edges. */
+template<typename Graph>
+class hohberg_vertex_processor
+{
+ typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
+ typedef typename graph_traits<Graph>::edge_descriptor Edge;
+ typedef typename graph_traits<Graph>::degree_size_type degree_size_type;
+ typedef typename graph_traits<Graph>::edges_size_type edges_size_type;
+ typedef typename boost::graph::parallel::process_group_type<Graph>::type
+ ProcessGroup;
+ typedef std::vector<Vertex> path_t;
+ typedef typename path_t::iterator path_iterator;
+
+ public:
+ hohberg_vertex_processor()
+ : phase(1),
+ parent(graph_traits<Graph>::null_vertex()),
+ eta(graph_traits<Graph>::null_vertex())
+ {
+ }
+
+ // Called to initialize a leader in the algorithm, which involves
+ // sending out the initial path messages and being ready to receive
+ // them.
+ void initialize_leader(Vertex alpha, const Graph& g);
+
+ /// Handle a path message on edge e. The path will be destroyed by
+ /// this operation.
+ void
+ operator()(Edge e, path_t& path, const Graph& g);
+
+ /// Handle a tree message on edge e. in_same_bicomponent will be
+ /// destroyed by this operation.
+ void
+ operator()(Edge e, Vertex gamma, path_t& in_same_bicomponent,
+ const Graph& g);
+
+ // Handle a name message.
+ void operator()(Edge e, edges_size_type name, const Graph& g);
+
+ // Retrieve the phase
+ unsigned char get_phase() const { return phase; }
+
+ // Start the naming phase. The current phase must be 3 (echo), and
+ // the offset contains the offset at which this processor should
+ // begin when labelling its bicomponents. The offset is just a
+ // parallel prefix sum of the number of bicomponents in each
+ // processor that precedes it (globally).
+ void
+ start_naming_phase(Vertex alpha, const Graph& g, edges_size_type offset);
+
+ /* Determine the number of bicomponents that we will be naming
+ * ourselves.
+ */
+ edges_size_type num_starting_bicomponents(Vertex alpha, const Graph& g);
+
+ // Fill in the edge component map with biconnected component
+ // numbers.
+ template<typename ComponentMap>
+ void fill_edge_map(Vertex alpha, const Graph& g, ComponentMap& component);
+
+ protected:
+ /* Start the echo phase (phase 3) where we propagate information up
+ the tree. */
+ void echo_phase(Vertex alpha, const Graph& g);
+
+ /* Retrieve the index of edge in the out-edges list of target(e, g). */
+ std::size_t get_edge_index(Edge e, const Graph& g);
+
+ /* Retrieve the index of the edge incidence on v in the out-edges
+ list of vertex u. */
+ std::size_t get_incident_edge_index(Vertex u, Vertex v, const Graph& g);
+
+ /* Keeps track of which phase of the algorithm we are in. There are
+ * four phases plus the "finished" phase:
+ *
+ * 1) Building the spanning tree
+ * 2) Discovering cycles
+ * 3) Echoing back up the spanning tree
+ * 4) Labelling biconnected components
+ * 5) Finished
+ */
+ unsigned char phase;
+
+ /* The parent of this vertex in the spanning tree. This value will
+ be graph_traits<Graph>::null_vertex() for the leader. */
+ Vertex parent;
+
+ /* The farthest ancestor up the tree that resides in the same
+ biconnected component as we do. This information is approximate:
+ we might not know about the actual farthest ancestor, but this is
+ the farthest one we've seen so far. */
+ Vertex eta;
+
+ /* The number of edges that have not yet transmitted any messages to
+ us. This starts at the degree of the vertex and decreases as we
+ receive messages. When this counter hits zero, we're done with
+ the second phase of the algorithm. In Hohberg's paper, the actual
+ remaining edge set E is stored with termination when all edges
+ have been removed from E, but we only need to detect termination
+ so the set E need not be explicitly represented. */
+ degree_size_type num_edges_not_transmitted;
+
+ /* The path from the root of the spanning tree to this vertex. This
+ vector will always store only the parts of the path leading up to
+ this vertex, and not the vertex itself. Thus, it will be empty
+ for the leader. */
+ std::vector<Vertex> path_from_root;
+
+ /* Structure containing all of the extra data we need to keep around
+ PER EDGE. This information can not be contained within a property
+ map, because it can't be shared among vertices without breaking
+ the algorithm. Decreasing the size of this structure will drastically */
+ struct per_edge_data
+ {
+ hohberg_message<Graph> msg;
+ std::vector<Vertex> M;
+ bool is_tree_edge;
+ degree_size_type partition;
+ };
+
+ /* Data for each edge in the graph. This structure will be indexed
+ by the position of the edge in the out_edges() list. */
+ std::vector<per_edge_data> edge_data;
+
+ /* The mapping from local partition numbers (0..n-1) to global
+ partition numbers. */
+ std::vector<edges_size_type> local_to_global_partitions;
+
+ friend class boost::serialization::access;
+
+ // We cannot actually serialize a vertex processor, nor would we
+ // want to. However, the fact that we're putting instances into a
+ // distributed_property_map means that we need to have a serialize()
+ // function available.
+ template<typename Archiver>
+ void serialize(Archiver&, const unsigned int /*version*/)
+ {
+ assert(false);
+ }
+};
+
+template<typename Graph>
+void
+hohberg_vertex_processor<Graph>::initialize_leader(Vertex alpha,
+ const Graph& g)
+{
+ using namespace hohberg_detail;
+
+ ProcessGroup pg = process_group(g);
+
+ typename property_map<Graph, vertex_owner_t>::const_type
+ owner = get(vertex_owner, g);
+
+ path_header<Edge> header;
+ header.path_length = 1;
+ BGL_FORALL_OUTEDGES_T(alpha, e, g, Graph) {
+ header.edge = e;
+ send(pg, get(owner, target(e, g)), msg_path_header, header);
+ send(pg, get(owner, target(e, g)), msg_path_vertices, alpha);
+ }
+
+ num_edges_not_transmitted = degree(alpha, g);
+ edge_data.resize(num_edges_not_transmitted);
+ phase = 2;
+}
+
+template<typename Graph>
+void
+hohberg_vertex_processor<Graph>::operator()(Edge e, path_t& path,
+ const Graph& g)
+{
+ using namespace hohberg_detail;
+
+ typename property_map<Graph, vertex_owner_t>::const_type
+ owner = get(vertex_owner, g);
+
+#ifdef PBGL_HOHBERG_DEBUG
+// std::cerr << local(source(e, g)) << '@' << owner(source(e, g)) << " -> "
+// << local(target(e, g)) << '@' << owner(target(e, g)) << ": path(";
+// for (std::size_t i = 0; i < path.size(); ++i) {
+// if (i > 0) std::cerr << ' ';
+// std::cerr << local(path[i]) << '@' << owner(path[i]);
+// }
+ std::cerr << "), phase = " << (int)phase << std::endl;
+#endif
+
+ // Get access to edge-specific data
+ if (edge_data.empty())
+ edge_data.resize(degree(target(e, g), g));
+ per_edge_data& edata = edge_data[get_edge_index(e, g)];
+
+ // Record the message. We'll need it in phase 3.
+ edata.msg.assign(path);
+
+ // Note: "alpha" refers to the vertex "processor" receiving the
+ // message.
+ Vertex alpha = target(e, g);
+
+ switch (phase) {
+ case 1:
+ {
+ num_edges_not_transmitted = degree(alpha, g) - 1;
+ edata.is_tree_edge = true;
+ parent = path.back();
+ eta = parent;
+ edata.M.clear(); edata.M.push_back(parent);
+
+ // Broadcast the path from the root to our potential children in
+ // the spanning tree.
+ path.push_back(alpha);
+ path_header<Edge> header;
+ header.path_length = path.size();
+ ProcessGroup pg = process_group(g);
+ BGL_FORALL_OUTEDGES_T(alpha, oe, g, Graph) {
+ // Skip the tree edge we just received
+ if (target(oe, g) != source(e, g)) {
+ header.edge = oe;
+ send(pg, get(owner, target(oe, g)), msg_path_header, header);
+ send(pg, get(owner, target(oe, g)), msg_path_vertices, &path[0],
+ header.path_length);
+ }
+ }
+ path.pop_back();
+
+ // Swap the old path in, to save some extra copying. Nobody
+ path_from_root.swap(path);
+
+ // Once we have received our place in the spanning tree, move on
+ // to phase 2.
+ phase = 2;
+
+ // If we only had only edge, skip to phase 3.
+ if (num_edges_not_transmitted == 0)
+ echo_phase(alpha, g);
+ return;
+ }
+
+ case 2:
+ {
+ --num_edges_not_transmitted;
+ edata.is_tree_edge = false;
+
+ // Determine if alpha (our vertex) is in the path
+ path_iterator pos = std::find(path.begin(), path.end(), alpha);
+ if (pos != path.end()) {
+ // Case A: There is a cycle alpha beta ... gamma alpha
+ // M(e) <- {beta, gammar}
+ edata.M.clear();
+ ++pos;
+ // If pos == path.end(), we have a self-loop
+ if (pos != path.end()) {
+ // Add beta
+ edata.M.push_back(*pos);
+ ++pos;
+ }
+ // If pos == path.end(), we have a self-loop or beta == gamma
+ // (parallel edge). Otherwise, add gamma.
+ if (pos != path.end()) edata.M.push_back(path.back());
+ } else {
+ // Case B: There is a cycle but we haven't seen alpha yet.
+ // M(e) = {parent, path.back()}
+ edata.M.clear();
+ edata.M.push_back(path.back());
+ if (parent != path.back()) edata.M.push_back(parent);
+
+ // eta = inf(eta, bra(pi_t, pi))
+ eta = infimum(path_from_root, eta, branch_point(path_from_root, path));
+ }
+ if (num_edges_not_transmitted == 0)
+ echo_phase(alpha, g);
+ break;
+ }
+
+ default:
+// std::cerr << "Phase is " << int(phase) << "\n";
+ assert(false);
+ }
+}
+
+template<typename Graph>
+void
+hohberg_vertex_processor<Graph>::operator()(Edge e, Vertex gamma,
+ path_t& in_same_bicomponent,
+ const Graph& g)
+{
+ using namespace hohberg_detail;
+
+#ifdef PBGL_HOHBERG_DEBUG
+ std::cerr << local(source(e, g)) << '@' << owner(source(e, g)) << " -> "
+ << local(target(e, g)) << '@' << owner(target(e, g)) << ": tree("
+ << local(gamma) << '@' << owner(gamma) << ", ";
+ for (std::size_t i = 0; i < in_same_bicomponent.size(); ++i) {
+ if (i > 0) std::cerr << ' ';
+ std::cerr << local(in_same_bicomponent[i]) << '@'
+ << owner(in_same_bicomponent[i]);
+ }
+ std::cerr << ", " << local(source(e, g)) << '@' << owner(source(e, g))
+ << "), phase = " << (int)phase << std::endl;
+#endif
+
+ // Get access to edge-specific data
+ per_edge_data& edata = edge_data[get_edge_index(e, g)];
+
+ // Record the message. We'll need it in phase 3.
+ edata.msg.assign(gamma, in_same_bicomponent);
+
+ // Note: "alpha" refers to the vertex "processor" receiving the
+ // message.
+ Vertex alpha = target(e, g);
+ Vertex beta = source(e, g);
+
+ switch (phase) {
+ case 2:
+ --num_edges_not_transmitted;
+ edata.is_tree_edge = true;
+
+ if (gamma == alpha) {
+ // Case C
+ edata.M.swap(in_same_bicomponent);
+ } else {
+ // Case D
+ edata.M.clear();
+ edata.M.push_back(parent);
+ if (beta != parent) edata.M.push_back(beta);
+ eta = infimum(path_from_root, eta, gamma);
+ }
+ if (num_edges_not_transmitted == 0)
+ echo_phase(alpha, g);
+ break;
+
+ default:
+ assert(false);
+ }
+}
+
+template<typename Graph>
+void
+hohberg_vertex_processor<Graph>::operator()(Edge e, edges_size_type name,
+ const Graph& g)
+{
+ using namespace hohberg_detail;
+
+#ifdef PBGL_HOHBERG_DEBUG
+ std::cerr << local(source(e, g)) << '@' << owner(source(e, g)) << " -> "
+ << local(target(e, g)) << '@' << owner(target(e, g)) << ": name("
+ << name << "), phase = " << (int)phase << std::endl;
+#endif
+
+ assert(phase == 4);
+
+ typename property_map<Graph, vertex_owner_t>::const_type
+ owner = get(vertex_owner, g);
+
+ // Send name messages along the spanning tree edges that are in the
+ // same bicomponent as the edge to our parent.
+ ProcessGroup pg = process_group(g);
+
+ Vertex alpha = target(e, g);
+
+ std::size_t idx = 0;
+ BGL_FORALL_OUTEDGES_T(alpha, e, g, Graph) {
+ per_edge_data& edata = edge_data[idx++];
+ if (edata.is_tree_edge
+ && find(edata.M.begin(), edata.M.end(), parent) != edata.M.end()
+ && target(e, g) != parent) {
+ // Notify our children in the spanning tree of this name
+ name_header<Edge> header;
+ header.edge = e;
+ header.name = name;
+ send(pg, get(owner, target(e, g)), msg_name, header);
+ } else if (target(e, g) == parent) {
+ // Map from local partition numbers to global bicomponent numbers
+ local_to_global_partitions[edata.partition] = name;
+ }
+ }
+
+ // Final stage
+ phase = 5;
+}
+
+template<typename Graph>
+typename hohberg_vertex_processor<Graph>::edges_size_type
+hohberg_vertex_processor<Graph>::
+num_starting_bicomponents(Vertex alpha, const Graph& g)
+{
+ edges_size_type not_mapped = (std::numeric_limits<edges_size_type>::max)();
+
+ edges_size_type result = 0;
+ std::size_t idx = 0;
+ BGL_FORALL_OUTEDGES_T(alpha, e, g, Graph) {
+ per_edge_data& edata = edge_data[idx++];
+ if (edata.is_tree_edge
+ && find(edata.M.begin(), edata.M.end(), parent) == edata.M.end()) {
+ // Map from local partition numbers to global bicomponent numbers
+ if (local_to_global_partitions[edata.partition] == not_mapped)
+ local_to_global_partitions[edata.partition] = result++;
+ }
+ }
+
+#ifdef PBGL_HOHBERG_DEBUG
+ std::cerr << local(alpha) << '@' << owner(alpha) << " has " << result
+ << " bicomponents originating at it." << std::endl;
+#endif
+
+ return result;
+}
+
+template<typename Graph>
+template<typename ComponentMap>
+void
+hohberg_vertex_processor<Graph>::
+fill_edge_map(Vertex alpha, const Graph& g, ComponentMap& component)
+{
+ std::size_t idx = 0;
+ BGL_FORALL_OUTEDGES_T(alpha, e, g, Graph) {
+ per_edge_data& edata = edge_data[idx++];
+ local_put(component, e, local_to_global_partitions[edata.partition]);
+
+#if defined(PBGL_HOHBERG_DEBUG) && PBGL_HOHBERG_DEBUG > 2
+ std::cerr << "component("
+ << local(source(e, g)) << '@' << owner(source(e, g)) << " -> "
+ << local(target(e, g)) << '@' << owner(target(e, g)) << ") = "
+ << local_to_global_partitions[edata.partition]
+ << " (partition = " << edata.partition << " of "
+ << local_to_global_partitions.size() << ")" << std::endl;
+#endif
+ }
+}
+
+template<typename Graph>
+void
+hohberg_vertex_processor<Graph>::
+start_naming_phase(Vertex alpha, const Graph& g, edges_size_type offset)
+{
+ using namespace hohberg_detail;
+
+ assert(phase == 4);
+
+ typename property_map<Graph, vertex_owner_t>::const_type
+ owner = get(vertex_owner, g);
+
+ // Send name messages along the spanning tree edges of the
+ // components that we get to number.
+ ProcessGroup pg = process_group(g);
+
+ bool has_more_children_to_name = false;
+
+ // Map from local partition numbers to global bicomponent numbers
+ edges_size_type not_mapped = (std::numeric_limits<edges_size_type>::max)();
+ for (std::size_t i = 0; i < local_to_global_partitions.size(); ++i) {
+ if (local_to_global_partitions[i] != not_mapped)
+ local_to_global_partitions[i] += offset;
+ }
+
+ std::size_t idx = 0;
+ BGL_FORALL_OUTEDGES_T(alpha, e, g, Graph) {
+ per_edge_data& edata = edge_data[idx++];
+ if (edata.is_tree_edge
+ && find(edata.M.begin(), edata.M.end(), parent) == edata.M.end()) {
+ // Notify our children in the spanning tree of this new name
+ name_header<Edge> header;
+ header.edge = e;
+ header.name = local_to_global_partitions[edata.partition];
+ send(pg, get(owner, target(e, g)), msg_name, header);
+ } else if (edata.is_tree_edge) {
+ has_more_children_to_name = true;
+ }
+#if defined(PBGL_HOHBERG_DEBUG) and PBGL_HOHBERG_DEBUG > 2
+ std::cerr << "M[" << local(source(e, g)) << '@' << owner(source(e, g))
+ << " -> " << local(target(e, g)) << '@' << owner(target(e, g))
+ << "] = ";
+ for (std::size_t i = 0; i < edata.M.size(); ++i) {
+ std::cerr << local(edata.M[i]) << '@' << owner(edata.M[i]) << ' ';
+ }
+ std::cerr << std::endl;
+#endif
+ }
+
+ // See if we're done.
+ if (!has_more_children_to_name)
+ // Final stage
+ phase = 5;
+}
+
+template<typename Graph>
+void
+hohberg_vertex_processor<Graph>::echo_phase(Vertex alpha, const Graph& g)
+{
+ using namespace hohberg_detail;
+
+ typename property_map<Graph, vertex_owner_t>::const_type
+ owner = get(vertex_owner, g);
+
+ /* We're entering the echo phase. */
+ phase = 3;
+
+ if (parent != graph_traits<Graph>::null_vertex()) {
+ Edge edge_to_parent;
+
+#if defined(PBGL_HOHBERG_DEBUG) and PBGL_HOHBERG_DEBUG > 1
+ std::cerr << local(alpha) << '@' << owner(alpha) << " echo: parent = "
+ << local(parent) << '@' << owner(parent) << ", eta = "
+ << local(eta) << '@' << owner(eta) << ", Gamma = ";
+#endif
+
+ std::vector<Vertex> bicomp;
+ std::size_t e_index = 0;
+ BGL_FORALL_OUTEDGES_T(alpha, e, g, Graph) {
+ if (target(e, g) == parent && parent == eta) {
+ edge_to_parent = e;
+ if (find(bicomp.begin(), bicomp.end(), alpha) == bicomp.end()) {
+#if defined(PBGL_HOHBERG_DEBUG) and PBGL_HOHBERG_DEBUG > 1
+ std::cerr << local(alpha) << '@' << owner(alpha) << ' ';
+#endif
+ bicomp.push_back(alpha);
+ }
+ } else {
+ if (target(e, g) == parent) edge_to_parent = e;
+
+ per_edge_data& edata = edge_data[e_index];
+
+ if (edata.msg.is_path()) {
+ path_iterator pos = std::find(edata.msg.path_or_bicomp.begin(),
+ edata.msg.path_or_bicomp.end(),
+ eta);
+ if (pos != edata.msg.path_or_bicomp.end()) {
+ ++pos;
+ if (pos != edata.msg.path_or_bicomp.end()
+ && find(bicomp.begin(), bicomp.end(), *pos) == bicomp.end()) {
+#if defined(PBGL_HOHBERG_DEBUG) and PBGL_HOHBERG_DEBUG > 1
+ std::cerr << local(*pos) << '@' << owner(*pos) << ' ';
+#endif
+ bicomp.push_back(*pos);
+ }
+ }
+ } else if (edata.msg.is_tree() && edata.msg.gamma == eta) {
+ for (path_iterator i = edata.msg.path_or_bicomp.begin();
+ i != edata.msg.path_or_bicomp.end(); ++i) {
+ if (find(bicomp.begin(), bicomp.end(), *i) == bicomp.end()) {
+#if defined(PBGL_HOHBERG_DEBUG) and PBGL_HOHBERG_DEBUG > 1
+ std::cerr << local(*i) << '@' << owner(*i) << ' ';
+#endif
+ bicomp.push_back(*i);
+ }
+ }
+ }
+ }
+ ++e_index;
+ }
+#ifdef PBGL_HOHBERG_DEBUG
+ std::cerr << std::endl;
+#endif
+
+ // Send tree(eta, bicomp) to parent
+ tree_header<Vertex, Edge> header;
+ header.edge = edge_to_parent;
+ header.gamma = eta;
+ header.bicomp_length = bicomp.size();
+ ProcessGroup pg = process_group(g);
+ send(pg, get(owner, parent), msg_tree_header, header);
+ send(pg, get(owner, parent), msg_tree_vertices, &bicomp[0],
+ header.bicomp_length);
+ }
+
+ // Compute the partition of edges such that iff two edges e1 and e2
+ // are in different subsets then M(e1) is disjoint from M(e2).
+
+ // Start by putting each edge in a different partition
+ std::vector<degree_size_type> parent_vec(edge_data.size());
+ degree_size_type idx = 0;
+ for (idx = 0; idx < edge_data.size(); ++idx)
+ parent_vec[idx] = idx;
+
+ // Go through each edge e, performing a union() on the edges
+ // incident on all vertices in M[e].
+ idx = 0;
+ BGL_FORALL_OUTEDGES_T(alpha, e, g, Graph) {
+ per_edge_data& edata = edge_data[idx++];
+
+ // Compute union of vertices in M
+ if (!edata.M.empty()) {
+ degree_size_type e1 = get_incident_edge_index(alpha, edata.M.front(), g);
+ while (parent_vec[e1] != e1) e1 = parent_vec[e1];
+
+ for (std::size_t i = 1; i < edata.M.size(); ++i) {
+ degree_size_type e2 = get_incident_edge_index(alpha, edata.M[i], g);
+ while (parent_vec[e2] != e2) e2 = parent_vec[e2];
+ parent_vec[e2] = e1;
+ }
+ }
+ }
+
+ edges_size_type not_mapped = (std::numeric_limits<edges_size_type>::max)();
+
+ // Determine the number of partitions
+ for (idx = 0; idx < parent_vec.size(); ++idx) {
+ if (parent_vec[idx] == idx) {
+ edge_data[idx].partition = local_to_global_partitions.size();
+ local_to_global_partitions.push_back(not_mapped);
+ }
+ }
+
+ // Assign partition numbers to each edge
+ for (idx = 0; idx < parent_vec.size(); ++idx) {
+ degree_size_type rep = parent_vec[idx];
+ while (rep != parent_vec[rep]) rep = parent_vec[rep];
+ edge_data[idx].partition = edge_data[rep].partition;
+ }
+
+ // Enter the naming phase (but don't send anything yet).
+ phase = 4;
+}
+
+template<typename Graph>
+std::size_t
+hohberg_vertex_processor<Graph>::get_edge_index(Edge e, const Graph& g)
+{
+ std::size_t result = 0;
+ BGL_FORALL_OUTEDGES_T(target(e, g), oe, g, Graph) {
+ if (source(e, g) == target(oe, g)) return result;
+ ++result;
+ }
+ assert(false);
+}
+
+template<typename Graph>
+std::size_t
+hohberg_vertex_processor<Graph>::get_incident_edge_index(Vertex u, Vertex v,
+ const Graph& g)
+{
+ std::size_t result = 0;
+ BGL_FORALL_OUTEDGES_T(u, e, g, Graph) {
+ if (target(e, g) == v) return result;
+ ++result;
+ }
+ assert(false);
+}
+
+template<typename Graph, typename InputIterator, typename ComponentMap,
+ typename VertexProcessorMap>
+typename graph_traits<Graph>::edges_size_type
+hohberg_biconnected_components
+ (const Graph& g,
+ ComponentMap component,
+ InputIterator first, InputIterator last,
+ VertexProcessorMap vertex_processor)
+{
+ using namespace boost::graph::parallel;
+ using namespace hohberg_detail;
+ using boost::parallel::all_reduce;
+
+ typename property_map<Graph, vertex_owner_t>::const_type
+ owner = get(vertex_owner, g);
+
+ // The graph must be undirected
+ BOOST_STATIC_ASSERT(
+ (is_convertible<typename graph_traits<Graph>::directed_category,
+ undirected_tag>::value));
+
+ // The graph must model Incidence Graph
+ function_requires< IncidenceGraphConcept<Graph> >();
+
+ typedef typename graph_traits<Graph>::edges_size_type edges_size_type;
+ typedef typename graph_traits<Graph>::degree_size_type degree_size_type;
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+ typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor;
+
+ // Retrieve the process group we will use for communication
+ typedef typename process_group_type<Graph>::type process_group_type;
+ process_group_type pg = process_group(g);
+
+ // Keeps track of the edges that we know to be tree edges.
+ std::vector<edge_descriptor> tree_edges;
+
+ // The leaders send out a path message to initiate the algorithm
+ while (first != last) {
+ vertex_descriptor leader = *first;
+ if (process_id(pg) == get(owner, leader))
+ vertex_processor[leader].initialize_leader(leader, g);
+ ++first;
+ }
+ synchronize(pg);
+
+ // Will hold the number of bicomponents in the graph.
+ edges_size_type num_bicomponents = 0;
+
+ // Keep track of the path length that we should expect, based on the
+ // level in the breadth-first search tree. At present, this is only
+ // used as a sanity check. TBD: This could be used to decrease the
+ // amount of communication required per-edge (by about 4 bytes).
+ std::size_t path_length = 1;
+
+ typedef std::vector<vertex_descriptor> path_t;
+ typedef typename path_t::iterator path_iterator;
+
+ unsigned char minimum_phase = 5;
+ do {
+ while (optional<std::pair<int, int> > msg = probe(pg)) {
+ switch (msg->second) {
+ case msg_path_header:
+ {
+ // Receive the path header
+ path_header<edge_descriptor> header;
+ receive(pg, msg->first, msg->second, header);
+ assert(path_length == header.path_length);
+
+ // Receive the path itself
+ path_t path(path_length);
+ receive(pg, msg->first, msg_path_vertices, &path[0], path_length);
+
+ edge_descriptor e = header.edge;
+ vertex_processor[target(e, g)](e, path, g);
+ }
+ break;
+
+ case msg_path_vertices:
+ // Should be handled in msg_path_header case, unless we're going
+ // stateless.
+ assert(false);
+ break;
+
+ case msg_tree_header:
+ {
+ // Receive the tree header
+ tree_header<vertex_descriptor, edge_descriptor> header;
+ receive(pg, msg->first, msg->second, header);
+
+ // Receive the tree itself
+ path_t in_same_bicomponent(header.bicomp_length);
+ receive(pg, msg->first, msg_tree_vertices, &in_same_bicomponent[0],
+ header.bicomp_length);
+
+ edge_descriptor e = header.edge;
+ vertex_processor[target(e, g)](e, header.gamma, in_same_bicomponent,
+ g);
+ }
+ break;
+
+ case msg_tree_vertices:
+ // Should be handled in msg_tree_header case, unless we're
+ // going stateless.
+ assert(false);
+ break;
+
+ case msg_name:
+ {
+ name_header<edge_descriptor> header;
+ receive(pg, msg->first, msg->second, header);
+ edge_descriptor e = header.edge;
+ vertex_processor[target(e, g)](e, header.name, g);
+ }
+ break;
+
+ default:
+ assert(false);
+ }
+ }
+ ++path_length;
+
+ // Compute minimum phase locally
+ minimum_phase = 5;
+ unsigned char maximum_phase = 1;
+ BGL_FORALL_VERTICES_T(v, g, Graph) {
+ minimum_phase = (std::min)(minimum_phase, vertex_processor[v].get_phase());
+ maximum_phase = (std::max)(maximum_phase, vertex_processor[v].get_phase());
+ }
+
+#ifdef PBGL_HOHBERG_DEBUG
+ if (process_id(pg) == 0)
+ std::cerr << "<---------End of stage------------->" << std::endl;
+#endif
+ // Compute minimum phase globally
+ minimum_phase = all_reduce(pg, minimum_phase, boost::mpi::minimum<char>());
+
+#ifdef PBGL_HOHBERG_DEBUG
+ if (process_id(pg) == 0)
+ std::cerr << "Minimum phase = " << (int)minimum_phase << std::endl;
+#endif
+
+ if (minimum_phase == 4
+ && all_reduce(pg, maximum_phase, boost::mpi::maximum<char>()) == 4) {
+
+#ifdef PBGL_HOHBERG_DEBUG
+ if (process_id(pg) == 0)
+ std::cerr << "<---------Naming phase------------->" << std::endl;
+#endif
+ // Compute the biconnected component number offsets for each
+ // vertex.
+ std::vector<edges_size_type> local_offsets;
+ local_offsets.reserve(num_vertices(g));
+ edges_size_type num_local_bicomponents = 0;
+ BGL_FORALL_VERTICES_T(v, g, Graph) {
+ local_offsets.push_back(num_local_bicomponents);
+ num_local_bicomponents +=
+ vertex_processor[v].num_starting_bicomponents(v, g);
+ }
+
+ synchronize(pg);
+
+ // Find our the number of bicomponent names that will originate
+ // from each process. This tells us how many bicomponents are in
+ // the entire graph and what our global offset is for computing
+ // our own biconnected component names.
+ std::vector<edges_size_type> all_bicomponents(num_processes(pg));
+ all_gather(pg, &num_local_bicomponents, &num_local_bicomponents + 1,
+ all_bicomponents);
+ num_bicomponents = 0;
+ edges_size_type my_global_offset = 0;
+ for (std::size_t i = 0; i < all_bicomponents.size(); ++i) {
+ if (i == (std::size_t)process_id(pg))
+ my_global_offset = num_bicomponents;
+ num_bicomponents += all_bicomponents[i];
+ }
+
+ std::size_t index = 0;
+ BGL_FORALL_VERTICES_T(v, g, Graph) {
+ edges_size_type offset = my_global_offset + local_offsets[index++];
+ vertex_processor[v].start_naming_phase(v, g, offset);
+ }
+ }
+
+ synchronize(pg);
+ } while (minimum_phase < 5);
+
+ // Number the edges appropriately.
+ BGL_FORALL_VERTICES_T(v, g, Graph)
+ vertex_processor[v].fill_edge_map(v, g, component);
+
+ return num_bicomponents;
+}
+
+template<typename Graph, typename ComponentMap, typename InputIterator>
+typename graph_traits<Graph>::edges_size_type
+hohberg_biconnected_components
+ (const Graph& g, ComponentMap component,
+ InputIterator first, InputIterator last)
+
+{
+ std::vector<hohberg_vertex_processor<Graph> >
+ vertex_processors(num_vertices(g));
+ return hohberg_biconnected_components
+ (g, component, first, last,
+ make_iterator_property_map(vertex_processors.begin(),
+ get(vertex_index, g)));
+}
+
+template<typename Graph, typename ComponentMap, typename ParentMap>
+typename graph_traits<Graph>::edges_size_type
+hohberg_biconnected_components(const Graph& g, ComponentMap component,
+ ParentMap parent)
+{
+ // We need the connected components of the graph, but we don't care
+ // about component numbers.
+ connected_components(g, dummy_property_map(), parent);
+
+ // Each root in the parent map is a leader
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+ std::vector<vertex_descriptor> leaders;
+ BGL_FORALL_VERTICES_T(v, g, Graph)
+ if (get(parent, v) == v) leaders.push_back(v);
+
+ return hohberg_biconnected_components(g, component,
+ leaders.begin(), leaders.end());
+}
+
+template<typename Graph, typename ComponentMap>
+typename graph_traits<Graph>::edges_size_type
+hohberg_biconnected_components(const Graph& g, ComponentMap component)
+{
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+ std::vector<vertex_descriptor> parents(num_vertices(g));
+ return hohberg_biconnected_components
+ (g, component, make_iterator_property_map(parents.begin(),
+ get(vertex_index, g)));
+}
+
+} } } // end namespace boost::graph::distributed
+
+#endif // BOOST_GRAPH_DISTRIBUTED_HOHBERG_BICONNECTED_COMPONENTS_HPP

Added: trunk/boost/graph/distributed/local_subgraph.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/local_subgraph.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,175 @@
+// 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
+#ifndef BOOST_GRAPH_LOCAL_SUBGRAPH_HPP
+#define BOOST_GRAPH_LOCAL_SUBGRAPH_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/graph/graph_traits.hpp>
+#include <boost/graph/filtered_graph.hpp>
+#include <boost/type_traits/is_same.hpp>
+#include <boost/type_traits/is_base_and_derived.hpp>
+#include <boost/graph/parallel/container_traits.hpp>
+
+namespace boost {
+
+namespace graph { namespace detail {
+ // Optionally, virtually derive from a base class
+ template<bool Derive, typename Base> struct derive_from_if;
+ template<typename Base> struct derive_from_if<true, Base> : virtual Base {};
+ template<typename Base> struct derive_from_if<false, Base> {};
+
+ template<typename NewBase, typename Tag, typename OldBase = NewBase>
+ struct derive_from_if_tag_is :
+ derive_from_if<(is_base_and_derived<OldBase, Tag>::value
+ || is_same<OldBase, Tag>::value),
+ NewBase>
+ {
+ };
+} } // end namespace graph::detail
+
+template<typename DistributedGraph>
+class is_local_edge
+{
+public:
+ typedef bool result_type;
+ typedef typename graph_traits<DistributedGraph>::edge_descriptor
+ argument_type;
+
+ is_local_edge() : g(0) {}
+ is_local_edge(DistributedGraph& g) : g(&g), owner(get(vertex_owner, g)) {}
+
+ // Since either the source or target vertex must be local, the
+ // equivalence of their owners indicates a local edge.
+ result_type operator()(const argument_type& e) const
+ { return get(owner, source(e, *g)) == get(owner, target(e, *g)); }
+
+private:
+ DistributedGraph* g;
+ typename property_map<DistributedGraph, vertex_owner_t>::const_type owner;
+};
+
+template<typename DistributedGraph>
+class is_local_vertex
+{
+public:
+ typedef bool result_type;
+ typedef typename graph_traits<DistributedGraph>::vertex_descriptor
+ argument_type;
+
+ is_local_vertex() : g(0) {}
+ is_local_vertex(DistributedGraph& g) : g(&g), owner(get(vertex_owner, g)) { }
+
+ // Since either the source or target vertex must be local, the
+ // equivalence of their owners indicates a local edge.
+ result_type operator()(const argument_type& v) const
+ {
+ return get(owner, v) == process_id(process_group(*g));
+ }
+
+private:
+ DistributedGraph* g;
+ typename property_map<DistributedGraph, vertex_owner_t>::const_type owner;
+};
+
+template<typename DistributedGraph>
+class local_subgraph
+ : public filtered_graph<DistributedGraph,
+ is_local_edge<DistributedGraph>,
+ is_local_vertex<DistributedGraph> >
+{
+ typedef filtered_graph<DistributedGraph,
+ is_local_edge<DistributedGraph>,
+ is_local_vertex<DistributedGraph> >
+ inherited;
+ typedef typename graph_traits<DistributedGraph>::traversal_category
+ inherited_category;
+
+public:
+ struct traversal_category :
+ graph::detail::derive_from_if_tag_is<incidence_graph_tag,
+ inherited_category>,
+ graph::detail::derive_from_if_tag_is<adjacency_graph_tag,
+ inherited_category>,
+ graph::detail::derive_from_if_tag_is<vertex_list_graph_tag,
+ inherited_category>,
+ graph::detail::derive_from_if_tag_is<edge_list_graph_tag,
+ inherited_category>,
+ graph::detail::derive_from_if_tag_is<vertex_list_graph_tag,
+ inherited_category,
+ distributed_vertex_list_graph_tag>,
+ graph::detail::derive_from_if_tag_is<edge_list_graph_tag,
+ inherited_category,
+ distributed_edge_list_graph_tag>
+ { };
+
+ local_subgraph(DistributedGraph& g)
+ : inherited(g,
+ is_local_edge<DistributedGraph>(g),
+ is_local_vertex<DistributedGraph>(g)),
+ g(g)
+ {
+ }
+
+ // Distributed Container
+ typedef typename boost::graph::parallel::process_group_type<DistributedGraph>::type
+ process_group_type;
+
+ process_group_type& process_group()
+ {
+ using boost::graph::parallel::process_group;
+ return process_group(g);
+ }
+ const process_group_type& process_group() const
+ {
+ using boost::graph::parallel::process_group;
+ return boost::graph::parallel::process_group(g);
+ }
+
+ DistributedGraph& base() { return g; }
+ const DistributedGraph& base() const { return g; }
+
+private:
+ DistributedGraph& g;
+};
+
+template<typename DistributedGraph, typename PropertyTag>
+class property_map<local_subgraph<DistributedGraph>, PropertyTag>
+ : public property_map<DistributedGraph, PropertyTag> { };
+
+template<typename DistributedGraph, typename PropertyTag>
+class property_map<local_subgraph<const DistributedGraph>, PropertyTag>
+{
+ public:
+ typedef typename property_map<DistributedGraph, PropertyTag>::const_type
+ type;
+ typedef type const_type;
+};
+
+template<typename PropertyTag, typename DistributedGraph>
+inline typename property_map<local_subgraph<DistributedGraph>, PropertyTag>::type
+get(PropertyTag p, local_subgraph<DistributedGraph>& g)
+{ return get(p, g.base()); }
+
+template<typename PropertyTag, typename DistributedGraph>
+inline typename property_map<local_subgraph<DistributedGraph>, PropertyTag>
+ ::const_type
+get(PropertyTag p, const local_subgraph<DistributedGraph>& g)
+{ return get(p, g.base()); }
+
+template<typename DistributedGraph>
+inline local_subgraph<DistributedGraph>
+make_local_subgraph(DistributedGraph& g)
+{ return local_subgraph<DistributedGraph>(g); }
+
+} // end namespace boost
+
+#endif // BOOST_GRAPH_LOCAL_SUBGRAPH_HPP

Added: trunk/boost/graph/distributed/mpi_process_group.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/mpi_process_group.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,809 @@
+// Copyright (C) 2004-2008 The Trustees of Indiana University.
+// Copyright (C) 2007 Douglas Gregor
+// Copyright (C) 2007 Matthias Troyer <troyer_at_[hidden]>
+
+// 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
+// Matthias Troyer
+// Andrew Lumsdaine
+#ifndef BOOST_GRAPH_DISTRIBUTED_MPI_PROCESS_GROUP
+#define BOOST_GRAPH_DISTRIBUTED_MPI_PROCESS_GROUP
+
+#ifndef BOOST_GRAPH_USE_MPI
+#error "Parallel BGL files should not be included unless <boost/graph/use_mpi.hpp> has been included"
+#endif
+
+//#define NO_SPLIT_BATCHES
+#define SEND_OOB_BSEND
+
+#include <boost/optional.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+#include <utility>
+#include <memory>
+#include <boost/function/function1.hpp>
+#include <boost/function/function2.hpp>
+#include <boost/function/function0.hpp>
+#include <boost/mpi.hpp>
+#include <boost/graph/parallel/process_group.hpp>
+#include <boost/utility/enable_if.hpp>
+
+namespace boost { namespace graph { namespace distributed {
+
+// Process group tags
+struct mpi_process_group_tag : virtual parallel::linear_process_group_tag { };
+
+class mpi_process_group
+{
+ struct impl;
+
+ public:
+ /// Number of tags available to each data structure.
+ static const int max_tags = 256;
+
+ /**
+ * The type of a "receive" handler, that will be provided with
+ * (source, tag) pairs when a message is received. Users can provide a
+ * receive handler for a distributed data structure, for example, to
+ * automatically pick up and respond to messages as needed.
+ */
+ typedef function<void(int source, int tag)> receiver_type;
+
+ /**
+ * The type of a handler for the on-synchronize event, which will be
+ * executed at the beginning of synchronize().
+ */
+ typedef function0<void> on_synchronize_event_type;
+
+ /// Used as a tag to help create an "empty" process group.
+ struct create_empty {};
+
+ /// The type used to buffer message data
+ typedef boost::mpi::packed_oprimitive::buffer_type buffer_type;
+
+ /// The type used to identify a process
+ typedef int process_id_type;
+
+ /// The type used to count the number of processes
+ typedef int process_size_type;
+
+ /// The type of communicator used to transmit data via MPI
+ typedef boost::mpi::communicator communicator_type;
+
+ /// Classification of the capabilities of this process group
+ struct communication_category
+ : virtual parallel::bsp_process_group_tag,
+ virtual mpi_process_group_tag { };
+
+ // TBD: We can eliminate the "source" field and possibly the
+ // "offset" field.
+ struct message_header {
+ /// The process that sent the message
+ process_id_type source;
+
+ /// The message tag
+ int tag;
+
+ /// The offset of the message into the buffer
+ std::size_t offset;
+
+ /// The length of the message in the buffer, in bytes
+ std::size_t bytes;
+
+ template <class Archive>
+ void serialize(Archive& ar, int)
+ {
+ ar & source & tag & offset & bytes;
+ }
+ };
+
+ /**
+ * Stores the outgoing messages for a particular processor.
+ *
+ * @todo Evaluate whether we should use a deque instance, which
+ * would reduce could reduce the cost of "sending" messages but
+ * increases the time spent in the synchronization step.
+ */
+ struct outgoing_messages {
+ outgoing_messages() {}
+ ~outgoing_messages() {}
+
+ std::vector<message_header> headers;
+ buffer_type buffer;
+
+ template <class Archive>
+ void serialize(Archive& ar, int)
+ {
+ ar & headers & buffer;
+ }
+
+ void swap(outgoing_messages& x)
+ {
+ headers.swap(x.headers);
+ buffer.swap(x.buffer);
+ }
+ };
+
+private:
+ /**
+ * Virtual base from which every trigger will be launched. See @c
+ * trigger_launcher for more information.
+ */
+ class trigger_base : boost::noncopyable
+ {
+ public:
+ explicit trigger_base(int tag) : tag_(tag) { }
+
+ /// Retrieve the tag associated with this trigger
+ int tag() const { return tag_; }
+
+ virtual ~trigger_base() { }
+
+ /**
+ * Invoked to receive a message that matches a particular trigger.
+ *
+ * @param source the source of the message
+ * @param tag the (local) tag of the message
+ * @param context the context under which the trigger is being
+ * invoked
+ */
+ virtual void
+ receive(mpi_process_group const& pg, int source, int tag,
+ trigger_receive_context context, int block=-1) const = 0;
+
+ protected:
+ // The message tag associated with this trigger
+ int tag_;
+ };
+
+ /**
+ * Launches a specific handler in response to a trigger. This
+ * function object wraps up the handler function object and a buffer
+ * for incoming data.
+ */
+ template<typename Type, typename Handler>
+ class trigger_launcher : public trigger_base
+ {
+ public:
+ explicit trigger_launcher(mpi_process_group& self, int tag,
+ const Handler& handler)
+ : trigger_base(tag), self(self), handler(handler)
+ {}
+
+ void
+ receive(mpi_process_group const& pg, int source, int tag,
+ trigger_receive_context context, int block=-1) const;
+
+ private:
+ mpi_process_group& self;
+ mutable Handler handler;
+ };
+
+ /**
+ * Launches a specific handler with a message reply in response to a
+ * trigger. This function object wraps up the handler function
+ * object and a buffer for incoming data.
+ */
+ template<typename Type, typename Handler>
+ class reply_trigger_launcher : public trigger_base
+ {
+ public:
+ explicit reply_trigger_launcher(mpi_process_group& self, int tag,
+ const Handler& handler)
+ : trigger_base(tag), self(self), handler(handler)
+ {}
+
+ void
+ receive(mpi_process_group const& pg, int source, int tag,
+ trigger_receive_context context, int block=-1) const;
+
+ private:
+ mpi_process_group& self;
+ mutable Handler handler;
+ };
+
+ template<typename Type, typename Handler>
+ class global_trigger_launcher : public trigger_base
+ {
+ public:
+ explicit global_trigger_launcher(mpi_process_group& self, int tag,
+ const Handler& handler)
+ : trigger_base(tag), handler(handler)
+ {
+ }
+
+ void
+ receive(mpi_process_group const& pg, int source, int tag,
+ trigger_receive_context context, int block=-1) const;
+
+ private:
+ mutable Handler handler;
+ // TBD: do not forget to cancel any outstanding Irecv when deleted,
+ // if we decide to use Irecv
+ };
+
+ template<typename Type, typename Handler>
+ class global_irecv_trigger_launcher : public trigger_base
+ {
+ public:
+ explicit global_irecv_trigger_launcher(mpi_process_group& self, int tag,
+ const Handler& handler, int sz)
+ : trigger_base(tag), handler(handler), buffer_size(sz)
+ {
+ prepare_receive(self,tag);
+ }
+
+ void
+ receive(mpi_process_group const& pg, int source, int tag,
+ trigger_receive_context context, int block=-1) const;
+
+ private:
+ void prepare_receive(mpi_process_group const& pg, int tag, bool force=false) const;
+ Handler handler;
+ int buffer_size;
+ // TBD: do not forget to cancel any outstanding Irecv when deleted,
+ // if we decide to use Irecv
+ };
+
+public:
+ /**
+ * Construct a new BSP process group from an MPI communicator. The
+ * MPI communicator will be duplicated to create a new communicator
+ * for this process group to use.
+ */
+ mpi_process_group(communicator_type parent_comm = communicator_type());
+
+ /**
+ * Construct a new BSP process group from an MPI communicator. The
+ * MPI communicator will be duplicated to create a new communicator
+ * for this process group to use. This constructor allows to tune the
+ * size of message batches.
+ *
+ * @param num_headers The maximum number of headers in a message batch
+ *
+ * @param buffer_size The maximum size of the message buffer in a batch.
+ *
+ */
+ mpi_process_group( std::size_t num_headers, std::size_t buffer_size,
+ communicator_type parent_comm = communicator_type());
+
+ /**
+ * Construct a copy of the BSP process group for a new distributed
+ * data structure. This data structure will synchronize with all
+ * other members of the process group's equivalence class (including
+ * @p other), but will have its own set of tags.
+ *
+ * @param other The process group that this new process group will
+ * be based on, using a different set of tags within the same
+ * communication and synchronization space.
+ *
+ * @param handler A message handler that will be passed (source,
+ * tag) pairs for each message received by this data
+ * structure. The handler is expected to receive the messages
+ * immediately. The handler can be changed after-the-fact by
+ * calling @c replace_handler.
+ *
+ * @param out_of_band_receive An anachronism. TODO: remove this.
+ */
+ mpi_process_group(const mpi_process_group& other,
+ const receiver_type& handler,
+ bool out_of_band_receive = false);
+
+ /**
+ * Construct a copy of the BSP process group for a new distributed
+ * data structure. This data structure will synchronize with all
+ * other members of the process group's equivalence class (including
+ * @p other), but will have its own set of tags.
+ */
+ mpi_process_group(const mpi_process_group& other,
+ attach_distributed_object,
+ bool out_of_band_receive = false);
+
+ /**
+ * Create an "empty" process group, with no information. This is an
+ * internal routine that users should never need.
+ */
+ explicit mpi_process_group(create_empty) {}
+
+ /**
+ * Destroys this copy of the process group.
+ */
+ ~mpi_process_group();
+
+ /**
+ * Replace the current message handler with a new message handler.
+ *
+ * @param handle The new message handler.
+ * @param out_of_band_receive An anachronism: remove this
+ */
+ void replace_handler(const receiver_type& handler,
+ bool out_of_band_receive = false);
+
+ /**
+ * Turns this process group into the process group for a new
+ * distributed data structure or object, allocating its own tag
+ * block.
+ */
+ void make_distributed_object();
+
+ /**
+ * Replace the handler to be invoked at the beginning of synchronize.
+ */
+ void
+ replace_on_synchronize_handler(const on_synchronize_event_type& handler = 0);
+
+ /**
+ * Return the block number of the current data structure. A value of
+ * 0 indicates that this particular instance of the process group is
+ * not associated with any distributed data structure.
+ */
+ int my_block_number() const { return block_num? *block_num : 0; }
+
+ /**
+ * Encode a block number/tag pair into a single encoded tag for
+ * transmission.
+ */
+ int encode_tag(int block_num, int tag) const
+ { return block_num * max_tags + tag; }
+
+ /**
+ * Decode an encoded tag into a block number/tag pair.
+ */
+ std::pair<int, int> decode_tag(int encoded_tag) const
+ { return std::make_pair(encoded_tag / max_tags, encoded_tag % max_tags); }
+
+ // @todo Actually write up the friend declarations so these could be
+ // private.
+
+ // private:
+
+ /** Allocate a block of tags for this instance. The block should not
+ * have been allocated already, e.g., my_block_number() ==
+ * 0. Returns the newly-allocated block number.
+ */
+ int allocate_block(bool out_of_band_receive = false);
+
+ /** Potentially emit a receive event out of band. Returns true if an event
+ * was actually sent, false otherwise.
+ */
+ bool maybe_emit_receive(int process, int encoded_tag) const;
+
+ /** Emit a receive event. Returns true if an event was actually
+ * sent, false otherwise.
+ */
+ bool emit_receive(int process, int encoded_tag) const;
+
+ /** Emit an on-synchronize event to all block handlers. */
+ void emit_on_synchronize() const;
+
+ /** Retrieve a reference to the stored receiver in this block. */
+ template<typename Receiver>
+ Receiver* get_receiver();
+
+ template<typename T>
+ void
+ send_impl(int dest, int tag, const T& value,
+ mpl::true_ /*is_mpi_datatype*/) const;
+
+ template<typename T>
+ void
+ send_impl(int dest, int tag, const T& value,
+ mpl::false_ /*is_mpi_datatype*/) const;
+
+ template<typename T>
+ typename disable_if<boost::mpi::is_mpi_datatype<T>, void>::type
+ array_send_impl(int dest, int tag, const T values[], std::size_t n) const;
+
+ template<typename T>
+ bool
+ receive_impl(int source, int tag, T& value,
+ mpl::true_ /*is_mpi_datatype*/) const;
+
+ template<typename T>
+ bool
+ receive_impl(int source, int tag, T& value,
+ mpl::false_ /*is_mpi_datatype*/) const;
+
+ // Receive an array of values
+ template<typename T>
+ typename disable_if<boost::mpi::is_mpi_datatype<T>, bool>::type
+ array_receive_impl(int source, int tag, T* values, std::size_t& n) const;
+
+ optional<std::pair<mpi_process_group::process_id_type, int> > probe() const;
+
+ void synchronize() const;
+
+ operator bool() { return impl_; }
+
+ mpi_process_group base() const;
+
+ /**
+ * Create a new trigger for a specific message tag. Triggers handle
+ * out-of-band messaging, and the handler itself will be called
+ * whenever a message is available. The handler itself accepts four
+ * arguments: the source of the message, the message tag (which will
+ * be the same as @p tag), the message data (of type @c Type), and a
+ * boolean flag that states whether the message was received
+ * out-of-band. The last will be @c true for out-of-band receives,
+ * or @c false for receives at the end of a synchronization step.
+ */
+ template<typename Type, typename Handler>
+ void trigger(int tag, const Handler& handler);
+
+ /**
+ * Create a new trigger for a specific message tag, along with a way
+ * to send a reply with data back to the sender. Triggers handle
+ * out-of-band messaging, and the handler itself will be called
+ * whenever a message is available. The handler itself accepts four
+ * arguments: the source of the message, the message tag (which will
+ * be the same as @p tag), the message data (of type @c Type), and a
+ * boolean flag that states whether the message was received
+ * out-of-band. The last will be @c true for out-of-band receives,
+ * or @c false for receives at the end of a synchronization
+ * step. The handler also returns a value, which will be routed back
+ * to the sender.
+ */
+ template<typename Type, typename Handler>
+ void trigger_with_reply(int tag, const Handler& handler);
+
+ template<typename Type, typename Handler>
+ void global_trigger(int tag, const Handler& handler, std::size_t buffer_size=0);
+
+
+
+ /**
+ * Poll for any out-of-band messages. This routine will check if any
+ * out-of-band messages are available. Those that are available will
+ * be handled immediately, if possible.
+ *
+ * @returns if an out-of-band message has been received, but we are
+ * unable to actually receive the message, a (source, tag) pair will
+ * be returned. Otherwise, returns an empty optional.
+ *
+ * @param wait When true, we should block until a message comes in.
+ *
+ * @param synchronizing whether we are currently synchronizing the
+ * process group
+ */
+ optional<std::pair<int, int> >
+ poll(bool wait = false, int block = -1, bool synchronizing = false) const;
+
+ /**
+ * Determines the context of the trigger currently executing. If
+ * multiple triggers are executing (recursively), then the context
+ * for the most deeply nested trigger will be returned. If no
+ * triggers are executing, returns @c trc_none. This might be used,
+ * for example, to determine whether a reply to a message should
+ * itself be sent out-of-band or whether it can go via the normal,
+ * slower communication route.
+ */
+ trigger_receive_context trigger_context() const;
+
+ /// INTERNAL ONLY
+ void receive_batch(process_id_type source, outgoing_messages& batch) const;
+
+ /// INTERNAL ONLY
+ ///
+ /// Determine the actual communicator and tag will be used for a
+ /// transmission with the given tag.
+ std::pair<boost::mpi::communicator, int>
+ actual_communicator_and_tag(int tag, int block) const;
+
+ /// set the size of the message buffer used for buffered oob sends
+
+ static void set_message_buffer_size(std::size_t s);
+
+ /// get the size of the message buffer used for buffered oob sends
+
+ static std::size_t message_buffer_size();
+ static int old_buffer_size;
+ static void* old_buffer;
+private:
+
+ void install_trigger(int tag, int block,
+ shared_ptr<trigger_base> const& launcher);
+
+ void poll_requests(int block=-1) const;
+
+
+ // send a batch if the buffer is full now or would get full
+ void maybe_send_batch(process_id_type dest) const;
+
+ // actually send a batch
+ void send_batch(process_id_type dest, outgoing_messages& batch) const;
+ void send_batch(process_id_type dest) const;
+
+ void pack_headers() const;
+
+ /**
+ * Process a batch of incoming messages immediately.
+ *
+ * @param source the source of these messages
+ */
+ void process_batch(process_id_type source) const;
+ void receive_batch(boost::mpi::status& status) const;
+
+ //void free_finished_sends() const;
+
+ /// Status messages used internally by the process group
+ enum status_messages {
+ /// the first of the reserved message tags
+ msg_reserved_first = 126,
+ /// Sent from a processor when sending batched messages
+ msg_batch = 126,
+ /// Sent from a processor when sending large batched messages, larger than
+ /// the maximum buffer size for messages to be received by MPI_Irecv
+ msg_large_batch = 127,
+ /// Sent from a source processor to everyone else when that
+ /// processor has entered the synchronize() function.
+ msg_synchronizing = 128,
+ /// the last of the reserved message tags
+ msg_reserved_last = 128
+ };
+
+ /**
+ * Description of a block of tags associated to a particular
+ * distributed data structure. This structure will live as long as
+ * the distributed data structure is around, and will be used to
+ * help send messages to the data structure.
+ */
+ struct block_type
+ {
+ block_type() { }
+
+ /// Handler for receive events
+ receiver_type on_receive;
+
+ /// Handler executed at the start of synchronization
+ on_synchronize_event_type on_synchronize;
+
+ /// Individual message triggers. Note: at present, this vector is
+ /// indexed by the (local) tag of the trigger. Any tags that
+ /// don't have triggers will have NULL pointers in that spot.
+ std::vector<shared_ptr<trigger_base> > triggers;
+ };
+
+ /**
+ * Data structure containing all of the blocks for the distributed
+ * data structures attached to a process group.
+ */
+ typedef std::vector<block_type*> blocks_type;
+
+ /// Iterator into @c blocks_type.
+ typedef blocks_type::iterator block_iterator;
+
+ /**
+ * Deleter used to deallocate a block when its distributed data
+ * structure is destroyed. This type will be used as the deleter for
+ * @c block_num.
+ */
+ struct deallocate_block;
+
+ static std::vector<char> message_buffer;
+
+public:
+ /**
+ * Data associated with the process group and all of its attached
+ * distributed data structures.
+ */
+ shared_ptr<impl> impl_;
+
+ /**
+ * When non-null, indicates that this copy of the process group is
+ * associated with a particular distributed data structure. The
+ * integer value contains the block number (a value > 0) associated
+ * with that data structure. The deleter for this @c shared_ptr is a
+ * @c deallocate_block object that will deallocate the associated
+ * block in @c impl_->blocks.
+ */
+ shared_ptr<int> block_num;
+
+ /**
+ * Rank of this process, to avoid having to call rank() repeatedly.
+ */
+ int rank;
+
+ /**
+ * Number of processes in this process group, to avoid having to
+ * call communicator::size() repeatedly.
+ */
+ int size;
+};
+
+
+
+inline mpi_process_group::process_id_type
+process_id(const mpi_process_group& pg)
+{ return pg.rank; }
+
+inline mpi_process_group::process_size_type
+num_processes(const mpi_process_group& pg)
+{ return pg.size; }
+
+mpi_process_group::communicator_type communicator(const mpi_process_group& pg);
+
+template<typename T>
+void
+send(const mpi_process_group& pg, mpi_process_group::process_id_type dest,
+ int tag, const T& value);
+
+template<typename InputIterator>
+void
+send(const mpi_process_group& pg, mpi_process_group::process_id_type dest,
+ int tag, InputIterator first, InputIterator last);
+
+template<typename T>
+inline void
+send(const mpi_process_group& pg, mpi_process_group::process_id_type dest,
+ int tag, T* first, T* last)
+{ send(pg, dest, tag, first, last - first); }
+
+template<typename T>
+inline void
+send(const mpi_process_group& pg, mpi_process_group::process_id_type dest,
+ int tag, const T* first, const T* last)
+{ send(pg, dest, tag, first, last - first); }
+
+template<typename T>
+mpi_process_group::process_id_type
+receive(const mpi_process_group& pg, int tag, T& value);
+
+template<typename T>
+mpi_process_group::process_id_type
+receive(const mpi_process_group& pg,
+ mpi_process_group::process_id_type source, int tag, T& value);
+
+optional<std::pair<mpi_process_group::process_id_type, int> >
+probe(const mpi_process_group& pg);
+
+void synchronize(const mpi_process_group& pg);
+
+template<typename T, typename BinaryOperation>
+T*
+all_reduce(const mpi_process_group& pg, T* first, T* last, T* out,
+ BinaryOperation bin_op);
+
+template<typename T, typename BinaryOperation>
+T*
+scan(const mpi_process_group& pg, T* first, T* last, T* out,
+ BinaryOperation bin_op);
+
+template<typename InputIterator, typename T>
+void
+all_gather(const mpi_process_group& pg,
+ InputIterator first, InputIterator last, std::vector<T>& out);
+
+template<typename InputIterator>
+mpi_process_group
+process_subgroup(const mpi_process_group& pg,
+ InputIterator first, InputIterator last);
+
+template<typename T>
+void
+broadcast(const mpi_process_group& pg, T& val,
+ mpi_process_group::process_id_type root);
+
+
+/*******************************************************************
+ * Out-of-band communication *
+ *******************************************************************/
+
+template<typename T>
+typename enable_if<boost::mpi::is_mpi_datatype<T> >::type
+send_oob(const mpi_process_group& pg, mpi_process_group::process_id_type dest,
+ int tag, const T& value, int block=-1)
+{
+ using boost::mpi::get_mpi_datatype;
+
+ // Determine the actual message tag we will use for the send, and which
+ // communicator we will use.
+ std::pair<boost::mpi::communicator, int> actual
+ = pg.actual_communicator_and_tag(tag, block);
+
+#ifdef SEND_OOB_BSEND
+ if (mpi_process_group::message_buffer_size()) {
+ MPI_Bsend(const_cast<T*>(&value), 1, get_mpi_datatype<T>(value), dest,
+ actual.second, actual.first);
+ return;
+ }
+#endif
+ MPI_Request request;
+ MPI_Isend(const_cast<T*>(&value), 1, get_mpi_datatype<T>(value), dest,
+ actual.second, actual.first, &request);
+
+ int done=0;
+ do {
+ pg.poll();
+ MPI_Test(&request,&done,MPI_STATUS_IGNORE);
+ } while (!done);
+}
+
+template<typename T>
+typename disable_if<boost::mpi::is_mpi_datatype<T> >::type
+send_oob(const mpi_process_group& pg, mpi_process_group::process_id_type dest,
+ int tag, const T& value, int block=-1)
+{
+ using boost::mpi::packed_oarchive;
+
+ // Determine the actual message tag we will use for the send, and which
+ // communicator we will use.
+ std::pair<boost::mpi::communicator, int> actual
+ = pg.actual_communicator_and_tag(tag, block);
+
+ // Serialize the data into a buffer
+ packed_oarchive out(actual.first);
+ out << value;
+ std::size_t size = out.size();
+
+ // Send the actual message data
+#ifdef SEND_OOB_BSEND
+ if (mpi_process_group::message_buffer_size()) {
+ MPI_Bsend(const_cast<void*>(out.address()), size, MPI_PACKED,
+ dest, actual.second, actual.first);
+ return;
+ }
+#endif
+ MPI_Request request;
+ MPI_Isend(const_cast<void*>(out.address()), size, MPI_PACKED,
+ dest, actual.second, actual.first, &request);
+
+ int done=0;
+ do {
+ pg.poll();
+ MPI_Test(&request,&done,MPI_STATUS_IGNORE);
+ } while (!done);
+}
+
+template<typename T>
+typename enable_if<boost::mpi::is_mpi_datatype<T> >::type
+receive_oob(const mpi_process_group& pg,
+ mpi_process_group::process_id_type source, int tag, T& value, int block=-1);
+
+template<typename T>
+typename disable_if<boost::mpi::is_mpi_datatype<T> >::type
+receive_oob(const mpi_process_group& pg,
+ mpi_process_group::process_id_type source, int tag, T& value, int block=-1);
+
+template<typename SendT, typename ReplyT>
+typename enable_if<boost::mpi::is_mpi_datatype<ReplyT> >::type
+send_oob_with_reply(const mpi_process_group& pg,
+ mpi_process_group::process_id_type dest,
+ int tag, const SendT& send_value, ReplyT& reply_value,
+ int block = -1);
+
+template<typename SendT, typename ReplyT>
+typename disable_if<boost::mpi::is_mpi_datatype<ReplyT> >::type
+send_oob_with_reply(const mpi_process_group& pg,
+ mpi_process_group::process_id_type dest,
+ int tag, const SendT& send_value, ReplyT& reply_value,
+ int block = -1);
+
+} } } // end namespace boost::graph::distributed
+
+BOOST_IS_BITWISE_SERIALIZABLE(boost::graph::distributed::mpi_process_group::message_header)
+namespace boost { namespace mpi {
+ template<>
+ struct is_mpi_datatype<boost::graph::distributed::mpi_process_group::message_header> : mpl::true_ { };
+} } // end namespace boost::mpi
+
+namespace std {
+/// optimized swap for outgoing messages
+inline void
+swap(boost::graph::distributed::mpi_process_group::outgoing_messages& x,
+ boost::graph::distributed::mpi_process_group::outgoing_messages& y)
+{
+ x.swap(y);
+}
+
+
+}
+
+BOOST_CLASS_IMPLEMENTATION(boost::graph::distributed::mpi_process_group::outgoing_messages,object_serializable)
+BOOST_CLASS_TRACKING(boost::graph::distributed::mpi_process_group::outgoing_messages,track_never)
+
+#include <boost/graph/distributed/detail/mpi_process_group.tpp>
+
+#endif // BOOST_PARALLEL_MPI_MPI_PROCESS_GROUP_HPP

Added: trunk/boost/graph/distributed/named_graph.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/named_graph.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,1239 @@
+// Copyright (C) 2007 Douglas Gregor
+// Copyright (C) 2007 Hartmut Kaiser
+
+// 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)
+
+// TODO:
+// - Cache (some) remote vertex names?
+#ifndef BOOST_GRAPH_DISTRIBUTED_NAMED_GRAPH_HPP
+#define BOOST_GRAPH_DISTRIBUTED_NAMED_GRAPH_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/graph/named_graph.hpp>
+#include <boost/functional/hash.hpp>
+#include <boost/variant.hpp>
+#include <boost/graph/parallel/simple_trigger.hpp>
+#include <boost/graph/parallel/process_group.hpp>
+#include <boost/graph/parallel/detail/property_holders.hpp>
+
+namespace boost { namespace graph { namespace distributed {
+
+using boost::parallel::trigger_receive_context;
+using boost::detail::parallel::pair_with_property;
+
+/*******************************************************************
+ * Hashed distribution of named entities *
+ *******************************************************************/
+
+template<typename T>
+struct hashed_distribution
+{
+ template<typename ProcessGroup>
+ hashed_distribution(const ProcessGroup& pg, std::size_t /*num_vertices*/ = 0)
+ : n(num_processes(pg)) { }
+
+ int operator()(const T& value) const
+ {
+ return hasher(value) % n;
+ }
+
+ std::size_t n;
+ hash<T> hasher;
+};
+
+/// Specialization for named graphs
+template <typename InDistribution, typename VertexProperty, typename VertexSize,
+ typename ProcessGroup,
+ typename ExtractName
+ = typename internal_vertex_name<VertexProperty>::type>
+struct select_distribution
+{
+private:
+ /// The type used to name vertices in the graph
+ typedef typename remove_cv<
+ typename remove_reference<
+ typename ExtractName::result_type>::type>::type
+ vertex_name_type;
+
+public:
+ /**
+ * The @c type field provides a distribution object that maps
+ * vertex names to processors. The distribution object will be
+ * constructed with the process group over which communication will
+ * occur. The distribution object shall also be a function
+ * object mapping from the type of the name to a processor number
+ * in @c [0, @c p) (for @c p processors). By default, the mapping
+ * function uses the @c boost::hash value of the name, modulo @c p.
+ */
+ typedef typename mpl::if_<is_same<InDistribution, defaultS>,
+ hashed_distribution<vertex_name_type>,
+ InDistribution>::type
+ type;
+
+ /// for named graphs the default type is the same as the stored distribution
+ /// type
+ typedef type default_type;
+};
+
+/// Specialization for non-named graphs
+template <typename InDistribution, typename VertexProperty, typename VertexSize,
+ typename ProcessGroup>
+struct select_distribution<InDistribution, VertexProperty, VertexSize,
+ ProcessGroup, void>
+{
+ /// the distribution type stored in the graph for non-named graphs should be
+ /// the variant_distribution type
+ typedef typename mpl::if_<is_same<InDistribution, defaultS>,
+ boost::parallel::variant_distribution<ProcessGroup,
+ VertexSize>,
+ InDistribution>::type type;
+
+ /// default_type is used as the distribution functor for the
+ /// adjacency_list, it should be parallel::block by default
+ typedef typename mpl::if_<is_same<InDistribution, defaultS>,
+ boost::parallel::block, type>::type
+ default_type;
+};
+
+
+/*******************************************************************
+ * Named graph mixin *
+ *******************************************************************/
+
+/**
+ * named_graph is a mixin that provides names for the vertices of a
+ * graph, including a mapping from names to vertices. Graph types that
+ * may or may not be have vertex names (depending on the properties
+ * supplied by the user) should use maybe_named_graph.
+ *
+ * Template parameters:
+ *
+ * Graph: the graph type that derives from named_graph
+ *
+ * Vertex: the type of a vertex descriptor in Graph. Note: we cannot
+ * use graph_traits here, because the Graph is not yet defined.
+ *
+ * VertexProperty: the type of the property stored along with the
+ * vertex.
+ *
+ * ProcessGroup: the process group over which the distributed name
+ * graph mixin will communicate.
+ */
+template<typename Graph, typename Vertex, typename Edge, typename Config>
+class named_graph
+{
+public:
+ /// Messages passed within the distributed named graph
+ enum message_kind {
+ /**
+ * Requests the addition of a vertex on a remote processor. The
+ * message data is a @c vertex_name_type.
+ */
+ msg_add_vertex_name,
+
+ /**
+ * Requests the addition of a vertex on a remote processor. The
+ * message data is a @c vertex_name_type. The remote processor
+ * will send back a @c msg_add_vertex_name_reply message
+ * containing the vertex descriptor.
+ */
+ msg_add_vertex_name_with_reply,
+
+ /**
+ * Requests the vertex descriptor corresponding to the given
+ * vertex name. The remote process will reply with a
+ * @c msg_find_vertex_reply message containing the answer.
+ */
+ msg_find_vertex,
+
+ /**
+ * Requests the addition of an edge on a remote processor. The
+ * data stored in these messages is a @c pair<source, target>@,
+ * where @c source and @c target may be either names (of type @c
+ * vertex_name_type) or vertex descriptors, depending on what
+ * information we have locally.
+ */
+ msg_add_edge_name_name,
+ msg_add_edge_vertex_name,
+ msg_add_edge_name_vertex,
+
+ /**
+ * These messages are identical to msg_add_edge_*_*, except that
+ * the process actually adding the edge will send back a @c
+ * pair<edge_descriptor,bool>
+ */
+ msg_add_edge_name_name_with_reply,
+ msg_add_edge_vertex_name_with_reply,
+ msg_add_edge_name_vertex_with_reply,
+
+ /**
+ * Requests the addition of an edge with a property on a remote
+ * processor. The data stored in these messages is a @c
+ * pair<vertex_property_type, pair<source, target>>@, where @c
+ * source and @c target may be either names (of type @c
+ * vertex_name_type) or vertex descriptors, depending on what
+ * information we have locally.
+ */
+ msg_add_edge_name_name_with_property,
+ msg_add_edge_vertex_name_with_property,
+ msg_add_edge_name_vertex_with_property,
+
+ /**
+ * These messages are identical to msg_add_edge_*_*_with_property,
+ * except that the process actually adding the edge will send back
+ * a @c pair<edge_descriptor,bool>.
+ */
+ msg_add_edge_name_name_with_reply_and_property,
+ msg_add_edge_vertex_name_with_reply_and_property,
+ msg_add_edge_name_vertex_with_reply_and_property
+ };
+
+ /// The vertex descriptor type
+ typedef Vertex vertex_descriptor;
+
+ /// The edge descriptor type
+ typedef Edge edge_descriptor;
+
+ /// The vertex property type
+ typedef typename Config::vertex_property_type vertex_property_type;
+
+ /// The vertex property type
+ typedef typename Config::edge_property_type edge_property_type;
+
+ /// The type used to extract names from the property structure
+ typedef typename internal_vertex_name<vertex_property_type>::type
+ extract_name_type;
+
+ /// The type used to name vertices in the graph
+ typedef typename remove_cv<
+ typename remove_reference<
+ typename extract_name_type::result_type>::type>::type
+ vertex_name_type;
+
+ /// The type used to distribute named vertices in the graph
+ typedef typename Config::distribution_type distribution_type;
+ typedef typename Config::base_distribution_type base_distribution_type;
+
+ /// The type used for communication in the distributed structure
+ typedef typename Config::process_group_type process_group_type;
+
+ /// Type used to identify processes
+ typedef typename process_group_type::process_id_type process_id_type;
+
+ /// a reference to this class, which is used for disambiguation of the
+ // add_vertex function
+ typedef named_graph named_graph_type;
+
+ /// Structure returned when adding a vertex by vertex name
+ struct lazy_add_vertex;
+ friend struct lazy_add_vertex;
+
+ /// Structure returned when adding an edge by vertex name
+ struct lazy_add_edge;
+ friend struct lazy_add_edge;
+
+ /// Structure returned when adding an edge by vertex name with a property
+ struct lazy_add_edge_with_property;
+ friend struct lazy_add_edge_with_property;
+
+ explicit named_graph(const process_group_type& pg);
+
+ named_graph(const process_group_type& pg, const base_distribution_type& distribution);
+
+ /// Set up triggers, but only for the BSP process group
+ void setup_triggers();
+
+ /// Retrieve the derived instance
+ Graph& derived() { return static_cast<Graph&>(*this); }
+ const Graph& derived() const { return static_cast<const Graph&>(*this); }
+
+ /// Retrieve the process group
+ process_group_type& process_group() { return process_group_; }
+ const process_group_type& process_group() const { return process_group_; }
+
+ // Retrieve the named distribution
+ distribution_type& named_distribution() { return distribution_; }
+ const distribution_type& named_distribution() const { return distribution_; }
+
+ /// Notify the named_graph that we have added the given vertex. This
+ /// is a no-op.
+ void added_vertex(Vertex) { }
+
+ /// Notify the named_graph that we are removing the given
+ /// vertex. This is a no-op.
+ void removing_vertex(Vertex) { }
+
+ /// Notify the named_graph that we are clearing the graph
+ void clearing_graph() { }
+
+ /// Retrieve the owner of a given vertex based on the properties
+ /// associated with that vertex. This operation just returns the
+ /// number of the local processor, adding all vertices locally.
+ process_id_type owner_by_property(const vertex_property_type&);
+
+protected:
+ void
+ handle_add_vertex_name(int source, int tag, const vertex_name_type& msg,
+ trigger_receive_context);
+
+ vertex_descriptor
+ handle_add_vertex_name_with_reply(int source, int tag,
+ const vertex_name_type& msg,
+ trigger_receive_context);
+
+ boost::parallel::detail::untracked_pair<vertex_descriptor, bool>
+ handle_find_vertex(int source, int tag, const vertex_name_type& msg,
+ trigger_receive_context);
+
+ template<typename U, typename V>
+ void handle_add_edge(int source, int tag, const boost::parallel::detail::untracked_pair<U, V>& msg,
+ trigger_receive_context);
+
+ template<typename U, typename V>
+ boost::parallel::detail::untracked_pair<edge_descriptor, bool>
+ handle_add_edge_with_reply(int source, int tag, const boost::parallel::detail::untracked_pair<U, V>& msg,
+ trigger_receive_context);
+
+ template<typename U, typename V>
+ void
+ handle_add_edge_with_property
+ (int source, int tag,
+ const pair_with_property<U, V, edge_property_type>& msg,
+ trigger_receive_context);
+
+ template<typename U, typename V>
+ boost::parallel::detail::untracked_pair<edge_descriptor, bool>
+ handle_add_edge_with_reply_and_property
+ (int source, int tag,
+ const pair_with_property<U, V, edge_property_type>& msg,
+ trigger_receive_context);
+
+ /// The process group for this distributed data structure
+ process_group_type process_group_;
+
+ /// The distribution we will use to map names to processors
+ distribution_type distribution_;
+};
+
+/// Helper macro containing the template parameters of named_graph
+#define BGL_NAMED_GRAPH_PARAMS \
+ typename Graph, typename Vertex, typename Edge, typename Config
+/// Helper macro containing the named_graph<...> instantiation
+#define BGL_NAMED_GRAPH \
+ named_graph<Graph, Vertex, Edge, Config>
+
+/**
+ * Data structure returned from add_vertex that will "lazily" add the
+ * vertex, either when it is converted to a @c vertex_descriptor or
+ * when the most recent copy has been destroyed.
+ */
+template<BGL_NAMED_GRAPH_PARAMS>
+struct BGL_NAMED_GRAPH::lazy_add_vertex
+{
+ /// Construct a new lazyily-added vertex
+ lazy_add_vertex(named_graph& self, const vertex_name_type& name)
+ : self(self), name(name), committed(false) { }
+
+ /// Transfer responsibility for adding the vertex from the source of
+ /// the copy to the newly-constructed opbject.
+ lazy_add_vertex(const lazy_add_vertex& other)
+ : self(self), name(other.name), committed(other.committed)
+ {
+ other.committed = true;
+ }
+
+ /// If the vertex has not been added yet, add it
+ ~lazy_add_vertex();
+
+ /// Add the vertex and return its descriptor. This conversion can
+ /// only occur once, and only when this object is responsible for
+ /// creating the vertex.
+ operator vertex_descriptor() const { return commit(); }
+
+ /// Add the vertex and return its descriptor. This can only be
+ /// called once, and only when this object is responsible for
+ /// creating the vertex.
+ vertex_descriptor commit() const;
+
+protected:
+ named_graph& self;
+ vertex_name_type name;
+ mutable bool committed;
+};
+
+template<BGL_NAMED_GRAPH_PARAMS>
+BGL_NAMED_GRAPH::lazy_add_vertex::~lazy_add_vertex()
+{
+ typedef typename BGL_NAMED_GRAPH::process_id_type process_id_type;
+
+ /// If this vertex has already been created or will be created by
+ /// someone else, or if someone threw an exception, we will not
+ /// create the vertex now.
+ if (committed || std::uncaught_exception())
+ return;
+
+ committed = true;
+
+ process_id_type owner = self.named_distribution()(name);
+ if (owner == process_id(self.process_group()))
+ /// Add the vertex locally
+ add_vertex(self.derived().base().vertex_constructor(name), self.derived());
+ else
+ /// Ask the owner of the vertex to add a vertex with this name
+ send(self.process_group(), owner, msg_add_vertex_name, name);
+}
+
+template<BGL_NAMED_GRAPH_PARAMS>
+typename BGL_NAMED_GRAPH::vertex_descriptor
+BGL_NAMED_GRAPH::lazy_add_vertex::commit() const
+{
+ typedef typename BGL_NAMED_GRAPH::process_id_type process_id_type;
+ assert (!committed);
+ committed = true;
+
+ process_id_type owner = self.named_distribution()(name);
+ if (owner == process_id(self.process_group()))
+ /// Add the vertex locally
+ return add_vertex(self.derived().base().vertex_constructor(name),
+ self.derived());
+ else {
+ /// Ask the owner of the vertex to add a vertex with this name
+ vertex_descriptor result;
+ send_oob_with_reply(self.process_group(), owner,
+ msg_add_vertex_name_with_reply, name, result);
+ return result;
+ }
+}
+
+/**
+ * Data structure returned from add_edge that will "lazily" add the
+ * edge, either when it is converted to a @c
+ * pair<edge_descriptor,bool> or when the most recent copy has been
+ * destroyed.
+ */
+template<BGL_NAMED_GRAPH_PARAMS>
+struct BGL_NAMED_GRAPH::lazy_add_edge
+{
+ /// The graph's edge descriptor
+ typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor;
+
+ /// Add an edge for the edge (u, v) based on vertex names
+ lazy_add_edge(BGL_NAMED_GRAPH& self,
+ const vertex_name_type& u_name,
+ const vertex_name_type& v_name)
+ : self(self), u(u_name), v(v_name), committed(false) { }
+
+ /// Add an edge for the edge (u, v) based on a vertex descriptor and name
+ lazy_add_edge(BGL_NAMED_GRAPH& self,
+ vertex_descriptor u,
+ const vertex_name_type& v_name)
+ : self(self), u(u), v(v_name), committed(false) { }
+
+ /// Add an edge for the edge (u, v) based on a vertex name and descriptor
+ lazy_add_edge(BGL_NAMED_GRAPH& self,
+ const vertex_name_type& u_name,
+ vertex_descriptor v)
+ : self(self), u(u_name), v(v), committed(false) { }
+
+ /// Add an edge for the edge (u, v) based on vertex descriptors
+ lazy_add_edge(BGL_NAMED_GRAPH& self,
+ vertex_descriptor u,
+ vertex_descriptor v)
+ : self(self), u(u), v(v), committed(false) { }
+
+ /// Copy a lazy_add_edge structure, which transfers responsibility
+ /// for adding the edge to the newly-constructed object.
+ lazy_add_edge(const lazy_add_edge& other)
+ : self(other.self), u(other.u), v(other.v), committed(other.committed)
+ {
+ other.committed = true;
+ }
+
+ /// If the edge has not yet been added, add the edge but don't wait
+ /// for a reply.
+ ~lazy_add_edge();
+
+ /// Returns commit().
+ operator std::pair<edge_descriptor, bool>() const { return commit(); }
+
+ // Add the edge. This operation will block if a remote edge is
+ // being added.
+ std::pair<edge_descriptor, bool> commit() const;
+
+protected:
+ BGL_NAMED_GRAPH& self;
+ mutable variant<vertex_descriptor, vertex_name_type> u;
+ mutable variant<vertex_descriptor, vertex_name_type> v;
+ mutable bool committed;
+
+private:
+ // No copy-assignment semantics
+ void operator=(lazy_add_edge&);
+};
+
+template<BGL_NAMED_GRAPH_PARAMS>
+BGL_NAMED_GRAPH::lazy_add_edge::~lazy_add_edge()
+{
+ typedef typename BGL_NAMED_GRAPH::process_id_type process_id_type;
+
+ using boost::parallel::detail::make_untracked_pair;
+
+ /// If this edge has already been created or will be created by
+ /// someone else, or if someone threw an exception, we will not
+ /// create the edge now.
+ if (committed || std::uncaught_exception())
+ return;
+
+ committed = true;
+
+ if (vertex_name_type* v_name = boost::get<vertex_name_type>(&v)) {
+ // We haven't resolved the target vertex to a descriptor yet, so
+ // it must not be local. Send a message to the owner of the target
+ // of the edge. If the owner of the target does not happen to own
+ // the source, it will resolve the target to a vertex descriptor
+ // and pass the message along to the owner of the source.
+ if (vertex_name_type* u_name = boost::get<vertex_name_type>(&u))
+ send(self.process_group(), self.distribution_(*v_name),
+ BGL_NAMED_GRAPH::msg_add_edge_name_name,
+ make_untracked_pair(*u_name, *v_name));
+ else
+ send(self.process_group(), self.distribution_(*v_name),
+ BGL_NAMED_GRAPH::msg_add_edge_vertex_name,
+ make_untracked_pair(boost::get<vertex_descriptor>(u), *v_name));
+ } else {
+ if (vertex_name_type* u_name = boost::get<vertex_name_type>(&u))
+ // We haven't resolved the source vertex to a descriptor yet, so
+ // it must not be local. Send a message to the owner of the
+ // source vertex requesting the edge addition.
+ send(self.process_group(), self.distribution_(*u_name),
+ BGL_NAMED_GRAPH::msg_add_edge_name_vertex,
+ make_untracked_pair(*u_name, boost::get<vertex_descriptor>(v)));
+ else
+ // We have descriptors for both of the vertices, either of which
+ // may be remote or local. Tell the owner of the source vertex
+ // to add the edge (it may be us!).
+ add_edge(boost::get<vertex_descriptor>(u),
+ boost::get<vertex_descriptor>(v),
+ self.derived());
+ }
+}
+
+template<BGL_NAMED_GRAPH_PARAMS>
+std::pair<typename graph_traits<Graph>::edge_descriptor, bool>
+BGL_NAMED_GRAPH::lazy_add_edge::commit() const
+{
+ typedef typename BGL_NAMED_GRAPH::process_id_type process_id_type;
+ using boost::parallel::detail::make_untracked_pair;
+
+ assert(!committed);
+ committed = true;
+
+ /// The result we will return, if we are sending a message to
+ /// request that someone else add the edge.
+ boost::parallel::detail::untracked_pair<edge_descriptor, bool> result;
+
+ /// The owner of the vertex "u"
+ process_id_type u_owner;
+
+ process_id_type rank = process_id(self.process_group());
+ if (const vertex_name_type* u_name = boost::get<vertex_name_type>(&u)) {
+ /// We haven't resolved the source vertex to a descriptor yet, so
+ /// it must not be local.
+ u_owner = self.named_distribution()(*u_name);
+
+ /// Send a message to the remote vertex requesting that it add the
+ /// edge. The message differs depending on whether we have a
+ /// vertex name or a vertex descriptor for the target.
+ if (const vertex_name_type* v_name = boost::get<vertex_name_type>(&v))
+ send_oob_with_reply(self.process_group(), u_owner,
+ BGL_NAMED_GRAPH::msg_add_edge_name_name_with_reply,
+ make_untracked_pair(*u_name, *v_name), result);
+ else
+ send_oob_with_reply(self.process_group(), u_owner,
+ BGL_NAMED_GRAPH::msg_add_edge_name_vertex_with_reply,
+ make_untracked_pair(*u_name,
+ boost::get<vertex_descriptor>(v)),
+ result);
+ } else {
+ /// We have resolved the source vertex to a descriptor, which may
+ /// either be local or remote.
+ u_owner
+ = get(vertex_owner, self.derived(),
+ boost::get<vertex_descriptor>(u));
+ if (u_owner == rank) {
+ /// The source is local. If we need to, resolve the target vertex.
+ if (const vertex_name_type* v_name = boost::get<vertex_name_type>(&v))
+ v = add_vertex(*v_name, self.derived());
+
+ /// Add the edge using vertex descriptors
+ return add_edge(boost::get<vertex_descriptor>(u),
+ boost::get<vertex_descriptor>(v),
+ self.derived());
+ } else {
+ /// The source is remote. Just send a message to its owner
+ /// requesting that the owner add the new edge, either directly
+ /// or via the derived class's add_edge function.
+ if (const vertex_name_type* v_name = boost::get<vertex_name_type>(&v))
+ send_oob_with_reply
+ (self.process_group(), u_owner,
+ BGL_NAMED_GRAPH::msg_add_edge_vertex_name_with_reply,
+ make_untracked_pair(boost::get<vertex_descriptor>(u), *v_name),
+ result);
+ else
+ return add_edge(boost::get<vertex_descriptor>(u),
+ boost::get<vertex_descriptor>(v),
+ self.derived());
+ }
+ }
+
+ // If we get here, the edge has been added remotely and "result"
+ // contains the result of that edge addition.
+ return result;
+}
+
+/**
+ * Data structure returned from add_edge that will "lazily" add the
+ * edge with a property, either when it is converted to a @c
+ * pair<edge_descriptor,bool> or when the most recent copy has been
+ * destroyed.
+ */
+template<BGL_NAMED_GRAPH_PARAMS>
+struct BGL_NAMED_GRAPH::lazy_add_edge_with_property
+{
+ /// The graph's edge descriptor
+ typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor;
+
+ /// The Edge property type for our graph
+ typedef typename Config::edge_property_type edge_property_type;
+
+ /// Add an edge for the edge (u, v) based on vertex names
+ lazy_add_edge_with_property(BGL_NAMED_GRAPH& self,
+ const vertex_name_type& u_name,
+ const vertex_name_type& v_name,
+ const edge_property_type& property)
+ : self(self), u(u_name), v(v_name), property(property), committed(false)
+ {
+ }
+
+ /// Add an edge for the edge (u, v) based on a vertex descriptor and name
+ lazy_add_edge_with_property(BGL_NAMED_GRAPH& self,
+ vertex_descriptor u,
+ const vertex_name_type& v_name,
+ const edge_property_type& property)
+ : self(self), u(u), v(v_name), property(property), committed(false) { }
+
+ /// Add an edge for the edge (u, v) based on a vertex name and descriptor
+ lazy_add_edge_with_property(BGL_NAMED_GRAPH& self,
+ const vertex_name_type& u_name,
+ vertex_descriptor v,
+ const edge_property_type& property)
+ : self(self), u(u_name), v(v), property(property), committed(false) { }
+
+ /// Add an edge for the edge (u, v) based on vertex descriptors
+ lazy_add_edge_with_property(BGL_NAMED_GRAPH& self,
+ vertex_descriptor u,
+ vertex_descriptor v,
+ const edge_property_type& property)
+ : self(self), u(u), v(v), property(property), committed(false) { }
+
+ /// Copy a lazy_add_edge_with_property structure, which transfers
+ /// responsibility for adding the edge to the newly-constructed
+ /// object.
+ lazy_add_edge_with_property(const lazy_add_edge_with_property& other)
+ : self(other.self), u(other.u), v(other.v), property(other.property),
+ committed(other.committed)
+ {
+ other.committed = true;
+ }
+
+ /// If the edge has not yet been added, add the edge but don't wait
+ /// for a reply.
+ ~lazy_add_edge_with_property();
+
+ /// Returns commit().
+ operator std::pair<edge_descriptor, bool>() const { return commit(); }
+
+ // Add the edge. This operation will block if a remote edge is
+ // being added.
+ std::pair<edge_descriptor, bool> commit() const;
+
+protected:
+ BGL_NAMED_GRAPH& self;
+ mutable variant<vertex_descriptor, vertex_name_type> u;
+ mutable variant<vertex_descriptor, vertex_name_type> v;
+ edge_property_type property;
+ mutable bool committed;
+
+private:
+ // No copy-assignment semantics
+ void operator=(lazy_add_edge_with_property&);
+};
+
+template<BGL_NAMED_GRAPH_PARAMS>
+BGL_NAMED_GRAPH::lazy_add_edge_with_property::~lazy_add_edge_with_property()
+{
+ typedef typename BGL_NAMED_GRAPH::process_id_type process_id_type;
+ using boost::detail::parallel::make_pair_with_property;
+
+ /// If this edge has already been created or will be created by
+ /// someone else, or if someone threw an exception, we will not
+ /// create the edge now.
+ if (committed || std::uncaught_exception())
+ return;
+
+ committed = true;
+
+ if (vertex_name_type* v_name = boost::get<vertex_name_type>(&v)) {
+ // We haven't resolved the target vertex to a descriptor yet, so
+ // it must not be local. Send a message to the owner of the target
+ // of the edge. If the owner of the target does not happen to own
+ // the source, it will resolve the target to a vertex descriptor
+ // and pass the message along to the owner of the source.
+ if (vertex_name_type* u_name = boost::get<vertex_name_type>(&u))
+ send(self.process_group(), self.distribution_(*v_name),
+ BGL_NAMED_GRAPH::msg_add_edge_name_name_with_property,
+ make_pair_with_property(*u_name, *v_name, property));
+ else
+ send(self.process_group(), self.distribution_(*v_name),
+ BGL_NAMED_GRAPH::msg_add_edge_vertex_name_with_property,
+ make_pair_with_property(boost::get<vertex_descriptor>(u), *v_name,
+ property));
+ } else {
+ if (vertex_name_type* u_name = boost::get<vertex_name_type>(&u))
+ // We haven't resolved the source vertex to a descriptor yet, so
+ // it must not be local. Send a message to the owner of the
+ // source vertex requesting the edge addition.
+ send(self.process_group(), self.distribution_(*u_name),
+ BGL_NAMED_GRAPH::msg_add_edge_name_vertex_with_property,
+ make_pair_with_property(*u_name, boost::get<vertex_descriptor>(v),
+ property));
+ else
+ // We have descriptors for both of the vertices, either of which
+ // may be remote or local. Tell the owner of the source vertex
+ // to add the edge (it may be us!).
+ add_edge(boost::get<vertex_descriptor>(u),
+ boost::get<vertex_descriptor>(v),
+ property,
+ self.derived());
+ }
+}
+
+template<BGL_NAMED_GRAPH_PARAMS>
+std::pair<typename graph_traits<Graph>::edge_descriptor, bool>
+BGL_NAMED_GRAPH::lazy_add_edge_with_property::commit() const
+{
+ using boost::detail::parallel::make_pair_with_property;
+ typedef typename BGL_NAMED_GRAPH::process_id_type process_id_type;
+ assert(!committed);
+ committed = true;
+
+ /// The result we will return, if we are sending a message to
+ /// request that someone else add the edge.
+ boost::parallel::detail::untracked_pair<edge_descriptor, bool> result;
+
+ /// The owner of the vertex "u"
+ process_id_type u_owner;
+
+ process_id_type rank = process_id(self.process_group());
+ if (const vertex_name_type* u_name = boost::get<vertex_name_type>(&u)) {
+ /// We haven't resolved the source vertex to a descriptor yet, so
+ /// it must not be local.
+ u_owner = self.named_distribution()(*u_name);
+
+ /// Send a message to the remote vertex requesting that it add the
+ /// edge. The message differs depending on whether we have a
+ /// vertex name or a vertex descriptor for the target.
+ if (const vertex_name_type* v_name = boost::get<vertex_name_type>(&v))
+ send_oob_with_reply
+ (self.process_group(), u_owner,
+ BGL_NAMED_GRAPH::msg_add_edge_name_name_with_reply_and_property,
+ make_pair_with_property(*u_name, *v_name, property),
+ result);
+ else
+ send_oob_with_reply
+ (self.process_group(), u_owner,
+ BGL_NAMED_GRAPH::msg_add_edge_name_vertex_with_reply_and_property,
+ make_pair_with_property(*u_name,
+ boost::get<vertex_descriptor>(v),
+ property),
+ result);
+ } else {
+ /// We have resolved the source vertex to a descriptor, which may
+ /// either be local or remote.
+ u_owner
+ = get(vertex_owner, self.derived(),
+ boost::get<vertex_descriptor>(u));
+ if (u_owner == rank) {
+ /// The source is local. If we need to, resolve the target vertex.
+ if (const vertex_name_type* v_name = boost::get<vertex_name_type>(&v))
+ v = add_vertex(*v_name, self.derived());
+
+ /// Add the edge using vertex descriptors
+ return add_edge(boost::get<vertex_descriptor>(u),
+ boost::get<vertex_descriptor>(v),
+ property,
+ self.derived());
+ } else {
+ /// The source is remote. Just send a message to its owner
+ /// requesting that the owner add the new edge, either directly
+ /// or via the derived class's add_edge function.
+ if (const vertex_name_type* v_name = boost::get<vertex_name_type>(&v))
+ send_oob_with_reply
+ (self.process_group(), u_owner,
+ BGL_NAMED_GRAPH::msg_add_edge_vertex_name_with_reply_and_property,
+ make_pair_with_property(boost::get<vertex_descriptor>(u), *v_name,
+ property),
+ result);
+ else
+ return add_edge(boost::get<vertex_descriptor>(u),
+ boost::get<vertex_descriptor>(v),
+ property,
+ self.derived());
+ }
+ }
+
+ // If we get here, the edge has been added remotely and "result"
+ // contains the result of that edge addition.
+ return result;
+}
+
+/// Construct the named_graph with a particular process group
+template<BGL_NAMED_GRAPH_PARAMS>
+BGL_NAMED_GRAPH::named_graph(const process_group_type& pg)
+ : process_group_(pg, parallel::attach_distributed_object()),
+ distribution_(pg)
+{
+ setup_triggers();
+}
+
+/// Construct the named_graph mixin with a particular process group
+/// and distribution function
+template<BGL_NAMED_GRAPH_PARAMS>
+BGL_NAMED_GRAPH::named_graph(const process_group_type& pg,
+ const base_distribution_type& distribution)
+ : process_group_(pg, parallel::attach_distributed_object()),
+ distribution_(pg, distribution)
+{
+ setup_triggers();
+}
+
+template<BGL_NAMED_GRAPH_PARAMS>
+void
+BGL_NAMED_GRAPH::setup_triggers()
+{
+ using boost::graph::parallel::simple_trigger;
+
+ simple_trigger(process_group_, msg_add_vertex_name, this,
+ &named_graph::handle_add_vertex_name);
+ simple_trigger(process_group_, msg_add_vertex_name_with_reply, this,
+ &named_graph::handle_add_vertex_name_with_reply);
+ simple_trigger(process_group_, msg_find_vertex, this,
+ &named_graph::handle_find_vertex);
+ simple_trigger(process_group_, msg_add_edge_name_name, this,
+ &named_graph::template handle_add_edge<vertex_name_type,
+ vertex_name_type>);
+ simple_trigger(process_group_, msg_add_edge_name_name_with_reply, this,
+ &named_graph::template handle_add_edge_with_reply
+ <vertex_name_type, vertex_name_type>);
+ simple_trigger(process_group_, msg_add_edge_name_vertex, this,
+ &named_graph::template handle_add_edge<vertex_name_type,
+ vertex_descriptor>);
+ simple_trigger(process_group_, msg_add_edge_name_vertex_with_reply, this,
+ &named_graph::template handle_add_edge_with_reply
+ <vertex_name_type, vertex_descriptor>);
+ simple_trigger(process_group_, msg_add_edge_vertex_name, this,
+ &named_graph::template handle_add_edge<vertex_descriptor,
+ vertex_name_type>);
+ simple_trigger(process_group_, msg_add_edge_vertex_name_with_reply, this,
+ &named_graph::template handle_add_edge_with_reply
+ <vertex_descriptor, vertex_name_type>);
+ simple_trigger(process_group_, msg_add_edge_name_name_with_property, this,
+ &named_graph::
+ template handle_add_edge_with_property<vertex_name_type,
+ vertex_name_type>);
+ simple_trigger(process_group_,
+ msg_add_edge_name_name_with_reply_and_property, this,
+ &named_graph::template handle_add_edge_with_reply_and_property
+ <vertex_name_type, vertex_name_type>);
+ simple_trigger(process_group_, msg_add_edge_name_vertex_with_property, this,
+ &named_graph::
+ template handle_add_edge_with_property<vertex_name_type,
+ vertex_descriptor>);
+ simple_trigger(process_group_,
+ msg_add_edge_name_vertex_with_reply_and_property, this,
+ &named_graph::template handle_add_edge_with_reply_and_property
+ <vertex_name_type, vertex_descriptor>);
+ simple_trigger(process_group_, msg_add_edge_vertex_name_with_property, this,
+ &named_graph::
+ template handle_add_edge_with_property<vertex_descriptor,
+ vertex_name_type>);
+ simple_trigger(process_group_,
+ msg_add_edge_vertex_name_with_reply_and_property, this,
+ &named_graph::template handle_add_edge_with_reply_and_property
+ <vertex_descriptor, vertex_name_type>);
+}
+
+/// Retrieve the vertex associated with the given name
+template<BGL_NAMED_GRAPH_PARAMS>
+optional<Vertex>
+find_vertex(typename BGL_NAMED_GRAPH::vertex_name_type const& name,
+ const BGL_NAMED_GRAPH& g)
+{
+ typedef typename Graph::local_vertex_descriptor local_vertex_descriptor;
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+
+ // Determine the owner of this name
+ typename BGL_NAMED_GRAPH::process_id_type owner
+ = g.named_distribution()(name);
+
+ if (owner == process_id(g.process_group())) {
+ // The vertex is local, so search for a mapping here
+ optional<local_vertex_descriptor> result
+ = find_vertex(name, g.derived().base());
+ if (result)
+ return Vertex(owner, *result);
+ else
+ return optional<Vertex>();
+ }
+ else {
+ // Ask the ownering process for the name of this vertex
+ boost::parallel::detail::untracked_pair<vertex_descriptor, bool> result;
+ send_oob_with_reply(g.process_group(), owner,
+ BGL_NAMED_GRAPH::msg_find_vertex, name, result);
+ if (result.second)
+ return result.first;
+ else
+ return optional<Vertex>();
+ }
+}
+
+/// meta-function helping in figuring out if the given VertextProerty belongs to
+/// a named graph
+template<typename VertexProperty>
+struct not_is_named_graph
+ : is_same<typename internal_vertex_name<VertexProperty>::type, void>
+{};
+
+/// Retrieve the vertex associated with the given name
+template<typename Graph>
+typename Graph::named_graph_type::lazy_add_vertex
+add_vertex(typename Graph::vertex_name_type const& name,
+ Graph& g,
+ typename disable_if<
+ not_is_named_graph<typename Graph::vertex_property_type>,
+ void*>::type = 0)
+{
+ return typename Graph::named_graph_type::lazy_add_vertex(g, name);
+}
+
+/// Add an edge using vertex names to refer to the vertices
+template<BGL_NAMED_GRAPH_PARAMS>
+typename BGL_NAMED_GRAPH::lazy_add_edge
+add_edge(typename BGL_NAMED_GRAPH::vertex_name_type const& u_name,
+ typename BGL_NAMED_GRAPH::vertex_name_type const& v_name,
+ BGL_NAMED_GRAPH& g)
+{
+ typedef typename BGL_NAMED_GRAPH::lazy_add_edge lazy_add_edge;
+ typedef typename BGL_NAMED_GRAPH::process_id_type process_id_type;
+
+ process_id_type rank = process_id(g.process_group());
+ process_id_type u_owner = g.named_distribution()(u_name);
+ process_id_type v_owner = g.named_distribution()(v_name);
+
+ // Resolve local vertex names before building the "lazy" edge
+ // addition structure.
+ if (u_owner == rank && v_owner == rank)
+ return lazy_add_edge(g, add_vertex(u_name, g), add_vertex(v_name, g));
+ else if (u_owner == rank && v_owner != rank)
+ return lazy_add_edge(g, add_vertex(u_name, g), v_name);
+ else if (u_owner != rank && v_owner == rank)
+ return lazy_add_edge(g, u_name, add_vertex(v_name, g));
+ else
+ return lazy_add_edge(g, u_name, v_name);
+}
+
+template<BGL_NAMED_GRAPH_PARAMS>
+typename BGL_NAMED_GRAPH::lazy_add_edge
+add_edge(typename BGL_NAMED_GRAPH::vertex_name_type const& u_name,
+ typename BGL_NAMED_GRAPH::vertex_descriptor const& v,
+ BGL_NAMED_GRAPH& g)
+{
+ // Resolve local vertex names before building the "lazy" edge
+ // addition structure.
+ typedef typename BGL_NAMED_GRAPH::lazy_add_edge lazy_add_edge;
+ if (g.named_distribution()(u_name) == process_id(g.process_group()))
+ return lazy_add_edge(g, add_vertex(u_name, g), v);
+ else
+ return lazy_add_edge(g, u_name, v);
+}
+
+template<BGL_NAMED_GRAPH_PARAMS>
+typename BGL_NAMED_GRAPH::lazy_add_edge
+add_edge(typename BGL_NAMED_GRAPH::vertex_descriptor const& u,
+ typename BGL_NAMED_GRAPH::vertex_name_type const& v_name,
+ BGL_NAMED_GRAPH& g)
+{
+ // Resolve local vertex names before building the "lazy" edge
+ // addition structure.
+ typedef typename BGL_NAMED_GRAPH::lazy_add_edge lazy_add_edge;
+ if (g.named_distribution()(v_name) == process_id(g.process_group()))
+ return lazy_add_edge(g, u, add_vertex(v_name, g));
+ else
+ return lazy_add_edge(g, u, v_name);
+}
+
+/// Add an edge using vertex names to refer to the vertices
+template<BGL_NAMED_GRAPH_PARAMS>
+typename BGL_NAMED_GRAPH::lazy_add_edge_with_property
+add_edge(typename BGL_NAMED_GRAPH::vertex_name_type const& u_name,
+ typename BGL_NAMED_GRAPH::vertex_name_type const& v_name,
+ typename Graph::edge_property_type const& property,
+ BGL_NAMED_GRAPH& g)
+{
+ typedef typename BGL_NAMED_GRAPH::lazy_add_edge_with_property lazy_add_edge;
+ typedef typename BGL_NAMED_GRAPH::process_id_type process_id_type;
+
+ process_id_type rank = process_id(g.process_group());
+ process_id_type u_owner = g.named_distribution()(u_name);
+ process_id_type v_owner = g.named_distribution()(v_name);
+
+ // Resolve local vertex names before building the "lazy" edge
+ // addition structure.
+ if (u_owner == rank && v_owner == rank)
+ return lazy_add_edge(g, add_vertex(u_name, g), add_vertex(v_name, g),
+ property);
+ else if (u_owner == rank && v_owner != rank)
+ return lazy_add_edge(g, add_vertex(u_name, g), v_name, property);
+ else if (u_owner != rank && v_owner == rank)
+ return lazy_add_edge(g, u_name, add_vertex(v_name, g), property);
+ else
+ return lazy_add_edge(g, u_name, v_name, property);
+}
+
+template<BGL_NAMED_GRAPH_PARAMS>
+typename BGL_NAMED_GRAPH::lazy_add_edge_with_property
+add_edge(typename BGL_NAMED_GRAPH::vertex_name_type const& u_name,
+ typename BGL_NAMED_GRAPH::vertex_descriptor const& v,
+ typename Graph::edge_property_type const& property,
+ BGL_NAMED_GRAPH& g)
+{
+ // Resolve local vertex names before building the "lazy" edge
+ // addition structure.
+ typedef typename BGL_NAMED_GRAPH::lazy_add_edge_with_property lazy_add_edge;
+ if (g.named_distribution()(u_name) == process_id(g.process_group()))
+ return lazy_add_edge(g, add_vertex(u_name, g), v, property);
+ else
+ return lazy_add_edge(g, u_name, v, property);
+}
+
+template<BGL_NAMED_GRAPH_PARAMS>
+typename BGL_NAMED_GRAPH::lazy_add_edge_with_property
+add_edge(typename BGL_NAMED_GRAPH::vertex_descriptor const& u,
+ typename BGL_NAMED_GRAPH::vertex_name_type const& v_name,
+ typename Graph::edge_property_type const& property,
+ BGL_NAMED_GRAPH& g)
+{
+ // Resolve local vertex names before building the "lazy" edge
+ // addition structure.
+ typedef typename BGL_NAMED_GRAPH::lazy_add_edge_with_property lazy_add_edge;
+ if (g.named_distribution()(v_name) == process_id(g.process_group()))
+ return lazy_add_edge(g, u, add_vertex(v_name, g), property);
+ else
+ return lazy_add_edge(g, u, v_name, property);
+}
+
+template<BGL_NAMED_GRAPH_PARAMS>
+typename BGL_NAMED_GRAPH::process_id_type
+BGL_NAMED_GRAPH::owner_by_property(const vertex_property_type& property)
+{
+ return distribution_(derived().base().extract_name(property));
+}
+
+
+/*******************************************************************
+ * Message handlers *
+ *******************************************************************/
+
+template<BGL_NAMED_GRAPH_PARAMS>
+void
+BGL_NAMED_GRAPH::
+handle_add_vertex_name(int /*source*/, int /*tag*/,
+ const vertex_name_type& msg, trigger_receive_context)
+{
+ add_vertex(msg, derived());
+}
+
+template<BGL_NAMED_GRAPH_PARAMS>
+typename BGL_NAMED_GRAPH::vertex_descriptor
+BGL_NAMED_GRAPH::
+handle_add_vertex_name_with_reply(int source, int /*tag*/,
+ const vertex_name_type& msg,
+ trigger_receive_context)
+{
+ return add_vertex(msg, derived());
+}
+
+template<BGL_NAMED_GRAPH_PARAMS>
+boost::parallel::detail::untracked_pair<typename BGL_NAMED_GRAPH::vertex_descriptor, bool>
+BGL_NAMED_GRAPH::
+handle_find_vertex(int source, int /*tag*/, const vertex_name_type& msg,
+ trigger_receive_context)
+{
+ using boost::parallel::detail::make_untracked_pair;
+
+ optional<vertex_descriptor> v = find_vertex(msg, derived());
+ if (v)
+ return make_untracked_pair(*v, true);
+ else
+ return make_untracked_pair(graph_traits<Graph>::null_vertex(), false);
+}
+
+template<BGL_NAMED_GRAPH_PARAMS>
+template<typename U, typename V>
+void
+BGL_NAMED_GRAPH::
+handle_add_edge(int source, int /*tag*/, const boost::parallel::detail::untracked_pair<U, V>& msg,
+ trigger_receive_context)
+{
+ add_edge(msg.first, msg.second, derived());
+}
+
+template<BGL_NAMED_GRAPH_PARAMS>
+template<typename U, typename V>
+boost::parallel::detail::untracked_pair<typename BGL_NAMED_GRAPH::edge_descriptor, bool>
+BGL_NAMED_GRAPH::
+handle_add_edge_with_reply(int source, int /*tag*/, const boost::parallel::detail::untracked_pair<U, V>& msg,
+ trigger_receive_context)
+{
+ std::pair<typename BGL_NAMED_GRAPH::edge_descriptor, bool> p =
+ add_edge(msg.first, msg.second, derived());
+ return p;
+}
+
+template<BGL_NAMED_GRAPH_PARAMS>
+template<typename U, typename V>
+void
+BGL_NAMED_GRAPH::
+handle_add_edge_with_property
+ (int source, int tag,
+ const pair_with_property<U, V, edge_property_type>& msg,
+ trigger_receive_context)
+{
+ add_edge(msg.first, msg.second, msg.get_property(), derived());
+}
+
+template<BGL_NAMED_GRAPH_PARAMS>
+template<typename U, typename V>
+boost::parallel::detail::untracked_pair<typename BGL_NAMED_GRAPH::edge_descriptor, bool>
+BGL_NAMED_GRAPH::
+handle_add_edge_with_reply_and_property
+ (int source, int tag,
+ const pair_with_property<U, V, edge_property_type>& msg,
+ trigger_receive_context)
+{
+ std:: pair<typename BGL_NAMED_GRAPH::edge_descriptor, bool> p =
+ add_edge(msg.first, msg.second, msg.get_property(), derived());
+ return p;
+}
+
+#undef BGL_NAMED_GRAPH
+#undef BGL_NAMED_GRAPH_PARAMS
+
+/*******************************************************************
+ * Maybe named graph mixin *
+ *******************************************************************/
+
+/**
+ * A graph mixin that can provide a mapping from names to vertices,
+ * and use that mapping to simplify creation and manipulation of
+ * graphs.
+ */
+template<typename Graph, typename Vertex, typename Edge, typename Config,
+ typename ExtractName
+ = typename internal_vertex_name<typename Config::vertex_property_type>::type>
+struct maybe_named_graph
+ : public named_graph<Graph, Vertex, Edge, Config>
+{
+private:
+ typedef named_graph<Graph, Vertex, Edge, Config> inherited;
+ typedef typename Config::process_group_type process_group_type;
+
+public:
+ /// The type used to distribute named vertices in the graph
+ typedef typename Config::distribution_type distribution_type;
+ typedef typename Config::base_distribution_type base_distribution_type;
+
+ explicit maybe_named_graph(const process_group_type& pg) : inherited(pg) { }
+
+ maybe_named_graph(const process_group_type& pg,
+ const base_distribution_type& distribution)
+ : inherited(pg, distribution) { }
+
+ distribution_type& distribution() { return this->distribution_; }
+ const distribution_type& distribution() const { return this->distribution_; }
+};
+
+/**
+ * A graph mixin that can provide a mapping from names to vertices,
+ * and use that mapping to simplify creation and manipulation of
+ * graphs. This partial specialization turns off this functionality
+ * when the @c VertexProperty does not have an internal vertex name.
+ */
+template<typename Graph, typename Vertex, typename Edge, typename Config>
+struct maybe_named_graph<Graph, Vertex, Edge, Config, void>
+{
+private:
+ typedef typename Config::process_group_type process_group_type;
+ typedef typename Config::vertex_property_type vertex_property_type;
+
+public:
+ typedef typename process_group_type::process_id_type process_id_type;
+
+ /// The type used to distribute named vertices in the graph
+ typedef typename Config::distribution_type distribution_type;
+ typedef typename Config::base_distribution_type base_distribution_type;
+
+ explicit maybe_named_graph(const process_group_type&) { }
+
+ maybe_named_graph(const process_group_type& pg,
+ const base_distribution_type& distribution)
+ : distribution_(pg, distribution) { }
+
+ /// Notify the named_graph that we have added the given vertex. This
+ /// is a no-op.
+ void added_vertex(Vertex) { }
+
+ /// Notify the named_graph that we are removing the given
+ /// vertex. This is a no-op.
+ void removing_vertex(Vertex) { }
+
+ /// Notify the named_graph that we are clearing the graph
+ void clearing_graph() { }
+
+ /// Retrieve the owner of a given vertex based on the properties
+ /// associated with that vertex. This operation just returns the
+ /// number of the local processor, adding all vertices locally.
+ process_id_type owner_by_property(const vertex_property_type&)
+ {
+ return process_id(pg);
+ }
+
+ distribution_type& distribution() { return distribution_; }
+ const distribution_type& distribution() const { return distribution_; }
+
+protected:
+ /// The process group of the graph
+ process_group_type pg;
+
+ /// The distribution used for the graph
+ distribution_type distribution_;
+};
+
+} } } // end namespace boost::graph::distributed
+
+#endif // BOOST_GRAPH_DISTRIBUTED_NAMED_GRAPH_HPP

Added: trunk/boost/graph/distributed/page_rank.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/page_rank.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,225 @@
+// Copyright (C) 2004-2006 The Trustees of Indiana University.
+// Copyright (C) 2002 Brad King and Douglas Gregor
+
+// 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
+// Brian Barrett
+#ifndef BOOST_PARALLEL_GRAPH_PAGE_RANK_HPP
+#define BOOST_PARALLEL_GRAPH_PAGE_RANK_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/graph/overloading.hpp>
+#include <boost/graph/page_rank.hpp>
+#include <boost/graph/distributed/concepts.hpp>
+#include <boost/property_map/parallel/distributed_property_map.hpp>
+#include <boost/property_map/parallel/caching_property_map.hpp>
+#include <boost/graph/parallel/algorithm.hpp>
+#include <boost/graph/parallel/container_traits.hpp>
+
+// #define WANT_MPI_ONESIDED 1
+
+namespace boost { namespace graph { namespace distributed {
+
+namespace detail {
+#ifdef WANT_MPI_ONESIDED
+ template<typename Graph, typename RankMap, typename owner_map_t>
+ void page_rank_step(const Graph& g, RankMap from_rank, MPI_Win to_win,
+ typename property_traits<RankMap>::value_type damping,
+ owner_map_t owner)
+ {
+ typedef typename property_traits<RankMap>::value_type rank_type;
+ int me, ret;
+ MPI_Comm_rank(MPI_COMM_WORLD, &me);
+
+ // MPI_Accumulate is not required to store the value of the data
+ // being sent, only the address. The value of the memory location
+ // must not change until the end of the access epoch, meaning the
+ // call to MPI_Fence. We therefore store the updated value back
+ // into the from_rank map before the accumulate rather than using
+ // a temporary. We're going to reset the values in the from_rank
+ // before the end of page_rank_step() anyway, so this isn't a huge
+ // deal. But MPI-2 One-sided is an abomination.
+ BGL_FORALL_VERTICES_T(u, g, Graph) {
+ put(from_rank, u, (damping * get(from_rank, u) / out_degree(u, g)));
+ BGL_FORALL_ADJ_T(u, v, g, Graph) {
+ ret = MPI_Accumulate(&(from_rank[u]),
+ 1, MPI_DOUBLE,
+ get(owner, v), local(v),
+ 1, MPI_DOUBLE, MPI_SUM, to_win);
+ assert(MPI_SUCCESS == ret);
+ }
+ }
+ MPI_Win_fence(0, to_win);
+
+ // Set new rank maps for the other map. Do this now to get around
+ // the stupid synchronization rules of MPI-2 One-sided
+ BGL_FORALL_VERTICES_T(v, g, Graph) put(from_rank, v, rank_type(1 - damping));
+ }
+#endif
+
+ template<typename T>
+ struct rank_accumulate_reducer {
+ BOOST_STATIC_CONSTANT(bool, non_default_resolver = true);
+
+ template<typename K>
+ T operator()(const K&) const { return T(0); }
+
+ template<typename K>
+ T operator()(const K&, const T& x, const T& y) const { return x + y; }
+ };
+} // end namespace detail
+
+template<typename Graph, typename RankMap, typename Done, typename RankMap2>
+void
+page_rank_impl(const Graph& g, RankMap rank_map, Done done,
+ typename property_traits<RankMap>::value_type damping,
+ typename graph_traits<Graph>::vertices_size_type n,
+ RankMap2 rank_map2)
+{
+ typedef typename property_traits<RankMap>::value_type rank_type;
+
+ int me;
+ MPI_Comm_rank(MPI_COMM_WORLD, &me);
+
+ typedef typename property_map<Graph, vertex_owner_t>
+ ::const_type vertex_owner_map;
+ typename property_map<Graph, vertex_owner_t>::const_type
+ owner = get(vertex_owner, g);
+
+ typedef typename boost::graph::parallel::process_group_type<Graph>
+ ::type process_group_type;
+ typedef typename process_group_type::process_id_type process_id_type;
+
+ process_group_type pg = process_group(g);
+ process_id_type id = process_id(pg);
+
+ assert(me == id);
+
+ rank_type initial_rank = rank_type(rank_type(1) / n);
+ BGL_FORALL_VERTICES_T(v, g, Graph) put(rank_map, v, initial_rank);
+
+#ifdef WANT_MPI_ONESIDED
+
+ assert(sizeof(rank_type) == sizeof(double));
+
+ bool to_map_2 = true;
+ MPI_Win win, win2;
+
+ MPI_Win_create(&(rank_map[*(vertices(g).first)]),
+ sizeof(double) * num_vertices(g),
+ sizeof(double),
+ MPI_INFO_NULL, MPI_COMM_WORLD, &win);
+ MPI_Win_set_name(win, "rank_map_win");
+ MPI_Win_create(&(rank_map2[*(vertices(g).first)]),
+ sizeof(double) * num_vertices(g),
+ sizeof(double),
+ MPI_INFO_NULL, MPI_COMM_WORLD, &win2);
+ MPI_Win_set_name(win, "rank_map2_win");
+
+ // set initial rank maps for the first iteration...
+ BGL_FORALL_VERTICES_T(v, g, Graph) put(rank_map2, v, rank_type(1 - damping));
+
+ MPI_Win_fence(0, win);
+ MPI_Win_fence(0, win2);
+
+ while ((to_map_2 && !done(rank_map, g)) ||
+ (!to_map_2 && !done(rank_map2, g))) {
+ if (to_map_2) {
+ graph::distributed::detail::page_rank_step(g, rank_map, win2, damping, owner);
+ to_map_2 = false;
+ } else {
+ graph::distributed::detail::page_rank_step(g, rank_map2, win, damping, owner);
+ to_map_2 = true;
+ }
+ }
+ synchronize(boost::graph::parallel::process_group(g));
+
+ MPI_Win_free(&win);
+ MPI_Win_free(&win2);
+
+#else
+ // The ranks accumulate after each step.
+ rank_map.set_reduce(detail::rank_accumulate_reducer<rank_type>());
+ rank_map2.set_reduce(detail::rank_accumulate_reducer<rank_type>());
+ rank_map.set_consistency_model(boost::parallel::cm_flush | boost::parallel::cm_reset);
+ rank_map2.set_consistency_model(boost::parallel::cm_flush | boost::parallel::cm_reset);
+
+ bool to_map_2 = true;
+ while ((to_map_2 && !done(rank_map, g)) ||
+ (!to_map_2 && !done(rank_map2, g))) {
+ /**
+ * PageRank can implemented slightly more efficiently on a
+ * bidirectional graph than on an incidence graph. However,
+ * distributed PageRank requires that we have the rank of the
+ * source vertex available locally, so we force the incidence
+ * graph implementation, which pushes rank from source to
+ * target.
+ */
+ typedef incidence_graph_tag category;
+ if (to_map_2) {
+ graph::detail::page_rank_step(g, rank_map, rank_map2, damping,
+ category());
+ to_map_2 = false;
+ } else {
+ graph::detail::page_rank_step(g, rank_map2, rank_map, damping,
+ category());
+ to_map_2 = true;
+ }
+ using boost::graph::parallel::process_group;
+ synchronize(process_group(g));
+ }
+
+ rank_map.reset();
+#endif
+
+ if (!to_map_2)
+ BGL_FORALL_VERTICES_T(v, g, Graph) put(rank_map, v, get(rank_map2, v));
+}
+
+template<typename Graph, typename RankMap, typename Done, typename RankMap2>
+void
+page_rank(const Graph& g, RankMap rank_map, Done done,
+ typename property_traits<RankMap>::value_type damping,
+ typename graph_traits<Graph>::vertices_size_type n,
+ RankMap2 rank_map2
+ BOOST_GRAPH_ENABLE_IF_MODELS_PARM(Graph, distributed_graph_tag))
+{
+ (page_rank_impl)(g, rank_map, done, damping, n, rank_map2);
+}
+
+template<typename MutableGraph>
+void
+remove_dangling_links(MutableGraph& g
+ BOOST_GRAPH_ENABLE_IF_MODELS_PARM(MutableGraph,
+ distributed_graph_tag))
+{
+ typename graph_traits<MutableGraph>::vertices_size_type old_n;
+ do {
+ old_n = num_vertices(g);
+
+ typename graph_traits<MutableGraph>::vertex_iterator vi, vi_end;
+ for (tie(vi, vi_end) = vertices(g); vi != vi_end; /* in loop */) {
+ typename graph_traits<MutableGraph>::vertex_descriptor v = *vi++;
+ if (out_degree(v, g) == 0) {
+ clear_vertex(v, g);
+ remove_vertex(v, g);
+ }
+ }
+ } while (num_vertices(g) < old_n);
+}
+
+} // end namespace distributed
+
+using distributed::page_rank;
+using distributed::remove_dangling_links;
+
+} } // end namespace boost::graph
+
+#endif // BOOST_PARALLEL_GRAPH_PAGE_RANK_HPP

Added: trunk/boost/graph/distributed/queue.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/queue.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,278 @@
+// 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
+#ifndef BOOST_GRAPH_DISTRIBUTED_QUEUE_HPP
+#define BOOST_GRAPH_DISTRIBUTED_QUEUE_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/graph/parallel/process_group.hpp>
+#include <boost/optional.hpp>
+#include <boost/shared_ptr.hpp>
+#include <vector>
+
+namespace boost { namespace graph { namespace distributed {
+
+/// A unary predicate that always returns "true".
+struct always_push
+{
+ template<typename T> bool operator()(const T&) const { return true; }
+};
+
+
+
+/** A distributed queue adaptor.
+ *
+ * Class template @c distributed_queue implements a distributed queue
+ * across a process group. The distributed queue is an adaptor over an
+ * existing (local) queue, which must model the @ref Buffer
+ * concept. Each process stores a distinct copy of the local queue,
+ * from which it draws or removes elements via the @ref pop and @ref
+ * top members.
+ *
+ * The value type of the local queue must be a model of the @ref
+ * GlobalDescriptor concept. The @ref push operation of the
+ * distributed queue passes (via a message) the value to its owning
+ * processor. Thus, the elements within a particular local queue are
+ * guaranteed to have the process owning that local queue as an owner.
+ *
+ * Synchronization of distributed queues occurs in the @ref empty and
+ * @ref size functions, which will only return "empty" values (true or
+ * 0, respectively) when the entire distributed queue is empty. If the
+ * local queue is empty but the distributed queue is not, the
+ * operation will block until either condition changes. When the @ref
+ * size function of a nonempty queue returns, it returns the size of
+ * the local queue. These semantics were selected so that sequential
+ * code that processes elements in the queue via the following idiom
+ * can be parallelized via introduction of a distributed queue:
+ *
+ * distributed_queue<...> Q;
+ * Q.push(x);
+ * while (!Q.empty()) {
+ * // do something, that may push a value onto Q
+ * }
+ *
+ * In the parallel version, the initial @ref push operation will place
+ * the value @c x onto its owner's queue. All processes will
+ * synchronize at the call to empty, and only the process owning @c x
+ * will be allowed to execute the loop (@ref Q.empty() returns
+ * false). This iteration may in turn push values onto other remote
+ * queues, so when that process finishes execution of the loop body
+ * and all processes synchronize again in @ref empty, more processes
+ * may have nonempty local queues to execute. Once all local queues
+ * are empty, @ref Q.empty() returns @c false for all processes.
+ *
+ * The distributed queue can receive messages at two different times:
+ * during synchronization and when polling @ref empty. Messages are
+ * always received during synchronization, to ensure that accurate
+ * local queue sizes can be determines. However, whether @ref empty
+ * should poll for messages is specified as an option to the
+ * constructor. Polling may be desired when the order in which
+ * elements in the queue are processed is not important, because it
+ * permits fewer synchronization steps and less communication
+ * overhead. However, when more strict ordering guarantees are
+ * required, polling may be semantically incorrect. By disabling
+ * polling, one ensures that parallel execution using the idiom above
+ * will not process an element at a later "level" before an earlier
+ * "level".
+ *
+ * The distributed queue nearly models the @ref Buffer
+ * concept. However, the @ref push routine does not necessarily
+ * increase the result of @c size() by one (although the size of the
+ * global queue does increase by one).
+ */
+template<typename ProcessGroup, typename OwnerMap, typename Buffer,
+ typename UnaryPredicate = always_push>
+class distributed_queue
+{
+ typedef distributed_queue self_type;
+
+ enum {
+ /** Message indicating a remote push. The message contains a
+ * single value x of type value_type that is to be pushed on the
+ * receiver's queue.
+ */
+ msg_push,
+ /** Push many elements at once. */
+ msg_multipush
+ };
+
+ public:
+ typedef ProcessGroup process_group_type;
+ typedef Buffer buffer_type;
+ typedef typename buffer_type::value_type value_type;
+ typedef typename buffer_type::size_type size_type;
+
+ /** Construct a new distributed queue.
+ *
+ * Build a new distributed queue that communicates over the given @p
+ * process_group, whose local queue is initialized via @p buffer and
+ * which may or may not poll for messages.
+ */
+ explicit
+ distributed_queue(const ProcessGroup& process_group,
+ const OwnerMap& owner,
+ const Buffer& buffer,
+ bool polling = false);
+
+ /** Construct a new distributed queue.
+ *
+ * Build a new distributed queue that communicates over the given @p
+ * process_group, whose local queue is initialized via @p buffer and
+ * which may or may not poll for messages.
+ */
+ explicit
+ distributed_queue(const ProcessGroup& process_group = ProcessGroup(),
+ const OwnerMap& owner = OwnerMap(),
+ const Buffer& buffer = Buffer(),
+ const UnaryPredicate& pred = UnaryPredicate(),
+ bool polling = false);
+
+ /** Construct a new distributed queue.
+ *
+ * Build a new distributed queue that communicates over the given @p
+ * process_group, whose local queue is default-initalized and which
+ * may or may not poll for messages.
+ */
+ distributed_queue(const ProcessGroup& process_group, const OwnerMap& owner,
+ const UnaryPredicate& pred, bool polling = false);
+
+ /** Virtual destructor required with virtual functions.
+ *
+ */
+ virtual ~distributed_queue() {}
+
+ /** Push an element onto the distributed queue.
+ *
+ * The element will be sent to its owner process to be added to that
+ * process's local queue. If polling is enabled for this queue and
+ * the owner process is the current process, the value will be
+ * immediately pushed onto the local queue.
+ *
+ * Complexity: O(1) messages of size O(sizeof(value_type)) will be
+ * transmitted.
+ */
+ void push(const value_type& x);
+
+ /** Pop an element off the local queue.
+ *
+ * @p @c !empty()
+ */
+ void pop() { buffer.pop(); }
+
+ /**
+ * Return the element at the top of the local queue.
+ *
+ * @p @c !empty()
+ */
+ value_type& top() { return buffer.top(); }
+
+ /**
+ * \overload
+ */
+ const value_type& top() const { return buffer.top(); }
+
+ /** Determine if the queue is empty.
+ *
+ * When the local queue is nonempty, returns @c true. If the local
+ * queue is empty, synchronizes with all other processes in the
+ * process group until either (1) the local queue is nonempty
+ * (returns @c true) (2) the entire distributed queue is empty
+ * (returns @c false).
+ */
+ bool empty() const;
+
+ /** Determine the size of the local queue.
+ *
+ * The behavior of this routine is equivalent to the behavior of
+ * @ref empty, except that when @ref empty returns true this
+ * function returns the size of the local queue and when @ref empty
+ * returns false this function returns zero.
+ */
+ size_type size() const;
+
+ // private:
+ /** Synchronize the distributed queue and determine if all queues
+ * are empty.
+ *
+ * \returns \c true when all local queues are empty, or false if at least
+ * one of the local queues is nonempty.
+ * Defined as virtual for derived classes like depth_limited_distributed_queue.
+ */
+ virtual bool do_synchronize() const;
+
+ private:
+ // Setup triggers
+ void setup_triggers();
+
+ // Message handlers
+ void
+ handle_push(int source, int tag, const value_type& value,
+ trigger_receive_context);
+
+ void
+ handle_multipush(int source, int tag, const std::vector<value_type>& values,
+ trigger_receive_context);
+
+ mutable ProcessGroup process_group;
+ OwnerMap owner;
+ mutable Buffer buffer;
+ UnaryPredicate pred;
+ bool polling;
+
+ typedef std::vector<value_type> outgoing_buffer_t;
+ typedef std::vector<outgoing_buffer_t> outgoing_buffers_t;
+ shared_ptr<outgoing_buffers_t> outgoing_buffers;
+};
+
+/// Helper macro containing the normal names for the template
+/// parameters to distributed_queue.
+#define BOOST_DISTRIBUTED_QUEUE_PARMS \
+ typename ProcessGroup, typename OwnerMap, typename Buffer, \
+ typename UnaryPredicate
+
+/// Helper macro containing the normal template-id for
+/// distributed_queue.
+#define BOOST_DISTRIBUTED_QUEUE_TYPE \
+ distributed_queue<ProcessGroup, OwnerMap, Buffer, UnaryPredicate>
+
+/** Synchronize all processes involved with the given distributed queue.
+ *
+ * This function will synchronize all of the local queues for a given
+ * distributed queue, by ensuring that no additional messages are in
+ * transit. It is rarely required by the user, because most
+ * synchronization of distributed queues occurs via the @c empty or @c
+ * size methods.
+ */
+template<BOOST_DISTRIBUTED_QUEUE_PARMS>
+inline void
+synchronize(const BOOST_DISTRIBUTED_QUEUE_TYPE& Q)
+{ Q.do_synchronize(); }
+
+/// Construct a new distributed queue.
+template<typename ProcessGroup, typename OwnerMap, typename Buffer>
+inline distributed_queue<ProcessGroup, OwnerMap, Buffer>
+make_distributed_queue(const ProcessGroup& process_group,
+ const OwnerMap& owner,
+ const Buffer& buffer,
+ bool polling = false)
+{
+ typedef distributed_queue<ProcessGroup, OwnerMap, Buffer> result_type;
+ return result_type(process_group, owner, buffer, polling);
+}
+
+} } } // end namespace boost::graph::distributed
+
+#include <boost/graph/distributed/detail/queue.cpp>
+
+#undef BOOST_DISTRIBUTED_QUEUE_TYPE
+#undef BOOST_DISTRIBUTED_QUEUE_PARMS
+
+#endif // BOOST_GRAPH_DISTRIBUTED_QUEUE_HPP

Added: trunk/boost/graph/distributed/reverse_graph.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/reverse_graph.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,38 @@
+// Copyright (C) 2005-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: Nick Edmonds
+// Andrew Lumsdaine
+#ifndef BOOST_GRAPH_DISTRIBUTED_REVERSE_GRAPH_HPP
+#define BOOST_GRAPH_DISTRIBUTED_REVERSE_GRAPH_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/graph/reverse_graph.hpp>
+#include <boost/graph/parallel/container_traits.hpp>
+
+namespace boost {
+ namespace graph {
+ namespace parallel {
+ /// Retrieve the process group from a reverse graph
+ template<typename Graph, typename GraphRef>
+ struct process_group_type<reverse_graph<Graph, GraphRef> >
+ : process_group_type<Graph> { };
+ }
+
+ }
+
+ /// Retrieve the process group from a reverse graph
+ template<typename Graph, typename GraphRef>
+ inline typename graph::parallel::process_group_type<Graph>::type
+ process_group(reverse_graph<Graph, GraphRef> const& g) {
+ return process_group(g.m_g);
+ }
+} // namespace boost
+
+#endif

Added: trunk/boost/graph/distributed/rmat_graph_generator.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/rmat_graph_generator.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,162 @@
+// Copyright 2004, 2005 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: Nick Edmonds
+// Andrew Lumsdaine
+#ifndef BOOST_GRAPH_DISTRIBUTED_RMAT_GENERATOR_HPP
+#define BOOST_GRAPH_DISTRIBUTED_RMAT_GENERATOR_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/graph/parallel/algorithm.hpp>
+#include <boost/graph/distributed/mpi_process_group.hpp>
+
+namespace boost {
+
+ // Memory-scalable (amount of memory required will scale down
+ // linearly as the number of processes increases) generator, which
+ // requires an MPI process group. Run-time is slightly worse than
+ // the unique rmat generator. Edge list generated is sorted and
+ // unique.
+ template<typename Distribution, typename RandomGenerator, typename Graph>
+ class scalable_rmat_iterator
+ {
+ typedef typename graph_traits<Graph>::directed_category directed_category;
+ typedef typename graph_traits<Graph>::vertices_size_type vertices_size_type;
+ typedef typename graph_traits<Graph>::edges_size_type edges_size_type;
+
+ public:
+ typedef std::input_iterator_tag iterator_category;
+ typedef std::pair<vertices_size_type, vertices_size_type> value_type;
+ typedef const value_type& reference;
+ typedef const value_type* pointer;
+ typedef void difference_type;
+
+ // No argument constructor, set to terminating condition
+ scalable_rmat_iterator()
+ : gen(), done(true)
+ { }
+
+ // Initialize for edge generation
+ scalable_rmat_iterator(boost::graph::distributed::mpi_process_group pg,
+ Distribution distrib,
+ RandomGenerator& gen, vertices_size_type n,
+ edges_size_type m, double a, double b, double c,
+ double d, bool permute_vertices = true)
+ : gen(), done(false)
+ {
+ assert(a + b + c + d == 1);
+ int id = process_id(pg);
+
+ this->gen.reset(new uniform_01<RandomGenerator>(gen));
+
+ std::vector<vertices_size_type> vertexPermutation;
+ if (permute_vertices)
+ generate_permutation_vector(gen, vertexPermutation, n);
+
+ int SCALE = int(floor(log2(n)));
+ boost::uniform_01<RandomGenerator> prob(gen);
+
+ std::map<value_type, bool> edge_map;
+
+ edges_size_type generated = 0, local_edges = 0;
+ do {
+ edges_size_type tossed = 0;
+ do {
+ vertices_size_type u, v;
+ tie(u, v) = generate_edge(this->gen, n, SCALE, a, b, c, d);
+
+ if (permute_vertices) {
+ u = vertexPermutation[u];
+ v = vertexPermutation[v];
+ }
+
+ // Lowest vertex number always comes first (this
+ // means we don't have to worry about i->j and j->i
+ // being in the edge list)
+ if (u > v && is_same<directed_category, undirected_tag>::value)
+ std::swap(u, v);
+
+ if (distrib(u) == id || distrib(v) == id) {
+ if (edge_map.find(std::make_pair(u, v)) == edge_map.end()) {
+ edge_map[std::make_pair(u, v)] = true;
+ local_edges++;
+ } else {
+ tossed++;
+
+ // special case - if both u and v are on same
+ // proc, ++ twice, since we divide by two (to
+ // cover the two process case)
+ if (distrib(u) == id && distrib(v) == id)
+ tossed++;
+ }
+ }
+ generated++;
+
+ } while (generated < m);
+ tossed = all_reduce(pg, tossed, boost::parallel::sum<vertices_size_type>());
+ generated -= (tossed / 2);
+ } while (generated < m);
+ // NGE - Asking for more than n^2 edges will result in an infinite loop here
+ // Asking for a value too close to n^2 edges may as well
+
+ values.reserve(local_edges);
+ typename std::map<value_type, bool>::reverse_iterator em_end = edge_map.rend();
+ for (typename std::map<value_type, bool>::reverse_iterator em_i = edge_map.rbegin();
+ em_i != em_end ;
+ ++em_i) {
+ values.push_back(em_i->first);
+ }
+
+ current = values.back();
+ values.pop_back();
+ }
+
+ reference operator*() const { return current; }
+ pointer operator->() const { return &current; }
+
+ scalable_rmat_iterator& operator++()
+ {
+ if (!values.empty()) {
+ current = values.back();
+ values.pop_back();
+ } else
+ done = true;
+
+ return *this;
+ }
+
+ scalable_rmat_iterator operator++(int)
+ {
+ scalable_rmat_iterator temp(*this);
+ ++(*this);
+ return temp;
+ }
+
+ bool operator==(const scalable_rmat_iterator& other) const
+ {
+ return values.empty() && other.values.empty() && done && other.done;
+ }
+
+ bool operator!=(const scalable_rmat_iterator& other) const
+ { return !(*this == other); }
+
+ private:
+
+ // Parameters
+ shared_ptr<uniform_01<RandomGenerator> > gen;
+
+ // Internal data structures
+ std::vector<value_type> values;
+ value_type current;
+ bool done;
+ };
+
+} // end namespace boost
+
+#endif // BOOST_GRAPH_DISTRIBUTED_RMAT_GENERATOR_HPP

Added: trunk/boost/graph/distributed/selector.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/selector.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,36 @@
+// Copyright (C) 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
+#ifndef BOOST_GRAPH_DISTRIBUTED_SELECTOR_HPP
+#define BOOST_GRAPH_DISTRIBUTED_SELECTOR_HPP
+
+#ifndef BOOST_GRAPH_USE_MPI
+#error "Parallel BGL files should not be included unless <boost/graph/use_mpi.hpp> has been included"
+#endif
+
+namespace boost {
+
+ /* The default local selector for a distributedS selector. */
+ struct defaultS {};
+
+ /**
+ * Selector that specifies that the graph should be distributed
+ * among different processes organized based on the given process
+ * group.
+ */
+ template<typename ProcessGroup, typename LocalS = defaultS,
+ typename DistributionS = defaultS>
+ struct distributedS
+ {
+ typedef ProcessGroup process_group_type;
+ typedef LocalS local_selector;
+ typedef DistributionS distribution;
+ };
+}
+
+#endif // BOOST_GRAPH_DISTRIBUTED_SELECTOR_HPP

Added: trunk/boost/graph/distributed/shuffled_distribution.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/shuffled_distribution.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,113 @@
+// Copyright Daniel Wallin 2007. 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)
+
+#ifndef BOOST_SHUFFLED_DISTRIBUTION_070923_HPP
+#define BOOST_SHUFFLED_DISTRIBUTION_070923_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/iterator/counting_iterator.hpp>
+# include <vector>
+
+namespace boost { namespace graph { namespace distributed {
+
+template <class BaseDistribution>
+struct shuffled_distribution : BaseDistribution
+{
+ typedef std::size_t size_type;
+
+ template <class ProcessGroup>
+ shuffled_distribution(ProcessGroup const& pg, BaseDistribution const& base)
+ : BaseDistribution(base)
+ , n(num_processes(pg))
+ , mapping_(make_counting_iterator(size_type(0)), make_counting_iterator(n))
+ , reverse_mapping(mapping_)
+ {}
+
+ std::vector<size_type> const& mapping() const
+ {
+ return mapping_;
+ }
+
+ template <class InputIterator>
+ void assign_mapping(InputIterator first, InputIterator last)
+ {
+ mapping_.assign(first, last);
+ assert(mapping_.size() == n);
+ reverse_mapping.resize(mapping_.size());
+
+ for (std::vector<size_t>::iterator i(mapping_.begin());
+ i != mapping_.end(); ++i)
+ {
+ reverse_mapping[*i] = i - mapping_.begin();
+ }
+ }
+
+ BaseDistribution& base()
+ {
+ return *this;
+ }
+
+ BaseDistribution const& base() const
+ {
+ return *this;
+ }
+
+ template <class ProcessID>
+ size_type block_size(ProcessID id, size_type n) const
+ {
+ return base().block_size(reverse_mapping[id], n);
+ }
+
+ template <class T>
+ size_type operator()(T const& value) const
+ {
+ return mapping_[base()(value)];
+ }
+
+ template <class ProcessID>
+ size_type start(ProcessID id) const
+ {
+ return base().start(reverse_mapping[id]);
+ }
+
+ size_type local(size_type i) const
+ {
+ return base().local(i);
+ }
+
+ size_type global(size_type i) const
+ {
+ return base().global(i);
+ }
+
+ template <class ProcessID>
+ size_type global(ProcessID id, size_type n) const
+ {
+ return base().global(reverse_mapping[id], n);
+ }
+
+ template <class Archive>
+ void serialize(Archive& ar, unsigned long /*version*/)
+ {
+ ar & serialization::make_nvp("base", base());
+ }
+
+ void clear()
+ {
+ base().clear();
+ }
+
+private:
+ size_type n;
+ std::vector<size_type> mapping_;
+ std::vector<size_type> reverse_mapping;
+};
+
+}}} // namespace boost::graph::distributed
+
+#endif // BOOST_SHUFFLED_DISTRIBUTION_070923_HPP
+

Added: trunk/boost/graph/distributed/st_connected.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/st_connected.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,186 @@
+// Copyright (C) 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
+#ifndef BOOST_GRAPH_DISTRIBUTED_ST_CONNECTED_HPP
+#define BOOST_GRAPH_DISTRIBUTED_ST_CONNECTED_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/graph/graph_traits.hpp>
+#include <boost/graph/two_bit_color_map.hpp>
+#include <boost/graph/parallel/distributed_queue.hpp>
+#include <boost/pending/queue.hpp>
+#include <boost/graph/iteration_macros.hpp>
+#include <boost/graph/parallel/container_traits.hpp>
+#include <boost/property_map/property_map.hpp>
+#include <boost/graph/parallel/algorithm.hpp>
+#include <utility>
+#include <boost/optional.hpp>
+
+namespace boost { namespace graph { namespace distributed {
+
+namespace detail {
+ struct pair_and_or
+ {
+ std::pair<bool, bool>
+ operator()(std::pair<bool, bool> x, std::pair<bool, bool> y) const
+ {
+ return std::pair<bool, bool>(x.first && y.first,
+ x.second || y.second);
+ }
+ };
+
+} // end namespace detail
+
+template<typename DistributedGraph, typename ColorMap, typename OwnerMap>
+bool
+st_connected(const DistributedGraph& g,
+ typename graph_traits<DistributedGraph>::vertex_descriptor s,
+ typename graph_traits<DistributedGraph>::vertex_descriptor t,
+ ColorMap color, OwnerMap owner)
+{
+ using boost::parallel::process_group;
+ using boost::parallel::process_group_type;
+ using boost::parallel::all_reduce;
+
+ typedef typename property_traits<ColorMap>::value_type Color;
+ typedef color_traits<Color> ColorTraits;
+ typedef typename process_group_type<DistributedGraph>::type ProcessGroup;
+ typedef typename ProcessGroup::process_id_type ProcessID;
+ typedef typename graph_traits<DistributedGraph>::vertex_descriptor Vertex;
+
+ // Set all vertices to white (unvisited)
+ BGL_FORALL_VERTICES_T(v, g, DistributedGraph)
+ put(color, v, ColorTraits::white());
+
+ // "color" plays the role of a color map, with no synchronization.
+ set_property_map_role(vertex_color, color);
+ color.set_consistency_model(0);
+
+ // Vertices found from the source are grey
+ put(color, s, ColorTraits::gray());
+
+ // Vertices found from the target are green
+ put(color, t, ColorTraits::green());
+
+ ProcessGroup pg = process_group(g);
+ ProcessID rank = process_id(pg);
+
+ // Build a local queue
+ queue<Vertex> Q;
+ if (get(owner, s) == rank) Q.push(s);
+ if (get(owner, t) == rank) Q.push(t);
+
+ queue<Vertex> other_Q;
+
+ while (true) {
+ bool found = false;
+
+ // Process all vertices in the local queue
+ while (!found && !Q.empty()) {
+ Vertex u = Q.top(); Q.pop();
+ Color u_color = get(color, u);
+
+ BGL_FORALL_OUTEDGES_T(u, e, g, DistributedGraph) {
+ Vertex v = target(e, g);
+ Color v_color = get(color, v);
+ if (v_color == ColorTraits::white()) {
+ // We have not seen "v" before; mark it with the same color as u
+ Color u_color = get(color, u);
+ put(color, v, u_color);
+
+ // Either push v into the local queue or send it off to its
+ // owner.
+ ProcessID v_owner = get(owner, v);
+ if (v_owner == rank)
+ other_Q.push(v);
+ else
+ send(pg, v_owner, 0,
+ std::make_pair(v, u_color == ColorTraits::gray()));
+ } else if (v_color != ColorTraits::black() && u_color != v_color) {
+ // Colors have collided. We're done!
+ found = true;
+ break;
+ }
+ }
+
+ // u is done, so mark it black
+ put(color, u, ColorTraits::black());
+ }
+
+ // Ensure that all transmitted messages have been received.
+ synchronize(pg);
+
+ // Move all of the send-to-self values into the local Q.
+ other_Q.swap(Q);
+
+ if (!found) {
+ // Receive all messages
+ while (optional<std::pair<ProcessID, int> > msg = probe(pg)) {
+ std::pair<Vertex, bool> data;
+ receive(pg, msg->first, msg->second, data);
+
+ // Determine the colors of u and v, the source and target
+ // vertices (v is local).
+ Vertex v = data.first;
+ Color v_color = get(color, v);
+ Color u_color = data.second? ColorTraits::gray() : ColorTraits::green();
+ if (v_color == ColorTraits::white()) {
+ // v had no color before, so give it u's color and push it
+ // into the queue.
+ Q.push(v);
+ put(color, v, u_color);
+ } else if (v_color != ColorTraits::black() && u_color != v_color) {
+ // Colors have collided. We're done!
+ found = true;
+ break;
+ }
+ }
+ }
+
+ // Check if either all queues are empty or
+ std::pair<bool, bool> results = all_reduce(pg,
+ boost::parallel::detail::make_untracked_pair(Q.empty(), found),
+ detail::pair_and_or());
+
+ // If someone found the answer, we're done!
+ if (results.second)
+ return true;
+
+ // If all queues are empty, we're done.
+ if (results.first)
+ return false;
+ }
+}
+
+template<typename DistributedGraph, typename ColorMap>
+inline bool
+st_connected(const DistributedGraph& g,
+ typename graph_traits<DistributedGraph>::vertex_descriptor s,
+ typename graph_traits<DistributedGraph>::vertex_descriptor t,
+ ColorMap color)
+{
+ return st_connected(g, s, t, color, get(vertex_owner, g));
+}
+
+template<typename DistributedGraph>
+inline bool
+st_connected(const DistributedGraph& g,
+ typename graph_traits<DistributedGraph>::vertex_descriptor s,
+ typename graph_traits<DistributedGraph>::vertex_descriptor t)
+{
+ return st_connected(g, s, t,
+ make_two_bit_color_map(num_vertices(g),
+ get(vertex_index, g)));
+}
+
+} } } // end namespace boost::graph::distributed
+
+#endif // BOOST_GRAPH_DISTRIBUTED_ST_CONNECTED_HPP

Added: trunk/boost/graph/distributed/strong_components.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/strong_components.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,985 @@
+// Copyright (C) 2004-2008 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: Nick Edmonds
+// Douglas Gregor
+// Andrew Lumsdaine
+#ifndef BOOST_GRAPH_DISTRIBUTED_SCC_HPP
+#define BOOST_GRAPH_DISTRIBUTED_SCC_HPP
+
+#ifndef BOOST_GRAPH_USE_MPI
+#error "Parallel BGL files should not be included unless <boost/graph/use_mpi.hpp> has been included"
+#endif
+
+// #define PBGL_SCC_DEBUG
+
+#include <boost/property_map/property_map.hpp>
+#include <boost/property_map/parallel/distributed_property_map.hpp>
+#include <boost/property_map/parallel/caching_property_map.hpp>
+#include <boost/graph/parallel/algorithm.hpp>
+#include <boost/graph/parallel/process_group.hpp>
+#include <boost/graph/distributed/queue.hpp>
+#include <boost/graph/distributed/filtered_graph.hpp>
+#include <boost/pending/indirect_cmp.hpp>
+#include <boost/graph/breadth_first_search.hpp>
+#include <boost/graph/graph_traits.hpp>
+#include <boost/graph/overloading.hpp>
+#include <boost/graph/distributed/concepts.hpp>
+#include <boost/graph/distributed/local_subgraph.hpp>
+#include <boost/graph/parallel/properties.hpp>
+#include <boost/graph/named_function_params.hpp>
+#include <boost/graph/random.hpp>
+#include <boost/graph/distributed/reverse_graph.hpp>
+#include <boost/optional.hpp>
+#include <boost/graph/distributed/detail/filtered_queue.hpp>
+#include <boost/graph/distributed/adjacency_list.hpp>
+#ifdef PBGL_SCC_DEBUG
+ #include <iostream>
+ #include <cstdlib>
+ #include <iomanip>
+ #include <sys/time.h>
+ #include <boost/graph/distributed/graphviz.hpp> // for ostringstream
+#endif
+#include <vector>
+#include <map>
+#include <boost/graph/parallel/container_traits.hpp>
+
+#ifdef PBGL_SCC_DEBUG
+# include <boost/graph/accounting.hpp>
+#endif /* PBGL_SCC_DEBUG */
+
+// If your graph is likely to have large numbers of small strongly connected
+// components then running the sequential SCC algorithm on the local subgraph
+// and filtering components with no remote edges may increase performance
+// #define FILTER_LOCAL_COMPONENTS
+
+namespace boost { namespace graph { namespace distributed { namespace detail {
+
+template<typename vertex_descriptor>
+struct v_sets{
+ std::vector<vertex_descriptor> pred, succ, intersect, ps_union;
+};
+
+/* Serialize vertex set */
+template<typename Graph>
+void
+marshal_set( std::vector<std::vector<typename graph_traits<Graph>::vertex_descriptor> > in,
+ std::vector<typename graph_traits<Graph>::vertex_descriptor>& out )
+{
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+
+ for( std::size_t i = 0; i < in.size(); ++i ) {
+ out.insert( out.end(), graph_traits<Graph>::null_vertex() );
+ out.insert( out.end(), in[i].begin(), in[i].end() );
+ }
+}
+
+/* Un-serialize vertex set */
+template<typename Graph>
+void
+unmarshal_set( std::vector<typename graph_traits<Graph>::vertex_descriptor> in,
+ std::vector<std::vector<typename graph_traits<Graph>::vertex_descriptor> >& out )
+{
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+ typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
+
+ while( !in.empty() ) {
+ typename std::vector<vertex_descriptor>::iterator end
+ = std::find( in.begin(), in.end(), graph_traits<Graph>::null_vertex() );
+
+ if( end == in.begin() )
+ in.erase( in.begin() );
+ else {
+ out.push_back(std::vector<vertex_descriptor>());
+ out[out.size() - 1].insert( out[out.size() - 1].end(), in.begin(), end );
+ in.erase( in.begin(), end );
+ }
+ }
+}
+
+/* Determine if vertex is in subset */
+template <typename Set>
+struct in_subset {
+ in_subset() : m_s(0) { }
+ in_subset(const Set& s) : m_s(&s) { }
+
+ template <typename Elt>
+ bool operator()(const Elt& x) const {
+ return ((*m_s).find(x) != (*m_s).end());
+ }
+
+private:
+ const Set* m_s;
+};
+
+template<typename T>
+struct vertex_identity_property_map
+ : public boost::put_get_helper<T, vertex_identity_property_map<T> >
+{
+ typedef T key_type;
+ typedef T value_type;
+ typedef T reference;
+ typedef boost::readable_property_map_tag category;
+
+ inline value_type operator[](const key_type& v) const { return v; }
+ inline void clear() { }
+};
+
+template <typename T>
+inline void synchronize( vertex_identity_property_map<T> & ) { }
+
+/* BFS visitor for SCC */
+template<typename Graph, typename SourceMap>
+struct scc_discovery_visitor : bfs_visitor<>
+{
+ scc_discovery_visitor(SourceMap& sourceM)
+ : sourceM(sourceM) {}
+
+ template<typename Edge>
+ void tree_edge(Edge e, const Graph& g)
+ {
+ put(sourceM, target(e,g), get(sourceM, source(e,g)));
+ }
+
+ private:
+ SourceMap& sourceM;
+};
+
+} } } } /* End namespace boost::graph::distributed::detail */
+
+namespace boost { namespace graph { namespace distributed {
+ enum fhp_message_tags { fhp_edges_size_msg, fhp_add_edges_msg, fhp_pred_size_msg,
+ fhp_pred_msg, fhp_succ_size_msg, fhp_succ_msg };
+
+ template<typename Graph, typename ReverseGraph,
+ typename VertexComponentMap, typename IsoMapFR, typename IsoMapRF,
+ typename VertexIndexMap>
+ void
+ fleischer_hendrickson_pinar_strong_components(const Graph& g,
+ VertexComponentMap c,
+ const ReverseGraph& gr,
+ IsoMapFR fr, IsoMapRF rf,
+ VertexIndexMap vertex_index_map)
+ {
+ typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+ typedef typename graph_traits<ReverseGraph>::vertex_iterator rev_vertex_iterator;
+ typedef typename graph_traits<ReverseGraph>::vertex_descriptor rev_vertex_descriptor;
+ typedef typename boost::graph::parallel::process_group_type<Graph>::type
+ process_group_type;
+ typedef typename process_group_type::process_id_type process_id_type;
+ typedef iterator_property_map<typename std::vector<vertex_descriptor>::iterator,
+ VertexIndexMap> ParentMap;
+ typedef iterator_property_map<typename std::vector<default_color_type>::iterator,
+ VertexIndexMap> ColorMap;
+ typedef iterator_property_map<typename std::vector<vertex_descriptor>::iterator,
+ VertexIndexMap> Rev_ParentMap;
+ typedef std::vector<std::pair<vertex_descriptor, vertex_descriptor> > VertexPairVec;
+
+ typedef typename property_map<Graph, vertex_owner_t>::const_type
+ OwnerMap;
+
+ OwnerMap owner = get(vertex_owner, g);
+
+ using boost::graph::parallel::process_group;
+ process_group_type pg = process_group(g);
+ process_id_type id = process_id(pg);
+ int num_procs = num_processes(pg);
+ int n = 0;
+
+ int my_n = num_vertices(g);
+ all_reduce(pg, &my_n, &my_n+1, &n, std::plus<int>());
+
+ //
+ // Initialization
+ //
+
+#ifdef PBGL_SCC_DEBUG
+ accounting::time_type start = accounting::get_time();
+#endif
+
+ vertex_iterator vstart, vend;
+ rev_vertex_iterator rev_vstart, rev_vend;
+ std::vector<std::vector<vertex_descriptor> > vertex_sets, new_vertex_sets;
+
+ vertex_sets.push_back(std::vector<vertex_descriptor>());
+
+ // Remove vertices that do not have at least one in edge and one out edge
+ new_vertex_sets.push_back(std::vector<vertex_descriptor>());
+ for( tie(vstart, vend) = vertices(g); vstart != vend; vstart++ )
+ if( out_degree( get(fr, *vstart), gr) > 0 && out_degree(*vstart, g) > 0 )
+ new_vertex_sets[0].push_back( *vstart );
+
+ // Perform sequential SCC on local subgraph, filter all components with external
+ // edges, mark remaining components and remove them from vertex_sets
+#ifdef FILTER_LOCAL_COMPONENTS
+ // This doesn't actually speed up SCC in connected graphs it seems, but it does work
+ // and may help in the case where there are lots of small strong components.
+ {
+ local_subgraph<const Graph> ls(g);
+ typedef typename property_map<local_subgraph<const Graph>, vertex_index_t>::type
+ local_index_map_type;
+ local_index_map_type local_index = get(vertex_index, ls);
+
+ std::vector<int> ls_components_vec(num_vertices(ls));
+ typedef iterator_property_map<std::vector<int>::iterator, local_index_map_type>
+ ls_components_map_type;
+ ls_components_map_type ls_component(ls_components_vec.begin(), local_index);
+ int num_comp = boost::strong_components(ls, ls_component);
+
+ // Create map of components
+ std::map<int, std::vector<vertex_descriptor> > local_comp_map;
+ typedef typename graph_traits<local_subgraph<const Graph> >::vertex_iterator ls_vertex_iterator;
+ ls_vertex_iterator vstart, vend;
+ for( tie(vstart,vend) = vertices(ls); vstart != vend; vstart++ )
+ local_comp_map[get(ls_component, *vstart)].push_back( *vstart );
+
+ // Filter components that have no non-local edges
+ typedef typename graph_traits<Graph>::adjacency_iterator adjacency_iterator;
+ typedef typename graph_traits<ReverseGraph>::adjacency_iterator rev_adjacency_iterator;
+ adjacency_iterator abegin, aend;
+ rev_adjacency_iterator rev_abegin, rev_aend;
+ for( std::size_t i = 0; i < num_comp; ++i ) {
+ bool local = true;
+ for( std::size_t j = 0; j < local_comp_map[i].size(); j++ ) {
+ for( tie(abegin,aend) = adjacent_vertices(local_comp_map[i][j], g);
+ abegin != aend; abegin++ )
+ if( get(owner, *abegin) != id ) {
+ local = false;
+ break;
+ }
+
+ if( local )
+ for( tie(rev_abegin,rev_aend) = adjacent_vertices(get(fr, local_comp_map[i][j]), gr);
+ rev_abegin != rev_aend; rev_abegin++ )
+ if( get(owner, *rev_abegin) != id ) {
+ local = false;
+ break;
+ }
+
+ if( !local ) break;
+ }
+
+ if( local ) // Mark and remove from new_vertex_sets
+ for( std::size_t j = 0; j < local_comp_map[i].size(); j++ ) {
+ put( c, local_comp_map[i][j], local_comp_map[i][0] );
+ typename std::vector<vertex_descriptor>::iterator pos =
+ std::find(new_vertex_sets[0].begin(), new_vertex_sets[0].end(), local_comp_map[i][j]);
+ if( pos != new_vertex_sets[0].end() )
+ new_vertex_sets[0].erase(pos);
+ }
+ }
+ }
+#endif // FILTER_LOCAL_COMPONENTS
+
+ all_gather( pg, new_vertex_sets[0].begin(), new_vertex_sets[0].end(), vertex_sets[0] );
+ new_vertex_sets.clear();
+
+#ifdef PBGL_SCC_DEBUG
+ accounting::time_type end = accounting::get_time();
+ if(id == 0)
+ std::cerr << "Trim local SCCs time = " << accounting::print_time(end - start) << " seconds.\n";
+#endif
+
+ if( vertex_sets[0].empty() ) return;
+
+ //
+ // Recursively determine SCCs
+ //
+
+#ifdef PBGL_SCC_DEBUG
+ int iterations = 0;
+#endif
+
+ // Only need to be able to map starting vertices for BFS from now on
+ fr.clear();
+
+ do {
+
+#ifdef PBGL_SCC_DEBUG
+ if(id == 0) {
+ printf("\n\nIteration %d:\n\n", iterations++);
+
+ if( iterations > 1 ) {
+ end = accounting::get_time();
+ std::cerr << "Running main loop destructors time = " << accounting::print_time(end - start) << " seconds.\n";
+ }
+
+ start = accounting::get_time();
+ }
+#endif
+
+ // Get forward->reverse mappings for BFS start vertices
+ for (std::size_t i = 0; i < vertex_sets.size(); ++i)
+ get(fr, vertex_sets[i][0]);
+ synchronize(fr);
+
+ // Determine local vertices to start BFS from
+ std::vector<vertex_descriptor> local_start;
+ for( std::size_t i = id; i < vertex_sets.size(); i += num_procs )
+ local_start.push_back(vertex_sets[i][0]);
+
+ if( local_start.empty() )
+ local_start.push_back(vertex_sets[0][0]);
+
+
+ // Make filtered graphs
+ typedef std::set<vertex_descriptor> VertexSet;
+ typedef std::set<rev_vertex_descriptor> Rev_VertexSet;
+
+ VertexSet filter_set_g;
+ Rev_VertexSet filter_set_gr;
+ typename VertexSet::iterator fs;
+
+ int active_vertices = 0;
+ for (std::size_t i = 0; i < vertex_sets.size(); i++)
+ active_vertices += vertex_sets[i].size();
+
+ // This is a completely random bound
+ if ( active_vertices < 0.05*n ) {
+ // TODO: This set insertion is ridiculously inefficient, make it an in-place-merge?
+ for (std::size_t i = 0; i < vertex_sets.size(); i++)
+ filter_set_g.insert(vertex_sets[i].begin(), vertex_sets[i].end());
+
+ for (fs = filter_set_g.begin(); fs != filter_set_g.end(); ++fs )
+ filter_set_gr.insert(get(fr, *fs));
+ }
+
+ filtered_graph<const Graph, keep_all, detail::in_subset<VertexSet> >
+ fg(g, keep_all(), detail::in_subset<VertexSet>(filter_set_g));
+
+ filtered_graph<const ReverseGraph, keep_all, detail::in_subset<VertexSet> >
+ fgr(gr, keep_all(), detail::in_subset<VertexSet>(filter_set_gr));
+
+ // Add additional starting vertices to BFS queue
+ typedef filtered_queue<queue<vertex_descriptor>, boost::detail::has_not_been_seen<VertexIndexMap> >
+ local_queue_type;
+ typedef boost::graph::distributed::distributed_queue<process_group_type, OwnerMap, local_queue_type>
+ queue_t;
+
+ typedef typename property_map<ReverseGraph, vertex_owner_t>::const_type
+ RevOwnerMap;
+
+ typedef filtered_queue<queue<rev_vertex_descriptor>, boost::detail::has_not_been_seen<VertexIndexMap> >
+ rev_local_queue_type;
+ typedef boost::graph::distributed::distributed_queue<process_group_type, RevOwnerMap, rev_local_queue_type>
+ rev_queue_t;
+
+ queue_t Q(process_group(g),
+ owner,
+ make_filtered_queue(queue<vertex_descriptor>(),
+ boost::detail::has_not_been_seen<VertexIndexMap>
+ (num_vertices(g), vertex_index_map)),
+ false);
+
+ rev_queue_t Qr(process_group(gr),
+ get(vertex_owner, gr),
+ make_filtered_queue(queue<rev_vertex_descriptor>(),
+ boost::detail::has_not_been_seen<VertexIndexMap>
+ (num_vertices(gr), vertex_index_map)),
+ false);
+
+ for( std::size_t i = 1; i < local_start.size(); ++i ) {
+ Q.push(local_start[i]);
+ Qr.push(get(fr, local_start[i]));
+ }
+
+#ifdef PBGL_SCC_DEBUG
+ end = accounting::get_time();
+ if(id == 0)
+ std::cerr << " Initialize BFS time = " << accounting::print_time(end - start) << " seconds.\n";
+ start = accounting::get_time();
+#endif
+
+#ifdef PBGL_SCC_DEBUG
+ accounting::time_type start2 = accounting::get_time();
+#endif
+
+ // Forward BFS
+ std::vector<default_color_type> color_map_s(num_vertices(g));
+ ColorMap color_map(color_map_s.begin(), vertex_index_map);
+ std::vector<vertex_descriptor> succ_map_s(num_vertices(g), graph_traits<Graph>::null_vertex());
+ ParentMap succ_map(succ_map_s.begin(), vertex_index_map);
+
+ for( std::size_t i = 0; i < vertex_sets.size(); ++i )
+ put(succ_map, vertex_sets[i][0], vertex_sets[i][0]);
+
+#ifdef PBGL_SCC_DEBUG
+ accounting::time_type end2 = accounting::get_time();
+ if(id == 0)
+ std::cerr << " Initialize forward BFS time = " << accounting::print_time(end2 - start2) << " seconds.\n";
+#endif
+
+ if (active_vertices < 0.05*n)
+ breadth_first_search(fg, local_start[0], Q,
+ detail::scc_discovery_visitor<filtered_graph<const Graph, keep_all,
+ detail::in_subset<VertexSet> >, ParentMap>
+ (succ_map),
+ color_map);
+ else
+ breadth_first_search(g, local_start[0], Q,
+ detail::scc_discovery_visitor<const Graph, ParentMap>(succ_map),
+ color_map);
+
+#ifdef PBGL_SCC_DEBUG
+ start2 = accounting::get_time();
+#endif
+
+ // Reverse BFS
+ color_map.clear(); // reuse color map since g and gr have same vertex index
+ std::vector<vertex_descriptor> pred_map_s(num_vertices(gr), graph_traits<Graph>::null_vertex());
+ Rev_ParentMap pred_map(pred_map_s.begin(), vertex_index_map);
+
+ for( std::size_t i = 0; i < vertex_sets.size(); ++i )
+ put(pred_map, get(fr, vertex_sets[i][0]), vertex_sets[i][0]);
+
+#ifdef PBGL_SCC_DEBUG
+ end2 = accounting::get_time();
+ if(id == 0)
+ std::cerr << " Initialize reverse BFS time = " << accounting::print_time(end2 - start2) << " seconds.\n";
+#endif
+
+ if (active_vertices < 0.05*n)
+ breadth_first_search(fgr, get(fr, local_start[0]),
+ Qr,
+ detail::scc_discovery_visitor<filtered_graph<const ReverseGraph, keep_all,
+ detail::in_subset<Rev_VertexSet> >, Rev_ParentMap>
+ (pred_map),
+ color_map);
+ else
+ breadth_first_search(gr, get(fr, local_start[0]),
+ Qr,
+ detail::scc_discovery_visitor<const ReverseGraph, Rev_ParentMap>(pred_map),
+ color_map);
+
+#ifdef PBGL_SCC_DEBUG
+ end = accounting::get_time();
+ if(id == 0)
+ std::cerr << " Perform forward and reverse BFS time = " << accounting::print_time(end - start) << " seconds.\n";
+ start = accounting::get_time();
+#endif
+
+ // Send predecessors and successors discovered by this proc to the proc responsible for
+ // this BFS tree
+ typedef struct detail::v_sets<vertex_descriptor> Vsets;
+ std::map<vertex_descriptor, Vsets> set_map;
+
+ std::map<vertex_descriptor, int> dest_map;
+
+ std::vector<VertexPairVec> successors(num_procs);
+ std::vector<VertexPairVec> predecessors(num_procs);
+
+ // Calculate destinations for messages
+ for (std::size_t i = 0; i < vertex_sets.size(); ++i)
+ dest_map[vertex_sets[i][0]] = i % num_procs;
+
+ for( tie(vstart, vend) = vertices(g); vstart != vend; vstart++ ) {
+ vertex_descriptor v = get(succ_map, *vstart);
+ if( v != graph_traits<Graph>::null_vertex() )
+ if (dest_map[v] == id)
+ set_map[v].succ.push_back(*vstart);
+ else
+ successors[dest_map[v]].push_back( std::make_pair(v, *vstart) );
+ }
+
+ for( tie(rev_vstart, rev_vend) = vertices(gr); rev_vstart != rev_vend; rev_vstart++ ) {
+ vertex_descriptor v = get(pred_map, *rev_vstart);
+ if( v != graph_traits<Graph>::null_vertex() )
+ if (dest_map[v] == id)
+ set_map[v].pred.push_back(get(rf, *rev_vstart));
+ else
+ predecessors[dest_map[v]].push_back( std::make_pair(v, get(rf, *rev_vstart)) );
+ }
+
+ // Send predecessor and successor messages
+ for (process_id_type i = 0; i < num_procs; ++i) {
+ if (!successors[i].empty()) {
+ send(pg, i, fhp_succ_size_msg, successors[i].size());
+ send(pg, i, fhp_succ_msg, &successors[i][0], successors[i].size());
+ }
+ if (!predecessors[i].empty()) {
+ send(pg, i, fhp_pred_size_msg, predecessors[i].size());
+ send(pg, i, fhp_pred_msg, &predecessors[i][0], predecessors[i].size());
+ }
+ }
+ synchronize(pg);
+
+ // Receive predecessor and successor messages and handle them
+ while (optional<std::pair<process_id_type, int> > m = probe(pg)) {
+ assert(m->second == fhp_succ_size_msg || m->second == fhp_pred_size_msg);
+ std::size_t num_requests;
+ receive(pg, m->first, m->second, num_requests);
+ VertexPairVec requests(num_requests);
+ if (m->second == fhp_succ_size_msg) {
+ receive(pg, m->first, fhp_succ_msg, &requests[0],
+ num_requests);
+
+ std::map<vertex_descriptor, int> added;
+ for (std::size_t i = 0; i < requests.size(); ++i) {
+ set_map[requests[i].first].succ.push_back(requests[i].second);
+ added[requests[i].first]++;
+ }
+
+ // If order of vertex traversal in vertices() is std::less<vertex_descriptor>,
+ // then the successor sets will be in order
+ for (std::size_t i = 0; i < local_start.size(); ++i)
+ if (added[local_start[i]] > 0)
+ std::inplace_merge(set_map[local_start[i]].succ.begin(),
+ set_map[local_start[i]].succ.end() - added[local_start[i]],
+ set_map[local_start[i]].succ.end(),
+ std::less<vertex_descriptor>());
+
+ } else {
+ receive(pg, m->first, fhp_pred_msg, &requests[0],
+ num_requests);
+
+ std::map<vertex_descriptor, int> added;
+ for (std::size_t i = 0; i < requests.size(); ++i) {
+ set_map[requests[i].first].pred.push_back(requests[i].second);
+ added[requests[i].first]++;
+ }
+
+ if (boost::is_same<detail::vertex_identity_property_map<vertex_descriptor>, IsoMapRF>::value)
+ for (std::size_t i = 0; i < local_start.size(); ++i)
+ if (added[local_start[i]] > 0)
+ std::inplace_merge(set_map[local_start[i]].pred.begin(),
+ set_map[local_start[i]].pred.end() - added[local_start[i]],
+ set_map[local_start[i]].pred.end(),
+ std::less<vertex_descriptor>());
+ }
+ }
+
+#ifdef PBGL_SCC_DEBUG
+ end = accounting::get_time();
+ if(id == 0)
+ std::cerr << " All gather successors and predecessors time = " << accounting::print_time(end - start) << " seconds.\n";
+ start = accounting::get_time();
+#endif
+
+ //
+ // Filter predecessor and successor sets and perform set arithmetic
+ //
+ new_vertex_sets.clear();
+
+ if( std::size_t(id) < vertex_sets.size() ) { //If this proc has one or more unique starting points
+
+ for( std::size_t i = 0; i < local_start.size(); ++i ) {
+
+ vertex_descriptor v = local_start[i];
+
+ // Replace this sort with an in-place merges during receive step if possible
+ if (!boost::is_same<detail::vertex_identity_property_map<vertex_descriptor>, IsoMapRF>::value)
+ std::sort(set_map[v].pred.begin(), set_map[v].pred.end(), std::less<vertex_descriptor>());
+
+ // Limit predecessor and successor sets to members of the original set
+ std::vector<vertex_descriptor> temp;
+
+ std::set_intersection( vertex_sets[id + i*num_procs].begin(), vertex_sets[id + i*num_procs].end(),
+ set_map[v].pred.begin(), set_map[v].pred.end(),
+ back_inserter(temp),
+ std::less<vertex_descriptor>());
+ set_map[v].pred.clear();
+ std::swap(set_map[v].pred, temp);
+
+ std::set_intersection( vertex_sets[id + i*num_procs].begin(), vertex_sets[id + i*num_procs].end(),
+ set_map[v].succ.begin(), set_map[v].succ.end(),
+ back_inserter(temp),
+ std::less<vertex_descriptor>());
+ set_map[v].succ.clear();
+ std::swap(set_map[v].succ, temp);
+
+ // Intersection(pred, succ)
+ std::set_intersection(set_map[v].pred.begin(), set_map[v].pred.end(),
+ set_map[v].succ.begin(), set_map[v].succ.end(),
+ back_inserter(set_map[v].intersect),
+ std::less<vertex_descriptor>());
+
+ // Union(pred, succ)
+ std::set_union(set_map[v].pred.begin(), set_map[v].pred.end(),
+ set_map[v].succ.begin(), set_map[v].succ.end(),
+ back_inserter(set_map[v].ps_union),
+ std::less<vertex_descriptor>());
+
+ new_vertex_sets.push_back(std::vector<vertex_descriptor>());
+ // Original set - Union(pred, succ)
+ std::set_difference(vertex_sets[id + i*num_procs].begin(), vertex_sets[id + i*num_procs].end(),
+ set_map[v].ps_union.begin(), set_map[v].ps_union.end(),
+ back_inserter(new_vertex_sets[new_vertex_sets.size() - 1]),
+ std::less<vertex_descriptor>());
+
+ set_map[v].ps_union.clear();
+
+ new_vertex_sets.push_back(std::vector<vertex_descriptor>());
+ // Pred - Intersect(pred, succ)
+ std::set_difference(set_map[v].pred.begin(), set_map[v].pred.end(),
+ set_map[v].intersect.begin(), set_map[v].intersect.end(),
+ back_inserter(new_vertex_sets[new_vertex_sets.size() - 1]),
+ std::less<vertex_descriptor>());
+
+ set_map[v].pred.clear();
+
+ new_vertex_sets.push_back(std::vector<vertex_descriptor>());
+ // Succ - Intersect(pred, succ)
+ std::set_difference(set_map[v].succ.begin(), set_map[v].succ.end(),
+ set_map[v].intersect.begin(), set_map[v].intersect.end(),
+ back_inserter(new_vertex_sets[new_vertex_sets.size() - 1]),
+ std::less<vertex_descriptor>());
+
+ set_map[v].succ.clear();
+
+ // Label SCC just identified with the 'first' vertex in that SCC
+ for( std::size_t j = 0; j < set_map[v].intersect.size(); j++ )
+ put(c, set_map[v].intersect[j], set_map[v].intersect[0]);
+
+ set_map[v].intersect.clear();
+ }
+ }
+
+#ifdef PBGL_SCC_DEBUG
+ end = accounting::get_time();
+ if(id == 0)
+ std::cerr << " Perform set arithemetic time = " << accounting::print_time(end - start) << " seconds.\n";
+ start = accounting::get_time();
+#endif
+
+ // Remove sets of size 1 from new_vertex_sets
+ typename std::vector<std::vector<vertex_descriptor> >::iterator vviter;
+ for( vviter = new_vertex_sets.begin(); vviter != new_vertex_sets.end(); /*in loop*/ )
+ if( (*vviter).size() < 2 )
+ vviter = new_vertex_sets.erase( vviter );
+ else
+ vviter++;
+
+ // All gather new sets and recur (gotta marshal and unmarshal sets first)
+ vertex_sets.clear();
+ std::vector<vertex_descriptor> serial_sets, all_serial_sets;
+ detail::marshal_set<Graph>( new_vertex_sets, serial_sets );
+ all_gather( pg, serial_sets.begin(), serial_sets.end(), all_serial_sets );
+ detail::unmarshal_set<Graph>( all_serial_sets, vertex_sets );
+
+#ifdef PBGL_SCC_DEBUG
+ end = accounting::get_time();
+ if(id == 0) {
+ std::cerr << " Serialize and gather new vertex sets time = " << accounting::print_time(end - start) << " seconds.\n\n\n";
+
+ printf("Vertex sets: %d\n", (int)vertex_sets.size() );
+ for( std::size_t i = 0; i < vertex_sets.size(); ++i )
+ printf(" %d: %d\n", i, (int)vertex_sets[i].size() );
+ }
+ start = accounting::get_time();
+#endif
+
+ // HACK!?! -- This would be more properly implemented as a topological sort
+ // Remove vertices without an edge to another vertex in the set and an edge from another
+ // vertex in the set
+ typedef typename graph_traits<Graph>::out_edge_iterator out_edge_iterator;
+ out_edge_iterator estart, eend;
+ typedef typename graph_traits<ReverseGraph>::out_edge_iterator r_out_edge_iterator;
+ r_out_edge_iterator restart, reend;
+ for (std::size_t i = 0; i < vertex_sets.size(); ++i) {
+ std::vector<vertex_descriptor> new_set;
+ for (std::size_t j = 0; j < vertex_sets[i].size(); ++j) {
+ vertex_descriptor v = vertex_sets[i][j];
+ if (get(owner, v) == id) {
+ tie(estart, eend) = out_edges(v, g);
+ while (estart != eend && find(vertex_sets[i].begin(), vertex_sets[i].end(),
+ target(*estart,g)) == vertex_sets[i].end()) estart++;
+ if (estart != eend) {
+ tie(restart, reend) = out_edges(get(fr, v), gr);
+ while (restart != reend && find(vertex_sets[i].begin(), vertex_sets[i].end(),
+ get(rf, target(*restart,g))) == vertex_sets[i].end()) restart++;
+ if (restart != reend)
+ new_set.push_back(v);
+ }
+ }
+ }
+ vertex_sets[i].clear();
+ all_gather(pg, new_set.begin(), new_set.end(), vertex_sets[i]);
+ std::sort(vertex_sets[i].begin(), vertex_sets[i].end(), std::less<vertex_descriptor>());
+ }
+#ifdef PBGL_SCC_DEBUG
+ end = accounting::get_time();
+ if(id == 0)
+ std::cerr << " Trim vertex sets time = " << accounting::print_time(end - start) << " seconds.\n\n\n";
+ start = accounting::get_time();
+#endif
+
+ } while ( !vertex_sets.empty() );
+
+
+ // Label vertices not in a SCC as their own SCC
+ for( tie(vstart, vend) = vertices(g); vstart != vend; vstart++ )
+ if( get(c, *vstart) == graph_traits<Graph>::null_vertex() )
+ put(c, *vstart, *vstart);
+
+ synchronize(c);
+ }
+
+ template<typename Graph, typename ReverseGraph, typename IsoMap>
+ void
+ build_reverse_graph( const Graph& g, ReverseGraph& gr, IsoMap& fr, IsoMap& rf )
+ {
+ typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+ typedef typename graph_traits<Graph>::out_edge_iterator out_edge_iterator;
+ typedef typename boost::graph::parallel::process_group_type<Graph>::type process_group_type;
+ typedef typename process_group_type::process_id_type process_id_type;
+ typedef std::vector<std::pair<vertex_descriptor, vertex_descriptor> > VertexPairVec;
+
+ typedef typename graph_traits<Graph>::directed_category directed_category;
+
+ typename property_map<Graph, vertex_owner_t>::const_type
+ owner = get(vertex_owner, g);
+
+ process_group_type pg = process_group(g);
+ process_id_type id = process_id(pg);
+
+ int n;
+ vertex_iterator vstart, vend;
+ int num_procs = num_processes(pg);
+
+ vertex_descriptor v;
+ out_edge_iterator oestart, oeend;
+ for( tie(vstart, vend) = vertices(g); vstart != vend; vstart++ )
+ {
+ v = add_vertex(gr);
+ put(fr, *vstart, v);
+ put(rf, v, *vstart);
+ }
+
+ gr.distribution() = g.distribution();
+
+ int my_n = num_vertices(g);
+ all_reduce(pg, &my_n, &my_n+1, &n, std::plus<int>());
+
+ for (int i = 0; i < n; ++i)
+ get(fr, vertex(i,g));
+ synchronize(fr);
+
+ // Add edges to gr
+ std::vector<std::pair<vertex_descriptor, vertex_descriptor> > new_edges;
+ for( tie(vstart, vend) = vertices(g); vstart != vend; vstart++ )
+ for( tie(oestart, oeend) = out_edges(*vstart, g); oestart != oeend; oestart++ )
+ new_edges.push_back( std::make_pair(get(fr, target(*oestart,g)), get(fr, source(*oestart, g))) );
+
+ std::vector<VertexPairVec> edge_requests(num_procs);
+
+ typename std::vector<std::pair<vertex_descriptor, vertex_descriptor> >::iterator iter;
+ for( iter = new_edges.begin(); iter != new_edges.end(); iter++ ) {
+ std::pair<vertex_descriptor, vertex_descriptor> p1 = *iter;
+ if( get(owner, p1.first ) == id )
+ add_edge( p1.first, p1.second, gr );
+ else
+ edge_requests[get(owner, p1.first)].push_back(p1);
+ }
+ new_edges.clear();
+
+ // Send edge addition requests
+ for (process_id_type p = 0; p < num_procs; ++p) {
+ if (!edge_requests[p].empty()) {
+ VertexPairVec reqs(edge_requests[p].begin(), edge_requests[p].end());
+ send(pg, p, fhp_edges_size_msg, reqs.size());
+ send(pg, p, fhp_add_edges_msg, &reqs[0], reqs.size());
+ }
+ }
+ synchronize(pg);
+
+ // Receive edge addition requests and handle them
+ while (optional<std::pair<process_id_type, int> > m = probe(pg)) {
+ assert(m->second == fhp_edges_size_msg);
+ std::size_t num_requests;
+ receive(pg, m->first, m->second, num_requests);
+ VertexPairVec requests(num_requests);
+ receive(pg, m->first, fhp_add_edges_msg, &requests[0],
+ num_requests);
+ for( std::size_t i = 0; i < requests.size(); ++i )
+ add_edge( requests[i].first, requests[i].second, gr );
+ }
+ synchronize(gr);
+ }
+
+ template<typename Graph, typename VertexComponentMap, typename ComponentMap>
+ typename property_traits<ComponentMap>::value_type
+ number_components(const Graph& g, VertexComponentMap r, ComponentMap c)
+ {
+ typedef typename boost::graph::parallel::process_group_type<Graph>::type process_group_type;
+ typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+ typedef typename property_traits<ComponentMap>::value_type ComponentMapType;
+ std::vector<vertex_descriptor> my_roots, all_roots;
+ vertex_iterator vstart, vend;
+
+ for( tie(vstart, vend) = vertices(g); vstart != vend; vstart++ )
+ if( find( my_roots.begin(), my_roots.end(), get(r, *vstart) ) == my_roots.end() )
+ my_roots.push_back( get(r, *vstart) );
+
+ all_gather( process_group(g), my_roots.begin(), my_roots.end(), all_roots );
+
+ /* Number components */
+ std::map<vertex_descriptor, ComponentMapType> comp_numbers;
+ ComponentMapType c_num = 0;
+
+ // Compute component numbers
+ for (std::size_t i = 0; i < all_roots.size(); ++i )
+ if ( comp_numbers.count(all_roots[i]) == 0 )
+ comp_numbers[all_roots[i]] = c_num++;
+
+ // Broadcast component numbers
+ for( tie(vstart, vend) = vertices(g); vstart != vend; vstart++ )
+ put( c, *vstart, comp_numbers[get(r,*vstart)] );
+
+ // Broadcast number of components
+ if (process_id(process_group(g)) == 0) {
+ typedef typename process_group_type::process_size_type
+ process_size_type;
+ for (process_size_type dest = 1, n = num_processes(process_group(g));
+ dest != n; ++dest)
+ send(process_group(g), dest, 0, c_num);
+ }
+
+ synchronize(process_group(g));
+
+ if (process_id(process_group(g)) != 0) receive(process_group(g), 0, 0, c_num);
+
+ synchronize(c);
+ return c_num;
+ }
+
+
+ template<typename Graph, typename ComponentMap, typename VertexComponentMap,
+ typename VertexIndexMap>
+ typename property_traits<ComponentMap>::value_type
+ fleischer_hendrickson_pinar_strong_components_impl
+ (const Graph& g,
+ ComponentMap c,
+ VertexComponentMap r,
+ VertexIndexMap vertex_index_map,
+ incidence_graph_tag)
+ {
+ typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+ typedef iterator_property_map<typename std::vector<vertex_descriptor>::iterator,
+ VertexIndexMap> IsoMap;
+ typename boost::graph::parallel::process_group_type<Graph>::type pg = process_group(g);
+
+#ifdef PBGL_SCC_DEBUG
+ accounting::time_type start = accounting::get_time();
+#endif
+
+ typedef adjacency_list<listS,
+ distributedS<typename boost::graph::parallel::process_group_type<Graph>::type, vecS>,
+ directedS > ReverseGraph;
+
+ ReverseGraph gr(0, pg);
+ std::vector<vertex_descriptor> fr_s(num_vertices(g));
+ std::vector<vertex_descriptor> rf_s(num_vertices(g));
+ IsoMap fr(fr_s.begin(), vertex_index_map); // fr = forward->reverse
+ IsoMap rf(rf_s.begin(), vertex_index_map); // rf = reverse->forward
+
+ build_reverse_graph(g, gr, fr, rf);
+
+#ifdef PBGL_SCC_DEBUG
+ accounting::time_type end = accounting::get_time();
+ if(process_id(process_group(g)) == 0)
+ std::cerr << "Reverse graph initialization time = " << accounting::print_time(end - start) << " seconds.\n";
+#endif
+
+ fleischer_hendrickson_pinar_strong_components(g, r, gr, fr, rf,
+ vertex_index_map);
+
+ typename property_traits<ComponentMap>::value_type c_num = number_components(g, r, c);
+
+ return c_num;
+ }
+
+ template<typename Graph, typename ComponentMap, typename VertexComponentMap,
+ typename VertexIndexMap>
+ typename property_traits<ComponentMap>::value_type
+ fleischer_hendrickson_pinar_strong_components_impl
+ (const Graph& g,
+ ComponentMap c,
+ VertexComponentMap r,
+ VertexIndexMap vertex_index_map,
+ bidirectional_graph_tag)
+ {
+ typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
+
+ reverse_graph<Graph> gr(g);
+ detail::vertex_identity_property_map<vertex_descriptor> fr, rf;
+
+ fleischer_hendrickson_pinar_strong_components(g, r, gr, fr, rf,
+ vertex_index_map);
+
+ typename property_traits<ComponentMap>::value_type c_num
+ = number_components(g, r, c);
+
+ return c_num;
+ }
+
+ template<typename Graph, typename ComponentMap, typename VertexIndexMap>
+ inline typename property_traits<ComponentMap>::value_type
+ fleischer_hendrickson_pinar_strong_components
+ (const Graph& g,
+ ComponentMap c,
+ VertexIndexMap vertex_index_map)
+ {
+ typedef typename graph_traits<Graph>::vertex_descriptor
+ vertex_descriptor;
+ typedef iterator_property_map<typename std::vector<vertex_descriptor>::iterator,
+ VertexIndexMap> VertexComponentMap;
+ typename boost::graph::parallel::process_group_type<Graph>::type pg
+ = process_group(g);
+
+ if (num_processes(pg) == 1) {
+ local_subgraph<const Graph> ls(g);
+ return boost::strong_components(ls, c);
+ }
+
+ // Create a VertexComponentMap for intermediate labeling of SCCs
+ std::vector<vertex_descriptor> r_s(num_vertices(g), graph_traits<Graph>::null_vertex());
+ VertexComponentMap r(r_s.begin(), vertex_index_map);
+
+ return fleischer_hendrickson_pinar_strong_components_impl
+ (g, c, r, vertex_index_map,
+ typename graph_traits<Graph>::traversal_category());
+ }
+
+ template<typename Graph, typename ComponentMap>
+ inline typename property_traits<ComponentMap>::value_type
+ fleischer_hendrickson_pinar_strong_components(const Graph& g,
+ ComponentMap c)
+ {
+ return fleischer_hendrickson_pinar_strong_components(g, c, get(vertex_index, g));
+ }
+
+} // end namespace distributed
+
+using distributed::fleischer_hendrickson_pinar_strong_components;
+
+} // end namespace graph
+
+template<class Graph, class ComponentMap, class P, class T, class R>
+inline typename property_traits<ComponentMap>::value_type
+strong_components
+ (const Graph& g, ComponentMap comp,
+ const bgl_named_params<P, T, R>&
+ BOOST_GRAPH_ENABLE_IF_MODELS_PARM(Graph, distributed_graph_tag))
+{
+ return graph::fleischer_hendrickson_pinar_strong_components(g, comp);
+}
+
+template<class Graph, class ComponentMap>
+inline typename property_traits<ComponentMap>::value_type
+strong_components
+ (const Graph& g, ComponentMap comp
+ BOOST_GRAPH_ENABLE_IF_MODELS_PARM(Graph, distributed_graph_tag))
+{
+ return graph::fleischer_hendrickson_pinar_strong_components(g, comp);
+}
+
+} /* end namespace boost */
+
+#endif // BOOST_GRAPH_DISTRIBUTED_SCC_HPP

Added: trunk/boost/graph/distributed/two_bit_color_map.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/two_bit_color_map.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,116 @@
+// Copyright (C) 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
+// Jeremiah Willcock
+// Andrew Lumsdaine
+
+// Distributed version of the two-bit color map
+#ifndef BOOST_DISTRIBUTED_TWO_BIT_COLOR_MAP_HPP
+#define BOOST_DISTRIBUTED_TWO_BIT_COLOR_MAP_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/graph/two_bit_color_map.hpp>
+#include <boost/property_map/parallel/distributed_property_map.hpp>
+#include <boost/property_map/parallel/local_property_map.hpp>
+
+namespace boost {
+
+template<typename ProcessGroup, typename GlobalMap, typename StorageMap>
+class two_bit_color_map<local_property_map<ProcessGroup,GlobalMap,StorageMap> >
+ : public parallel::distributed_property_map<ProcessGroup, GlobalMap,
+ two_bit_color_map<StorageMap> >
+{
+ typedef two_bit_color_map<StorageMap> local_map;
+
+ typedef parallel::distributed_property_map<ProcessGroup, GlobalMap,
+ local_map >
+ inherited;
+
+ typedef local_property_map<ProcessGroup, GlobalMap, StorageMap>
+ index_map_type;
+
+public:
+ two_bit_color_map(std::size_t inital_size,
+ const index_map_type& index = index_map_type())
+ : inherited(index.process_group(), index.global(),
+ local_map(inital_size, index.base())) { }
+
+ inherited& base() { return *this; }
+ const inherited& base() const { return *this; }
+};
+
+template<typename ProcessGroup, typename GlobalMap, typename StorageMap>
+inline two_bit_color_type
+get(two_bit_color_map<local_property_map<ProcessGroup,GlobalMap,StorageMap> >
+ const& pm,
+ typename two_bit_color_map<GlobalMap>::key_type key)
+{
+ return get(pm.base(), key);
+}
+
+template<typename ProcessGroup, typename GlobalMap, typename StorageMap>
+inline void
+put(two_bit_color_map<local_property_map<ProcessGroup,GlobalMap,StorageMap> >
+ const& pm,
+ typename two_bit_color_map<GlobalMap>::key_type key,
+ two_bit_color_type value)
+{
+ put(pm.base(), key, value);
+}
+
+template<typename ProcessGroup, typename GlobalMap, typename StorageMap>
+class two_bit_color_map<parallel::distributed_property_map<
+ ProcessGroup, GlobalMap, StorageMap> >
+ : public parallel::distributed_property_map<
+ ProcessGroup, GlobalMap, two_bit_color_map<StorageMap> >
+{
+ typedef two_bit_color_map<StorageMap> local_map;
+
+ typedef parallel::distributed_property_map<ProcessGroup,GlobalMap,local_map>
+ inherited;
+
+ typedef parallel::distributed_property_map<ProcessGroup, GlobalMap,
+ StorageMap>
+ index_map_type;
+
+public:
+ two_bit_color_map(std::size_t inital_size,
+ const index_map_type& index = index_map_type())
+ : inherited(index.process_group(), index.global(),
+ local_map(inital_size, index.base())) { }
+
+ inherited& base() { return *this; }
+ const inherited& base() const { return *this; }
+};
+
+template<typename ProcessGroup, typename GlobalMap, typename StorageMap>
+inline two_bit_color_type
+get(two_bit_color_map<
+ parallel::distributed_property_map<
+ ProcessGroup, GlobalMap, two_bit_color_map<StorageMap> > > const& pm,
+ typename two_bit_color_map<GlobalMap>::key_type key)
+{
+ return get(pm.base(), key);
+}
+
+template<typename ProcessGroup, typename GlobalMap, typename StorageMap>
+inline void
+put(two_bit_color_map<
+ parallel::distributed_property_map<
+ ProcessGroup, GlobalMap, two_bit_color_map<StorageMap> > > const& pm,
+ typename two_bit_color_map<GlobalMap>::key_type key,
+ two_bit_color_type value)
+{
+ put(pm.base(), key, value);
+}
+
+} // end namespace boost
+
+#endif // BOOST_DISTRIBUTED_TWO_BIT_COLOR_MAP_HPP

Added: trunk/boost/graph/distributed/unsafe_serialize.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/unsafe_serialize.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,75 @@
+// Copyright (C) 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 file contains the "unsafe_serialize" routine, which transforms
+// types they may not be serializable (such as void*) into
+// serializable equivalents.
+#ifndef PBGL_UNSAFE_SERIALIZE_HPP
+#define PBGL_UNSAFE_SERIALIZE_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/mpi/datatype.hpp>
+#include <boost/serialization/is_bitwise_serializable.hpp>
+#include <boost/mpl/bool.hpp>
+#include <boost/mpl/if.hpp>
+#include <utility>
+
+BOOST_IS_BITWISE_SERIALIZABLE(void*)
+namespace boost { namespace mpi {
+ template<> struct is_mpi_datatype<void*> : mpl::true_ { };
+} } // end namespace boost::mpi
+
+namespace boost {
+ typedef mpl::if_c<(sizeof(int) == sizeof(void*)),
+ int,
+ mpl::if_c<(sizeof(long) == sizeof(void*)), long, void>::type
+ >::type ptr_serialize_type;
+
+ template<typename T> inline T& unsafe_serialize(T& x) { return x; }
+
+ inline ptr_serialize_type& unsafe_serialize(void*& x)
+ { return reinterpret_cast<ptr_serialize_type&>(x); }
+
+ // Force Boost.MPI to serialize a void* like a ptr_serialize_type
+ namespace mpi {
+ template<> inline MPI_Datatype get_mpi_datatype<void*>(void* const& x)
+ {
+ return get_mpi_datatype<ptr_serialize_type>();
+ }
+ }
+
+ template<typename T, typename U>
+ struct unsafe_pair
+ {
+ unsafe_pair() { }
+ unsafe_pair(const T& t, const U& u) : first(t), second(u) { }
+ unsafe_pair(const std::pair<T, U>& p) : first(p.first), second(p.second) { }
+ T first;
+ U second;
+
+ template<typename Archiver>
+ void serialize(Archiver& ar, const unsigned /*version*/)
+ {
+ ar & unsafe_serialize(first) & unsafe_serialize(second);
+ }
+ };
+
+ template<typename T, typename U>
+ bool operator<(unsafe_pair<T,U> const& x, unsafe_pair<T,U> const& y)
+ {
+ return std::make_pair(x.first, x.second) <
+ std::make_pair(y.first, y.second);
+ }
+
+} // end namespace boost
+
+#endif // PBGL_UNSAFE_SERIALIZE_HPP

Added: trunk/boost/graph/distributed/vertex_list_adaptor.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/distributed/vertex_list_adaptor.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,403 @@
+// 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
+
+#ifndef BOOST_VERTEX_LIST_ADAPTOR_HPP
+#define BOOST_VERTEX_LIST_ADAPTOR_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/graph/graph_traits.hpp>
+#include <vector>
+#include <boost/shared_ptr.hpp>
+#include <boost/property_map/property_map.hpp>
+#include <boost/graph/parallel/algorithm.hpp>
+#include <boost/graph/parallel/container_traits.hpp>
+#include <boost/property_map/vector_property_map.hpp>
+
+namespace boost { namespace graph {
+
+// --------------------------------------------------------------------------
+// Global index map built from a distribution
+// --------------------------------------------------------------------------
+template<typename Distribution, typename OwnerPropertyMap,
+ typename LocalPropertyMap>
+class distribution_global_index_map
+{
+ public:
+ typedef std::size_t value_type;
+ typedef value_type reference;
+ typedef typename property_traits<OwnerPropertyMap>::key_type key_type;
+ typedef readable_property_map_tag category;
+
+ distribution_global_index_map(const Distribution& distribution,
+ const OwnerPropertyMap& owner,
+ const LocalPropertyMap& local)
+ : distribution_(distribution), owner(owner), local(local) { }
+
+ Distribution distribution_;
+ OwnerPropertyMap owner;
+ LocalPropertyMap local;
+};
+
+template<typename Distribution, typename OwnerPropertyMap,
+ typename LocalPropertyMap>
+inline
+typename distribution_global_index_map<Distribution, OwnerPropertyMap,
+ LocalPropertyMap>::value_type
+get(const distribution_global_index_map<Distribution, OwnerPropertyMap,
+ LocalPropertyMap>& p,
+ typename distribution_global_index_map<Distribution, OwnerPropertyMap,
+ LocalPropertyMap>::key_type x)
+{
+ using boost::get;
+ return p.distribution_.global(get(p.owner, x), get(p.local, x));
+}
+
+template<typename Graph, typename Distribution>
+inline
+distribution_global_index_map<
+ Distribution,
+ typename property_map<Graph, vertex_owner_t>::const_type,
+ typename property_map<Graph, vertex_local_t>::const_type>
+make_distribution_global_index_map(const Graph& g, const Distribution& d)
+{
+ typedef distribution_global_index_map<
+ Distribution,
+ typename property_map<Graph, vertex_owner_t>::const_type,
+ typename property_map<Graph, vertex_local_t>::const_type>
+ result_type;
+ return result_type(d, get(vertex_owner, g), get(vertex_local, g));
+}
+
+// --------------------------------------------------------------------------
+// Global index map built from a distributed index map and list of vertices
+// --------------------------------------------------------------------------
+template<typename IndexMap>
+class stored_global_index_map : public IndexMap
+{
+ public:
+ typedef readable_property_map_tag category;
+
+ stored_global_index_map(const IndexMap& index_map) : IndexMap(index_map) {
+ // When we have a global index, we need to always have the indices
+ // of every key we've seen
+ this->set_max_ghost_cells(0);
+ }
+};
+
+// --------------------------------------------------------------------------
+// Global index map support code
+// --------------------------------------------------------------------------
+namespace detail {
+ template<typename PropertyMap, typename ForwardIterator>
+ inline void
+ initialize_global_index_map(const PropertyMap&,
+ ForwardIterator, ForwardIterator)
+ { }
+
+ template<typename IndexMap, typename ForwardIterator>
+ void
+ initialize_global_index_map(stored_global_index_map<IndexMap>& p,
+ ForwardIterator first, ForwardIterator last)
+ {
+ using std::distance;
+
+ typedef typename property_traits<IndexMap>::value_type size_t;
+
+ size_t n = distance(first, last);
+ for (size_t i = 0; i < n; ++i, ++first) local_put(p, *first, i);
+ }
+}
+
+// --------------------------------------------------------------------------
+// Adapts a Distributed Vertex List Graph to a Vertex List Graph
+// --------------------------------------------------------------------------
+template<typename Graph, typename GlobalIndexMap>
+class vertex_list_adaptor : public graph_traits<Graph>
+{
+ typedef graph_traits<Graph> inherited;
+
+ typedef typename inherited::traversal_category base_traversal_category;
+
+ public:
+ typedef typename inherited::vertex_descriptor vertex_descriptor;
+ typedef typename std::vector<vertex_descriptor>::iterator vertex_iterator;
+ typedef typename std::vector<vertex_descriptor>::size_type
+ vertices_size_type;
+
+ struct traversal_category
+ : public virtual base_traversal_category,
+ public virtual vertex_list_graph_tag {};
+
+ vertex_list_adaptor(const Graph& g,
+ const GlobalIndexMap& index_map = GlobalIndexMap())
+ : g(&g), index_map(index_map)
+ {
+ using boost::vertices;
+
+ all_vertices_.reset(new std::vector<vertex_descriptor>());
+ all_gather(process_group(), vertices(g).first, vertices(g).second,
+ *all_vertices_);
+ detail::initialize_global_index_map(this->index_map,
+ all_vertices_->begin(),
+ all_vertices_->end());
+ }
+
+ const Graph& base() const { return *g; }
+
+ // --------------------------------------------------------------------------
+ // Distributed Container
+ // --------------------------------------------------------------------------
+ typedef typename boost::graph::parallel::process_group_type<Graph>::type
+ process_group_type;
+
+ process_group_type process_group() const
+ {
+ using boost::graph::parallel::process_group;
+ return process_group(*g);
+ }
+
+ std::pair<vertex_iterator, vertex_iterator> vertices() const
+ { return std::make_pair(all_vertices_->begin(), all_vertices_->end()); }
+
+ vertices_size_type num_vertices() const { return all_vertices_->size(); }
+
+ GlobalIndexMap get_index_map() const { return index_map; }
+
+ private:
+ const Graph* g;
+ GlobalIndexMap index_map;
+ shared_ptr<std::vector<vertex_descriptor> > all_vertices_;
+};
+
+template<typename Graph, typename GlobalIndexMap>
+inline vertex_list_adaptor<Graph, GlobalIndexMap>
+make_vertex_list_adaptor(const Graph& g, const GlobalIndexMap& index_map)
+{ return vertex_list_adaptor<Graph, GlobalIndexMap>(g, index_map); }
+
+namespace detail {
+ template<typename Graph>
+ class default_global_index_map
+ {
+ typedef typename graph_traits<Graph>::vertices_size_type value_type;
+ typedef typename property_map<Graph, vertex_index_t>::const_type local_map;
+
+ public:
+ typedef vector_property_map<value_type, local_map> distributed_map;
+ typedef stored_global_index_map<distributed_map> type;
+ };
+}
+
+template<typename Graph>
+inline
+vertex_list_adaptor<Graph,
+ typename detail::default_global_index_map<Graph>::type>
+make_vertex_list_adaptor(const Graph& g)
+{
+ typedef typename detail::default_global_index_map<Graph>::type
+ GlobalIndexMap;
+ typedef typename detail::default_global_index_map<Graph>::distributed_map
+ DistributedMap;
+ typedef vertex_list_adaptor<Graph, GlobalIndexMap> result_type;
+ return result_type(g,
+ GlobalIndexMap(DistributedMap(num_vertices(g),
+ get(vertex_index, g))));
+}
+
+// --------------------------------------------------------------------------
+// Incidence Graph
+// --------------------------------------------------------------------------
+template<typename Graph, typename GlobalIndexMap>
+inline typename vertex_list_adaptor<Graph, GlobalIndexMap>::vertex_descriptor
+source(typename vertex_list_adaptor<Graph, GlobalIndexMap>::edge_descriptor e,
+ const vertex_list_adaptor<Graph, GlobalIndexMap>& g)
+{ return source(e, g.base()); }
+
+template<typename Graph, typename GlobalIndexMap>
+inline typename vertex_list_adaptor<Graph, GlobalIndexMap>::vertex_descriptor
+target(typename vertex_list_adaptor<Graph, GlobalIndexMap>::edge_descriptor e,
+ const vertex_list_adaptor<Graph, GlobalIndexMap>& g)
+{ return target(e, g.base()); }
+
+template<typename Graph, typename GlobalIndexMap>
+inline
+std::pair<typename vertex_list_adaptor<Graph, GlobalIndexMap>::out_edge_iterator,
+ typename vertex_list_adaptor<Graph, GlobalIndexMap>::out_edge_iterator>
+out_edges(typename vertex_list_adaptor<Graph, GlobalIndexMap>::vertex_descriptor v,
+ const vertex_list_adaptor<Graph, GlobalIndexMap>& g)
+{ return out_edges(v, g.base()); }
+
+template<typename Graph, typename GlobalIndexMap>
+inline typename vertex_list_adaptor<Graph, GlobalIndexMap>::degree_size_type
+out_degree(typename vertex_list_adaptor<Graph, GlobalIndexMap>::vertex_descriptor v,
+ const vertex_list_adaptor<Graph, GlobalIndexMap>& g)
+{ return out_degree(v, g.base()); }
+
+// --------------------------------------------------------------------------
+// Bidirectional Graph
+// --------------------------------------------------------------------------
+template<typename Graph, typename GlobalIndexMap>
+inline
+std::pair<typename vertex_list_adaptor<Graph, GlobalIndexMap>::in_edge_iterator,
+ typename vertex_list_adaptor<Graph, GlobalIndexMap>::in_edge_iterator>
+in_edges(typename vertex_list_adaptor<Graph, GlobalIndexMap>::vertex_descriptor v,
+ const vertex_list_adaptor<Graph, GlobalIndexMap>& g)
+{ return in_edges(v, g.base()); }
+
+template<typename Graph, typename GlobalIndexMap>
+inline typename vertex_list_adaptor<Graph, GlobalIndexMap>::degree_size_type
+in_degree(typename vertex_list_adaptor<Graph, GlobalIndexMap>::vertex_descriptor v,
+ const vertex_list_adaptor<Graph, GlobalIndexMap>& g)
+{ return in_degree(v, g.base()); }
+
+template<typename Graph, typename GlobalIndexMap>
+inline typename vertex_list_adaptor<Graph, GlobalIndexMap>::degree_size_type
+degree(typename vertex_list_adaptor<Graph, GlobalIndexMap>::vertex_descriptor v,
+ const vertex_list_adaptor<Graph, GlobalIndexMap>& g)
+{ return degree(v, g.base()); }
+
+// --------------------------------------------------------------------------
+// Adjacency Graph
+// --------------------------------------------------------------------------
+template<typename Graph, typename GlobalIndexMap>
+inline
+std::pair<typename vertex_list_adaptor<Graph, GlobalIndexMap>::adjacency_iterator,
+ typename vertex_list_adaptor<Graph, GlobalIndexMap>::adjacency_iterator>
+adjacent_vertices(typename vertex_list_adaptor<Graph, GlobalIndexMap>::vertex_descriptor v,
+ const vertex_list_adaptor<Graph, GlobalIndexMap>& g)
+{ return adjacent_vertices(v, g.base()); }
+
+
+// --------------------------------------------------------------------------
+// Vertex List Graph
+// --------------------------------------------------------------------------
+template<typename Graph, typename GlobalIndexMap>
+inline
+std::pair<typename vertex_list_adaptor<Graph, GlobalIndexMap>::vertex_iterator,
+ typename vertex_list_adaptor<Graph, GlobalIndexMap>::vertex_iterator>
+vertices(const vertex_list_adaptor<Graph, GlobalIndexMap>& g)
+{ return g.vertices(); }
+
+template<typename Graph, typename GlobalIndexMap>
+inline
+typename vertex_list_adaptor<Graph, GlobalIndexMap>::vertices_size_type
+num_vertices(const vertex_list_adaptor<Graph, GlobalIndexMap>& g)
+{ return g.num_vertices(); }
+
+// --------------------------------------------------------------------------
+// Edge List Graph
+// --------------------------------------------------------------------------
+template<typename Graph, typename GlobalIndexMap>
+inline
+std::pair<typename vertex_list_adaptor<Graph, GlobalIndexMap>::edge_iterator,
+ typename vertex_list_adaptor<Graph, GlobalIndexMap>::edge_iterator>
+edges(const vertex_list_adaptor<Graph, GlobalIndexMap>& g)
+{ return edges(g.base()); }
+
+template<typename Graph, typename GlobalIndexMap>
+inline
+typename vertex_list_adaptor<Graph, GlobalIndexMap>::edges_size_type
+num_edges(const vertex_list_adaptor<Graph, GlobalIndexMap>& g)
+{ return num_edges(g.base()); }
+
+// --------------------------------------------------------------------------
+// Property Graph
+// --------------------------------------------------------------------------
+template<typename PropertyTag, typename Graph, typename GlobalIndexMap>
+inline typename property_map<Graph, PropertyTag>::type
+get(PropertyTag p, vertex_list_adaptor<Graph, GlobalIndexMap>& g)
+{ return get(p, g.base()); }
+
+template<typename PropertyTag, typename Graph, typename GlobalIndexMap>
+inline typename property_map<Graph, PropertyTag>::const_type
+get(PropertyTag p, const vertex_list_adaptor<Graph, GlobalIndexMap>& g)
+{ return get(p, g.base()); }
+
+template<typename PropertyTag, typename Graph, typename GlobalIndexMap>
+inline typename property_traits<
+ typename property_map<Graph, PropertyTag>::type
+ >::value_type
+get(PropertyTag p, const vertex_list_adaptor<Graph, GlobalIndexMap>& g,
+ typename property_traits<
+ typename property_map<Graph, PropertyTag>::type
+ >::key_type const& x)
+{ return get(p, g.base(), x); }
+
+template<typename PropertyTag, typename Graph, typename GlobalIndexMap>
+inline void
+put(PropertyTag p, vertex_list_adaptor<Graph, GlobalIndexMap>& g,
+ typename property_traits<
+ typename property_map<Graph, PropertyTag>::type
+ >::key_type const& x,
+ typename property_traits<
+ typename property_map<Graph, PropertyTag>::type
+ >::value_type const& v)
+{ return put(p, g.base(), x, v); }
+
+// --------------------------------------------------------------------------
+// Property Graph: vertex_index property
+// --------------------------------------------------------------------------
+template<typename Graph, typename GlobalIndexMap>
+inline GlobalIndexMap
+get(vertex_index_t, const vertex_list_adaptor<Graph, GlobalIndexMap>& g)
+{ return g.get_index_map(); }
+
+template<typename Graph, typename GlobalIndexMap>
+inline typename vertex_list_adaptor<Graph, GlobalIndexMap>::vertices_size_type
+get(vertex_index_t, const vertex_list_adaptor<Graph, GlobalIndexMap>& g,
+ typename vertex_list_adaptor<Graph, GlobalIndexMap>::vertex_descriptor x)
+{ return get(g.get_index_map(), x); }
+
+// --------------------------------------------------------------------------
+// Adjacency Matrix Graph
+// --------------------------------------------------------------------------
+template<typename Graph, typename GlobalIndexMap>
+std::pair<typename vertex_list_adaptor<Graph, GlobalIndexMap>::edge_descriptor,
+ bool>
+edge(typename vertex_list_adaptor<Graph, GlobalIndexMap>::vertex_descriptor u,
+ typename vertex_list_adaptor<Graph, GlobalIndexMap>::vertex_descriptor v,
+ vertex_list_adaptor<Graph, GlobalIndexMap>& g)
+{ return edge(u, v, g.base()); }
+
+} } // end namespace boost::graph
+
+namespace boost {
+
+// --------------------------------------------------------------------------
+// Property Graph: vertex_index property
+// --------------------------------------------------------------------------
+template<typename Graph, typename GlobalIndexMap>
+class property_map<vertex_index_t,
+ graph::vertex_list_adaptor<Graph, GlobalIndexMap> >
+{
+public:
+ typedef GlobalIndexMap type;
+ typedef type const_type;
+};
+
+template<typename Graph, typename GlobalIndexMap>
+class property_map<vertex_index_t,
+ const graph::vertex_list_adaptor<Graph, GlobalIndexMap> >
+{
+public:
+ typedef GlobalIndexMap type;
+ typedef type const_type;
+};
+
+using graph::distribution_global_index_map;
+using graph::make_distribution_global_index_map;
+using graph::stored_global_index_map;
+using graph::make_vertex_list_adaptor;
+using graph::vertex_list_adaptor;
+
+} // end namespace boost
+
+#endif // BOOST_VERTEX_LIST_ADAPTOR_HPP

Modified: trunk/boost/graph/fruchterman_reingold.hpp
==============================================================================
--- trunk/boost/graph/fruchterman_reingold.hpp (original)
+++ trunk/boost/graph/fruchterman_reingold.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -457,4 +457,8 @@
 
 } // end namespace boost
 
+#ifdef BOOST_GRAPH_USE_MPI
+# include <boost/graph/distributed/fruchterman_reingold.hpp>
+#endif
+
 #endif // BOOST_GRAPH_FRUCHTERMAN_REINGOLD_FORCE_DIRECTED_LAYOUT_HPP

Modified: trunk/boost/graph/graphviz.hpp
==============================================================================
--- trunk/boost/graph/graphviz.hpp (original)
+++ trunk/boost/graph/graphviz.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -794,4 +794,8 @@
 # include <boost/graph/detail/read_graphviz_spirit.hpp>
 #endif // BOOST_GRAPH_READ_GRAPHVIZ_ITERATORS
 
+#ifdef BOOST_GRAPH_USE_MPI
+# include <boost/graph/distributed/graphviz.hpp>
+#endif
+
 #endif // BOOST_GRAPHVIZ_HPP

Modified: trunk/boost/graph/page_rank.hpp
==============================================================================
--- trunk/boost/graph/page_rank.hpp (original)
+++ trunk/boost/graph/page_rank.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -154,4 +154,8 @@
 
 } } // end namespace boost::graph
 
+#ifdef BOOST_GRAPH_USE_MPI
+# include <boost/graph/distributed/page_rank.hpp>
+#endif
+
 #endif // BOOST_GRAPH_PAGE_RANK_HPP

Added: trunk/boost/graph/parallel/algorithm.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/parallel/algorithm.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,84 @@
+// Copyright 2004 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
+#ifndef BOOST_PARALLEL_ALGORITHM_HPP
+#define BOOST_PARALLEL_ALGORITHM_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/optional.hpp>
+#include <boost/config.hpp> // for BOOST_STATIC_CONSTANT
+#include <vector>
+#include <functional>
+
+namespace boost { namespace parallel {
+ template<typename BinaryOp>
+ struct is_commutative
+ {
+ BOOST_STATIC_CONSTANT(bool, value = false);
+ };
+
+ template<typename T>
+ struct minimum : std::binary_function<T, T, T>
+ {
+ const T& operator()(const T& x, const T& y) const { return x < y? x : y; }
+ };
+
+ template<typename T>
+ struct maximum : std::binary_function<T, T, T>
+ {
+ const T& operator()(const T& x, const T& y) const { return x < y? y : x; }
+ };
+
+ template<typename T>
+ struct sum : std::binary_function<T, T, T>
+ {
+ const T operator()(const T& x, const T& y) const { return x + y; }
+ };
+
+ template<typename ProcessGroup, typename InputIterator,
+ typename OutputIterator, typename BinaryOperation>
+ OutputIterator
+ reduce(ProcessGroup pg, typename ProcessGroup::process_id_type root,
+ InputIterator first, InputIterator last, OutputIterator out,
+ BinaryOperation bin_op);
+
+ template<typename ProcessGroup, typename T, typename BinaryOperation>
+ inline T
+ all_reduce(ProcessGroup pg, const T& value, BinaryOperation bin_op)
+ {
+ T result;
+ all_reduce(pg,
+ const_cast<T*>(&value), const_cast<T*>(&value+1),
+ &result, bin_op);
+ return result;
+ }
+
+ template<typename ProcessGroup, typename T, typename BinaryOperation>
+ inline T
+ scan(ProcessGroup pg, const T& value, BinaryOperation bin_op)
+ {
+ T result;
+ scan(pg,
+ const_cast<T*>(&value), const_cast<T*>(&value+1),
+ &result, bin_op);
+ return result;
+ }
+
+
+ template<typename ProcessGroup, typename InputIterator, typename T>
+ void
+ all_gather(ProcessGroup pg, InputIterator first, InputIterator last,
+ std::vector<T>& out);
+} } // end namespace boost::parallel
+
+#include <boost/graph/parallel/detail/inplace_all_to_all.hpp>
+
+#endif // BOOST_PARALLEL_ALGORITHM_HPP

Added: trunk/boost/graph/parallel/basic_reduce.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/parallel/basic_reduce.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,42 @@
+// Copyright 2005 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
+
+#ifndef BOOST_PARALLEL_BASIC_REDUCE_HPP
+#define BOOST_PARALLEL_BASIC_REDUCE_HPP
+
+#ifndef BOOST_GRAPH_USE_MPI
+#error "Parallel BGL files should not be included unless <boost/graph/use_mpi.hpp> has been included"
+#endif
+
+namespace boost { namespace parallel {
+
+/** Reduction operation used to reconcile differences between local
+ * and remote values for a particular key in a property map. The
+ * type @c T is typically the @c value_type of the property
+ * map. This basic reduction returns a default-constructed @c T as
+ * the default value and always resolves to the remote value.
+ */
+template<typename T>
+struct basic_reduce
+{
+ BOOST_STATIC_CONSTANT(bool, non_default_resolver = false);
+
+ /// Returns a default-constructed T object
+ template<typename Key>
+ T operator()(const Key&) const { return T(); }
+
+ /// Returns the remote value
+ template<typename Key>
+ const T& operator()(const Key&, const T&, const T& remote) const
+ { return remote; }
+};
+
+} } // end namespace boost::parallel
+
+#endif // BOOST_PARALLEL_BASIC_REDUCE_HPP

Added: trunk/boost/graph/parallel/container_traits.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/parallel/container_traits.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,45 @@
+// 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 file contains traits that describe
+//
+#ifndef BOOST_GRAPH_PARALLEL_CONTAINER_TRAITS_HPP
+#define BOOST_GRAPH_PARALLEL_CONTAINER_TRAITS_HPP
+
+#ifndef BOOST_GRAPH_USE_MPI
+#error "Parallel BGL files should not be included unless <boost/graph/use_mpi.hpp> has been included"
+#endif
+
+namespace boost { namespace graph { namespace parallel {
+
+template<typename T>
+struct process_group_type
+{
+ typedef typename T::process_group_type type;
+};
+
+template<typename T>
+inline typename process_group_type<T>::type
+process_group(const T& x)
+{ return x.process_group(); }
+
+// Helper function that algorithms should use to get the process group
+// out of a container.
+template<typename Container>
+inline typename process_group_type<Container>::type
+process_group_adl(const Container& container)
+{
+ return process_group(container);
+}
+
+
+} } } // end namespace boost::graph::parallel
+
+#endif // BOOST_GRAPH_PARALLEL_CONTAINER_TRAITS_HPP

Added: trunk/boost/graph/parallel/detail/inplace_all_to_all.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/parallel/detail/inplace_all_to_all.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,78 @@
+// Copyright 2005 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
+
+#ifndef BOOST_GRAPH_PARALLEL_INPLACE_ALL_TO_ALL_HPP
+#define BOOST_GRAPH_PARALLEL_INPLACE_ALL_TO_ALL_HPP
+
+#ifndef BOOST_GRAPH_USE_MPI
+#error "Parallel BGL files should not be included unless <boost/graph/use_mpi.hpp> has been included"
+#endif
+
+//
+// Implements the inplace all-to-all communication algorithm.
+//
+#include <vector>
+#include <iterator>
+
+namespace boost { namespace parallel {
+
+template<typename ProcessGroup, typename T>
+// where {LinearProcessGroup<ProcessGroup>, MessagingProcessGroup<ProcessGroup>}
+void
+inplace_all_to_all(ProcessGroup pg,
+ const std::vector<std::vector<T> >& outgoing,
+ std::vector<std::vector<T> >& incoming)
+{
+ typedef typename std::vector<T>::size_type size_type;
+
+ typedef typename ProcessGroup::process_size_type process_size_type;
+ typedef typename ProcessGroup::process_id_type process_id_type;
+
+ process_size_type p = num_processes(pg);
+
+ // Make sure there are no straggling messages
+ synchronize(pg);
+
+ // Send along the count (always) and the data (if count > 0)
+ for (process_id_type dest = 0; dest < p; ++dest) {
+ if (dest != process_id(pg)) {
+ send(pg, dest, 0, outgoing[dest].size());
+ if (!outgoing[dest].empty())
+ send(pg, dest, 1, &outgoing[dest].front(), outgoing[dest].size());
+ }
+ }
+
+ // Make sure all of the data gets transferred
+ synchronize(pg);
+
+ // Receive the sizes and data
+ for (process_id_type source = 0; source < p; ++source) {
+ if (source != process_id(pg)) {
+ size_type size;
+ receive(pg, source, 0, size);
+ incoming[source].resize(size);
+ if (size > 0)
+ receive(pg, source, 1, &incoming[source].front(), size);
+ } else if (&incoming != &outgoing) {
+ incoming[source] = outgoing[source];
+ }
+ }
+}
+
+template<typename ProcessGroup, typename T>
+// where {LinearProcessGroup<ProcessGroup>, MessagingProcessGroup<ProcessGroup>}
+void
+inplace_all_to_all(ProcessGroup pg, std::vector<std::vector<T> >& data)
+{
+ inplace_all_to_all(pg, data, data);
+}
+
+} } // end namespace boost::parallel
+
+#endif // BOOST_GRAPH_PARALLEL_INPLACE_ALL_TO_ALL_HPP

Added: trunk/boost/graph/parallel/detail/property_holders.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/parallel/detail/property_holders.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,152 @@
+// Copyright (C) 2007 Douglas Gregor and Matthias Troyer
+//
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+//
+// This file contains helper data structures for use in transmitting
+// properties. The basic idea is to optimize away any storage for the
+// properties when no properties are specified.
+#ifndef BOOST_PARALLEL_DETAIL_PROPERTY_HOLDERS_HPP
+#define BOOST_PARALLEL_DETAIL_PROPERTY_HOLDERS_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/mpi/datatype.hpp>
+#include <boost/property_map/property_map.hpp>
+#include <boost/serialization/base_object.hpp>
+#include <boost/mpl/and.hpp>
+#include <boost/graph/parallel/detail/untracked_pair.hpp>
+
+namespace boost { namespace detail { namespace parallel {
+
+/**
+ * This structure contains an instance of @c Property, unless @c
+ * Property is a placeholder for "no property". Always access the
+ * property through @c get_property. Typically used as a base class.
+ */
+template<typename Property>
+struct maybe_store_property
+{
+ maybe_store_property() {}
+ maybe_store_property(const Property& p) : p(p) {}
+
+ Property& get_property() { return p; }
+ const Property& get_property() const { return p; }
+
+private:
+ Property p;
+
+ friend class boost::serialization::access;
+
+ template<typename Archiver>
+ void serialize(Archiver& ar, const unsigned int /*version*/)
+ {
+ ar & p;
+ }
+};
+
+template<>
+struct maybe_store_property<no_property>
+{
+ maybe_store_property() {}
+ maybe_store_property(no_property) {}
+
+ no_property get_property() const { return no_property(); }
+
+private:
+ friend class boost::serialization::access;
+
+ template<typename Archiver>
+ void serialize(Archiver&, const unsigned int /*version*/) { }
+};
+
+/**
+ * This structure is a simple pair that also contains a property.
+ */
+template<typename T, typename U, typename Property>
+class pair_with_property
+ : public boost::parallel::detail::untracked_pair<T, U>
+ , public maybe_store_property<Property>
+{
+public:
+ typedef boost::parallel::detail::untracked_pair<T, U> pair_base;
+ typedef maybe_store_property<Property> property_base;
+
+ pair_with_property() { }
+
+ pair_with_property(const T& t, const U& u, const Property& property)
+ : pair_base(t, u), property_base(property) { }
+
+private:
+ friend class boost::serialization::access;
+
+ template<typename Archiver>
+ void serialize(Archiver& ar, const unsigned int /*version*/)
+ {
+ ar & boost::serialization::base_object<pair_base>(*this)
+ & boost::serialization::base_object<property_base>(*this);
+ }
+};
+
+template<typename T, typename U, typename Property>
+inline pair_with_property<T, U, Property>
+make_pair_with_property(const T& t, const U& u, const Property& property)
+{
+ return pair_with_property<T, U, Property>(t, u, property);
+}
+
+} } } // end namespace boost::parallel::detail
+
+namespace boost { namespace mpi {
+
+template<>
+struct is_mpi_datatype<boost::detail::parallel::maybe_store_property<no_property> > : mpl::true_ { };
+
+template<typename Property>
+struct is_mpi_datatype<boost::detail::parallel::maybe_store_property<Property> >
+ : is_mpi_datatype<Property> { };
+
+template<typename T, typename U, typename Property>
+struct is_mpi_datatype<boost::detail::parallel::pair_with_property<T, U, Property> >
+ : boost::mpl::and_<is_mpi_datatype<boost::parallel::detail::untracked_pair<T, U> >,
+ is_mpi_datatype<Property> > { };
+
+} } // end namespace boost::mpi
+
+BOOST_IS_BITWISE_SERIALIZABLE(boost::detail::parallel::maybe_store_property<no_property>)
+
+namespace boost { namespace serialization {
+
+template<typename Property>
+struct is_bitwise_serializable<boost::detail::parallel::maybe_store_property<Property> >
+ : is_bitwise_serializable<Property> { };
+
+template<typename Property>
+struct implementation_level<boost::detail::parallel::maybe_store_property<Property> >
+ : mpl::int_<object_serializable> {} ;
+
+template<typename Property>
+struct tracking_level<boost::detail::parallel::maybe_store_property<Property> >
+ : mpl::int_<track_never> {} ;
+
+template<typename T, typename U, typename Property>
+struct is_bitwise_serializable<
+ boost::detail::parallel::pair_with_property<T, U, Property> >
+ : boost::mpl::and_<is_bitwise_serializable<boost::parallel::detail::untracked_pair<T, U> >,
+ is_bitwise_serializable<Property> > { };
+
+template<typename T, typename U, typename Property>
+struct implementation_level<
+ boost::detail::parallel::pair_with_property<T, U, Property> >
+ : mpl::int_<object_serializable> {} ;
+
+template<typename T, typename U, typename Property>
+struct tracking_level<
+ boost::detail::parallel::pair_with_property<T, U, Property> >
+ : mpl::int_<track_never> {} ;
+
+} } // end namespace boost::serialization
+
+#endif // BOOST_PARALLEL_DETAIL_PROPERTY_HOLDERS_HPP

Added: trunk/boost/graph/parallel/detail/untracked_pair.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/parallel/detail/untracked_pair.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,85 @@
+// Copyright (C) 2007 Matthias Troyer
+//
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+//
+// This file contains helper data structures for use in transmitting
+// properties. The basic idea is to optimize away any storage for the
+// properties when no properties are specified.
+#ifndef BOOST_PARALLEL_DETAIL_UNTRACKED_PAIR_HPP
+#define BOOST_PARALLEL_DETAIL_UNTRACKED_PAIR_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/mpi/datatype.hpp>
+#include <utility> // for std::pair
+#include <boost/serialization/utility.hpp>
+
+namespace boost { namespace parallel { namespace detail {
+
+/**
+ * This structure is like std::pair, with the only difference
+ * that tracking in the serialization library is turned off.
+ */
+
+template<typename T, typename U>
+struct untracked_pair : public std::pair<T,U>
+{
+ untracked_pair() {}
+
+ untracked_pair(const T& t, const U& u)
+ : std::pair<T,U>(t,u) {}
+
+ template<class T1, class U1>
+ untracked_pair(std::pair<T1,U1> const& p)
+ : std::pair<T,U>(p) {}
+};
+
+template<typename T, typename U>
+inline untracked_pair<T, U>
+make_untracked_pair(const T& t, const U& u)
+{
+ return untracked_pair<T,U>(t,u);
+}
+
+} } } // end namespace boost::parallel::detail
+
+namespace boost { namespace mpi {
+
+template<typename T, typename U>
+struct is_mpi_datatype<boost::parallel::detail::untracked_pair<T, U> >
+ : is_mpi_datatype<std::pair<T,U> > {};
+
+} } // end namespace boost::mpi
+
+namespace boost { namespace serialization {
+
+// pair
+template<class Archive, class F, class S>
+inline void serialize(
+ Archive & ar,
+ boost::parallel::detail::untracked_pair<F, S> & p,
+ const unsigned int /* file_version */
+){
+ ar & boost::serialization::make_nvp("first", p.first);
+ ar & boost::serialization::make_nvp("second", p.second);
+}
+
+template<typename T, typename U>
+struct is_bitwise_serializable<
+ boost::parallel::detail::untracked_pair<T, U> >
+ : is_bitwise_serializable<std::pair<T, U> > {};
+
+template<typename T, typename U>
+struct implementation_level<boost::parallel::detail::untracked_pair<T, U> >
+ : mpl::int_<object_serializable> {} ;
+
+template<typename T, typename U>
+struct tracking_level<boost::parallel::detail::untracked_pair<T, U> >
+ : mpl::int_<track_never> {} ;
+
+} } // end namespace boost::serialization
+
+#endif // BOOST_PARALLEL_DETAIL_UNTRACKED_PAIR_HPP

Added: trunk/boost/graph/parallel/distribution.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/parallel/distribution.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,614 @@
+// Copyright 2004 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
+// Peter Gottschling
+// Andrew Lumsdaine
+#ifndef BOOST_PARALLEL_DISTRIBUTION_HPP
+#define BOOST_PARALLEL_DISTRIBUTION_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 <cstddef>
+#include <vector>
+#include <algorithm>
+#include <numeric>
+#include <boost/iterator/counting_iterator.hpp>
+#include <boost/random/uniform_int.hpp>
+#include <boost/shared_ptr.hpp>
+#include <typeinfo>
+
+namespace boost { namespace parallel {
+
+template<typename ProcessGroup, typename SizeType = std::size_t>
+class variant_distribution
+{
+public:
+ typedef typename ProcessGroup::process_id_type process_id_type;
+ typedef typename ProcessGroup::process_size_type process_size_type;
+ typedef SizeType size_type;
+
+private:
+ struct basic_distribution
+ {
+ virtual ~basic_distribution() {}
+ virtual size_type block_size(process_id_type, size_type) const = 0;
+ virtual process_id_type in_process(size_type) const = 0;
+ virtual size_type local(size_type) const = 0;
+ virtual size_type global(size_type) const = 0;
+ virtual size_type global(process_id_type, size_type) const = 0;
+ virtual void* address() = 0;
+ virtual const void* address() const = 0;
+ virtual const std::type_info& type() const = 0;
+ };
+
+ template<typename Distribution>
+ struct poly_distribution : public basic_distribution
+ {
+ explicit poly_distribution(const Distribution& distribution)
+ : distribution_(distribution) { }
+
+ virtual size_type block_size(process_id_type id, size_type n) const
+ { return distribution_.block_size(id, n); }
+
+ virtual process_id_type in_process(size_type i) const
+ { return distribution_(i); }
+
+ virtual size_type local(size_type i) const
+ { return distribution_.local(i); }
+
+ virtual size_type global(size_type n) const
+ { return distribution_.global(n); }
+
+ virtual size_type global(process_id_type id, size_type n) const
+ { return distribution_.global(id, n); }
+
+ virtual void* address() { return &distribution_; }
+ virtual const void* address() const { return &distribution_; }
+ virtual const std::type_info& type() const { return typeid(Distribution); }
+
+ private:
+ Distribution distribution_;
+ };
+
+public:
+ variant_distribution() { }
+
+ template<typename Distribution>
+ variant_distribution(const Distribution& distribution)
+ : distribution_(new poly_distribution<Distribution>(distribution)) { }
+
+ size_type block_size(process_id_type id, size_type n) const
+ { return distribution_->block_size(id, n); }
+
+ process_id_type operator()(size_type i) const
+ { return distribution_->in_process(i); }
+
+ size_type local(size_type i) const
+ { return distribution_->local(i); }
+
+ size_type global(size_type n) const
+ { return distribution_->global(n); }
+
+ size_type global(process_id_type id, size_type n) const
+ { return distribution_->global(id, n); }
+
+ operator bool() const { return distribution_; }
+
+ void clear() { distribution_.reset(); }
+
+ template<typename T>
+ T* as()
+ {
+ if (distribution_->type() == typeid(T))
+ return static_cast<T*>(distribution_->address());
+ else
+ return 0;
+ }
+
+ template<typename T>
+ const T* as() const
+ {
+ if (distribution_->type() == typeid(T))
+ return static_cast<T*>(distribution_->address());
+ else
+ return 0;
+ }
+
+private:
+ shared_ptr<basic_distribution> distribution_;
+};
+
+struct block
+{
+ template<typename LinearProcessGroup>
+ explicit block(const LinearProcessGroup& pg, std::size_t n)
+ : id(process_id(pg)), p(num_processes(pg)), n(n) { }
+
+ // If there are n elements in the distributed data structure, returns the number of elements stored locally.
+ template<typename SizeType>
+ SizeType block_size(SizeType n) const
+ { return (n / p) + ((std::size_t)(n % p) > id? 1 : 0); }
+
+ // If there are n elements in the distributed data structure, returns the number of elements stored on processor ID
+ template<typename SizeType, typename ProcessID>
+ SizeType block_size(ProcessID id, SizeType n) const
+ { return (n / p) + ((ProcessID)(n % p) > id? 1 : 0); }
+
+ // Returns the processor on which element with global index i is stored
+ template<typename SizeType>
+ SizeType operator()(SizeType i) const
+ {
+ SizeType cutoff_processor = n % p;
+ SizeType cutoff = cutoff_processor * (n / p + 1);
+
+ if (i < cutoff) return i / (n / p + 1);
+ else return cutoff_processor + (i - cutoff) / (n / p);
+ }
+
+ // Find the starting index for processor with the given id
+ template<typename ID>
+ std::size_t start(ID id) const
+ {
+ std::size_t estimate = id * (n / p + 1);
+ ID cutoff_processor = n % p;
+ if (id < cutoff_processor) return estimate;
+ else return estimate - (id - cutoff_processor);
+ }
+
+ // Find the local index for the ith global element
+ template<typename SizeType>
+ SizeType local(SizeType i) const
+ {
+ SizeType owner = (*this)(i);
+ return i - start(owner);
+ }
+
+ // Returns the global index of local element i
+ template<typename SizeType>
+ SizeType global(SizeType i) const
+ { return global(id, i); }
+
+ // Returns the global index of the ith local element on processor id
+ template<typename ProcessID, typename SizeType>
+ SizeType global(ProcessID id, SizeType i) const
+ { return i + start(id); }
+
+ private:
+ std::size_t id; //< The ID number of this processor
+ std::size_t p; //< The number of processors
+ std::size_t n; //< The size of the problem space
+};
+
+// Block distribution with arbitrary block sizes
+struct uneven_block
+{
+ typedef std::vector<std::size_t> size_vector;
+
+ template<typename LinearProcessGroup>
+ explicit uneven_block(const LinearProcessGroup& pg, const std::vector<std::size_t>& local_sizes)
+ : id(process_id(pg)), p(num_processes(pg)), local_sizes(local_sizes)
+ {
+ assert(local_sizes.size() == p);
+ local_starts.resize(p + 1);
+ local_starts[0] = 0;
+ std::partial_sum(local_sizes.begin(), local_sizes.end(), &local_starts[1]);
+ n = local_starts[p];
+ }
+
+ // To do maybe: enter local size in each process and gather in constructor (much handier)
+ // template<typename LinearProcessGroup>
+ // explicit uneven_block(const LinearProcessGroup& pg, std::size_t my_local_size)
+
+ // If there are n elements in the distributed data structure, returns the number of elements stored locally.
+ template<typename SizeType>
+ SizeType block_size(SizeType) const
+ { return local_sizes[id]; }
+
+ // If there are n elements in the distributed data structure, returns the number of elements stored on processor ID
+ template<typename SizeType, typename ProcessID>
+ SizeType block_size(ProcessID id, SizeType) const
+ { return local_sizes[id]; }
+
+ // Returns the processor on which element with global index i is stored
+ template<typename SizeType>
+ SizeType operator()(SizeType i) const
+ {
+ assert (i >= (SizeType) 0 && i < (SizeType) n); // check for valid range
+ size_vector::const_iterator lb = std::lower_bound(local_starts.begin(), local_starts.end(), (std::size_t) i);
+ return ((SizeType)(*lb) == i ? lb : --lb) - local_starts.begin();
+ }
+
+ // Find the starting index for processor with the given id
+ template<typename ID>
+ std::size_t start(ID id) const
+ {
+ return local_starts[id];
+ }
+
+ // Find the local index for the ith global element
+ template<typename SizeType>
+ SizeType local(SizeType i) const
+ {
+ SizeType owner = (*this)(i);
+ return i - start(owner);
+ }
+
+ // Returns the global index of local element i
+ template<typename SizeType>
+ SizeType global(SizeType i) const
+ { return global(id, i); }
+
+ // Returns the global index of the ith local element on processor id
+ template<typename ProcessID, typename SizeType>
+ SizeType global(ProcessID id, SizeType i) const
+ { return i + start(id); }
+
+ private:
+ std::size_t id; //< The ID number of this processor
+ std::size_t p; //< The number of processors
+ std::size_t n; //< The size of the problem space
+ std::vector<std::size_t> local_sizes; //< The sizes of all blocks
+ std::vector<std::size_t> local_starts; //< Lowest global index of each block
+};
+
+
+struct oned_block_cyclic
+{
+ template<typename LinearProcessGroup>
+ explicit oned_block_cyclic(const LinearProcessGroup& pg, std::size_t size)
+ : id(process_id(pg)), p(num_processes(pg)), size(size) { }
+
+ template<typename SizeType>
+ SizeType block_size(SizeType n) const
+ {
+ return block_size(id, n);
+ }
+
+ template<typename SizeType, typename ProcessID>
+ SizeType block_size(ProcessID id, SizeType n) const
+ {
+ SizeType all_blocks = n / size;
+ SizeType extra_elements = n % size;
+ SizeType everyone_gets = all_blocks / p;
+ SizeType extra_blocks = all_blocks % p;
+ SizeType my_blocks = everyone_gets + (p < extra_blocks? 1 : 0);
+ SizeType my_elements = my_blocks * size
+ + (p == extra_blocks? extra_elements : 0);
+ return my_elements;
+ }
+
+ template<typename SizeType>
+ SizeType operator()(SizeType i) const
+ {
+ return (i / size) % p;
+ }
+
+ template<typename SizeType>
+ SizeType local(SizeType i) const
+ {
+ return ((i / size) / p) * size + i % size;
+ }
+
+ template<typename SizeType>
+ SizeType global(SizeType i) const
+ { return global(id, i); }
+
+ template<typename ProcessID, typename SizeType>
+ SizeType global(ProcessID id, SizeType i) const
+ {
+ return ((i / size) * p + id) * size + i % size;
+ }
+
+ private:
+ std::size_t id; //< The ID number of this processor
+ std::size_t p; //< The number of processors
+ std::size_t size; //< Block size
+};
+
+struct twod_block_cyclic
+{
+ template<typename LinearProcessGroup>
+ explicit twod_block_cyclic(const LinearProcessGroup& pg,
+ std::size_t block_rows, std::size_t block_columns,
+ std::size_t data_columns_per_row)
+ : id(process_id(pg)), p(num_processes(pg)),
+ block_rows(block_rows), block_columns(block_columns),
+ data_columns_per_row(data_columns_per_row)
+ { }
+
+ template<typename SizeType>
+ SizeType block_size(SizeType n) const
+ {
+ return block_size(id, n);
+ }
+
+ template<typename SizeType, typename ProcessID>
+ SizeType block_size(ProcessID id, SizeType n) const
+ {
+ // TBD: This is really lame :)
+ int result = -1;
+ while (n > 0) {
+ --n;
+ if ((*this)(n) == id && (int)local(n) > result) result = local(n);
+ }
+ ++result;
+
+ // std::cerr << "Block size of id " << id << " is " << result << std::endl;
+ return result;
+ }
+
+ template<typename SizeType>
+ SizeType operator()(SizeType i) const
+ {
+ SizeType result = get_block_num(i) % p;
+ // std::cerr << "Item " << i << " goes on processor " << result << std::endl;
+ return result;
+ }
+
+ template<typename SizeType>
+ SizeType local(SizeType i) const
+ {
+ // Compute the start of the block
+ std::size_t block_num = get_block_num(i);
+ // std::cerr << "Item " << i << " is in block #" << block_num << std::endl;
+
+ std::size_t local_block_num = block_num / p;
+ std::size_t block_start = local_block_num * block_rows * block_columns;
+
+ // Compute the offset into the block
+ std::size_t data_row = i / data_columns_per_row;
+ std::size_t data_col = i % data_columns_per_row;
+ std::size_t block_offset = (data_row % block_rows) * block_columns
+ + (data_col % block_columns);
+
+ // std::cerr << "Item " << i << " maps to local index " << block_start+block_offset << std::endl;
+ return block_start + block_offset;
+ }
+
+ template<typename SizeType>
+ SizeType global(SizeType i) const
+ {
+ // Compute the (global) block in which this element resides
+ SizeType local_block_num = i / (block_rows * block_columns);
+ SizeType block_offset = i % (block_rows * block_columns);
+ SizeType block_num = local_block_num * p + id;
+
+ // Compute the position of the start of the block (globally)
+ SizeType block_start = block_num * block_rows * block_columns;
+
+ std::cerr << "Block " << block_num << " starts at index " << block_start
+ << std::endl;
+
+ // Compute the row and column of this block
+ SizeType block_row = block_num / (data_columns_per_row / block_columns);
+ SizeType block_col = block_num % (data_columns_per_row / block_columns);
+
+ SizeType row_in_block = block_offset / block_columns;
+ SizeType col_in_block = block_offset % block_columns;
+
+ std::cerr << "Local index " << i << " is in block at row " << block_row
+ << ", column " << block_col << ", in-block row " << row_in_block
+ << ", in-block col " << col_in_block << std::endl;
+
+ SizeType result = block_row * block_rows + block_col * block_columns
+ + row_in_block * block_rows + col_in_block;
+
+
+ std::cerr << "global(" << i << "@" << id << ") = " << result
+ << " =? " << local(result) << std::endl;
+ assert(i == local(result));
+ return result;
+ }
+
+ private:
+ template<typename SizeType>
+ std::size_t get_block_num(SizeType i) const
+ {
+ std::size_t data_row = i / data_columns_per_row;
+ std::size_t data_col = i % data_columns_per_row;
+ std::size_t block_row = data_row / block_rows;
+ std::size_t block_col = data_col / block_columns;
+ std::size_t blocks_in_row = data_columns_per_row / block_columns;
+ std::size_t block_num = block_col * blocks_in_row + block_row;
+ return block_num;
+ }
+
+ std::size_t id; //< The ID number of this processor
+ std::size_t p; //< The number of processors
+ std::size_t block_rows; //< The # of rows in each block
+ std::size_t block_columns; //< The # of columns in each block
+ std::size_t data_columns_per_row; //< The # of columns per row of data
+};
+
+class twod_random
+{
+ template<typename RandomNumberGen>
+ struct random_int
+ {
+ explicit random_int(RandomNumberGen& gen) : gen(gen) { }
+
+ template<typename T>
+ T operator()(T n) const
+ {
+ uniform_int<T> distrib(0, n-1);
+ return distrib(gen);
+ }
+
+ private:
+ RandomNumberGen& gen;
+ };
+
+ public:
+ template<typename LinearProcessGroup, typename RandomNumberGen>
+ explicit twod_random(const LinearProcessGroup& pg,
+ std::size_t block_rows, std::size_t block_columns,
+ std::size_t data_columns_per_row,
+ std::size_t n,
+ RandomNumberGen& gen)
+ : id(process_id(pg)), p(num_processes(pg)),
+ block_rows(block_rows), block_columns(block_columns),
+ data_columns_per_row(data_columns_per_row),
+ global_to_local(n / (block_rows * block_columns))
+ {
+ std::copy(make_counting_iterator(std::size_t(0)),
+ make_counting_iterator(global_to_local.size()),
+ global_to_local.begin());
+
+ random_int<RandomNumberGen> rand(gen);
+ std::random_shuffle(global_to_local.begin(), global_to_local.end(), rand);
+ }
+
+ template<typename SizeType>
+ SizeType block_size(SizeType n) const
+ {
+ return block_size(id, n);
+ }
+
+ template<typename SizeType, typename ProcessID>
+ SizeType block_size(ProcessID id, SizeType n) const
+ {
+ // TBD: This is really lame :)
+ int result = -1;
+ while (n > 0) {
+ --n;
+ if ((*this)(n) == id && (int)local(n) > result) result = local(n);
+ }
+ ++result;
+
+ // std::cerr << "Block size of id " << id << " is " << result << std::endl;
+ return result;
+ }
+
+ template<typename SizeType>
+ SizeType operator()(SizeType i) const
+ {
+ SizeType result = get_block_num(i) % p;
+ // std::cerr << "Item " << i << " goes on processor " << result << std::endl;
+ return result;
+ }
+
+ template<typename SizeType>
+ SizeType local(SizeType i) const
+ {
+ // Compute the start of the block
+ std::size_t block_num = get_block_num(i);
+ // std::cerr << "Item " << i << " is in block #" << block_num << std::endl;
+
+ std::size_t local_block_num = block_num / p;
+ std::size_t block_start = local_block_num * block_rows * block_columns;
+
+ // Compute the offset into the block
+ std::size_t data_row = i / data_columns_per_row;
+ std::size_t data_col = i % data_columns_per_row;
+ std::size_t block_offset = (data_row % block_rows) * block_columns
+ + (data_col % block_columns);
+
+ // std::cerr << "Item " << i << " maps to local index " << block_start+block_offset << std::endl;
+ return block_start + block_offset;
+ }
+
+ private:
+ template<typename SizeType>
+ std::size_t get_block_num(SizeType i) const
+ {
+ std::size_t data_row = i / data_columns_per_row;
+ std::size_t data_col = i % data_columns_per_row;
+ std::size_t block_row = data_row / block_rows;
+ std::size_t block_col = data_col / block_columns;
+ std::size_t blocks_in_row = data_columns_per_row / block_columns;
+ std::size_t block_num = block_col * blocks_in_row + block_row;
+ return global_to_local[block_num];
+ }
+
+ std::size_t id; //< The ID number of this processor
+ std::size_t p; //< The number of processors
+ std::size_t block_rows; //< The # of rows in each block
+ std::size_t block_columns; //< The # of columns in each block
+ std::size_t data_columns_per_row; //< The # of columns per row of data
+ std::vector<std::size_t> global_to_local;
+};
+
+class random_distribution
+{
+ template<typename RandomNumberGen>
+ struct random_int
+ {
+ explicit random_int(RandomNumberGen& gen) : gen(gen) { }
+
+ template<typename T>
+ T operator()(T n) const
+ {
+ uniform_int<T> distrib(0, n-1);
+ return distrib(gen);
+ }
+
+ private:
+ RandomNumberGen& gen;
+ };
+
+ public:
+ template<typename LinearProcessGroup, typename RandomNumberGen>
+ random_distribution(const LinearProcessGroup& pg, RandomNumberGen& gen,
+ std::size_t n)
+ : base(pg, n), local_to_global(n), global_to_local(n)
+ {
+ std::copy(make_counting_iterator(std::size_t(0)),
+ make_counting_iterator(n),
+ local_to_global.begin());
+
+ random_int<RandomNumberGen> rand(gen);
+ std::random_shuffle(local_to_global.begin(), local_to_global.end(), rand);
+
+
+ for (std::vector<std::size_t>::size_type i = 0; i < n; ++i)
+ global_to_local[local_to_global[i]] = i;
+ }
+
+ template<typename SizeType>
+ SizeType block_size(SizeType n) const
+ { return base.block_size(n); }
+
+ template<typename SizeType, typename ProcessID>
+ SizeType block_size(ProcessID id, SizeType n) const
+ { return base.block_size(id, n); }
+
+ template<typename SizeType>
+ SizeType operator()(SizeType i) const
+ {
+ return base(global_to_local[i]);
+ }
+
+ template<typename SizeType>
+ SizeType local(SizeType i) const
+ {
+ return base.local(global_to_local[i]);
+ }
+
+ template<typename ProcessID, typename SizeType>
+ SizeType global(ProcessID p, SizeType i) const
+ {
+ return local_to_global[base.global(p, i)];
+ }
+
+ template<typename SizeType>
+ SizeType global(SizeType i) const
+ {
+ return local_to_global[base.global(i)];
+ }
+
+ private:
+ block base;
+ std::vector<std::size_t> local_to_global;
+ std::vector<std::size_t> global_to_local;
+};
+
+} } // end namespace boost::parallel
+
+#endif // BOOST_PARALLEL_DISTRIBUTION_HPP
+

Added: trunk/boost/graph/parallel/process_group.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/parallel/process_group.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,101 @@
+// Copyright 2004 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
+#ifndef BOOST_GRAPH_PARALLEL_PROCESS_GROUP_HPP
+#define BOOST_GRAPH_PARALLEL_PROCESS_GROUP_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 <cstdlib>
+#include <utility>
+
+namespace boost { namespace graph { namespace parallel {
+
+/**
+ * A special type used as a flag to a process group constructor that
+ * indicates that the copy of a process group will represent a new
+ * distributed data structure.
+ */
+struct attach_distributed_object { };
+
+/**
+ * Describes the context in which a trigger is being invoked to
+ * receive a message.
+ */
+enum trigger_receive_context {
+ /// No trigger is active at this time.
+ trc_none,
+ /// The trigger is being invoked during synchronization, at the end
+ /// of a superstep.
+ trc_in_synchronization,
+ /// The trigger is being invoked as an "early" receive of a message
+ /// that was sent through the normal "send" operations to be
+ /// received by the end of the superstep, but the process group sent
+ /// the message earlier to clear its buffers.
+ trc_early_receive,
+ /// The trigger is being invoked for an out-of-band message, which
+ /// must be handled immediately.
+ trc_out_of_band,
+ /// The trigger is being invoked for an out-of-band message, which
+ /// must be handled immediately and has alredy been received by
+ /// an MPI_IRecv call.
+ trc_irecv_out_of_band
+};
+
+// Process group tags
+struct process_group_tag {};
+struct linear_process_group_tag : virtual process_group_tag {};
+struct messaging_process_group_tag : virtual process_group_tag {};
+struct immediate_process_group_tag : virtual messaging_process_group_tag {};
+struct bsp_process_group_tag : virtual messaging_process_group_tag {};
+struct batch_process_group_tag : virtual messaging_process_group_tag {};
+struct locking_process_group_tag : virtual process_group_tag {};
+struct spawning_process_group_tag : virtual process_group_tag {};
+
+struct process_group_archetype
+{
+ typedef int process_id_type;
+};
+
+void wait(process_group_archetype&);
+void synchronize(process_group_archetype&);
+int process_id(const process_group_archetype&);
+int num_processes(const process_group_archetype&);
+
+template<typename T> void send(process_group_archetype&, int, int, const T&);
+
+template<typename T>
+process_group_archetype::process_id_type
+receive(const process_group_archetype& pg,
+ process_group_archetype::process_id_type source, int tag, T& value);
+
+template<typename T>
+std::pair<process_group_archetype::process_id_type, std::size_t>
+receive(const process_group_archetype& pg, int tag, T values[], std::size_t n);
+
+template<typename T>
+std::pair<process_group_archetype::process_id_type, std::size_t>
+receive(const process_group_archetype& pg,
+ process_group_archetype::process_id_type source, int tag, T values[],
+ std::size_t n);
+
+} } } // end namespace boost::graph::parallel
+
+namespace boost { namespace graph { namespace distributed {
+ using parallel::trigger_receive_context;
+ using parallel::trc_early_receive;
+ using parallel::trc_out_of_band;
+ using parallel::trc_irecv_out_of_band;
+ using parallel::trc_in_synchronization;
+ using parallel::trc_none;
+ using parallel::attach_distributed_object;
+} } } // end namespace boost::graph::distributed
+
+#endif // BOOST_GRAPH_PARALLEL_PROCESS_GROUP_HPP

Added: trunk/boost/graph/parallel/properties.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/parallel/properties.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,111 @@
+// Copyright 2004 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
+
+#ifndef BOOST_GRAPH_PARALLEL_PROPERTIES_HPP
+#define BOOST_GRAPH_PARALLEL_PROPERTIES_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/graph/properties.hpp>
+#include <boost/property_map/parallel/distributed_property_map.hpp>
+
+namespace boost {
+ /***************************************************************************
+ * Property map reduction operations
+ ***************************************************************************/
+ /**
+ * Metafunction that produces a reduction operation for the given
+ * property. The default behavior merely forwards to @ref
+ * basic_reduce, but it is expected that this class template will be
+ * specified for important properties.
+ */
+ template<typename Property>
+ struct property_reduce
+ {
+ template<typename Value>
+ class apply : public parallel::basic_reduce<Value> {};
+ };
+
+ /**
+ * Reduction of vertex colors can only darken, not lighten, the
+ * color. Black cannot turn black, grey can only turn black, and
+ * white can be changed to either color. The default color is white.
+ */
+ template<>
+ struct property_reduce<vertex_color_t>
+ {
+ template<typename Color>
+ class apply
+ {
+ typedef color_traits<Color> traits;
+
+ public:
+ BOOST_STATIC_CONSTANT(bool, non_default_resolver = true);
+
+ template<typename Key>
+ Color operator()(const Key&) const { return traits::white(); }
+
+ template<typename Key>
+ Color operator()(const Key&, Color local, Color remote) const {
+ if (local == traits::white()) return remote;
+ else if (remote == traits::black()) return remote;
+ else return local;
+ }
+ };
+ };
+
+ /**
+ * Reduction of a distance always takes the shorter distance. The
+ * default distance value is the maximum value for the data type.
+ */
+ template<>
+ struct property_reduce<vertex_distance_t>
+ {
+ template<typename T>
+ class apply
+ {
+ public:
+ BOOST_STATIC_CONSTANT(bool, non_default_resolver = true);
+
+ template<typename Key>
+ T operator()(const Key&) const { return (std::numeric_limits<T>::max)(); }
+
+ template<typename Key>
+ T operator()(const Key&, T x, T y) const { return x < y? x : y; }
+ };
+ };
+
+ template<>
+ struct property_reduce<vertex_predecessor_t>
+ {
+ template<typename T>
+ class apply
+ {
+ public:
+ BOOST_STATIC_CONSTANT(bool, non_default_resolver = true);
+
+ T operator()(T key) const { return key; }
+ T operator()(T key, T, T y) const { return y; }
+ };
+ };
+
+ template<typename Property, typename PropertyMap>
+ inline void set_property_map_role(Property p, PropertyMap pm)
+ {
+ typedef typename property_traits<PropertyMap>::value_type value_type;
+ typedef property_reduce<Property> property_red;
+ typedef typename property_red::template apply<value_type> reduce;
+
+ pm.set_reduce(reduce());
+ }
+
+} // end namespace boost
+#endif // BOOST_GRAPH_PARALLEL_PROPERTIES_HPP

Added: trunk/boost/graph/parallel/simple_trigger.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/parallel/simple_trigger.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,108 @@
+// Copyright (C) 2007 Douglas Gregor
+
+// 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)
+
+// This file contains a simplification of the "trigger" method for
+// process groups. The simple trigger handles the common case where
+// the handler associated with a trigger is a member function bound to
+// a particular pointer.
+
+#ifndef BOOST_GRAPH_PARALLEL_SIMPLE_TRIGGER_HPP
+#define BOOST_GRAPH_PARALLEL_SIMPLE_TRIGGER_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/graph/parallel/process_group.hpp>
+
+namespace boost { namespace graph { namespace parallel {
+
+namespace detail {
+
+/**
+ * INTERNAL ONLY
+ *
+ * The actual function object that bridges from the normal trigger
+ * interface to the simplified interface. This is the equivalent of
+ * bind(pmf, self, _1, _2, _3, _4), but without the compile-time
+ * overhead of bind.
+ */
+template<typename Class, typename T, typename Result>
+class simple_trigger_t
+{
+public:
+ simple_trigger_t(Class* self,
+ Result (Class::*pmf)(int, int, const T&,
+ trigger_receive_context))
+ : self(self), pmf(pmf) { }
+
+ Result
+ operator()(int source, int tag, const T& data,
+ trigger_receive_context context) const
+ {
+ return (self->*pmf)(source, tag, data, context);
+ }
+
+private:
+ Class* self;
+ Result (Class::*pmf)(int, int, const T&, trigger_receive_context);
+};
+
+} // end namespace detail
+
+/**
+ * Simplified trigger interface that reduces the amount of code
+ * required to connect a process group trigger to a handler that is
+ * just a bound member function.
+ *
+ * INTERNAL ONLY
+ */
+template<typename ProcessGroup, typename Class, typename T>
+inline void
+simple_trigger(ProcessGroup& pg, int tag, Class* self,
+ void (Class::*pmf)(int source, int tag, const T& data,
+ trigger_receive_context context), int)
+{
+ pg.template trigger<T>(tag,
+ detail::simple_trigger_t<Class, T, void>(self, pmf));
+}
+
+/**
+ * Simplified trigger interface that reduces the amount of code
+ * required to connect a process group trigger with a reply to a
+ * handler that is just a bound member function.
+ *
+ * INTERNAL ONLY
+ */
+template<typename ProcessGroup, typename Class, typename T, typename Result>
+inline void
+simple_trigger(ProcessGroup& pg, int tag, Class* self,
+ Result (Class::*pmf)(int source, int tag, const T& data,
+ trigger_receive_context context), long)
+{
+ pg.template trigger_with_reply<T>
+ (tag, detail::simple_trigger_t<Class, T, Result>(self, pmf));
+}
+
+/**
+ * Simplified trigger interface that reduces the amount of code
+ * required to connect a process group trigger to a handler that is
+ * just a bound member function.
+ */
+template<typename ProcessGroup, typename Class, typename T, typename Result>
+inline void
+simple_trigger(ProcessGroup& pg, int tag, Class* self,
+ Result (Class::*pmf)(int source, int tag, const T& data,
+ trigger_receive_context context))
+{
+ // We pass 0 (an int) to help VC++ disambiguate calls to simple_trigger
+ // with Result=void.
+ simple_trigger(pg, tag, self, pmf, 0);
+}
+
+} } } // end namespace boost::graph::parallel
+
+#endif // BOOST_GRAPH_PARALLEL_SIMPLE_TRIGGER_HPP

Modified: trunk/boost/graph/rmat_graph_generator.hpp
==============================================================================
--- trunk/boost/graph/rmat_graph_generator.hpp (original)
+++ trunk/boost/graph/rmat_graph_generator.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -581,4 +581,8 @@
 
 } // end namespace boost
 
+#ifdef BOOST_GRAPH_USE_MPI
+#include <boost/graph/distributed/rmat_graph_generator.hpp>
+#endif // BOOST_GRAPH_USE_MPI
+
 #endif // BOOST_GRAPH_RMAT_GENERATOR_HPP

Modified: trunk/boost/graph/strong_components.hpp
==============================================================================
--- trunk/boost/graph/strong_components.hpp (original)
+++ trunk/boost/graph/strong_components.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -334,4 +334,8 @@
 
 } // namespace boost
 
+#ifdef BOOST_GRAPH_USE_MPI
+# include <boost/graph/distributed/strong_components.hpp>
+#endif
+
 #endif // BOOST_GRAPH_STRONG_COMPONENTS_HPP

Modified: trunk/boost/graph/topology.hpp
==============================================================================
--- trunk/boost/graph/topology.hpp (original)
+++ trunk/boost/graph/topology.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -246,6 +246,13 @@
     return dist;
   }
 
+ point_type center() const {
+ point_type result;
+ for (std::size_t i = 0; i < Dims; ++i)
+ result[i] = 0;
+ return result;
+ }
+
  private:
   shared_ptr<RandomNumberGenerator> gen_ptr;
   shared_ptr<rand_t> rand;
@@ -319,6 +326,13 @@
     return dist;
   }
 
+ point_type center() const {
+ point_type result;
+ result[0] = (left + right) / 2.;
+ result[1] = (top + bottom) / 2.;
+ return result;
+ }
+
  private:
   shared_ptr<RandomNumberGenerator> gen_ptr;
   shared_ptr<rand_t> rand;
@@ -392,6 +406,13 @@
     return radius - r;
   }
 
+ point_type center() const {
+ point_type result;
+ for (std::size_t i = 0; i < Dims; ++i)
+ result[i] = 0;
+ return result;
+ }
+
  private:
   shared_ptr<RandomNumberGenerator> gen_ptr;
   shared_ptr<rand_t> rand;

Modified: trunk/boost/graph/two_bit_color_map.hpp
==============================================================================
--- trunk/boost/graph/two_bit_color_map.hpp (original)
+++ trunk/boost/graph/two_bit_color_map.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -91,3 +91,7 @@
 } // end namespace boost
 
 #endif // BOOST_TWO_BIT_COLOR_MAP_HPP
+
+#ifdef BOOST_GRAPH_USE_MPI
+# include <boost/graph/distributed/two_bit_color_map.hpp>
+#endif

Added: trunk/boost/graph/use_mpi.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/use_mpi.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,15 @@
+// Copyright (C) 2004-2009 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: Nick Edmonds
+// Andrew Lumsdaine
+
+#ifndef BOOST_GRAPH_USE_MPI_HPP
+#define BOOST_GRAPH_USE_MPI_HPP
+
+#define BOOST_GRAPH_USE_MPI
+
+#endif // BOOST_GRAPH_USE_MPI_HPP

Modified: trunk/boost/pending/property_serialize.hpp
==============================================================================
--- trunk/boost/pending/property_serialize.hpp (original)
+++ trunk/boost/pending/property_serialize.hpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -7,6 +7,10 @@
 #define BOOST_PROPERTY_SERIALIZE_HPP
 
 #include <boost/pending/property.hpp>
+#ifdef BOOST_GRAPH_USE_MPI
+#include <boost/mpi/datatype.hpp>
+#include <boost/serialization/is_bitwise_serializable.hpp>
+#endif // BOOST_GRAPH_USE_MPI
 
 #include <boost/serialization/base_object.hpp>
 #include <boost/serialization/nvp.hpp>
@@ -23,6 +27,44 @@
     ar & serialization::make_nvp( "property_base" , boost::serialization::base_object<Base>(prop) );
     ar & serialization::make_nvp( "property_value" , prop.m_value );
   }
+
+#ifdef BOOST_GRAPH_USE_MPI
+ namespace mpi {
+ template<typename Tag, typename T, typename Base>
+ struct is_mpi_datatype<property<Tag, T, Base> >
+ : mpl::and_<is_mpi_datatype<T>,
+ is_mpi_datatype<Base> > { };
+ }
+
+ namespace serialization {
+ template<typename Tag, typename T, typename Base>
+ struct is_bitwise_serializable<property<Tag, T, Base> >
+ : mpl::and_<is_bitwise_serializable<T>,
+ is_bitwise_serializable<Base> > { };
+
+ template<typename Tag, typename T, typename Base>
+ struct implementation_level<property<Tag, T, Base> >
+ : mpl::int_<object_serializable> {} ;
+
+ template<typename Tag, typename T, typename Base>
+ struct tracking_level<property<Tag, T, Base> >
+ : mpl::int_<track_never> {} ;
+
+ }
+#endif // BOOST_GRAPH_USE_MPI
+
 } // end namespace boost
 
+#ifdef BOOST_GRAPH_USE_MPI
+namespace boost { namespace mpi {
+ template<>
+ struct is_mpi_datatype<boost::no_property> : mpl::true_ { };
+
+} } // end namespace boost::mpi
+
+BOOST_IS_BITWISE_SERIALIZABLE(boost::no_property)
+BOOST_CLASS_IMPLEMENTATION(boost::no_property,object_serializable)
+BOOST_CLASS_TRACKING(boost::no_property,track_never)
+#endif // BOOST_GRAPH_USE_MPI
+
 #endif // BOOST_PROPERTY_SERIALIZE_HPP

Modified: trunk/libs/graph/build/Jamfile.v2
==============================================================================
--- trunk/libs/graph/build/Jamfile.v2 (original)
+++ trunk/libs/graph/build/Jamfile.v2 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -4,6 +4,8 @@
 # (See accompanying file LICENSE_1_0.txt or copy at
 # http://www.boost.org/LICENSE_1_0.txt)
 
+import mpi ;
+
 project boost/graph
     : requirements <include>../src
     : source-location ../src
@@ -43,10 +45,33 @@
 }
 explicit graphml ;
 
+if [ mpi.configured ]
+{
+ alias pbgl
+ : mpi_process_group.cpp tag_allocator.cpp
+ : # requirements
+ <library>../../mpi/build//boost_mpi
+ <library>/mpi//mpi [ mpi.extra-requirements ]
+ : # default built
+ : # usage requirements
+ <library>../../mpi/build//boost_mpi
+ <library>/mpi//mpi [ mpi.extra-requirements ]
+ ;
+}
+else
+{
+ message pbgl
+ : "warning: Graph library does not contain MPI-based parallel components."
+ : "note: to enable them, add \"using mpi ;\" to your user_config.jam"
+ ;
+}
+explicit graphml ;
+
 lib boost_graph
     :
     read_graphviz_spirit.cpp
     graphml
+ pbgl
     :
     <define>BOOST_GRAPH_NO_LIB=1
     <link>shared:<define>BOOST_GRAPH_DYN_LINK=1
@@ -57,4 +82,4 @@
     <toolset>msvc-8.0:<cxxflags>-GR-
     ;
 
-boost-install boost_graph ;
\ No newline at end of file
+boost-install boost_graph ;

Added: trunk/libs/graph/src/mpi_process_group.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/graph/src/mpi_process_group.cpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,1108 @@
+// Copyright (C) 2004-2006 The Trustees of Indiana University.
+// Copyright (C) 2007 Douglas Gregor <doug.gregor_at_[hidden]>
+// Copyright (C) 2007 Matthias Troyer <troyer_at_[hidden]>
+
+// 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
+// Matthias Troyer
+#include <boost/graph/use_mpi.hpp>
+#include <boost/graph/distributed/mpi_process_group.hpp>
+#include <boost/mpi/environment.hpp>
+#include <boost/lexical_cast.hpp>
+#include <memory>
+#include <algorithm>
+
+//#define DEBUG 1
+
+
+//#define MAX_BATCHES 1500
+#define PREALLOCATE_BATCHES 250
+// 500 is a better setting for PREALLOCATE_BATCHES if you're not using process
+// subgroups and are building 64-bit binaries. 250 allows all the CTest
+// tests to pass in both 32 and 64-bit modes. If you create multiple process
+// groups with PREALLOCATE_BATCHES at a reasonable level in 32-bit mode you
+// _will_ run out of memory and get "malloc failed" errors
+
+//#define NO_ISEND_BATCHES
+//#define NO_IMMEDIATE_PROCESSING
+//#define NO_SPLIT_BATCHES
+#define IRECV_BATCH
+
+// we cannot keep track of how many we received if we do not process them
+#ifdef NO_IMMEDIATE_PROCESSING
+#undef IRECV_BATCH
+#endif
+
+#ifdef DEBUG
+# include <iostream>
+#endif // DEBUG
+
+namespace boost { namespace graph { namespace distributed {
+
+struct mpi_process_group::deallocate_block
+{
+ explicit deallocate_block(blocks_type* blocks) : blocks(blocks) { }
+
+ void operator()(int* block_num)
+ {
+ block_type* block = (*blocks)[*block_num];
+
+ // Mark this block as inactive
+ (*blocks)[*block_num] = 0;
+
+#ifdef DEBUG
+ fprintf(stderr, "Processor %i deallocated block #%i\n",
+ boost::mpi::communicator().rank(), *block_num);
+#endif
+
+ // Delete the block and its block number
+ delete block_num;
+ delete block;
+ }
+
+private:
+ blocks_type* blocks;
+};
+
+mpi_process_group::impl::incoming_messages::incoming_messages()
+{
+ next_header.push_back(headers.begin());
+}
+
+mpi_process_group::impl::impl(std::size_t num_headers, std::size_t buffer_sz,
+ communicator_type parent_comm)
+ : comm(parent_comm, boost::mpi::comm_duplicate),
+ oob_reply_comm(parent_comm, boost::mpi::comm_duplicate),
+ allocated_tags(boost::mpi::environment::max_tag())
+{
+ max_sent=0;
+ max_received=0;
+ int n = comm.size();
+ outgoing.resize(n);
+ incoming.resize(n);
+
+ // no synchronization stage means -1
+ // to keep the convention
+ synchronizing_stage.resize(n,-1);
+ number_sent_batches.resize(n);
+ number_received_batches.resize(n);
+ trigger_context = trc_none;
+ processing_batches = 0;
+
+ // Allocator a placeholder block "0"
+ blocks.push_back(new block_type);
+
+ synchronizing = false;
+
+ set_batch_size(num_headers,buffer_sz);
+
+ for (int i = 0; i < n; ++i) {
+ incoming[i].next_header.front() = incoming[i].headers.end();
+ outgoing[i].buffer.reserve(batch_message_size);
+ }
+
+#ifdef PREALLOCATE_BATCHES
+ batch_pool.resize(PREALLOCATE_BATCHES);
+ for (std::size_t i = 0 ; i < batch_pool.size(); ++i) {
+ batch_pool[i].buffer.reserve(batch_message_size);
+ batch_pool[i].request=MPI_REQUEST_NULL;
+ free_batches.push(i);
+ }
+#endif
+}
+
+void mpi_process_group::impl::set_batch_size(std::size_t header_num, std::size_t buffer_sz)
+{
+ batch_header_number = header_num;
+ batch_buffer_size = buffer_sz;
+
+ // determine batch message size by serializing the largest possible batch
+ outgoing_messages msg;
+ msg.headers.resize(batch_header_number);
+ msg.buffer.resize(batch_buffer_size);
+ boost::mpi::packed_oarchive oa(comm);
+ oa << const_cast<const outgoing_messages&>(msg);
+ batch_message_size = oa.size();
+}
+
+
+mpi_process_group::impl::~impl()
+{
+ // Delete the placeholder "0" block
+ delete blocks.front();
+ if (!boost::mpi::environment::finalized())
+ for (std::vector<MPI_Request>::iterator it=requests.begin();
+ it != requests.end();++it)
+ MPI_Cancel(&(*it));
+}
+
+namespace detail {
+// global batch handlers
+void handle_batch (mpi_process_group const& self, int source, int,
+ mpi_process_group::outgoing_messages& batch,bool out_of_band)
+{
+#ifdef DEBUG
+ std::cerr << "Processing batch trigger\n";
+ std::cerr << "BATCH: " << process_id(self) << " <- " << source << " ("
+ << batch.headers.size() << " headers, "
+ << batch.buffer.size() << " bytes)"
+ << std::endl;
+#endif
+ // If we are not synchronizing, then this must be an early receive
+ trigger_receive_context old_context = self.impl_->trigger_context;
+ if (self.impl_->trigger_context != trc_in_synchronization)
+ self.impl_->trigger_context = trc_early_receive;
+
+ // Receive the batched messages
+ self.receive_batch(source,batch);
+
+ // Restore the previous context
+ self.impl_->trigger_context = old_context;
+}
+
+// synchronization handler
+void handle_sync (mpi_process_group const& self, int source, int tag, int val,
+ bool)
+{
+ // increment the stage for the source
+ std::size_t stage = static_cast<std::size_t>(
+ ++self.impl_->synchronizing_stage[source]);
+
+ assert(source != process_id(self));
+
+#ifdef DEBUG
+ std::ostringstream out;
+ out << process_id(self) << ": handle_sync from " << source
+ << " (stage = " << self.impl_->synchronizing_stage[source] << ")\n";
+ std::cerr << out.str();
+#endif
+
+ // record how many still have messages to be sent
+ if (self.impl_->synchronizing_unfinished.size()<=stage) {
+ assert(self.impl_->synchronizing_unfinished.size() == stage);
+ self.impl_->synchronizing_unfinished.push_back(val >= 0 ? 1 : 0);
+ }
+ else
+ self.impl_->synchronizing_unfinished[stage]+=(val >= 0 ? 1 : 0);
+
+ // record how many are in that stage
+ if (self.impl_->processors_synchronizing_stage.size()<=stage) {
+ assert(self.impl_->processors_synchronizing_stage.size() == stage);
+ self.impl_->processors_synchronizing_stage.push_back(1);
+ }
+ else
+ ++self.impl_->processors_synchronizing_stage[stage];
+
+ // subtract how many batches we were supposed to receive
+ if (val>0)
+ self.impl_->number_received_batches[source] -= val;
+}
+
+
+}
+
+mpi_process_group::mpi_process_group(communicator_type parent_comm)
+{
+ // 64K messages and 1MB buffer turned out to be a reasonable choice
+ impl_.reset(new impl(64*1024,1024*1024,parent_comm));
+#ifndef IRECV_BATCH
+ global_trigger<outgoing_messages>(msg_batch,&detail::handle_batch);
+#else // use irecv version by providing a maximum buffer size
+ global_trigger<outgoing_messages>(msg_batch,&detail::handle_batch,
+ impl_->batch_message_size);
+#endif
+ global_trigger<outgoing_messages>(msg_large_batch,&detail::handle_batch);
+ global_trigger<int>(msg_synchronizing,&detail::handle_sync);
+ rank = impl_->comm.rank();
+ size = impl_->comm.size();
+
+#ifdef SEND_OOB_BSEND
+ // let us try with a default bufferr size of 16 MB
+ if (!message_buffer_size())
+ set_message_buffer_size(16*1024*1024);
+#endif
+}
+
+mpi_process_group::mpi_process_group(std::size_t h, std::size_t sz,
+ communicator_type parent_comm)
+{
+ impl_.reset(new impl(h,sz,parent_comm));
+#ifndef IRECV_BATCH
+ global_trigger<outgoing_messages>(msg_batch,&detail::handle_batch);
+#else // use irecv version by providing a maximum buffer size
+ global_trigger<outgoing_messages>(msg_batch,&detail::handle_batch,
+ impl_->batch_message_size);
+#endif
+ global_trigger<outgoing_messages>(msg_large_batch,&detail::handle_batch);
+ global_trigger<int>(msg_synchronizing,&detail::handle_sync);
+ rank = impl_->comm.rank();
+ size = impl_->comm.size();
+#ifdef SEND_OOB_BSEND
+ // let us try with a default bufferr size of 16 MB
+ if (!message_buffer_size())
+ set_message_buffer_size(16*1024*1024);
+#endif
+}
+
+mpi_process_group::mpi_process_group(const mpi_process_group& other,
+ const receiver_type& handler, bool)
+ : impl_(other.impl_)
+{
+ rank = impl_->comm.rank();
+ size = impl_->comm.size();
+ replace_handler(handler);
+}
+
+mpi_process_group::mpi_process_group(const mpi_process_group& other,
+ attach_distributed_object, bool)
+ : impl_(other.impl_)
+{
+ rank = impl_->comm.rank();
+ size = impl_->comm.size();
+ allocate_block();
+
+ for (std::size_t i = 0; i < impl_->incoming.size(); ++i) {
+ if (my_block_number() >= (int)impl_->incoming[i].next_header.size()) {
+ impl_->incoming[i].next_header
+ .push_back(impl_->incoming[i].headers.begin());
+ } else {
+ impl_->incoming[i].next_header[my_block_number()] =
+ impl_->incoming[i].headers.begin();
+ }
+
+#ifdef DEBUG
+ if (process_id(*this) == 0) {
+ std::cerr << "Allocated tag block " << my_block_number() << std::endl;
+ }
+#endif
+ }
+}
+
+mpi_process_group::~mpi_process_group() {
+ /*
+ std::string msg = boost::lexical_cast<std::string>(process_id(*this)) + " " +
+ boost::lexical_cast<std::string>(impl_->max_received) + " " +
+ boost::lexical_cast<std::string>(impl_->max_sent) + "\n";
+ std::cerr << msg << "\n";
+ */
+}
+
+
+mpi_process_group::communicator_type communicator(const mpi_process_group& pg)
+{ return pg.impl_->comm; }
+
+void
+mpi_process_group::replace_handler(const receiver_type& handler,
+ bool out_of_band_receive)
+{
+ make_distributed_object();
+
+ // Attach the receive handler
+ impl_->blocks[my_block_number()]->on_receive = handler;
+}
+
+void
+mpi_process_group::make_distributed_object()
+{
+ if (my_block_number() == 0) {
+ allocate_block();
+
+ for (std::size_t i = 0; i < impl_->incoming.size(); ++i) {
+ if (my_block_number() >= (int)impl_->incoming[i].next_header.size()) {
+ impl_->incoming[i].next_header
+ .push_back(impl_->incoming[i].headers.begin());
+ } else {
+ impl_->incoming[i].next_header[my_block_number()] =
+ impl_->incoming[i].headers.begin();
+ }
+
+#ifdef DEBUG
+ if (process_id(*this) == 0) {
+ std::cerr << "Allocated tag block " << my_block_number() << std::endl;
+ }
+#endif
+ }
+ } else {
+ // Clear out the existing triggers
+ std::vector<shared_ptr<trigger_base> >()
+ .swap(impl_->blocks[my_block_number()]->triggers);
+ }
+
+ // Clear out the receive handler
+ impl_->blocks[my_block_number()]->on_receive = 0;
+}
+
+void
+mpi_process_group::
+replace_on_synchronize_handler(const on_synchronize_event_type& handler)
+{
+ if (my_block_number() > 0)
+ impl_->blocks[my_block_number()]->on_synchronize = handler;
+}
+
+int mpi_process_group::allocate_block(bool out_of_band_receive)
+{
+ assert(!block_num);
+ block_iterator i = impl_->blocks.begin();
+ while (i != impl_->blocks.end() && *i) ++i;
+
+ if (i == impl_->blocks.end()) {
+ impl_->blocks.push_back(new block_type());
+ i = impl_->blocks.end() - 1;
+ } else {
+ *i = new block_type();
+ }
+
+ block_num.reset(new int(i - impl_->blocks.begin()),
+ deallocate_block(&impl_->blocks));
+
+#ifdef DEBUG
+ fprintf(stderr,
+ "Processor %i allocated block #%i\n", process_id(*this), *block_num);
+#endif
+
+ return *block_num;
+}
+
+bool mpi_process_group::maybe_emit_receive(int process, int encoded_tag) const
+{
+ std::pair<int, int> decoded = decode_tag(encoded_tag);
+
+ assert (decoded.first < static_cast<int>(impl_->blocks.size()));
+
+ block_type* block = impl_->blocks[decoded.first];
+ if (!block) {
+ std::cerr << "Received message from process " << process << " with tag "
+ << decoded.second << " for non-active block "
+ << decoded.first << std::endl;
+ std::cerr << "Active blocks are: ";
+ for (std::size_t i = 0; i < impl_->blocks.size(); ++i)
+ if (impl_->blocks[i])
+ std::cerr << i << ' ';
+ std::cerr << std::endl;
+ assert(block);
+ }
+
+ if (decoded.second < static_cast<int>(block->triggers.size())
+ && block->triggers[decoded.second]) {
+ // We have a trigger for this message; use it
+ trigger_receive_context old_context = impl_->trigger_context;
+ impl_->trigger_context = trc_out_of_band;
+ block->triggers[decoded.second]->receive(*this, process, decoded.second,
+ impl_->trigger_context,
+ decoded.first);
+ impl_->trigger_context = old_context;
+ }
+ else
+ return false;
+ // We receives the message above
+ return true;
+}
+
+bool mpi_process_group::emit_receive(int process, int encoded_tag) const
+{
+ std::pair<int, int> decoded = decode_tag(encoded_tag);
+
+ if (decoded.first >= static_cast<int>(impl_->blocks.size()))
+ // This must be an out-of-band message destined for
+ // send_oob_with_reply; ignore it.
+ return false;
+
+ // Find the block that will receive this message
+ block_type* block = impl_->blocks[decoded.first];
+ assert(block);
+ if (decoded.second < static_cast<int>(block->triggers.size())
+ && block->triggers[decoded.second])
+ // We have a trigger for this message; use it
+ block->triggers[decoded.second]->receive(*this,process, decoded.second,
+ impl_->trigger_context);
+ else if (block->on_receive)
+ // Fall back to the normal message handler
+ block->on_receive(process, decoded.second);
+ else
+ // There was no handler for this message
+ return false;
+
+ // The message was handled above
+ return true;
+}
+
+void mpi_process_group::emit_on_synchronize() const
+{
+ for (block_iterator i = impl_->blocks.begin(); i != impl_->blocks.end(); ++i)
+ if (*i && (*i)->on_synchronize) (*i)->on_synchronize();
+}
+
+
+optional<std::pair<mpi_process_group::process_id_type, int> >
+mpi_process_group::probe() const
+{
+#ifdef DEBUG
+ std::cerr << "PROBE: " << process_id(*this) << ", tag block = "
+ << my_block_number() << std::endl;
+#endif
+
+ typedef std::pair<process_id_type, int> result_type;
+
+ int tag_block = my_block_number();
+
+ for (std::size_t source = 0; source < impl_->incoming.size(); ++source) {
+ impl::incoming_messages& incoming = impl_->incoming[source];
+ std::vector<impl::message_header>::iterator& i =
+ incoming.next_header[tag_block];
+ std::vector<impl::message_header>::iterator end = incoming.headers.end();
+
+ while (i != end) {
+ if (i->tag != -1 && decode_tag(i->tag).first == my_block_number()) {
+#ifdef DEBUG
+ std::cerr << "PROBE: " << process_id(*this) << " <- " << source
+ << ", block = " << my_block_number() << ", tag = "
+ << decode_tag(i->tag).second << ", bytes = " << i->bytes
+ << std::endl;
+#endif
+ return result_type(source, decode_tag(i->tag).second);
+ }
+ ++i;
+ }
+ }
+ return optional<result_type>();
+}
+
+void
+mpi_process_group::maybe_send_batch(process_id_type dest) const
+{
+#ifndef NO_SPLIT_BATCHES
+ impl::outgoing_messages& outgoing = impl_->outgoing[dest];
+ if (outgoing.buffer.size() >= impl_->batch_buffer_size ||
+ outgoing.headers.size() >= impl_->batch_header_number) {
+ // we are full and need to send
+ outgoing_messages batch;
+ batch.buffer.reserve(impl_->batch_buffer_size);
+ batch.swap(outgoing);
+ if (batch.buffer.size() >= impl_->batch_buffer_size
+ && batch.headers.size()>1 ) {
+ // we are too large, keep the last message in the outgoing buffer
+ std::copy(batch.buffer.begin()+batch.headers.back().offset,
+ batch.buffer.end(),std::back_inserter(outgoing.buffer));
+ batch.buffer.resize(batch.headers.back().offset);
+ outgoing.headers.push_back(batch.headers.back());
+ batch.headers.pop_back();
+ outgoing.headers.front().offset=0;
+ }
+ send_batch(dest,batch);
+ }
+#endif
+}
+
+void
+mpi_process_group::send_batch(process_id_type dest) const
+{
+ impl::outgoing_messages& outgoing = impl_->outgoing[dest];
+ if (outgoing.headers.size()) {
+ // need to copy to avoid race conditions
+ outgoing_messages batch;
+ batch.buffer.reserve(impl_->batch_buffer_size);
+ batch.swap(outgoing);
+ send_batch(dest,batch);
+ }
+}
+
+
+void
+mpi_process_group::send_batch(process_id_type dest,
+ outgoing_messages& outgoing) const
+{
+ impl_->free_sent_batches();
+ process_id_type id = process_id(*this);
+
+ // clear the batch
+#ifdef DEBUG
+ std::cerr << "Sending batch: " << id << " -> " << dest << std::endl;
+#endif
+ // we increment the number of batches sent
+ ++impl_->number_sent_batches[dest];
+ // and send the batch
+ assert(outgoing.headers.size() <= impl_->batch_header_number);
+ if (id != dest) {
+#ifdef NO_ISEND_BATCHES
+ impl::batch_request req;
+#else
+#ifdef PREALLOCATE_BATCHES
+ while (impl_->free_batches.empty()) {
+ impl_->free_sent_batches();
+ poll();
+ }
+ impl::batch_request& req = impl_->batch_pool[impl_->free_batches.top()];
+ impl_->free_batches.pop();
+#else
+ impl_->sent_batches.push_back(impl::batch_request());
+ impl::batch_request& req = impl_->sent_batches.back();
+#endif
+#endif
+ boost::mpi::packed_oarchive oa(impl_->comm,req.buffer);
+ oa << outgoing;
+
+ int tag = msg_batch;
+
+#ifdef IRECV_BATCH
+ if (oa.size() > impl_->batch_message_size)
+ tag = msg_large_batch;
+#endif
+
+ int result = MPI_Isend(const_cast<void*>(oa.address()), oa.size(),
+ MPI_PACKED, dest, tag, impl_->comm,
+ &req.request);
+ assert(result == MPI_SUCCESS);
+ impl_->max_sent = (std::max)(impl_->max_sent,impl_->sent_batches.size());
+#ifdef NO_ISEND_BATCHES
+ int done=0;
+ do {
+ poll();
+ MPI_Test(&req.request,&done,MPI_STATUS_IGNORE);
+ } while (!done);
+#else
+#ifdef MAX_BATCHES
+ while (impl_->sent_batches.size() >= MAX_BATCHES-1) {
+ impl_->free_sent_batches();
+ poll();
+ }
+#endif
+#endif
+ }
+ else
+ receive_batch(id,outgoing);
+}
+
+void mpi_process_group::process_batch(int source) const
+{
+ bool processing_from_queue = !impl_->new_batches.empty();
+ impl_->processing_batches++;
+ typedef std::vector<impl::message_header>::iterator iterator;
+
+ impl::incoming_messages& incoming = impl_->incoming[source];
+
+ // Set up the iterators pointing to the next header in each block
+ for (std::size_t i = 0; i < incoming.next_header.size(); ++i)
+ incoming.next_header[i] = incoming.headers.begin();
+
+ buffer_type remaining_buffer;
+ std::vector<impl::message_header> remaining_headers;
+
+ iterator end = incoming.headers.end();
+
+ for (iterator i = incoming.headers.begin(); i != end; ++i) {
+ // This this message has already been received, skip it
+ if (i->tag == -1)
+ continue;
+
+#ifdef BATCH_DEBUG
+ std::cerr << process_id(*this) << ": emit_receive(" << source << ", "
+ << decode_tag(i->tag).first << ":" << decode_tag(i->tag).second
+ << ")\n";
+#endif
+
+ if (!emit_receive(source, i->tag)) {
+#ifdef BATCH_DEBUG
+ std::cerr << process_id(*this) << ": keeping message # "
+ << remaining_headers.size() << " from " << source << " ("
+ << decode_tag(i->tag).first << ":"
+ << decode_tag(i->tag).second << ", "
+ << i->bytes << " bytes)\n";
+#endif
+ // Hold on to this message until the next stage
+ remaining_headers.push_back(*i);
+ remaining_headers.back().offset = remaining_buffer.size();
+ remaining_buffer.insert(remaining_buffer.end(),
+ &incoming.buffer[i->offset],
+ &incoming.buffer[i->offset] + i->bytes);
+ }
+ }
+
+ // Swap the remaining messages into the "incoming" set.
+ incoming.headers.swap(remaining_headers);
+ incoming.buffer.swap(remaining_buffer);
+
+ // Set up the iterators pointing to the next header in each block
+ for (std::size_t i = 0; i < incoming.next_header.size(); ++i)
+ incoming.next_header[i] = incoming.headers.begin();
+ impl_->processing_batches--;
+
+ if (!processing_from_queue)
+ while (!impl_->new_batches.empty()) {
+ receive_batch(impl_->new_batches.front().first,
+ impl_->new_batches.front().second);
+ impl_->new_batches.pop();
+ }
+}
+
+
+void mpi_process_group::receive_batch(process_id_type source,
+ outgoing_messages& new_messages) const
+{
+ impl_->free_sent_batches();
+ if(!impl_->processing_batches) {
+ // increase the number of received batches
+ ++impl_->number_received_batches[source];
+ // and receive the batch
+ impl::incoming_messages& incoming = impl_->incoming[source];
+ typedef std::vector<impl::message_header>::iterator iterator;
+ iterator end = new_messages.headers.end();
+ for (iterator i = new_messages.headers.begin(); i != end; ++i) {
+ incoming.headers.push_back(*i);
+ incoming.headers.back().offset = incoming.buffer.size();
+ incoming.buffer.insert(incoming.buffer.end(),
+ &new_messages.buffer[i->offset],
+ &new_messages.buffer[i->offset] + i->bytes);
+ }
+ // Set up the iterators pointing to the next header in each block
+ for (std::size_t i = 0; i < incoming.next_header.size(); ++i)
+ incoming.next_header[i] = incoming.headers.begin();
+#ifndef NO_IMMEDIATE_PROCESSING
+ process_batch(source);
+#endif
+ }
+ else {
+ #ifdef DEBUG
+ std::cerr << "Pushing incoming message batch onto queue since we are already processing a batch.\n";
+ #endif
+ // use swap to avoid copying
+ impl_->new_batches.push(std::make_pair(int(source),outgoing_messages()));
+ impl_->new_batches.back().second.swap(new_messages);
+ impl_->max_received = (std::max)(impl_->max_received,impl_->new_batches.size());
+ }
+}
+
+
+void mpi_process_group::pack_headers() const
+{
+ for (process_id_type other = 0; other < num_processes(*this); ++other) {
+ typedef std::vector<impl::message_header>::iterator iterator;
+
+ impl::incoming_messages& incoming = impl_->incoming[other];
+
+ buffer_type remaining_buffer;
+ std::vector<impl::message_header> remaining_headers;
+
+ iterator end = incoming.headers.end();
+ for (iterator i = incoming.headers.begin(); i != end; ++i) {
+ if (i->tag == -1)
+ continue;
+
+ // Hold on to this message until the next stage
+ remaining_headers.push_back(*i);
+ remaining_headers.back().offset = remaining_buffer.size();
+ remaining_buffer.insert(remaining_buffer.end(),
+ &incoming.buffer[i->offset],
+ &incoming.buffer[i->offset] + i->bytes);
+ }
+
+ // Swap the remaining messages into the "incoming" set.
+ incoming.headers.swap(remaining_headers);
+ incoming.buffer.swap(remaining_buffer);
+
+ // Set up the iterators pointing to the next header in each block
+ for (std::size_t i = 0; i < incoming.next_header.size(); ++i)
+ incoming.next_header[i] = incoming.headers.begin();
+ }
+}
+
+void mpi_process_group::receive_batch(boost::mpi::status& status) const
+{
+ //std::cerr << "Handling batch\n";
+ outgoing_messages batch;
+ //impl_->comm.recv(status.source(),status.tag(),batch);
+
+ //receive_oob(*this,status.source(),msg_batch,batch);
+
+ // Determine how big the receive buffer should be
+#if BOOST_VERSION >= 103600
+ int size = status.count<boost::mpi::packed>().get();
+#else
+ int size;
+ MPI_Status mpi_status(status);
+ MPI_Get_count(&mpi_status, MPI_PACKED, &size);
+#endif
+
+ // Allocate the receive buffer
+ boost::mpi::packed_iarchive in(impl_->comm,size);
+
+ // Receive the message data
+ MPI_Recv(in.address(), size, MPI_PACKED,
+ status.source(), status.tag(),
+ impl_->comm, MPI_STATUS_IGNORE);
+
+ // Unpack the message data
+ in >> batch;
+ receive_batch(status.source(),batch);
+}
+
+std::pair<boost::mpi::communicator, int>
+mpi_process_group::actual_communicator_and_tag(int tag, int block) const
+{
+ if (tag >= max_tags * static_cast<int>(impl_->blocks.size()))
+ // Use the out-of-band reply communicator
+ return std::make_pair(impl_->oob_reply_comm, tag);
+ else
+ // Use the normal communicator and translate the tag
+ return std::make_pair(impl_->comm,
+ encode_tag(block == -1? my_block_number() : block,
+ tag));
+}
+
+
+void mpi_process_group::synchronize() const
+{
+ // Don't synchronize if we've already finished
+ if (boost::mpi::environment::finalized())
+ return;
+
+#ifdef DEBUG
+ std::cerr << "SYNC: " << process_id(*this) << std::endl;
+#endif
+
+ emit_on_synchronize();
+
+ process_id_type id = process_id(*this); // Our rank
+ process_size_type p = num_processes(*this); // The number of processes
+
+ // Pack the remaining incoming messages into the beginning of the
+ // buffers, so that we can receive new messages in this
+ // synchronization step without losing those messages that have not
+ // yet been received.
+ pack_headers();
+
+ impl_->synchronizing_stage[id] = -1;
+ int stage=-1;
+ bool no_new_messages = false;
+ while (true) {
+ ++stage;
+#ifdef DEBUG
+ std::cerr << "SYNC: " << id << " starting stage " << (stage+1) << ".\n";
+#endif
+
+ // Tell everyone that we are synchronizing. Note: we use MPI_Isend since
+ // we absolutely cannot have any of these operations blocking.
+
+ // increment the stage for the source
+ ++impl_->synchronizing_stage[id];
+ if (impl_->synchronizing_stage[id] != stage)
+ std::cerr << "Expected stage " << stage << ", got " << impl_->synchronizing_stage[id] << std::endl;
+ assert(impl_->synchronizing_stage[id]==stage);
+ // record how many still have messages to be sent
+ if (static_cast<int>(impl_->synchronizing_unfinished.size())<=stage) {
+ assert(static_cast<int>(impl_->synchronizing_unfinished.size()) == stage);
+ impl_->synchronizing_unfinished.push_back(no_new_messages ? 0 : 1);
+ }
+ else
+ impl_->synchronizing_unfinished[stage]+=(no_new_messages ? 0 : 1);
+
+ // record how many are in that stage
+ if (static_cast<int>(impl_->processors_synchronizing_stage.size())<=stage) {
+ assert(static_cast<int>(impl_->processors_synchronizing_stage.size()) == stage);
+ impl_->processors_synchronizing_stage.push_back(1);
+ }
+ else
+ ++impl_->processors_synchronizing_stage[stage];
+
+ impl_->synchronizing = true;
+
+ for (int dest = 0; dest < p; ++dest) {
+ int sync_message = no_new_messages ? -1 : impl_->number_sent_batches[dest];
+ if (dest != id) {
+ impl_->number_sent_batches[dest]=0;
+ MPI_Request request;
+ MPI_Isend(&sync_message, 1, MPI_INT, dest, msg_synchronizing, impl_->comm,&request);
+ int done=0;
+ do {
+ poll();
+ MPI_Test(&request,&done,MPI_STATUS_IGNORE);
+ } while (!done);
+ }
+ else { // need to subtract how many messages I should have received
+ impl_->number_received_batches[id] -=impl_->number_sent_batches[id];
+ impl_->number_sent_batches[id]=0;
+ }
+ }
+
+ // Keep handling out-of-band messages until everyone has gotten
+ // to this point.
+ while (impl_->processors_synchronizing_stage[stage] <p) {
+ // with the trigger based solution we cannot easily pass true here
+ poll(/*wait=*/false, -1, true);
+
+ }
+
+ // check that everyone is at least here
+ for (int source=0; source<p ; ++source)
+ assert(impl_->synchronizing_stage[source] >= stage);
+
+ // receive any batches sent in the meantime
+ // all have to be available already
+ while (true) {
+ bool done=true;
+ for (int source=0; source<p ; ++source)
+ if(impl_->number_received_batches[source] < 0)
+ done = false;
+ if (done)
+ break;
+ poll(false,-1,true);
+ }
+
+#ifndef NO_IMMEDIATE_PROCESSING
+ for (int source=0; source<p ; ++source)
+ assert(impl_->number_received_batches[source] >= 0);
+#endif
+
+ impl_->synchronizing = false;
+
+ // Flush out remaining messages
+ if (impl_->synchronizing_unfinished[stage]==0)
+ break;
+#ifdef NO_IMMEDIATE_PROCESSING
+ for (process_id_type dest = 0; dest < p; ++dest)
+ process_batch(dest);
+#endif
+
+ no_new_messages = true;
+ for (process_id_type dest = 0; dest < p; ++dest) {
+ if (impl_->outgoing[dest].headers.size() ||
+ impl_->number_sent_batches[dest]>0)
+ no_new_messages = false;
+ send_batch(dest);
+ }
+ }
+
+ impl_->comm.barrier/*nomacro*/();
+#if 0
+ // set up for next synchronize call
+ for (int source=0; source<p; ++source) {
+ if (impl_->synchronizing_stage[source] != stage) {
+ std::cerr << id << ": expecting stage " << stage << " from source "
+ << source << ", got " << impl_->synchronizing_stage[source]
+ << std::endl;
+ }
+ assert(impl_->synchronizing_stage[source]==stage);
+ }
+#endif
+ std::fill(impl_->synchronizing_stage.begin(),
+ impl_->synchronizing_stage.end(), -1);
+
+ // get rid of the information regarding recorded numbers of processors
+ // for the stages we just finished
+ impl_->processors_synchronizing_stage.clear();
+ impl_->synchronizing_unfinished.clear();
+
+ for (process_id_type dest = 0; dest < p; ++dest)
+ assert (impl_->outgoing[dest].headers.empty());
+#ifndef NO_IMMEDIATE_PROCESSING
+ for (int source=0; source<p ; ++source)
+ assert (impl_->number_received_batches[source] == 0);
+#endif
+
+ impl_->free_sent_batches();
+#ifdef DEBUG
+ std::cerr << "SYNC: " << process_id(*this) << " completed." << std::endl;
+#endif
+}
+
+optional<std::pair<mpi_process_group::process_id_type, int> >
+probe(const mpi_process_group& pg)
+{ return pg.probe(); }
+
+void mpi_process_group::poll_requests(int block) const
+{
+ int size = impl_->requests.size();
+ if (size==0)
+ return;
+ std::vector<MPI_Status> statuses(size);
+ std::vector<int> indices(size);
+
+ while (true) {
+ MPI_Testsome(impl_->requests.size(),&impl_->requests[0],
+ &size,&indices[0],&statuses[0]);
+ if (size==0)
+ return; // no message waiting
+
+ // remove handled requests before we get the chance to be recursively called
+ if (size) {
+ std::vector<MPI_Request> active_requests;
+ std::size_t i=0;
+ int j=0;
+ for (;i< impl_->requests.size() && j< size; ++i) {
+ if (int(i)==indices[j])
+ // release the dealt-with request
+ ++j;
+ else // copy and keep the request
+ active_requests.push_back(impl_->requests[i]);
+ }
+ while (i < impl_->requests.size())
+ active_requests.push_back(impl_->requests[i++]);
+ impl_->requests.swap(active_requests);
+ }
+
+ optional<std::pair<int, int> > result;
+ for (int i=0;i < size; ++i) {
+ std::pair<int, int> decoded = decode_tag(statuses[i].MPI_TAG);
+ block_type* block = impl_->blocks[decoded.first];
+
+ assert (decoded.second < static_cast<int>(block->triggers.size()) && block->triggers[decoded.second]);
+ // We have a trigger for this message; use it
+ trigger_receive_context old_context = impl_->trigger_context;
+ impl_->trigger_context = trc_irecv_out_of_band;
+ block->triggers[decoded.second]->receive(*this, statuses[i].MPI_SOURCE,
+ decoded.second, impl_->trigger_context, decoded.first);
+ impl_->trigger_context = old_context;
+ }
+ }
+}
+
+
+optional<std::pair<int, int> >
+mpi_process_group::
+poll(bool wait, int block, bool synchronizing) const
+{
+ // Set the new trigger context for these receive operations
+ trigger_receive_context old_context = impl_->trigger_context;
+ if (synchronizing)
+ impl_->trigger_context = trc_in_synchronization;
+ else
+ impl_->trigger_context = trc_out_of_band;
+
+ //wait = false;
+ optional<boost::mpi::status> status;
+ bool finished=false;
+ optional<std::pair<int, int> > result;
+ do {
+ poll_requests(block);
+ // Check for any messages not yet received.
+#ifdef PBGL_PROCESS_GROUP_NO_IRECV
+ if (wait)
+ status = impl_->comm.probe();
+ else
+#endif
+ status = impl_->comm.iprobe();
+
+ if (status) { // we have a message
+ // Decode the message
+ std::pair<int, int> decoded = decode_tag(status.get().tag());
+ if (maybe_emit_receive(status.get().source(), status.get().tag()))
+ // We received the message out-of-band; poll again
+ finished = false;
+ else if (decoded.first == (block == -1 ? my_block_number() : block)) {
+ // This message is for us, but not through a trigger. Return
+ // the decoded message.
+ result = std::make_pair(status.get().source(), decoded.second);
+ // otherwise we didn't match any message we know how to deal with, so
+ // pretend no message exists.
+ finished = true;
+ }
+ }
+ else
+ // We don't have a message to process; we're done.
+ finished=!wait;
+ } while (!finished);
+
+ // Restore the prior trigger context
+ impl_->trigger_context = old_context;
+ poll_requests(block);
+ return result;
+}
+
+void synchronize(const mpi_process_group& pg) { pg.synchronize(); }
+
+mpi_process_group mpi_process_group::base() const
+{
+ mpi_process_group copy(*this);
+ copy.block_num.reset();
+ return copy;
+}
+
+
+void mpi_process_group::impl::free_sent_batches()
+{
+ typedef std::list<batch_request>::iterator iterator;
+ iterator it = sent_batches.begin();
+ int flag;
+ int result;
+ while(it != sent_batches.end()) {
+ result = MPI_Test(&it->request,&flag,MPI_STATUS_IGNORE);
+ assert(result == MPI_SUCCESS);
+ iterator next=it;
+ ++next;
+ if (flag)
+ sent_batches.erase(it);
+ it=next;
+ }
+#ifdef PREALLOCATE_BATCHES
+ for (std::size_t i=0; i< batch_pool.size();++i) {
+ if(batch_pool[i].request != MPI_REQUEST_NULL) {
+ result = MPI_Test(&batch_pool[i].request,&flag,MPI_STATUS_IGNORE);
+ assert(result == MPI_SUCCESS);
+ if (flag) {
+ free_batches.push(i);
+ batch_pool[i].request = MPI_REQUEST_NULL;
+ batch_pool[i].buffer.resize(0);
+ }
+ }
+ }
+#endif
+}
+
+void
+mpi_process_group::install_trigger(int tag, int block,
+ shared_ptr<trigger_base> const& launcher)
+{
+ block_type* my_block = impl_->blocks[block];
+ assert(my_block);
+
+ // Make sure we have enough space in the structure for this trigger.
+ if (launcher->tag() >= static_cast<int>(my_block->triggers.size()))
+ my_block->triggers.resize(launcher->tag() + 1);
+
+ // If someone already put a trigger in this spot, we have a big
+ // problem.
+ if (my_block->triggers[launcher->tag()]) {
+ std::cerr << "Block " << my_block_number()
+ << " already has a trigger for tag " << launcher->tag()
+ << std::endl;
+ }
+ assert(!my_block->triggers[launcher->tag()]);
+
+ // Attach a new trigger launcher
+ my_block->triggers[launcher->tag()] = launcher;
+}
+
+std::size_t mpi_process_group::message_buffer_size()
+{
+ return message_buffer.size();
+}
+
+
+void mpi_process_group::set_message_buffer_size(std::size_t s)
+{
+ int sz;
+ void* ptr;
+ if (!message_buffer.empty()) {
+ MPI_Buffer_detach(&ptr,&sz);
+ assert(ptr == &message_buffer.front());
+ assert(static_cast<std::size_t>(sz) == message_buffer.size());
+ }
+ else if (old_buffer != 0)
+ MPI_Buffer_detach(&old_buffer,&old_buffer_size);
+ message_buffer.resize(s);
+ if (s)
+ MPI_Buffer_attach(&message_buffer.front(), message_buffer.size());
+ else if (old_buffer_size)
+ MPI_Buffer_attach(old_buffer, old_buffer_size);
+}
+
+
+std::vector<char> mpi_process_group::message_buffer;
+int mpi_process_group::old_buffer_size=0;
+void* mpi_process_group::old_buffer=0;
+
+} } } // end namespace boost::graph::distributed

Added: trunk/libs/graph/src/tag_allocator.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/graph/src/tag_allocator.cpp 2009-04-09 17:10:55 EDT (Thu, 09 Apr 2009)
@@ -0,0 +1,45 @@
+// Copyright (C) 2007 Douglas Gregor <doug.gregor_at_[hidden]>
+
+// 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)
+#include <boost/graph/use_mpi.hpp>
+#include <boost/graph/distributed/detail/tag_allocator.hpp>
+
+namespace boost { namespace graph { namespace distributed { namespace detail {
+
+tag_allocator::token tag_allocator::get_tag()
+{
+ int tag;
+
+ if (!freed.empty()) {
+ // Grab the tag at the top of the stack.
+ tag = freed.back();
+ freed.pop_back();
+ } else
+ // Grab the bottom tag and move the bottom down
+ tag = bottom--;
+
+ return token(this, tag);
+}
+
+tag_allocator::token::token(const token& other)
+ : allocator(other.allocator), tag_(other.tag_)
+{
+ // other no longer owns this tag
+ other.tag_ = -1;
+}
+
+tag_allocator::token::~token()
+{
+ if (tag_ != -1) {
+ if (tag_ == allocator->bottom + 1)
+ // This is the bottommost tag: just bump up the bottom
+ ++allocator->bottom;
+ else
+ // This tag isn't the bottom, so push it only the freed stack
+ allocator->freed.push_back(tag_);
+ }
+}
+
+} } } } // end namespace boost::graph::distributed::detail


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk