diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2013-06-25 22:59:01 +0000 |
---|---|---|
committer | <> | 2013-09-27 11:49:28 +0000 |
commit | 8c4528713d907ee2cfd3bfcbbad272c749867f84 (patch) | |
tree | c09e2ce80f47b90c85cc720f5139089ad9c8cfff /libs/mpi/src | |
download | boost-tarball-baserock/morph.tar.gz |
Imported from /home/lorry/working-area/delta_boost-tarball/boost_1_54_0.tar.bz2.boost_1_54_0baserock/morph
Diffstat (limited to 'libs/mpi/src')
35 files changed, 3447 insertions, 0 deletions
diff --git a/libs/mpi/src/broadcast.cpp b/libs/mpi/src/broadcast.cpp new file mode 100644 index 000000000..2b807774d --- /dev/null +++ b/libs/mpi/src/broadcast.cpp @@ -0,0 +1,151 @@ +// Copyright 2005 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) + +// Message Passing Interface 1.1 -- Section 4.4. Broadcast + +#include <boost/mpi/collectives/broadcast.hpp> +#include <boost/mpi/skeleton_and_content.hpp> +#include <boost/mpi/detail/point_to_point.hpp> +#include <boost/mpi/environment.hpp> + +namespace boost { namespace mpi { + +template<> +void +broadcast<const packed_oarchive>(const communicator& comm, + const packed_oarchive& oa, + int root) +{ + // Only the root can broadcast the packed_oarchive + assert(comm.rank() == root); + + int size = comm.size(); + if (size < 2) return; + + // Determine maximum tag value + int tag = environment::collectives_tag(); + + // Broadcast data to all nodes + std::vector<MPI_Request> requests(size * 2); + int num_requests = 0; + for (int dest = 0; dest < size; ++dest) { + if (dest != root) { + // Build up send requests for each child send. + num_requests += detail::packed_archive_isend(comm, dest, tag, oa, + &requests[num_requests], 2); + } + } + + // Complete all of the sends + BOOST_MPI_CHECK_RESULT(MPI_Waitall, + (num_requests, &requests[0], MPI_STATUSES_IGNORE)); +} + +template<> +void +broadcast<packed_oarchive>(const communicator& comm, packed_oarchive& oa, + int root) +{ + broadcast(comm, const_cast<const packed_oarchive&>(oa), root); +} + +template<> +void +broadcast<packed_iarchive>(const communicator& comm, packed_iarchive& ia, + int root) +{ + int size = comm.size(); + if (size < 2) return; + + // Determine maximum tag value + int tag = environment::collectives_tag(); + + // Receive data from the root. + if (comm.rank() != root) { + MPI_Status status; + detail::packed_archive_recv(comm, root, tag, ia, status); + } else { + // Broadcast data to all nodes + std::vector<MPI_Request> requests(size * 2); + int num_requests = 0; + for (int dest = 0; dest < size; ++dest) { + if (dest != root) { + // Build up send requests for each child send. + num_requests += detail::packed_archive_isend(comm, dest, tag, ia, + &requests[num_requests], + 2); + } + } + + // Complete all of the sends + BOOST_MPI_CHECK_RESULT(MPI_Waitall, + (num_requests, &requests[0], MPI_STATUSES_IGNORE)); + } +} + +template<> +void +broadcast<const packed_skeleton_oarchive>(const communicator& comm, + const packed_skeleton_oarchive& oa, + int root) +{ + broadcast(comm, oa.get_skeleton(), root); +} + +template<> +void +broadcast<packed_skeleton_oarchive>(const communicator& comm, + packed_skeleton_oarchive& oa, int root) +{ + broadcast(comm, oa.get_skeleton(), root); +} + +template<> +void +broadcast<packed_skeleton_iarchive>(const communicator& comm, + packed_skeleton_iarchive& ia, int root) +{ + broadcast(comm, ia.get_skeleton(), root); +} + +template<> +void broadcast<content>(const communicator& comm, content& c, int root) +{ + broadcast(comm, const_cast<const content&>(c), root); +} + +template<> +void broadcast<const content>(const communicator& comm, const content& c, + int root) +{ +#ifdef LAM_MPI + if (comm.size() < 2) + return; + + // Some versions of LAM/MPI behave badly when broadcasting using + // MPI_BOTTOM, so we'll instead use manual send/recv operations. + if (comm.rank() == root) { + for (int p = 0; p < comm.size(); ++p) { + if (p != root) { + BOOST_MPI_CHECK_RESULT(MPI_Send, + (MPI_BOTTOM, 1, c.get_mpi_datatype(), + p, environment::collectives_tag(), comm)); + } + } + } else { + BOOST_MPI_CHECK_RESULT(MPI_Recv, + (MPI_BOTTOM, 1, c.get_mpi_datatype(), + root, environment::collectives_tag(), + comm, MPI_STATUS_IGNORE)); + } +#else + BOOST_MPI_CHECK_RESULT(MPI_Bcast, + (MPI_BOTTOM, 1, c.get_mpi_datatype(), + root, comm)); +#endif +} + +} } // end namespace boost::mpi diff --git a/libs/mpi/src/communicator.cpp b/libs/mpi/src/communicator.cpp new file mode 100644 index 000000000..c1e145ca1 --- /dev/null +++ b/libs/mpi/src/communicator.cpp @@ -0,0 +1,322 @@ +// Copyright (C) 2005, 2006 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) +#include <boost/mpi/communicator.hpp> +#include <boost/mpi/group.hpp> +#include <boost/mpi/intercommunicator.hpp> +#include <boost/mpi/graph_communicator.hpp> +#include <boost/mpi/skeleton_and_content.hpp> +#include <boost/mpi/detail/point_to_point.hpp> + +namespace boost { namespace mpi { + +/*************************************************************************** + * status * + ***************************************************************************/ +bool status::cancelled() const +{ + int flag = 0; + BOOST_MPI_CHECK_RESULT(MPI_Test_cancelled, (&m_status, &flag)); + return flag != 0; +} + +/*************************************************************************** + * communicator * + ***************************************************************************/ + +communicator::communicator() +{ + comm_ptr.reset(new MPI_Comm(MPI_COMM_WORLD)); +} + +communicator::communicator(const MPI_Comm& comm, comm_create_kind kind) +{ + if (comm == MPI_COMM_NULL) + /* MPI_COMM_NULL indicates that the communicator is not usable. */ + return; + + switch (kind) { + case comm_duplicate: + { + MPI_Comm newcomm; + BOOST_MPI_CHECK_RESULT(MPI_Comm_dup, (comm, &newcomm)); + comm_ptr.reset(new MPI_Comm(newcomm), comm_free()); + MPI_Errhandler_set(newcomm, MPI_ERRORS_RETURN); + break; + } + + case comm_take_ownership: + comm_ptr.reset(new MPI_Comm(comm), comm_free()); + break; + + case comm_attach: + comm_ptr.reset(new MPI_Comm(comm)); + break; + } +} + +communicator::communicator(const communicator& comm, + const boost::mpi::group& subgroup) +{ + MPI_Comm newcomm; + BOOST_MPI_CHECK_RESULT(MPI_Comm_create, + ((MPI_Comm)comm, (MPI_Group)subgroup, &newcomm)); + comm_ptr.reset(new MPI_Comm(newcomm), comm_free()); +} + +int communicator::size() const +{ + int size_; + BOOST_MPI_CHECK_RESULT(MPI_Comm_size, (MPI_Comm(*this), &size_)); + return size_; +} + +int communicator::rank() const +{ + int rank_; + BOOST_MPI_CHECK_RESULT(MPI_Comm_rank, (MPI_Comm(*this), &rank_)); + return rank_; +} + +boost::mpi::group communicator::group() const +{ + MPI_Group gr; + BOOST_MPI_CHECK_RESULT(MPI_Comm_group, ((MPI_Comm)*this, &gr)); + return boost::mpi::group(gr, /*adopt=*/true); +} + +void communicator::send(int dest, int tag) const +{ + BOOST_MPI_CHECK_RESULT(MPI_Send, + (MPI_BOTTOM, 0, MPI_PACKED, + dest, tag, MPI_Comm(*this))); +} + +status communicator::recv(int source, int tag) const +{ + status stat; + BOOST_MPI_CHECK_RESULT(MPI_Recv, + (MPI_BOTTOM, 0, MPI_PACKED, + source, tag, MPI_Comm(*this), &stat.m_status)); + return stat; +} + +optional<status> communicator::iprobe(int source, int tag) const +{ + typedef optional<status> result_type; + + status stat; + int flag; + BOOST_MPI_CHECK_RESULT(MPI_Iprobe, + (source, tag, MPI_Comm(*this), &flag, + &stat.m_status)); + if (flag) return stat; + else return result_type(); +} + +status communicator::probe(int source, int tag) const +{ + typedef optional<status> result_type; + + status stat; + BOOST_MPI_CHECK_RESULT(MPI_Probe, + (source, tag, MPI_Comm(*this), &stat.m_status)); + return stat; +} + +void (communicator::barrier)() const +{ + BOOST_MPI_CHECK_RESULT(MPI_Barrier, (MPI_Comm(*this))); +} + + +communicator::operator MPI_Comm() const +{ + if (comm_ptr) return *comm_ptr; + else return MPI_COMM_NULL; +} + +communicator communicator::split(int color) const +{ + return split(color, rank()); +} + +communicator communicator::split(int color, int key) const +{ + MPI_Comm newcomm; + BOOST_MPI_CHECK_RESULT(MPI_Comm_split, + (MPI_Comm(*this), color, key, &newcomm)); + return communicator(newcomm, comm_take_ownership); +} + +optional<intercommunicator> communicator::as_intercommunicator() const +{ + int flag; + BOOST_MPI_CHECK_RESULT(MPI_Comm_test_inter, ((MPI_Comm)*this, &flag)); + if (flag) + return intercommunicator(comm_ptr); + else + return optional<intercommunicator>(); +} + +optional<graph_communicator> communicator::as_graph_communicator() const +{ + int status; + BOOST_MPI_CHECK_RESULT(MPI_Topo_test, ((MPI_Comm)*this, &status)); + if (status == MPI_GRAPH) + return graph_communicator(comm_ptr); + else + return optional<graph_communicator>(); +} + +bool communicator::has_cartesian_topology() const +{ + int status; + BOOST_MPI_CHECK_RESULT(MPI_Topo_test, ((MPI_Comm)*this, &status)); + + return status == MPI_CART; +} + +void communicator::abort(int errcode) const +{ + BOOST_MPI_CHECK_RESULT(MPI_Abort, (MPI_Comm(*this), errcode)); +} + +/************************************************************* + * archived send/recv * + *************************************************************/ +template<> +void +communicator::send<packed_oarchive>(int dest, int tag, + const packed_oarchive& ar) const +{ + detail::packed_archive_send(MPI_Comm(*this), dest, tag, ar); +} + +template<> +void +communicator::send<packed_skeleton_oarchive> + (int dest, int tag, const packed_skeleton_oarchive& ar) const +{ + this->send(dest, tag, ar.get_skeleton()); +} + +template<> +void communicator::send<content>(int dest, int tag, const content& c) const +{ + BOOST_MPI_CHECK_RESULT(MPI_Send, + (MPI_BOTTOM, 1, c.get_mpi_datatype(), + dest, tag, MPI_Comm(*this))); +} + +template<> +status +communicator::recv<packed_iarchive>(int source, int tag, + packed_iarchive& ar) const +{ + status stat; + detail::packed_archive_recv(MPI_Comm(*this), source, tag, ar, + stat.m_status); + return stat; +} + +template<> +status +communicator::recv<packed_skeleton_iarchive> + (int source, int tag, packed_skeleton_iarchive& ar) const +{ + return this->recv(source, tag, ar.get_skeleton()); +} + +template<> +status +communicator::recv<const content>(int source, int tag, const content& c) const +{ + status stat; + BOOST_MPI_CHECK_RESULT(MPI_Recv, + (MPI_BOTTOM, 1, c.get_mpi_datatype(), + source, tag, MPI_Comm(*this), &stat.m_status)); + return stat; +} + +/************************************************************* + * non-blocking send/recv * + *************************************************************/ +template<> +request +communicator::isend<packed_oarchive>(int dest, int tag, + const packed_oarchive& ar) const +{ + request req; + detail::packed_archive_isend(MPI_Comm(*this), dest, tag, ar, + &req.m_requests[0] ,2); + return req; +} + +template<> +request +communicator::isend<packed_skeleton_oarchive> + (int dest, int tag, const packed_skeleton_oarchive& ar) const +{ + return this->isend(dest, tag, ar.get_skeleton()); +} + +template<> +request communicator::isend<content>(int dest, int tag, const content& c) const +{ + request req; + BOOST_MPI_CHECK_RESULT(MPI_Isend, + (MPI_BOTTOM, 1, c.get_mpi_datatype(), + dest, tag, MPI_Comm(*this), &req.m_requests[0])); + return req; +} + +request communicator::isend(int dest, int tag) const +{ + request req; + BOOST_MPI_CHECK_RESULT(MPI_Isend, + (MPI_BOTTOM, 0, MPI_PACKED, + dest, tag, MPI_Comm(*this), &req.m_requests[0])); + return req; +} + +template<> +request +communicator::irecv<packed_skeleton_iarchive> + (int source, int tag, packed_skeleton_iarchive& ar) const +{ + return this->irecv(source, tag, ar.get_skeleton()); +} + +template<> +request +communicator::irecv<const content>(int source, int tag, + const content& c) const +{ + request req; + BOOST_MPI_CHECK_RESULT(MPI_Irecv, + (MPI_BOTTOM, 1, c.get_mpi_datatype(), + source, tag, MPI_Comm(*this), &req.m_requests[0])); + return req; +} + +request communicator::irecv(int source, int tag) const +{ + request req; + BOOST_MPI_CHECK_RESULT(MPI_Irecv, + (MPI_BOTTOM, 0, MPI_PACKED, + source, tag, MPI_Comm(*this), &req.m_requests[0])); + return req; +} + +bool operator==(const communicator& comm1, const communicator& comm2) +{ + int result; + BOOST_MPI_CHECK_RESULT(MPI_Comm_compare, + ((MPI_Comm)comm1, (MPI_Comm)comm2, &result)); + return result == MPI_IDENT; +} + +} } // end namespace boost::mpi diff --git a/libs/mpi/src/computation_tree.cpp b/libs/mpi/src/computation_tree.cpp new file mode 100644 index 000000000..60de534d1 --- /dev/null +++ b/libs/mpi/src/computation_tree.cpp @@ -0,0 +1,72 @@ +// Copyright (C) 2005 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) + +// Compute parents, children, levels, etc. to effect a parallel +// computation tree. + +#include <boost/mpi/detail/computation_tree.hpp> + +namespace boost { namespace mpi { namespace detail { + +int computation_tree::default_branching_factor = 3; + +computation_tree +::computation_tree(int rank, int size, int root, int branching_factor) + : rank(rank), size(size), root(root), + branching_factor_(branching_factor > 1? branching_factor + /* default */: default_branching_factor), + level_(0) +{ + // The position in the tree, once we've adjusted for non-zero + // roots. + int n = (rank + size - root) % size; + int sum = 0; + int term = 1; + + /* The level is the smallest value of k such that + + f^0 + f^1 + ... + f^k > n + + for branching factor f and index n in the tree. */ + while (sum <= n) { + ++level_; + term *= branching_factor_; + sum += term; + } +} + +int computation_tree::level_index(int n) const +{ + int sum = 0; + int term = 1; + while (n--) { + sum += term; + term *= branching_factor_; + } + return sum; +} + +int computation_tree::parent() const +{ + if (rank == root) return rank; + int n = rank + size - 1 - root; + return ((n % size / branching_factor_) + root) % size ; +} + +int computation_tree::child_begin() const +{ + // Zero-based index of this node + int n = (rank + size - root) % size; + + // Compute the index of the child (in a zero-based tree) + int child_index = level_index(level_ + 1) + + branching_factor_ * (n - level_index(level_)); + + if (child_index >= size) return root; + else return (child_index + root) % size; +} + +} } } // end namespace boost::mpi::detail diff --git a/libs/mpi/src/content_oarchive.cpp b/libs/mpi/src/content_oarchive.cpp new file mode 100644 index 000000000..0ec1ece37 --- /dev/null +++ b/libs/mpi/src/content_oarchive.cpp @@ -0,0 +1,20 @@ +// (C) Copyright 2005 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 +// http://www.boost.org/LICENSE_1_0.txt) + +// Authors: Matthias Troyer + +#define BOOST_ARCHIVE_SOURCE +#include <boost/archive/detail/archive_serializer_map.hpp> +#include <boost/archive/impl/archive_serializer_map.ipp> +#include <boost/mpi/skeleton_and_content.hpp> + +namespace boost { namespace archive { namespace detail { +// explicitly instantiate all required template functions + +template class archive_serializer_map<mpi::detail::content_oarchive> ; +template class archive_serializer_map<boost::mpi::detail::ignore_skeleton_oarchive<boost::mpi::detail::content_oarchive> >; +template class archive_serializer_map<boost::mpi::detail::ignore_skeleton_oarchive<boost::mpi::detail::mpi_datatype_oarchive> >; +} } } diff --git a/libs/mpi/src/environment.cpp b/libs/mpi/src/environment.cpp new file mode 100644 index 000000000..4da844724 --- /dev/null +++ b/libs/mpi/src/environment.cpp @@ -0,0 +1,125 @@ +// Copyright (C) 2005-2006 Douglas Gregor <doug.gregor@gmail.com> + +// 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) + +// Message Passing Interface 1.1 -- 7.1.1. Environmental Inquiries +#include <boost/mpi/environment.hpp> +#include <boost/mpi/exception.hpp> +#include <boost/mpi/detail/mpi_datatype_cache.hpp> +#include <cassert> +#include <exception> +#include <stdexcept> + +namespace boost { namespace mpi { + +#ifdef BOOST_MPI_HAS_NOARG_INITIALIZATION +environment::environment(bool abort_on_exception) + : i_initialized(false), + abort_on_exception(abort_on_exception) +{ + if (!initialized()) { + BOOST_MPI_CHECK_RESULT(MPI_Init, (0, 0)); + i_initialized = true; + } + + MPI_Errhandler_set(MPI_COMM_WORLD, MPI_ERRORS_RETURN); +} +#endif + +environment::environment(int& argc, char** &argv, bool abort_on_exception) + : i_initialized(false), + abort_on_exception(abort_on_exception) +{ + if (!initialized()) { + BOOST_MPI_CHECK_RESULT(MPI_Init, (&argc, &argv)); + i_initialized = true; + } + + MPI_Errhandler_set(MPI_COMM_WORLD, MPI_ERRORS_RETURN); +} + +environment::~environment() +{ + if (i_initialized) { + if (std::uncaught_exception() && abort_on_exception) { + abort(-1); + } else if (!finalized()) { + detail::mpi_datatype_cache().clear(); + BOOST_MPI_CHECK_RESULT(MPI_Finalize, ()); + } + } +} + +void environment::abort(int errcode) +{ + BOOST_MPI_CHECK_RESULT(MPI_Abort, (MPI_COMM_WORLD, errcode)); +} + +bool environment::initialized() +{ + int flag; + BOOST_MPI_CHECK_RESULT(MPI_Initialized, (&flag)); + return flag != 0; +} + +bool environment::finalized() +{ + int flag; + BOOST_MPI_CHECK_RESULT(MPI_Finalized, (&flag)); + return flag != 0; +} + +int environment::max_tag() +{ + int* max_tag_value; + int found = 0; + + BOOST_MPI_CHECK_RESULT(MPI_Attr_get, + (MPI_COMM_WORLD, MPI_TAG_UB, &max_tag_value, &found)); + assert(found != 0); + return *max_tag_value - num_reserved_tags; +} + +int environment::collectives_tag() +{ + return max_tag() + 1; +} + +optional<int> environment::host_rank() +{ + int* host; + int found = 0; + + BOOST_MPI_CHECK_RESULT(MPI_Attr_get, + (MPI_COMM_WORLD, MPI_HOST, &host, &found)); + if (!found || *host == MPI_PROC_NULL) + return optional<int>(); + else + return *host; +} + +optional<int> environment::io_rank() +{ + int* io; + int found = 0; + + BOOST_MPI_CHECK_RESULT(MPI_Attr_get, + (MPI_COMM_WORLD, MPI_IO, &io, &found)); + if (!found || *io == MPI_PROC_NULL) + return optional<int>(); + else + return *io; +} + +std::string environment::processor_name() +{ + char name[MPI_MAX_PROCESSOR_NAME]; + int len; + + BOOST_MPI_CHECK_RESULT(MPI_Get_processor_name, (name, &len)); + return std::string(name, len); +} + +} } // end namespace boost::mpi diff --git a/libs/mpi/src/exception.cpp b/libs/mpi/src/exception.cpp new file mode 100644 index 000000000..9cb4c25e7 --- /dev/null +++ b/libs/mpi/src/exception.cpp @@ -0,0 +1,29 @@ +// Copyright (C) 2007 Trustees of Indiana University + +// Authors: Douglas Gregor +// Andrew Lumsdaine + +// 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/mpi/exception.hpp> + +namespace boost { namespace mpi { + +exception::exception(const char* routine, int result_code) + : routine_(routine), result_code_(result_code) +{ + // Query the MPI implementation for its reason for failure + char buffer[MPI_MAX_ERROR_STRING]; + int len; + MPI_Error_string(result_code, buffer, &len); + + // Construct the complete error message + message.append(routine_); + message.append(": "); + message.append(buffer, len); +} + +exception::~exception() throw() { } + +} } // end namespace boost::mpi diff --git a/libs/mpi/src/graph_communicator.cpp b/libs/mpi/src/graph_communicator.cpp new file mode 100644 index 000000000..586b57bf9 --- /dev/null +++ b/libs/mpi/src/graph_communicator.cpp @@ -0,0 +1,73 @@ +// Copyright (C) 2007 Trustees of Indiana University + +// Authors: Douglas Gregor +// Andrew Lumsdaine + +// 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/mpi/graph_communicator.hpp> + +namespace boost { namespace mpi { + +// Incidence Graph requirements +std::pair<detail::comm_out_edge_iterator, detail::comm_out_edge_iterator> +out_edges(int vertex, const graph_communicator& comm) +{ + int nneighbors = out_degree(vertex, comm); + shared_array<int> neighbors(new int[nneighbors]); + BOOST_MPI_CHECK_RESULT(MPI_Graph_neighbors, + ((MPI_Comm)comm, vertex, nneighbors, neighbors.get())); + return std::make_pair(detail::comm_out_edge_iterator(vertex, neighbors, 0), + detail::comm_out_edge_iterator(vertex, neighbors, + nneighbors)); +} + +int out_degree(int vertex, const graph_communicator& comm) +{ + int nneighbors; + BOOST_MPI_CHECK_RESULT(MPI_Graph_neighbors_count, + ((MPI_Comm)comm, vertex, &nneighbors)); + return nneighbors; +} + +// Adjacency Graph requirements +std::pair<detail::comm_adj_iterator, detail::comm_adj_iterator> +adjacent_vertices(int vertex, const graph_communicator& comm) +{ + int nneighbors = out_degree(vertex, comm); + shared_array<int> neighbors(new int[nneighbors]); + BOOST_MPI_CHECK_RESULT(MPI_Graph_neighbors, + ((MPI_Comm)comm, vertex, nneighbors, neighbors.get())); + return std::make_pair(detail::comm_adj_iterator(neighbors, 0), + detail::comm_adj_iterator(neighbors, nneighbors)); +} + +// Edge List Graph requirements +std::pair<detail::comm_edge_iterator, detail::comm_edge_iterator> +edges(const graph_communicator& comm); + +std::pair<detail::comm_edge_iterator, detail::comm_edge_iterator> +edges(const graph_communicator& comm) +{ + int nnodes, nedges; + BOOST_MPI_CHECK_RESULT(MPI_Graphdims_get, ((MPI_Comm)comm, &nnodes, &nedges)); + + shared_array<int> indices(new int[nnodes]); + shared_array<int> edges(new int[nedges]); + BOOST_MPI_CHECK_RESULT(MPI_Graph_get, + ((MPI_Comm)comm, nnodes, nedges, + indices.get(), edges.get())); + return std::make_pair(detail::comm_edge_iterator(indices, edges), + detail::comm_edge_iterator(nedges)); +} + + +int num_edges(const graph_communicator& comm) +{ + int nnodes, nedges; + BOOST_MPI_CHECK_RESULT(MPI_Graphdims_get, ((MPI_Comm)comm, &nnodes, &nedges)); + return nedges; +} + +} } // end namespace boost::mpi diff --git a/libs/mpi/src/group.cpp b/libs/mpi/src/group.cpp new file mode 100644 index 000000000..034d08f78 --- /dev/null +++ b/libs/mpi/src/group.cpp @@ -0,0 +1,106 @@ +// Copyright (C) 2007 Trustees of Indiana University + +// Authors: Douglas Gregor +// Andrew Lumsdaine + +// 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/mpi/group.hpp> +#include <boost/mpi/communicator.hpp> + +namespace boost { namespace mpi { + +group::group(const MPI_Group& in_group, bool adopt) +{ + if (in_group != MPI_GROUP_EMPTY) { + if (adopt) group_ptr.reset(new MPI_Group(in_group), group_free()); + else group_ptr.reset(new MPI_Group(in_group)); + } +} + +optional<int> group::rank() const +{ + if (!group_ptr) + return optional<int>(); + + int rank; + BOOST_MPI_CHECK_RESULT(MPI_Group_rank, (*group_ptr, &rank)); + if (rank == MPI_UNDEFINED) + return optional<int>(); + else + return rank; +} + +int group::size() const +{ + if (!group_ptr) + return 0; + + int size; + BOOST_MPI_CHECK_RESULT(MPI_Group_size, (*group_ptr, &size)); + return size; +} + +bool operator==(const group& g1, const group& g2) +{ + int result; + BOOST_MPI_CHECK_RESULT(MPI_Group_compare, + ((MPI_Group)g1, (MPI_Group)g2, &result)); + return result == MPI_IDENT; +} + +group operator|(const group& g1, const group& g2) +{ + MPI_Group result; + BOOST_MPI_CHECK_RESULT(MPI_Group_union, + ((MPI_Group)g1, (MPI_Group)g2, &result)); + return group(result, /*adopt=*/true); +} + +group operator&(const group& g1, const group& g2) +{ + MPI_Group result; + BOOST_MPI_CHECK_RESULT(MPI_Group_intersection, + ((MPI_Group)g1, (MPI_Group)g2, &result)); + return group(result, /*adopt=*/true); +} + +group operator-(const group& g1, const group& g2) +{ + MPI_Group result; + BOOST_MPI_CHECK_RESULT(MPI_Group_difference, + ((MPI_Group)g1, (MPI_Group)g2, &result)); + return group(result, /*adopt=*/true); +} + +template<> +int* +group::translate_ranks(int* first, int* last, const group& to_group, int* out) +{ + BOOST_MPI_CHECK_RESULT(MPI_Group_translate_ranks, + ((MPI_Group)*this, + last-first, + first, + (MPI_Group)to_group, + out)); + return out + (last - first); +} + +template<> group group::include(int* first, int* last) +{ + MPI_Group result; + BOOST_MPI_CHECK_RESULT(MPI_Group_incl, + ((MPI_Group)*this, last - first, first, &result)); + return group(result, /*adopt=*/true); +} + +template<> group group::exclude(int* first, int* last) +{ + MPI_Group result; + BOOST_MPI_CHECK_RESULT(MPI_Group_excl, + ((MPI_Group)*this, last - first, first, &result)); + return group(result, /*adopt=*/true); +} + +} } // end namespace boost::mpi diff --git a/libs/mpi/src/intercommunicator.cpp b/libs/mpi/src/intercommunicator.cpp new file mode 100644 index 000000000..6b072853c --- /dev/null +++ b/libs/mpi/src/intercommunicator.cpp @@ -0,0 +1,54 @@ +// Copyright (C) 2007 Trustees of Indiana University + +// Authors: Douglas Gregor +// Andrew Lumsdaine + +// 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/mpi/intercommunicator.hpp> +#include <boost/mpi/environment.hpp> +#include <boost/mpi/group.hpp> + +namespace boost { namespace mpi { + +intercommunicator::intercommunicator(const communicator& local, + int local_leader, + const communicator& peer, + int remote_leader) +{ + MPI_Comm comm; + BOOST_MPI_CHECK_RESULT(MPI_Intercomm_create, + ((MPI_Comm)local, local_leader, + (MPI_Comm)peer, remote_leader, + environment::collectives_tag(), &comm)); + comm_ptr.reset(new MPI_Comm(comm), comm_free()); +} + +boost::mpi::group intercommunicator::local_group() const +{ + return this->group(); +} + +int intercommunicator::remote_size() const +{ + int size; + BOOST_MPI_CHECK_RESULT(MPI_Comm_remote_size, ((MPI_Comm)*this, &size)); + return size; +} + +boost::mpi::group intercommunicator::remote_group() const +{ + MPI_Group gr; + BOOST_MPI_CHECK_RESULT(MPI_Comm_remote_group, ((MPI_Comm)*this, &gr)); + return boost::mpi::group(gr, /*adopt=*/true); +} + +communicator intercommunicator::merge(bool high) const +{ + MPI_Comm comm; + BOOST_MPI_CHECK_RESULT(MPI_Intercomm_merge, ((MPI_Comm)*this, high, &comm)); + return communicator(comm, comm_take_ownership); +} + +} } // end namespace boost::mpi diff --git a/libs/mpi/src/mpi_datatype_cache.cpp b/libs/mpi/src/mpi_datatype_cache.cpp new file mode 100644 index 000000000..8437e9d02 --- /dev/null +++ b/libs/mpi/src/mpi_datatype_cache.cpp @@ -0,0 +1,66 @@ +// (C) Copyright 2005 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 +// http://www.boost.org/LICENSE_1_0.txt) + +// Authors: Matthias Troyer + +#include <boost/archive/detail/archive_serializer_map.hpp> +#include <boost/mpi/detail/mpi_datatype_cache.hpp> +#include <map> + +namespace boost { namespace mpi { namespace detail { + + typedef std::map<std::type_info const*,MPI_Datatype,type_info_compare> + stored_map_type; + + struct mpi_datatype_map::implementation + { + stored_map_type map; + }; + + mpi_datatype_map::mpi_datatype_map() + { + impl = new implementation(); + } + + void mpi_datatype_map::clear() + { + // do not free after call to MPI_FInalize + int finalized=0; + BOOST_MPI_CHECK_RESULT(MPI_Finalized,(&finalized)); + if (!finalized) { + // ignore errors in the destructor + for (stored_map_type::iterator it=impl->map.begin(); it != impl->map.end(); ++it) + MPI_Type_free(&(it->second)); + } + } + + + mpi_datatype_map::~mpi_datatype_map() + { + clear(); + delete impl; + } + + MPI_Datatype mpi_datatype_map::get(const std::type_info* t) + { + stored_map_type::iterator pos = impl->map.find(t); + if (pos != impl->map.end()) + return pos->second; + else + return MPI_DATATYPE_NULL; + } + + void mpi_datatype_map::set(const std::type_info* t, MPI_Datatype datatype) + { + impl->map[t] = datatype; + } + + mpi_datatype_map& mpi_datatype_cache() + { + static mpi_datatype_map cache; + return cache; + } +} } } diff --git a/libs/mpi/src/mpi_datatype_oarchive.cpp b/libs/mpi/src/mpi_datatype_oarchive.cpp new file mode 100644 index 000000000..05d58427c --- /dev/null +++ b/libs/mpi/src/mpi_datatype_oarchive.cpp @@ -0,0 +1,19 @@ +// (C) Copyright 2005 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 +// http://www.boost.org/LICENSE_1_0.txt) + +// Authors: Matthias Troyer + +#define BOOST_ARCHIVE_SOURCE +#include <boost/archive/detail/archive_serializer_map.hpp> +#include <boost/archive/impl/archive_serializer_map.ipp> +#include <boost/mpi/detail/mpi_datatype_oarchive.hpp> + +namespace boost { namespace archive { namespace detail { +// explicitly instantiate all required template functions + +template class archive_serializer_map<mpi::detail::mpi_datatype_oarchive> ; + +} } } diff --git a/libs/mpi/src/packed_iarchive.cpp b/libs/mpi/src/packed_iarchive.cpp new file mode 100644 index 000000000..a0ea5a6b5 --- /dev/null +++ b/libs/mpi/src/packed_iarchive.cpp @@ -0,0 +1,21 @@ +// (C) Copyright 2005 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 +// http://www.boost.org/LICENSE_1_0.txt) + +// Authors: Matthias Troyer + +#define BOOST_ARCHIVE_SOURCE +#include <boost/mpi/packed_iarchive.hpp> + +#include <boost/archive/detail/archive_serializer_map.hpp> +#include <boost/archive/impl/archive_serializer_map.ipp> + +namespace boost { namespace archive { + +// explicitly instantiate all required templates + +template class detail::archive_serializer_map<mpi::packed_iarchive> ; + +} } // end namespace boost::archive diff --git a/libs/mpi/src/packed_oarchive.cpp b/libs/mpi/src/packed_oarchive.cpp new file mode 100644 index 000000000..d340a40f9 --- /dev/null +++ b/libs/mpi/src/packed_oarchive.cpp @@ -0,0 +1,19 @@ +// (C) Copyright 2005 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 +// http://www.boost.org/LICENSE_1_0.txt) + +// Authors: Matthias Troyer + +#define BOOST_ARCHIVE_SOURCE +#include <boost/mpi/packed_oarchive.hpp> +#include <boost/archive/detail/archive_serializer_map.hpp> +#include <boost/archive/impl/archive_serializer_map.ipp> + +namespace boost { namespace archive { +// explicitly instantiate all required templates + +template class detail::archive_serializer_map<mpi::packed_oarchive> ; + +} } // end namespace boost::archive diff --git a/libs/mpi/src/packed_skeleton_iarchive.cpp b/libs/mpi/src/packed_skeleton_iarchive.cpp new file mode 100644 index 000000000..97115c20c --- /dev/null +++ b/libs/mpi/src/packed_skeleton_iarchive.cpp @@ -0,0 +1,25 @@ +// (C) Copyright 2005 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 +// http://www.boost.org/LICENSE_1_0.txt) + +// Authors: Matthias Troyer + +#define BOOST_ARCHIVE_SOURCE + +#include <boost/archive/detail/archive_serializer_map.hpp> +#include <boost/archive/impl/archive_serializer_map.ipp> +#include <boost/mpi/skeleton_and_content.hpp> + +namespace boost { namespace archive { + +// explicitly instantiate all required templates + +// template class basic_binary_iarchive<mpi::packed_skeleton_iarchive> ; +template class detail::archive_serializer_map<mpi::packed_skeleton_iarchive> ; +template class detail::archive_serializer_map< + mpi::detail::forward_skeleton_iarchive< + boost::mpi::packed_skeleton_iarchive, boost::mpi::packed_iarchive> > ; + +} } // end namespace boost::archive diff --git a/libs/mpi/src/packed_skeleton_oarchive.cpp b/libs/mpi/src/packed_skeleton_oarchive.cpp new file mode 100644 index 000000000..5cfecc077 --- /dev/null +++ b/libs/mpi/src/packed_skeleton_oarchive.cpp @@ -0,0 +1,22 @@ +// (C) Copyright 2005 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 +// http://www.boost.org/LICENSE_1_0.txt) + +// Authors: Matthias Troyer + +#define BOOST_ARCHIVE_SOURCE +#include <boost/mpi/skeleton_and_content.hpp> +#include <boost/archive/detail/archive_serializer_map.hpp> +#include <boost/archive/impl/archive_serializer_map.ipp> + +namespace boost { namespace archive { +// explicitly instantiate all required templates + +template class detail::archive_serializer_map<mpi::packed_skeleton_oarchive> ; +template class detail::archive_serializer_map< + mpi::detail::forward_skeleton_oarchive< + boost::mpi::packed_skeleton_oarchive, boost::mpi::packed_oarchive> > ; + +} } // end namespace boost::archive diff --git a/libs/mpi/src/point_to_point.cpp b/libs/mpi/src/point_to_point.cpp new file mode 100644 index 000000000..dd0d9bcf0 --- /dev/null +++ b/libs/mpi/src/point_to_point.cpp @@ -0,0 +1,98 @@ +// Copyright 2005 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) + +// Message Passing Interface 1.1 -- Section 3. MPI Point-to-point + +/* There is the potential for optimization here. We could keep around + a "small message" buffer of size N that we just receive into by + default. If the message is N - sizeof(int) bytes or smaller, it can + just be sent with that buffer. If it's larger, we send the first N + - sizeof(int) bytes in the first packet followed by another + packet. The size of the second packet will be stored in an integer + at the end of the first packet. + + We will introduce this optimization later, when we have more + performance test cases and have met our functionality goals. */ + +#include <boost/mpi/detail/point_to_point.hpp> +#include <boost/mpi/datatype.hpp> +#include <boost/mpi/exception.hpp> +#include <cassert> + +namespace boost { namespace mpi { namespace detail { + +void +packed_archive_send(MPI_Comm comm, int dest, int tag, + const packed_oarchive& ar) +{ + const void* size = &ar.size(); + BOOST_MPI_CHECK_RESULT(MPI_Send, + (const_cast<void*>(size), 1, + get_mpi_datatype<std::size_t>(ar.size()), + dest, tag, comm)); + BOOST_MPI_CHECK_RESULT(MPI_Send, + (const_cast<void*>(ar.address()), ar.size(), + MPI_PACKED, + dest, tag, comm)); +} + +int +packed_archive_isend(MPI_Comm comm, int dest, int tag, + const packed_oarchive& ar, + MPI_Request* out_requests, int num_out_requests) +{ + assert(num_out_requests >= 2); + const void* size = &ar.size(); + BOOST_MPI_CHECK_RESULT(MPI_Isend, + (const_cast<void*>(size), 1, + get_mpi_datatype<std::size_t>(ar.size()), + dest, tag, comm, out_requests)); + BOOST_MPI_CHECK_RESULT(MPI_Isend, + (const_cast<void*>(ar.address()), ar.size(), + MPI_PACKED, + dest, tag, comm, out_requests + 1)); + + return 2; +} + +int +packed_archive_isend(MPI_Comm comm, int dest, int tag, + const packed_iarchive& ar, + MPI_Request* out_requests, int num_out_requests) +{ + assert(num_out_requests >= 2); + + const void* size = &ar.size(); + BOOST_MPI_CHECK_RESULT(MPI_Isend, + (const_cast<void*>(size), 1, + get_mpi_datatype<std::size_t>(ar.size()), + dest, tag, comm, out_requests)); + BOOST_MPI_CHECK_RESULT(MPI_Isend, + (const_cast<void*>(ar.address()), ar.size(), + MPI_PACKED, + dest, tag, comm, out_requests + 1)); + + return 2; +} + +void +packed_archive_recv(MPI_Comm comm, int source, int tag, packed_iarchive& ar, + MPI_Status& status) +{ + std::size_t count; + BOOST_MPI_CHECK_RESULT(MPI_Recv, + (&count, 1, get_mpi_datatype<std::size_t>(count), + source, tag, comm, &status)); + + // Prepare input buffer and receive the message + ar.resize(count); + BOOST_MPI_CHECK_RESULT(MPI_Recv, + (ar.address(), ar.size(), MPI_PACKED, + status.MPI_SOURCE, status.MPI_TAG, + comm, &status)); +} + +} } } // end namespace boost::mpi::detail diff --git a/libs/mpi/src/python/collectives.cpp b/libs/mpi/src/python/collectives.cpp new file mode 100644 index 000000000..fc4bf7b4c --- /dev/null +++ b/libs/mpi/src/python/collectives.cpp @@ -0,0 +1,144 @@ +// (C) Copyright 2006 Douglas Gregor <doug.gregor -at- gmail.com> + +// 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 + +/** @file status.cpp + * + * This file reflects the Boost.MPI @c status class into + * Python. + */ +#include <boost/python.hpp> +#include <boost/mpi.hpp> +#include <boost/mpi/python/serialize.hpp> + +using namespace boost::python; +using namespace boost::mpi; + +namespace boost { namespace mpi { namespace python { + +extern const char* all_gather_docstring; +extern const char* all_reduce_docstring; +extern const char* all_to_all_docstring; +extern const char* broadcast_docstring; +extern const char* gather_docstring; +extern const char* reduce_docstring; +extern const char* scan_docstring; +extern const char* scatter_docstring; + +object all_gather(const communicator& comm, object value) +{ + std::vector<object> values; + boost::mpi::all_gather(comm, value, values); + + boost::python::list l; + for (int i = 0; i < comm.size(); ++i) + l.append(values[i]); + return boost::python::tuple(l); +} + +object all_to_all(const communicator& comm, object in_values) +{ + // Build input values + std::vector<object> in_values_vec(comm.size()); + object iterator = object(handle<>(PyObject_GetIter(in_values.ptr()))); + for (int i = 0; i < comm.size(); ++i) + in_values_vec[i] = object(handle<>(PyIter_Next(iterator.ptr()))); + + std::vector<object> out_values_vec(comm.size()); + boost::mpi::all_to_all(comm, in_values_vec, out_values_vec); + + boost::python::list l; + for (int i = 0; i < comm.size(); ++i) + l.append(out_values_vec[i]); + return boost::python::tuple(l); +} + +object broadcast(const communicator& comm, object value, int root) +{ + boost::mpi::broadcast(comm, value, root); + return value; +} + +object gather(const communicator& comm, object value, int root) +{ + if (comm.rank() == root) { + std::vector<object> values; + boost::mpi::gather(comm, value, values, root); + + boost::python::list l; + for (int i = 0; i < comm.size(); ++i) + l.append(values[i]); + return boost::python::tuple(l); + } else { + boost::mpi::gather(comm, value, root); + return object(); + } +} + +object reduce(const communicator& comm, object value, object op, int root) +{ + if (comm.rank() == root) { + object out_value; + boost::mpi::reduce(comm, value, out_value, op, root); + return out_value; + } else { + boost::mpi::reduce(comm, value, op, root); + return object(); + } +} + +object scatter(const communicator& comm, object values, int root) +{ + object result; + + if (comm.rank() == root) { + std::vector<object> values_vec(comm.size()); + object iterator = object(handle<>(PyObject_GetIter(values.ptr()))); + for (int i = 0; i < comm.size(); ++i) + values_vec[i] = object(handle<>(PyIter_Next(iterator.ptr()))); + + boost::mpi::scatter(comm, values_vec, result, root); + } else { + boost::mpi::scatter(comm, result, root); + } + return result; +} + +void export_collectives() +{ + using boost::python::arg; + + def("all_reduce", + (object (*)(const communicator&, const object&, object))&all_reduce, + (arg("comm") = communicator(), arg("value"), arg("op")), + all_reduce_docstring); + def("all_gather", &all_gather, + (arg("comm") = communicator(), arg("value") = object()), + all_gather_docstring); + def("all_to_all", &all_to_all, + (arg("comm") = communicator(), arg("values") = object()), + all_to_all_docstring); + def("broadcast", &broadcast, + (arg("comm") = communicator(), arg("value") = object(), arg("root")), + broadcast_docstring); + def("gather", &gather, + (arg("comm") = communicator(), arg("value") = object(), arg("root")), + gather_docstring); + def("reduce", &reduce, + (arg("comm") = communicator(), arg("value"), arg("op"), + arg("root")), + reduce_docstring); + def("scan", + (object (*)(const communicator&, const object&, object))&scan, + (arg("comm") = communicator(), arg("value"), arg("op")), + scan_docstring); + def("scatter", &scatter, + (arg("comm") = communicator(), arg("values") = object(), arg("root")), + scatter_docstring); +} + +} } } // end namespace boost::mpi::python diff --git a/libs/mpi/src/python/datatypes.cpp b/libs/mpi/src/python/datatypes.cpp new file mode 100644 index 000000000..586fc0395 --- /dev/null +++ b/libs/mpi/src/python/datatypes.cpp @@ -0,0 +1,25 @@ +// (C) Copyright 2006 Douglas Gregor <doug.gregor -at- gmail.com> + +// 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 + +/** @file datatypes.cpp + * + * This file provides datatypes support for Boost.MPI in Python. + */ +#include <boost/mpi/python/serialize.hpp> +#include <boost/mpi.hpp> + +namespace boost { namespace mpi { namespace python { + +void export_datatypes() +{ + register_serialized(long(0), &PyInt_Type); + register_serialized(false, &PyBool_Type); + register_serialized(double(0.0), &PyFloat_Type); +} + +} } } // end namespace boost::mpi::python diff --git a/libs/mpi/src/python/documentation.cpp b/libs/mpi/src/python/documentation.cpp new file mode 100644 index 000000000..ef28fb3b5 --- /dev/null +++ b/libs/mpi/src/python/documentation.cpp @@ -0,0 +1,671 @@ +// (C) Copyright 2005 The Trustees of Indiana University. +// (C) Copyright 2006 Douglas Gregor <doug.gregor -at- gmail.com> + +// 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 + +/** @file documentation.cpp + * + * This file contains all of the documentation strings for the + * Boost.MPI Python bindings. + */ +namespace boost { namespace mpi { namespace python { + +const char* module_docstring = + "The boost.mpi module contains Python wrappers for Boost.MPI.\n" + "Boost.MPI is a C++ interface to the Message Passing Interface 1.1,\n" + "a high-performance message passing library for parallel programming.\n" + "\n" + "This module supports the most commonly used subset of MPI 1.1. All\n" + "communication operations can transmit any Python object that can be\n" + "pickled and unpickled, along with C++-serialized data types and\n" + "separation of the structure of a data type from its content.\n" + "Collectives that have a user-supplied functions,\n" + "such as reduce() or scan(), accept arbitrary Python functions, and\n" + "all collectives can operate on any serializable or picklable data type.\n" + "\n" + "IMPORTANT MODULE DATA\n" + " any_source This constant may be used for the source parameter of\n" + " receive and probe operations to indicate that a\n" + " message may be received from any source.\n" + "\n" + " any_tag This constant may be used for the tag parameter of\n" + " receive or probe operations to indicate that a send\n" + " with any tag will be matched.\n" + "\n" + " collectives_tag Returns the reserved tag value used by the Boost.MPI\n" + " implementation for collective operations. Although\n" + " users are not permitted to use this tag to send or\n" + " receive messages with this tag, it may be useful when\n" + " monitoring communication patterns.\n" + "\n" + " host_rank If there is a host process, this is the rank of that\n" + " that process. Otherwise, this value will be None. MPI\n" + " does not define the meaning of a \"host\" process: \n" + " consult the documentation for your MPI implementation.\n" + "\n" + " io_rank The rank of a process that can perform input/output\n" + " via the standard facilities. If every process can\n" + " perform I/O using the standard facilities, this value\n" + " will be the same as any_source. If no process can\n" + " perform I/O, this value will be None.\n" + "\n" + " max_tag The maximum value that may be used for the tag\n" + " parameter of send/receive operations. This value will\n" + " be somewhat smaller than the value of MPI_TAG_UB,\n" + " because the Boost.MPI implementation reserves some\n" + " tags for collective operations.\n" + "\n" + " processor_name The name of this processor. The actual form of the\n" + " of the name is unspecified, but may be documented by\n" + " the underlying MPI implementation.\n" + "\n" + " rank The rank of this process in the \"world\" communicator.\n" + "\n" + " size The number of processes in the \"world\" communicator.\n" + " that process. Otherwise, this value will be None. MPI\n" + " does not define the meaning of a \"host\" process: \n" + "\n" + " world The \"world\" communicator from which all other\n" + " communicators will be derived. This is the equivalent\n" + " of MPI_COMM_WORLD.\n" + "\n" + "TRANSMITTING USER-DEFINED DATA\n" + " Boost.MPI can transmit user-defined data in several different ways.\n" + " Most importantly, it can transmit arbitrary Python objects by pickling\n" + " them at the sender and unpickling them at the receiver, allowing\n" + " arbitrarily complex Python data structures to interoperate with MPI.\n" + "\n" + " Boost.MPI also supports efficient serialization and transmission of\n" + " C++ objects (that have been exposed to Python) through its C++\n" + " interface. Any C++ type that provides (de-)serialization routines that\n" + " meet the requirements of the Boost.Serialization library is eligible\n" + " for this optimization, but the type must be registered in advance. To\n" + " register a C++ type, invoke the C++ function:\n" + " boost::mpi::python::register_serialized\n" + "\n" + " Finally, Boost.MPI supports separation of the structure of an object\n" + " from the data it stores, allowing the two pieces to be transmitted\n" + " separately. This \"skeleton/content\" mechanism, described in more\n" + " detail in a later section, is a communication optimization suitable\n" + " for problems with fixed data structures whose internal data changes\n" + " frequently.\n" + "\n" + "COLLECTIVES\n" + " Boost.MPI supports all of the MPI collectives (scatter, reduce, scan,\n" + " broadcast, etc.) for any type of data that can be transmitted with the\n" + " point-to-point communication operations. For the MPI collectives that\n" + " require a user-specified operation (e.g., reduce and scan), the\n" + " operation can be an arbitrary Python function. For instance, one could\n" + " concatenate strings with all_reduce:\n\n" + " mpi.all_reduce(my_string, lambda x,y: x + y)\n\n" + " The following module-level functions implement MPI collectives:\n" + " all_gather Gather the values from all processes.\n" + " all_reduce Combine the results from all processes.\n" + " all_to_all Every process sends data to every other process.\n" + " broadcast Broadcast data from one process to all other processes.\n" + " gather Gather the values from all processes to the root.\n" + " reduce Combine the results from all processes to the root.\n" + " scan Prefix reduction of the values from all processes.\n" + " scatter Scatter the values stored at the root to all processes.\n" + "\n" + "SKELETON/CONTENT MECHANISM\n" + " Boost.MPI provides a skeleton/content mechanism that allows the\n" + " transfer of large data structures to be split into two separate stages,\n" + " with the `skeleton' (or, `shape') of the data structure sent first and\n" + " the content (or, `data') of the data structure sent later, potentially\n" + " several times, so long as the structure has not changed since the\n" + " skeleton was transferred. The skeleton/content mechanism can improve\n" + " performance when the data structure is large and its shape is fixed,\n" + " because while the skeleton requires serialization (it has an unknown\n" + " size), the content transfer is fixed-size and can be done without\n" + " extra copies.\n" + "\n" + " To use the skeleton/content mechanism from Python, you must first\n" + " register the type of your data structure with the skeleton/content\n" + " mechanism *from C++*. The registration function is\n" + " boost::mpi::python::register_skeleton_and_content\n" + " and resides in the <boost/mpi/python.hpp> header.\n" + "\n" + " Once you have registered your C++ data structures, you can extract\n" + " the skeleton for an instance of that data structure with skeleton().\n" + " The resulting SkeletonProxy can be transmitted via the normal send\n" + " routine, e.g.,\n\n" + " mpi.world.send(1, 0, skeleton(my_data_structure))\n\n" + " SkeletonProxy objects can be received on the other end via recv(),\n" + " which stores a newly-created instance of your data structure with the\n" + " same `shape' as the sender in its `object' attribute:\n\n" + " shape = mpi.world.recv(0, 0)\n" + " my_data_structure = shape.object\n\n" + " Once the skeleton has been transmitted, the content (accessed via \n" + " get_content) can be transmitted in much the same way. Note, however,\n" + " that the receiver also specifies get_content(my_data_structure) in its\n" + " call to receive:\n\n" + " if mpi.rank == 0:\n" + " mpi.world.send(1, 0, get_content(my_data_structure))\n" + " else:\n" + " mpi.world.recv(0, 0, get_content(my_data_structure))\n\n" + " Of course, this transmission of content can occur repeatedly, if the\n" + " values in the data structure--but not its shape--changes.\n" + "\n" + " The skeleton/content mechanism is a structured way to exploit the\n" + " interaction between custom-built MPI datatypes and MPI_BOTTOM, to\n" + " eliminate extra buffer copies.\n" + "\n" + "C++/PYTHON MPI COMPATIBILITY\n" + " Boost.MPI is a C++ library whose facilities have been exposed to Python\n" + " via the Boost.Python library. Since the Boost.MPI Python bindings are\n" + " build directly on top of the C++ library, and nearly every feature of\n" + " C++ library is available in Python, hybrid C++/Python programs using\n" + " Boost.MPI can interact, e.g., sending a value from Python but receiving\n" + " that value in C++ (or vice versa). However, doing so requires some\n" + " care. Because Python objects are dynamically typed, Boost.MPI transfers\n" + " type information along with the serialized form of the object, so that\n" + " the object can be received even when its type is not known. This\n" + " mechanism differs from its C++ counterpart, where the static types of\n" + " transmitted values are always known.\n" + "\n" + " The only way to communicate between the C++ and Python views on \n" + " Boost.MPI is to traffic entirely in Python objects. For Python, this is\n" + " the normal state of affairs, so nothing will change. For C++, this\n" + " means sending and receiving values of type boost::python::object, from\n" + " the Boost.Python library. For instance, say we want to transmit an\n" + " integer value from Python:\n\n" + " comm.send(1, 0, 17)\n\n" + " In C++, we would receive that value into a Python object and then\n" + " `extract' an integer value:\n\n" + " boost::python::object value;\n" + " comm.recv(0, 0, value);\n" + " int int_value = boost::python::extract<int>(value);\n\n" + " In the future, Boost.MPI will be extended to allow improved\n" + " interoperability with the C++ Boost.MPI and the C MPI bindings.\n" + ; + +/*********************************************************** + * environment documentation * + ***********************************************************/ +const char* environment_init_docstring = + "Initialize the MPI environment. Users should not need to call\n" + "this function directly, because the MPI environment will be\n" + "automatically initialized when the Boost.MPI module is loaded.\n"; + +const char* environment_finalize_docstring = + "Finalize (shut down) the MPI environment. Users only need to\n" + "invoke this function if MPI should be shut down before program\n" + "termination. Boost.MPI will automatically finalize the MPI\n" + "environment when the program exits.\n"; + +const char* environment_abort_docstring = + "Aborts all MPI processes and returns to the environment. The\n" + "precise behavior will be defined by the underlying MPI\n" + "implementation. This is equivalent to a call to MPI_Abort with\n" + "MPI_COMM_WORLD.\n" + "errcode is the error code to return from aborted processes.\n"; + +const char* environment_initialized_docstring = + "Determine if the MPI environment has already been initialized.\n"; + +const char* environment_finalized_docstring = + "Determine if the MPI environment has already been finalized.\n"; + +/*********************************************************** + * nonblocking documentation * + ***********************************************************/ +const char* request_list_init_docstring= + "Without arguments, constructs an empty RequestList.\n" + "With one argument `iterable', copies request objects from this\n" + "iterable to the new RequestList.\n"; + +const char* nonblocking_wait_any_docstring = + "Waits until any of the given requests has been completed. It provides\n" + "functionality equivalent to MPI_Waitany.\n" + "\n" + "requests must be a RequestList instance.\n" + "\n" + "Returns a triple (value, status, index) consisting of received value\n" + "(or None), the Status object for the completed request, and its index\n" + "in the RequestList.\n"; + +const char* nonblocking_test_any_docstring = + "Tests if any of the given requests have been completed, but does not wait\n" + "for completion. It provides functionality equivalent to MPI_Testany.\n" + "\n" + "requests must be a RequestList instance.\n" + "\n" + "Returns a triple (value, status, index) like wait_any or None if no request\n" + "is complete.\n"; + +const char* nonblocking_wait_all_docstring = + "Waits until all of the given requests have been completed. It provides\n" + "functionality equivalent to MPI_Waitall.\n" + "\n" + "requests must be a RequestList instance.\n" + "\n" + "If the second parameter `callable' is provided, it is called with each\n" + "completed request's received value (or None) and it s Status object as\n" + "its arguments. The calls occur in the order given by the `requests' list.\n"; + +const char* nonblocking_test_all_docstring = + "Tests if all of the given requests have been completed. It provides\n" + "functionality equivalent to MPI_Testall.\n" + "\n" + "Returns True if all requests have been completed.\n" + "\n" + "requests must be a RequestList instance.\n" + "\n" + "If the second parameter `callable' is provided, it is called with each\n" + "completed request's received value (or None) and it s Status object as\n" + "its arguments. The calls occur in the order given by the `requests' list.\n"; + +const char* nonblocking_wait_some_docstring = + "Waits until at least one of the given requests has completed. It\n" + "then completes all of the requests it can, partitioning the input\n" + "sequence into pending requests followed by completed requests.\n" + "\n" + "This routine provides functionality equivalent to MPI_Waitsome.\n" + "\n" + "Returns the index of the first completed request." + "\n" + "requests must be a RequestList instance.\n" + "\n" + "If the second parameter `callable' is provided, it is called with each\n" + "completed request's received value (or None) and it s Status object as\n" + "its arguments. The calls occur in the order given by the `requests' list.\n"; + +const char* nonblocking_test_some_docstring = + "Tests to see if any of the given requests has completed. It completes\n" + "all of the requests it can, partitioning the input sequence into pending\n" + "requests followed by completed requests. This routine is similar to\n" + "wait_some, but does not wait until any requests have completed.\n" + "\n" + "This routine provides functionality equivalent to MPI_Testsome.\n" + "\n" + "Returns the index of the first completed request." + "\n" + "requests must be a RequestList instance.\n" + "\n" + "If the second parameter `callable' is provided, it is called with each\n" + "completed request's received value (or None) and it s Status object as\n" + "its arguments. The calls occur in the order given by the `requests' list.\n"; + +/*********************************************************** + * exception documentation * + ***********************************************************/ +const char* exception_docstring = + "Instances of this class will be thrown when an MPI error\n" + "occurs. MPI failures that trigger these exceptions may or may not\n" + "be recoverable, depending on the underlying MPI implementation.\n" + "Consult the documentation for your MPI implementation to determine\n" + "the effect of MPI errors.\n"; + +const char* exception_what_docstring = + "A description of the error that occured. At present, this refers\n" + "only to the name of the MPI routine that failed.\n"; + +const char* exception_routine_docstring = + "The name of the MPI routine that reported the error.\n"; + +const char* exception_result_code_docstring = + "The result code returned from the MPI routine that reported the\n" + "error.\n"; + +/*********************************************************** + * collectives documentation * + ***********************************************************/ +const char* all_gather_docstring = + "all_gather is a collective algorithm that collects the values\n" + "stored at each process into a tuple of values indexed by the\n" + "process number they came from. all_gather is (semantically) a\n" + "gather followed by a broadcast. The same tuple of values is\n" + "returned to all processes.\n"; + +const char* all_reduce_docstring = + "all_reduce is a collective algorithm that combines the values\n" + "stored by each process into a single value. The values can be\n" + "combined arbitrarily, specified via any function. The values\n" + "a1, a2, .., ap provided by p processors will be combined by the\n" + "binary function op into the result\n" + " op(a1, op(a2, ... op(ap-1,ap)))\n" + "that will be returned to all processes. This function is the\n" + "equivalent of calling all_gather() and then applying the built-in\n" + "reduce() function to the returned sequence. op is assumed to be\n" + "associative.\n"; + +const char* all_to_all_docstring = + "all_to_all is a collective algorithm that transmits values from\n" + "every process to every other process. On process i, the jth value\n" + "of the values sequence is sent to process j and placed in the ith\n" + "position of the tuple that will be returned from all_to_all.\n"; + +const char* broadcast_docstring = + "broadcast is a collective algorithm that transfers a value from an\n" + "arbitrary root process to every other process that is part of the\n" + "given communicator (comm). The root parameter must be the same for\n" + "every process. The value parameter need only be specified at the root\n" + "root. broadcast() returns the same broadcasted value to every process.\n"; + +const char* gather_docstring = + "gather is a collective algorithm that collects the values\n" + "stored at each process into a tuple of values at the root\n" + "process. This tuple is indexed by the process number that the\n" + "value came from, and will be returned only by the root process.\n" + "All other processes return None.\n"; + +const char* reduce_docstring = + "reduce is a collective algorithm that combines the values\n" + "stored by each process into a single value at the root. The\n" + "values can be combined arbitrarily, specified via any function.\n" + "The values a1, a2, .., ap provided by p processors will be\n" + "combined by the binary function op into the result\n" + " op(a1, op(a2, ... op(ap-1,ap)))\n" + "that will be returned on the root process. This function is the\n" + "equivalent of calling gather() to the root and then applying the\n" + "built-in reduce() function to the returned sequence. All non-root\n" + "processes return None. op is assumed to be associative.\n"; + +const char* scan_docstring = + "@c scan computes a prefix reduction of values from all processes.\n" + "It is a collective algorithm that combines the values stored by\n" + "each process with the values of all processes with a smaller rank.\n" + "The values can be arbitrarily combined, specified via a binary\n" + "function op. If each process i provides the value ai, then scan\n" + "returns op(a1, op(a2, ... op(ai-1, ai))) to the ith process. op is\n" + "assumed to be associative. This routine is the equivalent of an\n" + "all_gather(), followed by a built-in reduce() on the first i+1\n" + "values in the resulting sequence on processor i. op is assumed\n" + "to be associative.\n"; + +const char* scatter_docstring = + "scatter is a collective algorithm that scatters the values stored\n" + "in the root process (as a container with comm.size elements) to\n" + "all of the processes in the communicator. The values parameter \n" + "(only significant at the root) is indexed by the process number to\n" + "which the corresponding value will be sent. The value received by \n" + "each process is returned from scatter.\n"; + +/*********************************************************** + * communicator documentation * + ***********************************************************/ +const char* communicator_docstring = + "The Communicator class abstracts a set of communicating\n" + "processes in MPI. All of the processes that belong to a certain\n" + "communicator can determine the size of the communicator, their rank\n" + "within the communicator, and communicate with any other processes\n" + "in the communicator.\n"; + +const char* communicator_default_constructor_docstring = + "Build a new Boost.MPI Communicator instance for MPI_COMM_WORLD.\n"; + +const char* communicator_rank_docstring = + "Returns the rank of the process in the communicator, which will be a\n" + "value in [0, size).\n"; + +const char* communicator_size_docstring = + "Returns the number of processes in the communicator.\n"; + +const char* communicator_send_docstring = + "This routine executes a potentially blocking send with the given\n" + "tag to the process with rank dest. It can be received by the\n" + "destination process with a matching recv call. The value will be\n" + "transmitted in one of several ways:\n" + "\n" + " - For C++ objects registered via register_serialized(), the value\n" + " will be serialized and transmitted.\n" + "\n" + " - For SkeletonProxy objects, the skeleton of the object will be\n" + " serialized and transmitted.\n" + "\n" + " - For Content objects, the content will be transmitted directly.\n" + " This content can be received by a matching recv/irecv call that\n" + " provides a suitable `buffer' argument.\n" + "\n" + " - For all other Python objects, the value will be pickled and\n" + " transmitted.\n"; + +const char* communicator_recv_docstring = + "This routine blocks until it receives a message from the process\n" + "source with the given tag. If the source parameter is not specified,\n" + "the message can be received from any process. Likewise, if the tag\n" + "parameter is not specified, a message with any tag can be received.\n" + "If return_status is True, returns a tuple containing the received\n" + "object followed by a Status object describing the communication.\n" + "Otherwise, recv() returns just the received object.\n" + "\n" + "When receiving the content of a data type that has been sent separately\n" + "from its skeleton, user code must provide a value for the `buffer'\n" + "argument. This value should be the Content object returned from\n" + "get_content().\n"; + +const char* communicator_isend_docstring = + "This routine executes a nonblocking send with the given\n" + "tag to the process with rank dest. It can be received by the\n" + "destination process with a matching recv call. The value will be\n" + "transmitted in the same way as with send().\n" + "This routine returns a Request object, which can be used to query\n" + "when the transmission has completed, wait for its completion, or\n" + "cancel the transmission.\n"; + +const char* communicator_irecv_docstring = + "This routine initiates a non-blocking receive from the process\n" + "source with the given tag. If the source parameter is not specified,\n" + "the message can be received from any process. Likewise, if the tag\n" + "parameter is not specified, a message with any tag can be received.\n" + "This routine returns a Request object, which can be used to query\n" + "when the transmission has completed, wait for its completion, or\n" + "cancel the transmission. The received value be accessible\n" + "through the `value' attribute of the Request object once transmission\n" + "has completed.\n" + "\n" + "As with the recv() routine, when receiving the content of a data type\n" + "that has been sent separately from its skeleton, user code must provide\n" + "a value for the `buffer' argument. This value should be the Content\n" + "object returned from get_content().\n"; + + const char* communicator_probe_docstring = + "This operation waits until a message matching (source, tag)\n" + "is available to be received. It then returns information about\n" + "that message. If source is omitted, a message from any process\n" + "will match. If tag is omitted, a message with any tag will match.\n" + "The actual source and tag can be retrieved from the returned Status\n" + "object. To check if a message is available without blocking, use\n" + "iprobe.\n"; + +const char* communicator_iprobe_docstring = + "This operation determines if a message matching (source, tag) is\n" + "available to be received. If so, it returns information about that\n" + "message; otherwise, it returns None. If source is omitted, a message\n" + "from any process will match. If tag is omitted, a message with any\n" + "tag will match. The actual source and tag can be retrieved from the\n" + "returned Status object. To wait for a message to become available, use\n" + "probe.\n"; + +const char* communicator_barrier_docstring = + "Wait for all processes within a communicator to reach the\n" + "barrier.\n"; + +const char* communicator_split_docstring = + "Split the communicator into multiple, disjoint communicators\n" + "each of which is based on a particular color. This is a\n" + "collective operation that returns a new communicator that is a\n" + "subgroup of this. This routine is functionally equivalent to\n" + "MPI_Comm_split.\n\n" + "color is the color of this process. All processes with the\n" + "same color value will be placed into the same group.\n\n" + "If provided, key is a key value that will be used to determine\n" + "the ordering of processes with the same color in the resulting\n" + "communicator. If omitted, the key will default to the rank of\n" + "the process in the current communicator.\n\n" + "Returns a new Communicator instance containing all of the \n" + "processes in this communicator that have the same color.\n"; + +const char* communicator_abort_docstring = + "Makes a \"best attempt\" to abort all of the tasks in the group of\n" + "this communicator. Depending on the underlying MPI\n" + "implementation, this may either abort the entire program (and\n" + "possibly return errcode to the environment) or only abort\n" + "some processes, allowing the others to continue. Consult the\n" + "documentation for your MPI implementation. This is equivalent to\n" + "a call to MPI_Abort\n\n" + "errcode is the error code to return from aborted processes.\n"; + +/*********************************************************** + * request documentation * + ***********************************************************/ +const char* request_docstring = + "The Request class contains information about a non-blocking send\n" + "or receive and will be returned from isend or irecv, respectively.\n" + "When a Request object represents a completed irecv, the `value' \n" + "attribute will contain the received value.\n"; + +const char* request_with_value_docstring = + "This class is an implementation detail. Any call that accepts a\n" + "Request also accepts a RequestWithValue, and vice versa.\n"; + +const char* request_wait_docstring = + "Wait until the communication associated with this request has\n" + "completed. For a request that is associated with an isend(), returns\n" + "a Status object describing the communication. For an irecv()\n" + "operation, returns the received value by default. However, when\n" + "return_status=True, a (value, status) pair is returned by a\n" + "completed irecv request.\n"; + +const char* request_test_docstring = + "Determine whether the communication associated with this request\n" + "has completed successfully. If so, returns the Status object\n" + "describing the communication (for an isend request) or a tuple\n" + "containing the received value and a Status object (for an irecv\n" + "request). Note that once test() returns a Status object, the\n" + "request has completed and wait() should not be called.\n"; + +const char* request_cancel_docstring = + "Cancel a pending communication, assuming it has not already been\n" + "completed.\n"; + +const char* request_value_docstring = + "If this request originated in an irecv(), this property makes the" + "sent value accessible once the request completes.\n" + "\n" + "If no value is available, ValueError is raised.\n"; + +/*********************************************************** + * skeleton/content documentation * + ***********************************************************/ +const char* object_without_skeleton_docstring = + "The ObjectWithoutSkeleton class is an exception class used only\n" + "when the skeleton() or get_content() function is called with an\n" + "object that is not supported by the skeleton/content mechanism.\n" + "All C++ types for which skeletons and content can be transmitted\n" + "must be registered with the C++ routine:\n" + " boost::mpi::python::register_skeleton_and_content\n"; + +const char* object_without_skeleton_object_docstring = + "The object on which skeleton() or get_content() was invoked.\n"; + +const char* skeleton_proxy_docstring = + "The SkeletonProxy class is used to represent the skeleton of an\n" + "object. The SkeletonProxy can be used as the value parameter of\n" + "send() or isend() operations, but instead of transmitting the\n" + "entire object, only its skeleton (\"shape\") will be sent, without\n" + "the actual data. Its content can then be transmitted, separately.\n" + "\n" + "User code cannot generate SkeletonProxy instances directly. To\n" + "refer to the skeleton of an object, use skeleton(object). Skeletons\n" + "can also be received with the recv() and irecv() methods.\n" + "\n" + "Note that the skeleton/content mechanism can only be used with C++\n" + "types that have been explicitly registered.\n"; + +const char* skeleton_proxy_object_docstring = + "The actual object whose skeleton is represented by this proxy object.\n"; + +const char* content_docstring = + "The content is a proxy class that represents the content of an object,\n" + "which can be separately sent or received from its skeleton.\n" + "\n" + "User code cannot generate content instances directly. Call the\n" + "get_content() routine to retrieve the content proxy for a particular\n" + "object. The content instance can be used with any of the send() or\n" + "recv() variants. Note that get_content() can only be used with C++\n" + "data types that have been explicitly registered with the Python\n" + "skeleton/content mechanism.\n"; + +const char* skeleton_docstring = + "The skeleton function retrieves the SkeletonProxy for its object\n" + "parameter, allowing the transmission of the skeleton (or \"shape\")\n" + "of the object separately from its data. The skeleton/content mechanism\n" + "is useful when a large data structure remains structurally the same\n" + "throughout a computation, but its content (i.e., the values in the\n" + "structure) changes several times. Tranmission of the content part does\n" + "not require any serialization or unnecessary buffer copies, so it is\n" + "very efficient for large data structures.\n" + "\n" + "Only C++ types that have been explicitly registered with the Boost.MPI\n" + "Python library can be used with the skeleton/content mechanism. Use:\b" + " boost::mpi::python::register_skeleton_and_content\n"; + +const char* get_content_docstring = + "The get_content function retrieves the content for its object parameter,\n" + "allowing the transmission of the data in a data structure separately\n" + "from its skeleton (or \"shape\"). The skeleton/content mechanism\n" + "is useful when a large data structure remains structurally the same\n" + "throughout a computation, but its content (i.e., the values in the\n" + "structure) changes several times. Tranmission of the content part does\n" + "not require any serialization or unnecessary buffer copies, so it is\n" + "very efficient for large data structures.\n" + "\n" + "Only C++ types that have been explicitly registered with the Boost.MPI\n" + "Python library can be used with the skeleton/content mechanism. Use:\b" + " boost::mpi::python::register_skeleton_and_content\n"; + +/*********************************************************** + * status documentation * + ***********************************************************/ +const char* status_docstring = + "The Status class stores information about a given message, including\n" + "its source, tag, and whether the message transmission was cancelled\n" + "or resulted in an error.\n"; + +const char* status_source_docstring = + "The source of the incoming message.\n"; + +const char* status_tag_docstring = + "The tag of the incoming message.\n"; + +const char* status_error_docstring = + "The error code associated with this transmission.\n"; + +const char* status_cancelled_docstring = + "Whether this transmission was cancelled.\n"; + +/*********************************************************** + * timer documentation * + ***********************************************************/ +const char* timer_docstring = + "The Timer class is a simple wrapper around the MPI timing facilities.\n"; + +const char* timer_default_constructor_docstring = + "Initializes the timer. After this call, elapsed == 0.\n"; + +const char* timer_restart_docstring = + "Restart the timer, after which elapsed == 0.\n"; + +const char* timer_elapsed_docstring = + "The time elapsed since initialization or the last restart(),\n" + "whichever is more recent.\n"; + +const char* timer_elapsed_min_docstring = + "Returns the minimum non-zero value that elapsed may return\n" + "This is the resolution of the timer.\n"; + +const char* timer_elapsed_max_docstring = + "Return an estimate of the maximum possible value of elapsed. Note\n" + "that this routine may return too high a value on some systems.\n"; + +const char* timer_time_is_global_docstring = + "Determines whether the elapsed time values are global times or\n" + "local processor times.\n"; + +} } } // end namespace boost::mpi::python diff --git a/libs/mpi/src/python/exception.cpp b/libs/mpi/src/python/exception.cpp new file mode 100644 index 000000000..e19c0ebdc --- /dev/null +++ b/libs/mpi/src/python/exception.cpp @@ -0,0 +1,55 @@ +// Copyright (C) 2006 Douglas Gregor <doug.gregor -at- gmail.com> +// Copyright (C) 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 + +/** @file exception.cpp + * + * This file reflects the Boost.MPI @c mpi_error class into + * Python. + */ +#include <boost/python.hpp> +#include <boost/mpi/exception.hpp> +#include <string> +#include <boost/lexical_cast.hpp> +#include "utility.hpp" + +using namespace boost::python; +using namespace boost::mpi; + +namespace boost { namespace mpi { namespace python { + +extern const char* exception_docstring; +extern const char* exception_what_docstring; +extern const char* exception_routine_docstring; +extern const char* exception_result_code_docstring; + +str exception_str(const exception& e) +{ + return str("MPI routine `" + std::string(e.routine()) + + "' returned error code " + + lexical_cast<std::string>(e.result_code())); +} + +void export_exception() +{ + using boost::python::arg; + using boost::python::object; + + object type = + class_<exception> + ("exception", exception_docstring, no_init) + .add_property("what", &exception::what, exception_what_docstring) + .add_property("routine", &exception::what, exception_routine_docstring) + .add_property("result_code", &exception::what, + exception_result_code_docstring) + .def("__str__", &exception_str) + ; + translate_exception<exception>::declare(type); +} + +} } } // end namespace boost::mpi::python diff --git a/libs/mpi/src/python/module.cpp b/libs/mpi/src/python/module.cpp new file mode 100644 index 000000000..5e27e88ca --- /dev/null +++ b/libs/mpi/src/python/module.cpp @@ -0,0 +1,55 @@ +// (C) Copyright 2006 Douglas Gregor <doug.gregor -at- gmail.com> + +// 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 + +/** @file module.cpp + * + * This file provides the top-level module for the Boost.MPI Python + * bindings. + */ +#include <boost/python.hpp> +#include <boost/mpi.hpp> + +using namespace boost::python; +using namespace boost::mpi; + +namespace boost { namespace mpi { namespace python { + +extern void export_environment(); +extern void export_exception(); +extern void export_collectives(); +extern void export_communicator(); +extern void export_datatypes(); +extern void export_request(); +extern void export_status(); +extern void export_timer(); +extern void export_nonblocking(); + +extern const char* module_docstring; + +BOOST_PYTHON_MODULE(mpi) +{ + // Setup module documentation + scope().attr("__doc__") = module_docstring; + scope().attr("__author__") = "Douglas Gregor <doug.gregor@gmail.com>"; + scope().attr("__date__") = "$LastChangedDate: 2008-06-26 12:25:44 -0700 (Thu, 26 Jun 2008) $"; + scope().attr("__version__") = "$Revision: 46743 $"; + scope().attr("__copyright__") = "Copyright (C) 2006 Douglas Gregor"; + scope().attr("__license__") = "http://www.boost.org/LICENSE_1_0.txt"; + + export_environment(); + export_exception(); + export_communicator(); + export_collectives(); + export_datatypes(); + export_request(); + export_status(); + export_timer(); + export_nonblocking(); +} + +} } } // end namespace boost::mpi::python diff --git a/libs/mpi/src/python/py_communicator.cpp b/libs/mpi/src/python/py_communicator.cpp new file mode 100644 index 000000000..6e53f56f9 --- /dev/null +++ b/libs/mpi/src/python/py_communicator.cpp @@ -0,0 +1,133 @@ +// (C) Copyright 2006 Douglas Gregor <doug.gregor -at- gmail.com> + +// 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 + +/** @file communicator.cpp + * + * This file reflects the Boost.MPI @c communicator class into + * Python. + */ +#include <boost/python.hpp> +#include <boost/mpi.hpp> +#include <boost/mpi/python/serialize.hpp> +#include "request_with_value.hpp" + +using namespace boost::python; +using namespace boost::mpi; + +namespace boost { namespace mpi { namespace python { + +extern const char* communicator_docstring; +extern const char* communicator_default_constructor_docstring; +extern const char* communicator_rank_docstring; +extern const char* communicator_size_docstring; +extern const char* communicator_send_docstring; +extern const char* communicator_recv_docstring; +extern const char* communicator_isend_docstring; +extern const char* communicator_irecv_docstring; +extern const char* communicator_probe_docstring; +extern const char* communicator_iprobe_docstring; +extern const char* communicator_barrier_docstring; +extern const char* communicator_split_docstring; +extern const char* communicator_split_key_docstring; +extern const char* communicator_abort_docstring; + +object +communicator_recv(const communicator& comm, int source, int tag, + bool return_status) +{ + using boost::python::make_tuple; + + object result; + status stat = comm.recv(source, tag, result); + if (return_status) + return make_tuple(result, stat); + else + return result; +} + +request_with_value +communicator_irecv(const communicator& comm, int source, int tag) +{ + boost::shared_ptr<object> result(new object()); + request_with_value req(comm.irecv(source, tag, *result)); + req.m_internal_value = result; + return req; +} + +object +communicator_iprobe(const communicator& comm, int source, int tag) +{ + if (boost::optional<status> result = comm.iprobe(source, tag)) + return object(*result); + else + return object(); +} + +extern void export_skeleton_and_content(class_<communicator>&); + +void export_communicator() +{ + using boost::python::arg; + using boost::python::object; + + class_<communicator> comm("Communicator", communicator_docstring); + comm + .def(init<>()) + .add_property("rank", &communicator::rank, communicator_rank_docstring) + .add_property("size", &communicator::size, communicator_size_docstring) + .def("send", + (void (communicator::*)(int, int, const object&) const) + &communicator::send<object>, + (arg("dest"), arg("tag") = 0, arg("value") = object()), + communicator_send_docstring) + .def("recv", &communicator_recv, + (arg("source") = any_source, arg("tag") = any_tag, + arg("return_status") = false), + communicator_recv_docstring) + .def("isend", + (request (communicator::*)(int, int, const object&) const) + &communicator::isend<object>, + (arg("dest"), arg("tag") = 0, arg("value") = object()), + communicator_isend_docstring) + .def("irecv", &communicator_irecv, + (arg("source") = any_source, arg("tag") = any_tag), + communicator_irecv_docstring) + .def("probe", &communicator::probe, + (arg("source") = any_source, arg("tag") = any_tag), + communicator_probe_docstring) + .def("iprobe", &communicator_iprobe, + (arg("source") = any_source, arg("tag") = any_tag), + communicator_iprobe_docstring) + .def("barrier", &communicator::barrier, communicator_barrier_docstring) + .def("__nonzero__", &communicator::operator bool) + .def("split", + (communicator (communicator::*)(int) const)&communicator::split, + (arg("color")), communicator_split_docstring) + .def("split", + (communicator (communicator::*)(int, int) const)&communicator::split, + (arg("color"), arg("key"))) + .def("abort", &communicator::abort, arg("errcode"), + communicator_abort_docstring) + ; + + // Module-level attributes + scope().attr("any_source") = any_source; + scope().attr("any_tag") = any_tag; + + { + communicator world; + scope().attr("world") = world; + scope().attr("rank") = world.rank(); + scope().attr("size") = world.size(); + } + + // Export skeleton and content + export_skeleton_and_content(comm); +} + +} } } // end namespace boost::mpi::python diff --git a/libs/mpi/src/python/py_environment.cpp b/libs/mpi/src/python/py_environment.cpp new file mode 100644 index 000000000..bcd95e4a8 --- /dev/null +++ b/libs/mpi/src/python/py_environment.cpp @@ -0,0 +1,111 @@ +// (C) Copyright 2006 Douglas Gregor <doug.gregor -at- gmail.com> + +// 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 + +/** @file environment.cpp + * + * This file reflects the Boost.MPI "environment" class into Python + * methods at module level. + */ +#include <boost/python.hpp> +#include <boost/mpi.hpp> + +using namespace boost::python; +using namespace boost::mpi; + +namespace boost { namespace mpi { namespace python { + +extern const char* environment_init_docstring; +extern const char* environment_finalize_docstring; +extern const char* environment_abort_docstring; +extern const char* environment_initialized_docstring; +extern const char* environment_finalized_docstring; + +/** + * The environment used by the Boost.MPI Python module. This will be + * zero-initialized before it is used. + */ +static environment* env; + +bool mpi_init(list python_argv, bool abort_on_exception) +{ + // If MPI is already initialized, do nothing. + if (environment::initialized()) + return false; + + // Convert Python argv into C-style argc/argv. + int my_argc = extract<int>(python_argv.attr("__len__")()); + char** my_argv = new char*[my_argc]; + for (int arg = 0; arg < my_argc; ++arg) + my_argv[arg] = strdup(extract<const char*>(python_argv[arg])); + + // Initialize MPI + int mpi_argc = my_argc; + char** mpi_argv = my_argv; + env = new environment(mpi_argc, mpi_argv, abort_on_exception); + + // If anything changed, convert C-style argc/argv into Python argv + if (mpi_argv != my_argv) + PySys_SetArgv(mpi_argc, mpi_argv); + + for (int arg = 0; arg < my_argc; ++arg) + free(my_argv[arg]); + delete [] my_argv; + + return true; +} + +void mpi_finalize() +{ + if (env) { + delete env; + env = 0; + } +} + +void export_environment() +{ + using boost::python::arg; + + def("init", mpi_init, (arg("argv"), arg("abort_on_exception") = true), + environment_init_docstring); + def("finalize", mpi_finalize, environment_finalize_docstring); + + // Setup initialization and finalization code + if (!environment::initialized()) { + // MPI_Init from sys.argv + object sys = object(handle<>(PyImport_ImportModule("sys"))); + mpi_init(extract<list>(sys.attr("argv")), true); + + // Setup MPI_Finalize call when the program exits + object atexit = object(handle<>(PyImport_ImportModule("atexit"))); + object finalize = scope().attr("finalize"); + atexit.attr("register")(finalize); + } + + def("abort", &environment::abort, arg("errcode"), + environment_abort_docstring); + def("initialized", &environment::initialized, + environment_initialized_docstring); + def("finalized", &environment::finalized, + environment_finalized_docstring); + scope().attr("max_tag") = environment::max_tag(); + scope().attr("collectives_tag") = environment::collectives_tag(); + scope().attr("processor_name") = environment::processor_name(); + + if (optional<int> host_rank = environment::host_rank()) + scope().attr("host_rank") = *host_rank; + else + scope().attr("host_rank") = object(); + + if (optional<int> io_rank = environment::io_rank()) + scope().attr("io_rank") = *io_rank; + else + scope().attr("io_rank") = object(); +} + +} } } // end namespace boost::mpi::python diff --git a/libs/mpi/src/python/py_exception.cpp b/libs/mpi/src/python/py_exception.cpp new file mode 100644 index 000000000..fee48c4d1 --- /dev/null +++ b/libs/mpi/src/python/py_exception.cpp @@ -0,0 +1,54 @@ +// Copyright (C) 2006 Douglas Gregor <doug.gregor -at- gmail.com> +// Copyright (C) 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 + +/** @file exception.cpp + * + * This file reflects the Boost.MPI @c mpi_error class into + * Python. + */ +#include <boost/python.hpp> +#include <boost/mpi/exception.hpp> +#include <string> +#include <boost/lexical_cast.hpp> +#include "utility.hpp" + +using namespace boost::python; +using namespace boost::mpi; + +namespace boost { namespace mpi { namespace python { + +extern const char* exception_docstring; +extern const char* exception_what_docstring; +extern const char* exception_routine_docstring; +extern const char* exception_result_code_docstring; + +str exception_str(const exception& e) +{ + return str(std::string(e.what()) + + " (code " + lexical_cast<std::string>(e.result_code())+")"); +} + +void export_exception() +{ + using boost::python::arg; + using boost::python::object; + + object type = + class_<exception> + ("Exception", exception_docstring, no_init) + .add_property("what", &exception::what, exception_what_docstring) + .add_property("routine", &exception::what, exception_routine_docstring) + .add_property("result_code", &exception::result_code, + exception_result_code_docstring) + .def("__str__", &exception_str) + ; + translate_exception<exception>::declare(type); +} + +} } } // end namespace boost::mpi::python diff --git a/libs/mpi/src/python/py_nonblocking.cpp b/libs/mpi/src/python/py_nonblocking.cpp new file mode 100644 index 000000000..22f289f9e --- /dev/null +++ b/libs/mpi/src/python/py_nonblocking.cpp @@ -0,0 +1,255 @@ +// (C) Copyright 2007 +// Douglas Gregor <doug.gregor -at- gmail.com> +// Andreas Kloeckner <inform -at- tiker.net> + +// 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, Andreas Kloeckner + +/** @file py_nonblocking.cpp + * + * This file reflects the Boost.MPI nonblocking operations into Python + * functions. + */ + +#include <vector> +#include <iterator> +#include <algorithm> +#include <boost/operators.hpp> +#include <boost/python.hpp> +#include <boost/python/stl_iterator.hpp> +#include <boost/python/suite/indexing/vector_indexing_suite.hpp> +#include <boost/mpi.hpp> +#include "request_with_value.hpp" + +using namespace std; +using namespace boost::python; +using namespace boost::mpi; + + + + +namespace +{ + template <class ValueType, class RequestIterator> + class py_call_output_iterator : + public boost::output_iterator_helper< + py_call_output_iterator<ValueType, RequestIterator> > + { + private: + object m_callable; + RequestIterator m_request_iterator; + + public: + explicit py_call_output_iterator(object callable, + const RequestIterator &req_it) + : m_callable(callable), m_request_iterator(req_it) + { } + + py_call_output_iterator &operator=(ValueType const &v) + { + m_callable((m_request_iterator++)->get_value_or_none(), v); + return *this; + } + }; + + + + + typedef std::vector<python::request_with_value> request_list; + typedef py_call_output_iterator<status, request_list::iterator> + status_value_iterator; + + + + + std::auto_ptr<request_list> make_request_list_from_py_list(object iterable) + { + std::auto_ptr<request_list> result(new request_list); + std::copy( + stl_input_iterator<python::request_with_value>(iterable), + stl_input_iterator<python::request_with_value>(), + back_inserter(*result)); + return result; + } + + + + + class request_list_indexing_suite : + public vector_indexing_suite<request_list, false, request_list_indexing_suite> + { + public: + // FIXME: requests are not comparable, thus __contains__ makes no sense. + // Unfortunately, indexing_suites insist on having __contains__ available. + // Just make it error out for now. + + static bool + contains(request_list& container, request const& key) + { + PyErr_SetString(PyExc_NotImplementedError, "mpi requests are not comparable"); + throw error_already_set(); + } + }; + + + + + void check_request_list_not_empty(const request_list &requests) + { + if (requests.size() == 0) + { + PyErr_SetString(PyExc_ValueError, "cannot wait on an empty request vector"); + throw error_already_set(); + } + + } + + + + + + object wrap_wait_any(request_list &requests) + { + check_request_list_not_empty(requests); + + pair<status, request_list::iterator> result = + wait_any(requests.begin(), requests.end()); + + return make_tuple( + result.second->get_value_or_none(), + result.first, + distance(requests.begin(), result.second)); + } + + + + + object wrap_test_any(request_list &requests) + { + check_request_list_not_empty(requests); + ::boost::optional<pair<status, request_list::iterator> > result = + test_any(requests.begin(), requests.end()); + + if (result) + return make_tuple( + result->second->get_value_or_none(), + result->first, + distance(requests.begin(), result->second)); + else + return object(); + } + + + + + + void wrap_wait_all(request_list &requests, object py_callable) + { + check_request_list_not_empty(requests); + if (py_callable != object()) + wait_all(requests.begin(), requests.end(), + status_value_iterator(py_callable, requests.begin())); + else + wait_all(requests.begin(), requests.end()); + } + + + + + bool wrap_test_all(request_list &requests, object py_callable) + { + check_request_list_not_empty(requests); + if (py_callable != object()) + return test_all(requests.begin(), requests.end(), + status_value_iterator(py_callable, requests.begin())); + else + return test_all(requests.begin(), requests.end()); + } + + + + + int wrap_wait_some(request_list &requests, object py_callable) + { + check_request_list_not_empty(requests); + request_list::iterator first_completed; + if (py_callable != object()) + first_completed = wait_some(requests.begin(), requests.end(), + status_value_iterator(py_callable, requests.begin())).second; + else + first_completed = wait_some(requests.begin(), requests.end()); + + return distance(requests.begin(), first_completed); + } + + + + + int wrap_test_some(request_list &requests, object py_callable) + { + check_request_list_not_empty(requests); + request_list::iterator first_completed; + if (py_callable != object()) + first_completed = test_some(requests.begin(), requests.end(), + status_value_iterator(py_callable, requests.begin())).second; + else + first_completed = test_some(requests.begin(), requests.end()); + + return distance(requests.begin(), first_completed); + } +} + + + + +namespace boost { namespace mpi { namespace python { + +extern const char* request_list_init_docstring; +extern const char* request_list_append_docstring; + +extern const char* nonblocking_wait_any_docstring; +extern const char* nonblocking_test_any_docstring; +extern const char* nonblocking_wait_all_docstring; +extern const char* nonblocking_test_all_docstring; +extern const char* nonblocking_wait_some_docstring; +extern const char* nonblocking_test_some_docstring; + +void export_nonblocking() +{ + using boost::python::arg; + + { + typedef request_list cl; + class_<cl>("RequestList", "A list of Request objects.") + .def("__init__", make_constructor(make_request_list_from_py_list), + /*arg("iterable"),*/ request_list_init_docstring) + .def(request_list_indexing_suite()) + ; + } + + def("wait_any", wrap_wait_any, + (arg("requests")), + nonblocking_wait_any_docstring); + def("test_any", wrap_test_any, + (arg("requests")), + nonblocking_test_any_docstring); + + def("wait_all", wrap_wait_all, + (arg("requests"), arg("callable") = object()), + nonblocking_wait_all_docstring); + def("test_all", wrap_test_all, + (arg("requests"), arg("callable") = object()), + nonblocking_test_all_docstring); + + def("wait_some", wrap_wait_some, + (arg("requests"), arg("callable") = object()), + nonblocking_wait_some_docstring); + def("test_some", wrap_test_some, + (arg("requests"), arg("callable") = object()), + nonblocking_test_some_docstring); +} + +} } } diff --git a/libs/mpi/src/python/py_request.cpp b/libs/mpi/src/python/py_request.cpp new file mode 100644 index 000000000..53aa4dedf --- /dev/null +++ b/libs/mpi/src/python/py_request.cpp @@ -0,0 +1,102 @@ +// (C) Copyright 2006 Douglas Gregor <doug.gregor -at- gmail.com> + +// 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 + +/** @file request.cpp + * + * This file reflects the Boost.MPI @c request class into + * Python. + */ +#include <boost/python.hpp> +#include <boost/mpi.hpp> +#include "request_with_value.hpp" + +using namespace boost::python; +using namespace boost::mpi; + +const object python::request_with_value::get_value() const +{ + if (m_internal_value.get()) + return *m_internal_value; + else if (m_external_value) + return *m_external_value; + else + { + PyErr_SetString(PyExc_ValueError, "request value not available"); + throw boost::python::error_already_set(); + } +} + +const object python::request_with_value::get_value_or_none() const +{ + if (m_internal_value.get()) + return *m_internal_value; + else if (m_external_value) + return *m_external_value; + else + return object(); +} + +const object python::request_with_value::wrap_wait() +{ + status stat = request::wait(); + if (m_internal_value.get() || m_external_value) + return boost::python::make_tuple(get_value(), stat); + else + return object(stat); +} + +const object python::request_with_value::wrap_test() +{ + ::boost::optional<status> stat = request::test(); + if (stat) + { + if (m_internal_value.get() || m_external_value) + return boost::python::make_tuple(get_value(), *stat); + else + return object(*stat); + } + else + return object(); +} + + +namespace boost { namespace mpi { namespace python { + +extern const char* request_docstring; +extern const char* request_with_value_docstring; +extern const char* request_wait_docstring; +extern const char* request_test_docstring; +extern const char* request_cancel_docstring; +extern const char* request_value_docstring; + +void export_request() +{ + using boost::python::arg; + using boost::python::object; + + { + typedef request cl; + class_<cl>("Request", request_docstring, no_init) + .def("wait", &cl::wait, request_wait_docstring) + .def("test", &cl::test, request_test_docstring) + .def("cancel", &cl::cancel, request_cancel_docstring) + ; + } + { + typedef request_with_value cl; + class_<cl, bases<request> >( + "RequestWithValue", request_with_value_docstring, no_init) + .def("wait", &cl::wrap_wait, request_wait_docstring) + .def("test", &cl::wrap_test, request_test_docstring) + ; + } + + implicitly_convertible<request, request_with_value>(); +} + +} } } // end namespace boost::mpi::python diff --git a/libs/mpi/src/python/py_timer.cpp b/libs/mpi/src/python/py_timer.cpp new file mode 100644 index 000000000..88b1b4062 --- /dev/null +++ b/libs/mpi/src/python/py_timer.cpp @@ -0,0 +1,48 @@ +// (C) Copyright 2006 Douglas Gregor <doug.gregor -at- gmail.com> + +// 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 + +/** @file timer.cpp + * + * This file reflects the Boost.MPI @c timer class into + * Python. + */ +#include <boost/python.hpp> +#include <boost/mpi/timer.hpp> + +using namespace boost::python; +using namespace boost::mpi; + +namespace boost { namespace mpi { namespace python { + +extern const char* timer_docstring; +extern const char* timer_default_constructor_docstring; +extern const char* timer_restart_docstring; +extern const char* timer_elapsed_docstring; +extern const char* timer_elapsed_min_docstring; +extern const char* timer_elapsed_max_docstring; +extern const char* timer_time_is_global_docstring; + +void export_timer() +{ + using boost::python::arg; + using boost::python::object; + + class_<timer>("Timer", timer_docstring) + .def(init<>()) + .def("restart", &timer::restart, timer_restart_docstring) + .add_property("elapsed", &timer::elapsed, timer_elapsed_docstring) + .add_property("elapsed_min", &timer::elapsed_min, + timer_elapsed_min_docstring) + .add_property("elapsed_max", &timer::elapsed_max, + timer_elapsed_max_docstring) + .add_property("time_is_global", &timer::time_is_global, + timer_time_is_global_docstring) + ; +} + +} } } // end namespace boost::mpi::python diff --git a/libs/mpi/src/python/request_with_value.hpp b/libs/mpi/src/python/request_with_value.hpp new file mode 100644 index 000000000..04d0c53e9 --- /dev/null +++ b/libs/mpi/src/python/request_with_value.hpp @@ -0,0 +1,71 @@ +// (C) Copyright 2006 +// Douglas Gregor <doug.gregor -at- gmail.com> +// Andreas Kloeckner <inform -at- tiker.net> + +// 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, Andreas Kloeckner + +#ifndef BOOST_MPI_PYTHON_REQUEST_WITH_VALUE_HPP +#define BOOST_MPI_PYTHON_REQUEST_WITH_VALUE_HPP + +#include <boost/python.hpp> +#include <boost/mpi.hpp> + +namespace boost { namespace mpi { namespace python { + + /** This wrapper adds a @c boost::python::object value to the @c + * boost::mpi::request structure, for the benefit of @c irecv() requests. + * + * In order to be able to return the value of his requests to the user, we + * need a handle that we can update to contain the transmitted value once the + * request completes. Since we're passing the address on to irecv to fill at + * any time in the future, this address may not change over time. + * + * There are two possible cases: + * - plain irecv() + * - skeleton-content irecv() + * + * In the first case, we need to own the storage from this object, the + * m_internal_value is used for this. In the second case, the updated + * python::object is part of a boost::mpi::python::content object: the + * m_external_value field handles this case. Furthermore, in the latter case, + * we now have a lifetime dependency on that content object; this can be + * handled with the BPL's with_custodian_and_ward facility. + * + * Since requests and request_with_value are supposed to be copyconstructible, + * we can't put the handle immediately inside this instance. Moreover, since + * we need to be able to put request_with_value inside request_vectors, any + * values we own must be held in a shared_ptr instance. + */ + + class request_with_value : public request + { + private: + boost::shared_ptr<boost::python::object> m_internal_value; + boost::python::object *m_external_value; + + public: + request_with_value() + : m_external_value(0) + { } + request_with_value(const request &req) + : request(req), m_external_value(0) + { } + + const boost::python::object get_value() const; + const boost::python::object get_value_or_none() const; + + const boost::python::object wrap_wait(); + const boost::python::object wrap_test(); + + friend request_with_value communicator_irecv(const communicator &, int, int); + friend request_with_value communicator_irecv_content( + const communicator&, int, int, content&); + }; + +} } } + +#endif diff --git a/libs/mpi/src/python/serialize.cpp b/libs/mpi/src/python/serialize.cpp new file mode 100644 index 000000000..92004a340 --- /dev/null +++ b/libs/mpi/src/python/serialize.cpp @@ -0,0 +1,79 @@ +// (C) Copyright 2006 Douglas Gregor <doug.gregor -at- gmail.com> + +// 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 + +/** @file serialize.cpp + * + * This file provides Boost.Serialization support for Python objects. + */ +#include <boost/mpi/python/serialize.hpp> +#include <boost/mpi/python/skeleton_and_content.hpp> +#include <boost/mpi.hpp> + +namespace boost { namespace python { + +struct pickle::data_t { + object module; + object dumps; + object loads; +}; + + +/// Data used for communicating with the Python `pickle' module. +pickle::data_t* pickle::data; + +str pickle::dumps(object obj, int protocol) +{ + if (!data) initialize_data(); + return extract<str>((data->dumps)(obj, protocol)); +} + +object pickle::loads(str s) +{ + if (!data) initialize_data(); + return ((data->loads)(s)); +} + +void pickle::initialize_data() +{ + data = new data_t; + data->module = object(handle<>(PyImport_ImportModule("pickle"))); + data->dumps = data->module.attr("dumps"); + data->loads = data->module.attr("loads"); +} + +} } // end namespace boost::python + +BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE_IMPL( + ::boost::mpi::packed_iarchive, + ::boost::mpi::packed_oarchive) + +namespace boost { namespace mpi { namespace python { namespace detail { + + boost::python::object skeleton_proxy_base_type; + + // A map from Python type objects to skeleton/content handlers + typedef std::map<PyTypeObject*, skeleton_content_handler> + skeleton_content_handlers_type; + + BOOST_MPI_PYTHON_DECL skeleton_content_handlers_type skeleton_content_handlers; + + bool + skeleton_and_content_handler_registered(PyTypeObject* type) + { + return + skeleton_content_handlers.find(type) != skeleton_content_handlers.end(); + } + + void + register_skeleton_and_content_handler(PyTypeObject* type, + const skeleton_content_handler& handler) + { + skeleton_content_handlers[type] = handler; + } + +} } } } // end namespace boost::mpi::python::detail diff --git a/libs/mpi/src/python/skeleton_and_content.cpp b/libs/mpi/src/python/skeleton_and_content.cpp new file mode 100644 index 000000000..d5376c14f --- /dev/null +++ b/libs/mpi/src/python/skeleton_and_content.cpp @@ -0,0 +1,173 @@ +// (C) Copyright 2006 Douglas Gregor <doug.gregor -at- gmail.com> + +// 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 + +/** @file skeleton_and_content.cpp + * + * This file reflects the skeleton/content facilities into Python. + */ +#include <boost/mpi/python/skeleton_and_content.hpp> +#include <boost/mpi/python/serialize.hpp> +#include <boost/python/list.hpp> +#include <typeinfo> +#include <list> +#include "utility.hpp" +#include "request_with_value.hpp" + +using namespace boost::python; +using namespace boost::mpi; + +namespace boost { namespace mpi { namespace python { + +namespace detail { + typedef std::map<PyTypeObject*, skeleton_content_handler> + skeleton_content_handlers_type; + +// We're actually importing skeleton_content_handlers from skeleton_and_content.cpp. +#if defined(BOOST_HAS_DECLSPEC) && (defined(BOOST_MPI_PYTHON_DYN_LINK) || defined(BOOST_ALL_DYN_LINK)) +# define BOOST_SC_DECL __declspec(dllimport) +#else +# define BOOST_SC_DECL +#endif + + extern BOOST_SC_DECL skeleton_content_handlers_type skeleton_content_handlers; +} + +/** + * An exception that will be thrown when the object passed to the + * Python version of skeleton() does not have a skeleton. + */ +struct object_without_skeleton : public std::exception { + explicit object_without_skeleton(object value) : value(value) { } + virtual ~object_without_skeleton() throw() { } + + object value; +}; + +str object_without_skeleton_str(const object_without_skeleton& e) +{ + return str("\nThe skeleton() or get_content() function was invoked for a Python\n" + "object that is not supported by the Boost.MPI skeleton/content\n" + "mechanism. To transfer objects via skeleton/content, you must\n" + "register the C++ type of this object with the C++ function:\n" + " boost::mpi::python::register_skeleton_and_content()\n" + "Object: " + str(e.value) + "\n"); +} + +/** + * Extract the "skeleton" from a Python object. In truth, all we're + * doing at this point is verifying that the object is a C++ type that + * has been registered for the skeleton/content mechanism. + */ +object skeleton(object value) +{ + PyTypeObject* type = value.ptr()->ob_type; + detail::skeleton_content_handlers_type::iterator pos = + detail::skeleton_content_handlers.find(type); + if (pos == detail::skeleton_content_handlers.end()) + throw object_without_skeleton(value); + else + return pos->second.get_skeleton_proxy(value); +} + +/** + * Extract the "content" from a Python object, which must be a C++ + * type that has been registered for the skeleton/content mechanism. + */ +content get_content(object value) +{ + PyTypeObject* type = value.ptr()->ob_type; + detail::skeleton_content_handlers_type::iterator pos = + detail::skeleton_content_handlers.find(type); + if (pos == detail::skeleton_content_handlers.end()) + throw object_without_skeleton(value); + else + return pos->second.get_content(value); +} + +/// Send the content part of a Python object. +void +communicator_send_content(const communicator& comm, int dest, int tag, + const content& c) +{ + comm.send(dest, tag, c.base()); +} + +/// Receive the content of a Python object. We return the object +/// received, not the content wrapper. +object +communicator_recv_content(const communicator& comm, int source, int tag, + const content& c, bool return_status) +{ + using boost::python::make_tuple; + + status stat = comm.recv(source, tag, c.base()); + if (return_status) + return make_tuple(c.object, stat); + else + return c.object; +} + +/// Receive the content of a Python object. The request object's value +/// attribute will reference the object whose content is being +/// received, not the content wrapper. +request_with_value +communicator_irecv_content(const communicator& comm, int source, int tag, + content& c) +{ + request_with_value req(comm.irecv(source, tag, c.base())); + req.m_external_value = &c.object; + return req; +} + +extern const char* object_without_skeleton_docstring; +extern const char* object_without_skeleton_object_docstring; +extern const char* skeleton_proxy_docstring; +extern const char* skeleton_proxy_object_docstring; +extern const char* content_docstring; +extern const char* skeleton_docstring; +extern const char* get_content_docstring; + +void export_skeleton_and_content(class_<communicator>& comm) +{ + using boost::python::arg; + + // Expose the object_without_skeleton exception + object type = + class_<object_without_skeleton> + ("ObjectWithoutSkeleton", object_without_skeleton_docstring, no_init) + .def_readonly("object", &object_without_skeleton::value, + object_without_skeleton_object_docstring) + .def("__str__", &object_without_skeleton_str) + ; + translate_exception<object_without_skeleton>::declare(type); + + // Expose the Python variants of "skeleton_proxy" and "content", and + // their generator functions. + detail::skeleton_proxy_base_type = + class_<skeleton_proxy_base>("SkeletonProxy", skeleton_proxy_docstring, + no_init) + .def_readonly("object", &skeleton_proxy_base::object, + skeleton_proxy_object_docstring); + class_<content>("Content", content_docstring, no_init); + def("skeleton", &skeleton, arg("object"), skeleton_docstring); + def("get_content", &get_content, arg("object"), get_content_docstring); + + // Expose communicator send/recv operations for content. + comm + .def("send", communicator_send_content, + (arg("dest"), arg("tag") = 0, arg("value"))) + .def("recv", communicator_recv_content, + (arg("source") = any_source, arg("tag") = any_tag, arg("buffer"), + arg("return_status") = false)) + .def("irecv", communicator_irecv_content, + (arg("source") = any_source, arg("tag") = any_tag, arg("buffer")), + with_custodian_and_ward_postcall<0, 4>() + ); +} + +} } } // end namespace boost::mpi::python diff --git a/libs/mpi/src/python/status.cpp b/libs/mpi/src/python/status.cpp new file mode 100644 index 000000000..a74221a7a --- /dev/null +++ b/libs/mpi/src/python/status.cpp @@ -0,0 +1,41 @@ +// (C) Copyright 2006 Douglas Gregor <doug.gregor -at- gmail.com> + +// 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 + +/** @file status.cpp + * + * This file reflects the Boost.MPI @c status class into + * Python. + */ +#include <boost/python.hpp> +#include <boost/mpi.hpp> + +using namespace boost::python; +using namespace boost::mpi; + +namespace boost { namespace mpi { namespace python { + +extern const char* status_docstring; +extern const char* status_source_docstring; +extern const char* status_tag_docstring; +extern const char* status_error_docstring; +extern const char* status_cancelled_docstring; + +void export_status() +{ + using boost::python::arg; + using boost::python::object; + + class_<status>("Status", status_docstring, no_init) + .add_property("source", &status::source, status_source_docstring) + .add_property("tag", &status::tag, status_tag_docstring) + .add_property("error", &status::error, status_error_docstring) + .add_property("cancelled", &status::cancelled, status_cancelled_docstring) + ; +} + +} } } // end namespace boost::mpi::python diff --git a/libs/mpi/src/python/utility.hpp b/libs/mpi/src/python/utility.hpp new file mode 100644 index 000000000..ed0016761 --- /dev/null +++ b/libs/mpi/src/python/utility.hpp @@ -0,0 +1,43 @@ +// (C) Copyright 2006 Douglas Gregor <doug.gregor -at- gmail.com> + +// 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 +#ifndef BOOST_MPI_PYTHON_UTILITY_HPP +#define BOOST_MPI_PYTHON_UTILITY_HPP + +/** @file utility.hpp + * + * This file is a utility header for the Boost.MPI Python bindings. + */ +#include <boost/python.hpp> + +namespace boost { namespace mpi { namespace python { + +template<typename E> +class translate_exception +{ + explicit translate_exception(boost::python::object type) : type(type) { } + +public: + static void declare(boost::python::object type) + { + using boost::python::register_exception_translator; + register_exception_translator<E>(translate_exception(type)); + } + + void operator()(const E& e) const + { + using boost::python::object; + PyErr_SetObject(type.ptr(), object(e).ptr()); + } + +private: + boost::python::object type; +}; + +} } } // end namespace boost::mpi::python + +#endif // BOOST_MPI_PYTHON_UTILITY_HPP diff --git a/libs/mpi/src/request.cpp b/libs/mpi/src/request.cpp new file mode 100644 index 000000000..9bc842f2e --- /dev/null +++ b/libs/mpi/src/request.cpp @@ -0,0 +1,120 @@ +// Copyright (C) 2006 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) +#include <boost/mpi/request.hpp> +#include <boost/mpi/status.hpp> + +namespace boost { namespace mpi { + +/*************************************************************************** + * request * + ***************************************************************************/ +request::request() + : m_handler(0), m_data() +{ + m_requests[0] = MPI_REQUEST_NULL; + m_requests[1] = MPI_REQUEST_NULL; +} + +status request::wait() +{ + if (m_handler) { + // This request is a receive for a serialized type. Use the + // handler to wait for completion. + return *m_handler(this, ra_wait); + } else if (m_requests[1] == MPI_REQUEST_NULL) { + // This request is either a send or a receive for a type with an + // associated MPI datatype, or a serialized datatype that has been + // packed into a single message. Just wait on the one receive/send + // and return the status to the user. + status result; + BOOST_MPI_CHECK_RESULT(MPI_Wait, (&m_requests[0], &result.m_status)); + return result; + } else { + // This request is a send of a serialized type, broken into two + // separate messages. Complete both sends at once. + MPI_Status stats[2]; + int error_code = MPI_Waitall(2, m_requests, stats); + if (error_code == MPI_ERR_IN_STATUS) { + // Dig out which status structure has the error, and use that + // one when throwing the exception. + if (stats[0].MPI_ERROR == MPI_SUCCESS + || stats[0].MPI_ERROR == MPI_ERR_PENDING) + boost::throw_exception(exception("MPI_Waitall", stats[1].MPI_ERROR)); + else + boost::throw_exception(exception("MPI_Waitall", stats[0].MPI_ERROR)); + } else if (error_code != MPI_SUCCESS) { + // There was an error somewhere in the MPI_Waitall call; throw + // an exception for it. + boost::throw_exception(exception("MPI_Waitall", error_code)); + } + + // No errors. Returns the first status structure. + status result; + result.m_status = stats[0]; + return result; + } +} + +optional<status> request::test() +{ + if (m_handler) { + // This request is a receive for a serialized type. Use the + // handler to test for completion. + return m_handler(this, ra_test); + } else if (m_requests[1] == MPI_REQUEST_NULL) { + // This request is either a send or a receive for a type with an + // associated MPI datatype, or a serialized datatype that has been + // packed into a single message. Just test the one receive/send + // and return the status to the user if it has completed. + status result; + int flag = 0; + BOOST_MPI_CHECK_RESULT(MPI_Test, + (&m_requests[0], &flag, &result.m_status)); + return flag != 0? optional<status>(result) : optional<status>(); + } else { + // This request is a send of a serialized type, broken into two + // separate messages. We only get a result if both complete. + MPI_Status stats[2]; + int flag = 0; + int error_code = MPI_Testall(2, m_requests, &flag, stats); + if (error_code == MPI_ERR_IN_STATUS) { + // Dig out which status structure has the error, and use that + // one when throwing the exception. + if (stats[0].MPI_ERROR == MPI_SUCCESS + || stats[0].MPI_ERROR == MPI_ERR_PENDING) + boost::throw_exception(exception("MPI_Testall", stats[1].MPI_ERROR)); + else + boost::throw_exception(exception("MPI_Testall", stats[0].MPI_ERROR)); + } else if (error_code != MPI_SUCCESS) { + // There was an error somewhere in the MPI_Testall call; throw + // an exception for it. + boost::throw_exception(exception("MPI_Testall", error_code)); + } + + // No errors. Returns the second status structure if the send has + // completed. + if (flag != 0) { + status result; + result.m_status = stats[1]; + return result; + } else { + return optional<status>(); + } + } +} + +void request::cancel() +{ + if (m_handler) { + m_handler(this, ra_cancel); + } else { + BOOST_MPI_CHECK_RESULT(MPI_Cancel, (&m_requests[0])); + if (m_requests[1] != MPI_REQUEST_NULL) + BOOST_MPI_CHECK_RESULT(MPI_Cancel, (&m_requests[1])); + } +} + +} } // end namespace boost::mpi diff --git a/libs/mpi/src/text_skeleton_oarchive.cpp b/libs/mpi/src/text_skeleton_oarchive.cpp new file mode 100644 index 000000000..7817ea785 --- /dev/null +++ b/libs/mpi/src/text_skeleton_oarchive.cpp @@ -0,0 +1,20 @@ +// (C) Copyright 2005 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 +// http://www.boost.org/LICENSE_1_0.txt) + +// Authors: Matthias Troyer + +#define BOOST_ARCHIVE_SOURCE +#include <boost/mpi/detail/text_skeleton_oarchive.hpp> + +#include <boost/archive/detail/archive_serializer_map.hpp> +#include <boost/archive/impl/archive_serializer_map.ipp> + +namespace boost { namespace archive { +// explicitly instantiate all required templates + +// template class detail::archive_serializer_map<text_oarchive>; + +} } // end namespace boost::archive diff --git a/libs/mpi/src/timer.cpp b/libs/mpi/src/timer.cpp new file mode 100644 index 000000000..32d65b401 --- /dev/null +++ b/libs/mpi/src/timer.cpp @@ -0,0 +1,25 @@ +// Copyright (C) 2006 Douglas Gregor <doug.gregor -at- gmail.com> + +// 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/mpi/timer.hpp> +#include <boost/mpi/exception.hpp> + +namespace boost { namespace mpi { + +bool timer::time_is_global() +{ + int* is_global; + int found = 0; + + BOOST_MPI_CHECK_RESULT(MPI_Attr_get, + (MPI_COMM_WORLD, MPI_WTIME_IS_GLOBAL, &is_global, + &found)); + if (!found) + return false; + else + return *is_global != 0; +} + +} } // end namespace boost::mpi |