1 // Copyright (C) 2007 Trustees of Indiana University
2 
3 // Authors: Douglas Gregor
4 //          Andrew Lumsdaine
5 
6 // Use, modification and distribution is subject to the Boost Software
7 // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
8 // http://www.boost.org/LICENSE_1_0.txt)
9 
10 /** @file group.hpp
11  *
12  *  This header defines the @c group class, which allows one to
13  *  manipulate and query groups of processes.
14  */
15 #ifndef BOOST_MPI_GROUP_HPP
16 #define BOOST_MPI_GROUP_HPP
17 
18 #include <boost/mpi/exception.hpp>
19 #include <boost/shared_ptr.hpp>
20 #include <boost/optional.hpp>
21 #include <vector>
22 
23 namespace boost { namespace mpi {
24 
25 /**
26  * @brief A @c group is a representation of a subset of the processes
27  * within a @c communicator.
28  *
29  * The @c group class allows one to create arbitrary subsets of the
30  * processes within a communicator. One can compute the union,
31  * intersection, or difference of two groups, or create new groups by
32  * specifically including or excluding certain processes. Given a
33  * group, one can create a new communicator containing only the
34  * processes in that group.
35  */
36 class BOOST_MPI_DECL group
37 {
38 public:
39   /**
40    * @brief Constructs an empty group.
41    */
group()42   group() : group_ptr() { }
43 
44   /**
45    * @brief Constructs a group from an @c MPI_Group.
46    *
47    * This routine allows one to construct a Boost.MPI @c group from a
48    * C @c MPI_Group. The @c group object can (optionally) adopt the @c
49    * MPI_Group, after which point the @c group object becomes
50    * responsible for freeing the @c MPI_Group when the last copy of @c
51    * group disappears.
52    *
53    * @param in_group The @c MPI_Group used to construct this @c group.
54    *
55    * @param adopt Whether the @c group should adopt the @c
56    * MPI_Group. When true, the @c group object (or one of its copies)
57    * will free the group (via @c MPI_Comm_free) when the last copy is
58    * destroyed. Otherwise, the user is responsible for calling @c
59    * MPI_Group_free.
60    */
61   group(const MPI_Group& in_group, bool adopt);
62 
63   /**
64    * @brief Determine the rank of the calling process in the group.
65    *
66    * This routine is equivalent to @c MPI_Group_rank.
67    *
68    * @returns The rank of the calling process in the group, which will
69    * be a value in [0, size()). If the calling process is not in the
70    * group, returns an empty value.
71    */
72   optional<int> rank() const;
73 
74   /**
75    * @brief Determine the number of processes in the group.
76    *
77    * This routine is equivalent to @c MPI_Group_size.
78    *
79    * @returns The number of processes in the group.
80    */
81   int size() const;
82 
83   /**
84    * @brief Translates the ranks from one group into the ranks of the
85    * same processes in another group.
86    *
87    * This routine translates each of the integer rank values in the
88    * iterator range @c [first, last) from the current group into rank
89    * values of the corresponding processes in @p to_group. The
90    * corresponding rank values are written via the output iterator @c
91    * out. When there is no correspondence between a rank in the
92    * current group and a rank in @c to_group, the value @c
93    * MPI_UNDEFINED is written to the output iterator.
94    *
95    * @param first Beginning of the iterator range of ranks in the
96    * current group.
97    *
98    * @param last Past the end of the iterator range of ranks in the
99    * current group.
100    *
101    * @param to_group The group that we are translating ranks to.
102    *
103    * @param out The output iterator to which the translated ranks will
104    * be written.
105    *
106    * @returns the output iterator, which points one step past the last
107    * rank written.
108    */
109   template<typename InputIterator, typename OutputIterator>
110   OutputIterator translate_ranks(InputIterator first, InputIterator last,
111                                  const group& to_group, OutputIterator out);
112 
113   /**
114    * @brief Determines whether the group is non-empty.
115    *
116    * @returns True if the group is not empty, false if it is empty.
117    */
operator bool() const118   operator bool() const { return (bool)group_ptr; }
119 
120   /**
121    * @brief Retrieves the underlying @c MPI_Group associated with this
122    * group.
123    *
124    * @returns The @c MPI_Group handle manipulated by this object. If
125    * this object represents the empty group, returns @c
126    * MPI_GROUP_EMPTY.
127    */
operator MPI_Group() const128   operator MPI_Group() const
129   {
130     if (group_ptr)
131       return *group_ptr;
132     else
133       return MPI_GROUP_EMPTY;
134   }
135 
136   /**
137    *  @brief Creates a new group including a subset of the processes
138    *  in the current group.
139    *
140    *  This routine creates a new @c group which includes only those
141    *  processes in the current group that are listed in the integer
142    *  iterator range @c [first, last). Equivalent to @c
143    *  MPI_Group_incl.
144    *
145    *  @c first The beginning of the iterator range of ranks to include.
146    *
147    *  @c last Past the end of the iterator range of ranks to include.
148    *
149    *  @returns A new group containing those processes with ranks @c
150    *  [first, last) in the current group.
151    */
152   template<typename InputIterator>
153   group include(InputIterator first, InputIterator last);
154 
155   /**
156    *  @brief Creates a new group from all of the processes in the
157    *  current group, exluding a specific subset of the processes.
158    *
159    *  This routine creates a new @c group which includes all of the
160    *  processes in the current group except those whose ranks are
161    *  listed in the integer iterator range @c [first,
162    *  last). Equivalent to @c MPI_Group_excl.
163    *
164    *  @c first The beginning of the iterator range of ranks to exclude.
165    *
166    *  @c last Past the end of the iterator range of ranks to exclude.
167    *
168    *  @returns A new group containing all of the processes in the
169    *  current group except those processes with ranks @c [first, last)
170    *  in the current group.
171    */
172   template<typename InputIterator>
173   group exclude(InputIterator first, InputIterator last);
174 
175 
176 protected:
177   /**
178    * INTERNAL ONLY
179    *
180    * Function object that frees an MPI group and deletes the
181    * memory associated with it. Intended to be used as a deleter with
182    * shared_ptr.
183    */
184   struct group_free
185   {
operator ()boost::mpi::group::group_free186     void operator()(MPI_Group* comm) const
187     {
188       int finalized;
189       BOOST_MPI_CHECK_RESULT(MPI_Finalized, (&finalized));
190       if (!finalized)
191         BOOST_MPI_CHECK_RESULT(MPI_Group_free, (comm));
192       delete comm;
193     }
194   };
195 
196   /**
197    * The underlying MPI group. This is a shared pointer, so the actual
198    * MPI group which will be shared among all related instances of the
199    * @c group class. When there are no more such instances, the group
200    * will be automatically freed.
201    */
202   shared_ptr<MPI_Group> group_ptr;
203 };
204 
205 /**
206  * @brief Determines whether two process groups are identical.
207  *
208  * Equivalent to calling @c MPI_Group_compare and checking whether the
209  * result is @c MPI_IDENT.
210  *
211  * @returns True when the two process groups contain the same
212  * processes in the same order.
213  */
214 BOOST_MPI_DECL bool operator==(const group& g1, const group& g2);
215 
216 /**
217  * @brief Determines whether two process groups are not identical.
218  *
219  * Equivalent to calling @c MPI_Group_compare and checking whether the
220  * result is not @c MPI_IDENT.
221  *
222  * @returns False when the two process groups contain the same
223  * processes in the same order.
224  */
operator !=(const group & g1,const group & g2)225 inline bool operator!=(const group& g1, const group& g2)
226 {
227   return !(g1 == g2);
228 }
229 
230 /**
231  * @brief Computes the union of two process groups.
232  *
233  * This routine returns a new @c group that contains all processes
234  * that are either in group @c g1 or in group @c g2 (or both). The
235  * processes that are in @c g1 will be first in the resulting group,
236  * followed by the processes from @c g2 (but not also in @c
237  * g1). Equivalent to @c MPI_Group_union.
238  */
239 BOOST_MPI_DECL group operator|(const group& g1, const group& g2);
240 
241 /**
242  * @brief Computes the intersection of two process groups.
243  *
244  * This routine returns a new @c group that contains all processes
245  * that are in group @c g1 and in group @c g2, ordered in the same way
246  * as @c g1. Equivalent to @c MPI_Group_intersection.
247  */
248 BOOST_MPI_DECL group operator&(const group& g1, const group& g2);
249 
250 /**
251  * @brief Computes the difference between two process groups.
252  *
253  * This routine returns a new @c group that contains all processes
254  * that are in group @c g1 but not in group @c g2, ordered in the same way
255  * as @c g1. Equivalent to @c MPI_Group_difference.
256  */
257 BOOST_MPI_DECL group operator-(const group& g1, const group& g2);
258 
259 /************************************************************************
260  * Implementation details                                               *
261  ************************************************************************/
262 template<typename InputIterator, typename OutputIterator>
263 OutputIterator
translate_ranks(InputIterator first,InputIterator last,const group & to_group,OutputIterator out)264 group::translate_ranks(InputIterator first, InputIterator last,
265                        const group& to_group, OutputIterator out)
266 {
267   std::vector<int> in_array(first, last);
268   if (in_array.empty())
269     return out;
270 
271   std::vector<int> out_array(in_array.size());
272   BOOST_MPI_CHECK_RESULT(MPI_Group_translate_ranks,
273                          ((MPI_Group)*this,
274                           in_array.size(),
275                           &in_array[0],
276                           (MPI_Group)to_group,
277                           &out_array[0]));
278 
279   for (std::vector<int>::size_type i = 0, n = out_array.size(); i < n; ++i)
280     *out++ = out_array[i];
281   return out;
282 }
283 
284 /**
285  * INTERNAL ONLY
286  *
287  * Specialization of translate_ranks that handles the one case where
288  * we can avoid any memory allocation or copying.
289  */
290 template<>
291 BOOST_MPI_DECL int*
292 group::translate_ranks(int* first, int* last, const group& to_group, int* out);
293 
294 template<typename InputIterator>
include(InputIterator first,InputIterator last)295 group group::include(InputIterator first, InputIterator last)
296 {
297   if (first == last)
298     return group();
299 
300   std::vector<int> ranks(first, last);
301   MPI_Group result;
302   BOOST_MPI_CHECK_RESULT(MPI_Group_incl,
303                          ((MPI_Group)*this, ranks.size(), &ranks[0], &result));
304   return group(result, /*adopt=*/true);
305 }
306 
307 /**
308  * INTERNAL ONLY
309  *
310  * Specialization of group::include that handles the one case where we
311  * can avoid any memory allocation or copying before creating the
312  * group.
313  */
314 template<> BOOST_MPI_DECL group group::include(int* first, int* last);
315 
316 template<typename InputIterator>
exclude(InputIterator first,InputIterator last)317 group group::exclude(InputIterator first, InputIterator last)
318 {
319   if (first == last)
320     return group();
321 
322   std::vector<int> ranks(first, last);
323   MPI_Group result;
324   BOOST_MPI_CHECK_RESULT(MPI_Group_excl,
325                          ((MPI_Group)*this, ranks.size(), &ranks[0], &result));
326   return group(result, /*adopt=*/true);
327 }
328 
329 /**
330  * INTERNAL ONLY
331  *
332  * Specialization of group::exclude that handles the one case where we
333  * can avoid any memory allocation or copying before creating the
334  * group.
335  */
336 template<> BOOST_MPI_DECL group group::exclude(int* first, int* last);
337 
338 } } // end namespace boost::mpi
339 
340 #endif // BOOST_MPI_GROUP_HPP
341