Mercurial > hg > vamp-build-and-test
diff DEPENDENCIES/generic/include/boost/graph/distributed/named_graph.hpp @ 16:2665513ce2d3
Add boost headers
author | Chris Cannam |
---|---|
date | Tue, 05 Aug 2014 11:11:38 +0100 |
parents | |
children | c530137014c0 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DEPENDENCIES/generic/include/boost/graph/distributed/named_graph.hpp Tue Aug 05 11:11:38 2014 +0100 @@ -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/assert.hpp> +#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. + template <typename VertexIterStability> + void removing_vertex(Vertex, VertexIterStability) { } + + /// 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; + BOOST_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() +{ + 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; + + BOOST_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() +{ + 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; + BOOST_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. + template <typename VertexIterStability> + void removing_vertex(Vertex, VertexIterStability) { } + + /// 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