1 // Copyright (C) 2004 The Trustees of Indiana University.
2 // Copyright (C) 2005-2006 Douglas Gregor <doug.gregor -at- gmail.com>
3 
4 // Use, modification and distribution is subject to the Boost Software
5 // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
6 // http://www.boost.org/LICENSE_1_0.txt)
7 
8 //  Authors: Douglas Gregor
9 //           Andrew Lumsdaine
10 
11 /** @file operations.hpp
12  *
13  *  This header provides a mapping from function objects to @c MPI_Op
14  *  constants used in MPI collective operations. It also provides
15  *  several new function object types not present in the standard @c
16  *  <functional> header that have direct mappings to @c MPI_Op.
17  */
18 #ifndef BOOST_MPI_IS_MPI_OP_HPP
19 #define BOOST_MPI_IS_MPI_OP_HPP
20 
21 #include <boost/mpi/config.hpp>
22 #include <boost/mpl/bool.hpp>
23 #include <boost/mpl/if.hpp>
24 #include <boost/mpl/and.hpp>
25 #include <boost/mpi/datatype.hpp>
26 #include <boost/utility/enable_if.hpp>
27 #include <functional>
28 
29 namespace boost { namespace mpi {
30 
31 template<typename Op, typename T> struct is_mpi_op;
32 
33 /**
34  * @brief Determine if a function object type is commutative.
35  *
36  * This trait determines if an operation @c Op is commutative when
37  * applied to values of type @c T. Parallel operations such as @c
38  * reduce and @c prefix_sum can be implemented more efficiently with
39  * commutative operations. To mark an operation as commutative, users
40  * should specialize @c is_commutative and derive from the class @c
41  * mpl::true_.
42  */
43 template<typename Op, typename T>
44 struct is_commutative : public mpl::false_ { };
45 
46 /**************************************************************************
47  * Function objects for MPI operations not in <functional> header         *
48  **************************************************************************/
49 
50 /**
51  *  @brief Compute the maximum of two values.
52  *
53  *  This binary function object computes the maximum of the two values
54  *  it is given. When used with MPI and a type @c T that has an
55  *  associated, built-in MPI data type, translates to @c MPI_MAX.
56  */
57 template<typename T>
58 struct maximum
59 {
60   typedef T first_argument_type;
61   typedef T second_argument_type;
62   typedef T result_type;
63   /** @returns the maximum of x and y. */
operator ()boost::mpi::maximum64   const T& operator()(const T& x, const T& y) const
65   {
66     return x < y? y : x;
67   }
68 };
69 
70 /**
71  *  @brief Compute the minimum of two values.
72  *
73  *  This binary function object computes the minimum of the two values
74  *  it is given. When used with MPI and a type @c T that has an
75  *  associated, built-in MPI data type, translates to @c MPI_MIN.
76  */
77 template<typename T>
78 struct minimum
79 {
80   typedef T first_argument_type;
81   typedef T second_argument_type;
82   typedef T result_type;
83   /** @returns the minimum of x and y. */
operator ()boost::mpi::minimum84   const T& operator()(const T& x, const T& y) const
85   {
86     return x < y? x : y;
87   }
88 };
89 
90 
91 /**
92  *  @brief Compute the bitwise AND of two integral values.
93  *
94  *  This binary function object computes the bitwise AND of the two
95  *  values it is given. When used with MPI and a type @c T that has an
96  *  associated, built-in MPI data type, translates to @c MPI_BAND.
97  */
98 template<typename T>
99 struct bitwise_and
100 {
101   typedef T first_argument_type;
102   typedef T second_argument_type;
103   typedef T result_type;
104   /** @returns @c x & y. */
operator ()boost::mpi::bitwise_and105   T operator()(const T& x, const T& y) const
106   {
107     return x & y;
108   }
109 };
110 
111 /**
112  *  @brief Compute the bitwise OR of two integral values.
113  *
114  *  This binary function object computes the bitwise OR of the two
115  *  values it is given. When used with MPI and a type @c T that has an
116  *  associated, built-in MPI data type, translates to @c MPI_BOR.
117  */
118 template<typename T>
119 struct bitwise_or
120 {
121   typedef T first_argument_type;
122   typedef T second_argument_type;
123   typedef T result_type;
124   /** @returns the @c x | y. */
operator ()boost::mpi::bitwise_or125   T operator()(const T& x, const T& y) const
126   {
127     return x | y;
128   }
129 };
130 
131 /**
132  *  @brief Compute the logical exclusive OR of two integral values.
133  *
134  *  This binary function object computes the logical exclusive of the
135  *  two values it is given. When used with MPI and a type @c T that has
136  *  an associated, built-in MPI data type, translates to @c MPI_LXOR.
137  */
138 template<typename T>
139 struct logical_xor
140 {
141   typedef T first_argument_type;
142   typedef T second_argument_type;
143   typedef T result_type;
144   /** @returns the logical exclusive OR of x and y. */
operator ()boost::mpi::logical_xor145   T operator()(const T& x, const T& y) const
146   {
147     return (x || y) && !(x && y);
148   }
149 };
150 
151 /**
152  *  @brief Compute the bitwise exclusive OR of two integral values.
153  *
154  *  This binary function object computes the bitwise exclusive OR of
155  *  the two values it is given. When used with MPI and a type @c T that
156  *  has an associated, built-in MPI data type, translates to @c
157  *  MPI_BXOR.
158  */
159 template<typename T>
160 struct bitwise_xor
161 {
162   typedef T first_argument_type;
163   typedef T second_argument_type;
164   typedef T result_type;
165   /** @returns @c x ^ y. */
operator ()boost::mpi::bitwise_xor166   T operator()(const T& x, const T& y) const
167   {
168     return x ^ y;
169   }
170 };
171 
172 /**************************************************************************
173  * MPI_Op queries                                                         *
174  **************************************************************************/
175 
176 /**
177  *  @brief Determine if a function object has an associated @c MPI_Op.
178  *
179  *  This trait determines if a function object type @c Op, when used
180  *  with argument type @c T, has an associated @c MPI_Op. If so, @c
181  *  is_mpi_op<Op,T> will derive from @c mpl::false_ and will
182  *  contain a static member function @c op that takes no arguments but
183  *  returns the associated @c MPI_Op value. For instance, @c
184  *  is_mpi_op<std::plus<int>,int>::op() returns @c MPI_SUM.
185  *
186  *  Users may specialize @c is_mpi_op for any other class templates
187  *  that map onto operations that have @c MPI_Op equivalences, such as
188  *  bitwise OR, logical and, or maximum. However, users are encouraged
189  *  to use the standard function objects in the @c functional and @c
190  *  boost/mpi/operations.hpp headers whenever possible. For
191  *  function objects that are class templates with a single template
192  *  parameter, it may be easier to specialize @c is_builtin_mpi_op.
193  */
194 template<typename Op, typename T>
195 struct is_mpi_op : public mpl::false_ { };
196 
197 /// INTERNAL ONLY
198 template<typename T>
199 struct is_mpi_op<maximum<T>, T>
200   : public boost::mpl::or_<is_mpi_integer_datatype<T>,
201                            is_mpi_floating_point_datatype<T> >
202 {
opboost::mpi::is_mpi_op203   static MPI_Op op() { return MPI_MAX; }
204 };
205 
206 /// INTERNAL ONLY
207 template<typename T>
208 struct is_mpi_op<minimum<T>, T>
209   : public boost::mpl::or_<is_mpi_integer_datatype<T>,
210                            is_mpi_floating_point_datatype<T> >
211 {
opboost::mpi::is_mpi_op212   static MPI_Op op() { return MPI_MIN; }
213 };
214 
215 /// INTERNAL ONLY
216 template<typename T>
217  struct is_mpi_op<std::plus<T>, T>
218   : public boost::mpl::or_<is_mpi_integer_datatype<T>,
219                            is_mpi_floating_point_datatype<T>,
220                            is_mpi_complex_datatype<T> >
221 {
opboost::mpi::is_mpi_op222   static MPI_Op op() { return MPI_SUM; }
223 };
224 
225 /// INTERNAL ONLY
226 template<typename T>
227  struct is_mpi_op<std::multiplies<T>, T>
228   : public boost::mpl::or_<is_mpi_integer_datatype<T>,
229                            is_mpi_floating_point_datatype<T>,
230                            is_mpi_complex_datatype<T> >
231 {
opboost::mpi::is_mpi_op232   static MPI_Op op() { return MPI_PROD; }
233 };
234 
235 /// INTERNAL ONLY
236 template<typename T>
237  struct is_mpi_op<std::logical_and<T>, T>
238   : public boost::mpl::or_<is_mpi_integer_datatype<T>,
239                            is_mpi_logical_datatype<T> >
240 {
opboost::mpi::is_mpi_op241   static MPI_Op op() { return MPI_LAND; }
242 };
243 
244 /// INTERNAL ONLY
245 template<typename T>
246  struct is_mpi_op<std::logical_or<T>, T>
247   : public boost::mpl::or_<is_mpi_integer_datatype<T>,
248                            is_mpi_logical_datatype<T> >
249 {
opboost::mpi::is_mpi_op250   static MPI_Op op() { return MPI_LOR; }
251 };
252 
253 /// INTERNAL ONLY
254 template<typename T>
255  struct is_mpi_op<logical_xor<T>, T>
256   : public boost::mpl::or_<is_mpi_integer_datatype<T>,
257                            is_mpi_logical_datatype<T> >
258 {
opboost::mpi::is_mpi_op259   static MPI_Op op() { return MPI_LXOR; }
260 };
261 
262 /// INTERNAL ONLY
263 template<typename T>
264  struct is_mpi_op<bitwise_and<T>, T>
265   : public boost::mpl::or_<is_mpi_integer_datatype<T>,
266                            is_mpi_byte_datatype<T> >
267 {
opboost::mpi::is_mpi_op268   static MPI_Op op() { return MPI_BAND; }
269 };
270 
271 /// INTERNAL ONLY
272 template<typename T>
273  struct is_mpi_op<bitwise_or<T>, T>
274   : public boost::mpl::or_<is_mpi_integer_datatype<T>,
275                            is_mpi_byte_datatype<T> >
276 {
opboost::mpi::is_mpi_op277   static MPI_Op op() { return MPI_BOR; }
278 };
279 
280 /// INTERNAL ONLY
281 template<typename T>
282  struct is_mpi_op<bitwise_xor<T>, T>
283   : public boost::mpl::or_<is_mpi_integer_datatype<T>,
284                            is_mpi_byte_datatype<T> >
285 {
opboost::mpi::is_mpi_op286   static MPI_Op op() { return MPI_BXOR; }
287 };
288 
289 namespace detail {
290   // A helper class used to create user-defined MPI_Ops
291   template<typename Op, typename T>
292   class user_op
293   {
294   public:
user_op()295     user_op()
296     {
297       BOOST_MPI_CHECK_RESULT(MPI_Op_create,
298                              (&user_op<Op, T>::perform,
299                               is_commutative<Op, T>::value,
300                               &mpi_op));
301     }
302 
~user_op()303     ~user_op()
304     {
305       if (std::uncaught_exception()) {
306         // Ignore failure cases: there are obviously other problems
307         // already, and we don't want to cause program termination if
308         // MPI_Op_free fails.
309         MPI_Op_free(&mpi_op);
310       } else {
311         BOOST_MPI_CHECK_RESULT(MPI_Op_free, (&mpi_op));
312       }
313     }
314 
get_mpi_op()315     MPI_Op& get_mpi_op()
316     {
317       return mpi_op;
318     }
319 
320   private:
321     MPI_Op mpi_op;
322 
perform(void * vinvec,void * voutvec,int * plen,MPI_Datatype *)323     static void BOOST_MPI_CALLING_CONVENTION perform(void* vinvec, void* voutvec, int* plen, MPI_Datatype*)
324     {
325       T* invec = static_cast<T*>(vinvec);
326       T* outvec = static_cast<T*>(voutvec);
327       Op op;
328       std::transform(invec, invec + *plen, outvec, outvec, op);
329     }
330   };
331 
332 } // end namespace detail
333 
334 } } // end namespace boost::mpi
335 
336 #endif // BOOST_MPI_GET_MPI_OP_HPP
337