Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-02-22 10:26:15

0001 // Copyright Jim Bosch 2010-2012.
0002 // Copyright Stefan Seefeld 2016.
0003 // Distributed under the Boost Software License, Version 1.0.
0004 // (See accompanying file LICENSE_1_0.txt or copy at
0005 // http://www.boost.org/LICENSE_1_0.txt)
0006 
0007 #ifndef boost_python_numpy_ufunc_hpp_
0008 #define boost_python_numpy_ufunc_hpp_
0009 
0010 /**
0011  *  @brief Utilities to create ufunc-like broadcasting functions out of C++ functors.
0012  */
0013 
0014 #include <boost/python.hpp>
0015 #include <boost/python/numpy/numpy_object_mgr_traits.hpp>
0016 #include <boost/python/numpy/dtype.hpp>
0017 #include <boost/python/numpy/ndarray.hpp>
0018 #include <boost/python/numpy/config.hpp>
0019 
0020 namespace boost { namespace python { namespace numpy {
0021 
0022 /**
0023  *  @brief A boost.python "object manager" (subclass of object) for PyArray_MultiIter.
0024  *
0025  *  multi_iter is a Python object, but a very low-level one.  It should generally only be used
0026  *  in loops of the form:
0027  *  @code
0028  *  while (iter.not_done()) {
0029  *      ...
0030  *      iter.next();
0031  *  }
0032  *  @endcode
0033  *
0034  *  @todo I can't tell if this type is exposed in Python anywhere; if it is, we should use that name.
0035  *        It's more dangerous than most object managers, however - maybe it actually belongs in
0036  *        a detail namespace?
0037  */
0038 class BOOST_NUMPY_DECL multi_iter : public object
0039 {
0040 public:
0041 
0042   BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(multi_iter, object);
0043 
0044   /// @brief Increment the iterator.
0045   void next();
0046 
0047   /// @brief Check if the iterator is at its end.
0048   bool not_done() const;
0049 
0050   /// @brief Return a pointer to the element of the nth broadcasted array.
0051   char * get_data(int n) const;
0052 
0053   /// @brief Return the number of dimensions of the broadcasted array expression.
0054   int get_nd() const;
0055     
0056   /// @brief Return the shape of the broadcasted array expression as an array of integers.
0057   Py_intptr_t const * get_shape() const;
0058 
0059   /// @brief Return the shape of the broadcasted array expression in the nth dimension.
0060   Py_intptr_t shape(int n) const;
0061     
0062 };
0063 
0064 /// @brief Construct a multi_iter over a single sequence or scalar object.
0065 BOOST_NUMPY_DECL multi_iter make_multi_iter(object const & a1);
0066 
0067 /// @brief Construct a multi_iter by broadcasting two objects.
0068 BOOST_NUMPY_DECL multi_iter make_multi_iter(object const & a1, object const & a2);
0069 
0070 /// @brief Construct a multi_iter by broadcasting three objects.
0071 BOOST_NUMPY_DECL multi_iter make_multi_iter(object const & a1, object const & a2, object const & a3);
0072 
0073 /**
0074  *  @brief Helps wrap a C++ functor taking a single scalar argument as a broadcasting ufunc-like
0075  *         Python object.
0076  *
0077  *  Typical usage looks like this:
0078  *  @code
0079  *  struct TimesPI 
0080  *  {
0081  *    typedef double argument_type;
0082  *    typedef double result_type;
0083  *    double operator()(double input) const { return input * M_PI; }
0084  *  };
0085  *  
0086  *  BOOST_PYTHON_MODULE(example)
0087  *  {
0088  *    class_< TimesPI >("TimesPI")
0089  *      .def("__call__", unary_ufunc<TimesPI>::make());
0090  *  }
0091  *  @endcode
0092  *  
0093  */
0094 template <typename TUnaryFunctor, 
0095           typename TArgument=typename TUnaryFunctor::argument_type,
0096           typename TResult=typename TUnaryFunctor::result_type>
0097 struct unary_ufunc 
0098 {
0099 
0100   /**
0101    *  @brief A C++ function with object arguments that broadcasts its arguments before
0102    *         passing them to the underlying C++ functor.
0103    */
0104   static object call(TUnaryFunctor & self, object const & input, object const & output)
0105   {
0106     dtype in_dtype = dtype::get_builtin<TArgument>();
0107     dtype out_dtype = dtype::get_builtin<TResult>();
0108     ndarray in_array = from_object(input, in_dtype, ndarray::ALIGNED);
0109     ndarray out_array = ! output.is_none() ?
0110       from_object(output, out_dtype, ndarray::ALIGNED | ndarray::WRITEABLE)
0111       : zeros(in_array.get_nd(), in_array.get_shape(), out_dtype);
0112     multi_iter iter = make_multi_iter(in_array, out_array);
0113     while (iter.not_done()) 
0114     {
0115       TArgument * argument = reinterpret_cast<TArgument*>(iter.get_data(0));
0116       TResult * result = reinterpret_cast<TResult*>(iter.get_data(1));
0117       *result = self(*argument);
0118       iter.next();
0119     } 
0120     return out_array.scalarize();
0121   }
0122 
0123   /**
0124    *  @brief Construct a boost.python function object from call() with reasonable keyword names.
0125    *
0126    *  Users will often want to specify their own keyword names with the same signature, but this
0127    *  is a convenient shortcut.
0128    */
0129   static object make()
0130   {
0131     return make_function(call, default_call_policies(), (arg("input"), arg("output")=object()));
0132   }
0133 };
0134 
0135 /**
0136  *  @brief Helps wrap a C++ functor taking a pair of scalar arguments as a broadcasting ufunc-like
0137  *         Python object.
0138  *
0139  *  Typical usage looks like this:
0140  *  @code
0141  *  struct CosSum 
0142  *  {
0143  *    typedef double first_argument_type;
0144  *    typedef double second_argument_type;
0145  *    typedef double result_type;
0146  *    double operator()(double input1, double input2) const { return std::cos(input1 + input2); }
0147  *  };
0148  *  
0149  *  BOOST_PYTHON_MODULE(example) 
0150  *  {
0151  *    class_< CosSum >("CosSum")
0152  *      .def("__call__", binary_ufunc<CosSum>::make());
0153  *  }
0154  *  @endcode
0155  *  
0156  */
0157 template <typename TBinaryFunctor, 
0158           typename TArgument1=typename TBinaryFunctor::first_argument_type,
0159           typename TArgument2=typename TBinaryFunctor::second_argument_type,
0160           typename TResult=typename TBinaryFunctor::result_type>
0161 struct binary_ufunc 
0162 {
0163 
0164   static object
0165   call(TBinaryFunctor & self, object const & input1, object const & input2,
0166        object const & output)
0167   {
0168     dtype in1_dtype = dtype::get_builtin<TArgument1>();
0169     dtype in2_dtype = dtype::get_builtin<TArgument2>();
0170     dtype out_dtype = dtype::get_builtin<TResult>();
0171     ndarray in1_array = from_object(input1, in1_dtype, ndarray::ALIGNED);
0172     ndarray in2_array = from_object(input2, in2_dtype, ndarray::ALIGNED);
0173     multi_iter iter = make_multi_iter(in1_array, in2_array);
0174     ndarray out_array = !output.is_none()
0175       ? from_object(output, out_dtype, ndarray::ALIGNED | ndarray::WRITEABLE)
0176       : zeros(iter.get_nd(), iter.get_shape(), out_dtype);
0177     iter = make_multi_iter(in1_array, in2_array, out_array);
0178     while (iter.not_done()) 
0179     {
0180       TArgument1 * argument1 = reinterpret_cast<TArgument1*>(iter.get_data(0));
0181       TArgument2 * argument2 = reinterpret_cast<TArgument2*>(iter.get_data(1));
0182       TResult * result = reinterpret_cast<TResult*>(iter.get_data(2));
0183       *result = self(*argument1, *argument2);
0184       iter.next();
0185     } 
0186     return out_array.scalarize();
0187   }
0188 
0189   static object make()
0190   {
0191     return make_function(call, default_call_policies(),
0192                 (arg("input1"), arg("input2"), arg("output")=object()));
0193   }
0194 
0195 };
0196 
0197 } // namespace boost::python::numpy
0198 
0199 namespace converter 
0200 {
0201 
0202 NUMPY_OBJECT_MANAGER_TRAITS(numpy::multi_iter);
0203 
0204 }}} // namespace boost::python::converter
0205 
0206 #endif