206 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
		
		
			
		
	
	
			206 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
|   | // Copyright Jim Bosch 2010-2012. | ||
|  | // Copyright Stefan Seefeld 2016. | ||
|  | // Distributed under 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) | ||
|  | 
 | ||
|  | #ifndef boost_python_numpy_ufunc_hpp_ | ||
|  | #define boost_python_numpy_ufunc_hpp_ | ||
|  | 
 | ||
|  | /** | ||
|  |  *  @brief Utilities to create ufunc-like broadcasting functions out of C++ functors. | ||
|  |  */ | ||
|  | 
 | ||
|  | #include <boost/python.hpp> | ||
|  | #include <boost/python/numpy/numpy_object_mgr_traits.hpp> | ||
|  | #include <boost/python/numpy/dtype.hpp> | ||
|  | #include <boost/python/numpy/ndarray.hpp> | ||
|  | 
 | ||
|  | namespace boost { namespace python { namespace numpy { | ||
|  | 
 | ||
|  | /** | ||
|  |  *  @brief A boost.python "object manager" (subclass of object) for PyArray_MultiIter. | ||
|  |  * | ||
|  |  *  multi_iter is a Python object, but a very low-level one.  It should generally only be used | ||
|  |  *  in loops of the form: | ||
|  |  *  @code | ||
|  |  *  while (iter.not_done()) { | ||
|  |  *      ... | ||
|  |  *      iter.next(); | ||
|  |  *  } | ||
|  |  *  @endcode | ||
|  |  * | ||
|  |  *  @todo I can't tell if this type is exposed in Python anywhere; if it is, we should use that name. | ||
|  |  *        It's more dangerous than most object managers, however - maybe it actually belongs in | ||
|  |  *        a detail namespace? | ||
|  |  */ | ||
|  | class multi_iter : public object | ||
|  | { | ||
|  | public: | ||
|  | 
 | ||
|  |   BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(multi_iter, object); | ||
|  | 
 | ||
|  |   /// @brief Increment the iterator. | ||
|  |   void next(); | ||
|  | 
 | ||
|  |   /// @brief Check if the iterator is at its end. | ||
|  |   bool not_done() const; | ||
|  | 
 | ||
|  |   /// @brief Return a pointer to the element of the nth broadcasted array. | ||
|  |   char * get_data(int n) const; | ||
|  | 
 | ||
|  |   /// @brief Return the number of dimensions of the broadcasted array expression. | ||
|  |   int get_nd() const; | ||
|  |      | ||
|  |   /// @brief Return the shape of the broadcasted array expression as an array of integers. | ||
|  |   Py_intptr_t const * get_shape() const; | ||
|  | 
 | ||
|  |   /// @brief Return the shape of the broadcasted array expression in the nth dimension. | ||
|  |   Py_intptr_t shape(int n) const; | ||
|  |      | ||
|  | }; | ||
|  | 
 | ||
|  | /// @brief Construct a multi_iter over a single sequence or scalar object. | ||
|  | multi_iter make_multi_iter(object const & a1); | ||
|  | 
 | ||
|  | /// @brief Construct a multi_iter by broadcasting two objects. | ||
|  | multi_iter make_multi_iter(object const & a1, object const & a2); | ||
|  | 
 | ||
|  | /// @brief Construct a multi_iter by broadcasting three objects. | ||
|  | multi_iter make_multi_iter(object const & a1, object const & a2, object const & a3); | ||
|  | 
 | ||
|  | /** | ||
|  |  *  @brief Helps wrap a C++ functor taking a single scalar argument as a broadcasting ufunc-like | ||
|  |  *         Python object. | ||
|  |  * | ||
|  |  *  Typical usage looks like this: | ||
|  |  *  @code | ||
|  |  *  struct TimesPI  | ||
|  |  *  { | ||
|  |  *    typedef double argument_type; | ||
|  |  *    typedef double result_type; | ||
|  |  *    double operator()(double input) const { return input * M_PI; } | ||
|  |  *  }; | ||
|  |  *   | ||
|  |  *  BOOST_PYTHON_MODULE(example) | ||
|  |  *  { | ||
|  |  *    class_< TimesPI >("TimesPI") | ||
|  |  *      .def("__call__", unary_ufunc<TimesPI>::make()); | ||
|  |  *  } | ||
|  |  *  @endcode | ||
|  |  *   | ||
|  |  */ | ||
|  | template <typename TUnaryFunctor,  | ||
|  |           typename TArgument=typename TUnaryFunctor::argument_type, | ||
|  |           typename TResult=typename TUnaryFunctor::result_type> | ||
|  | struct unary_ufunc  | ||
|  | { | ||
|  | 
 | ||
|  |   /** | ||
|  |    *  @brief A C++ function with object arguments that broadcasts its arguments before | ||
|  |    *         passing them to the underlying C++ functor. | ||
|  |    */ | ||
|  |   static object call(TUnaryFunctor & self, object const & input, object const & output) | ||
|  |   { | ||
|  |     dtype in_dtype = dtype::get_builtin<TArgument>(); | ||
|  |     dtype out_dtype = dtype::get_builtin<TResult>(); | ||
|  |     ndarray in_array = from_object(input, in_dtype, ndarray::ALIGNED); | ||
|  |     ndarray out_array = (output != object()) ? | ||
|  |       from_object(output, out_dtype, ndarray::ALIGNED | ndarray::WRITEABLE) | ||
|  |       : zeros(in_array.get_nd(), in_array.get_shape(), out_dtype); | ||
|  |     multi_iter iter = make_multi_iter(in_array, out_array); | ||
|  |     while (iter.not_done())  | ||
|  |     { | ||
|  |       TArgument * argument = reinterpret_cast<TArgument*>(iter.get_data(0)); | ||
|  |       TResult * result = reinterpret_cast<TResult*>(iter.get_data(1)); | ||
|  |       *result = self(*argument); | ||
|  |       iter.next(); | ||
|  |     }  | ||
|  |     return out_array.scalarize(); | ||
|  |   } | ||
|  | 
 | ||
|  |   /** | ||
|  |    *  @brief Construct a boost.python function object from call() with reasonable keyword names. | ||
|  |    * | ||
|  |    *  Users will often want to specify their own keyword names with the same signature, but this | ||
|  |    *  is a convenient shortcut. | ||
|  |    */ | ||
|  |   static object make() | ||
|  |   { | ||
|  |     return make_function(call, default_call_policies(), (arg("input"), arg("output")=object())); | ||
|  |   } | ||
|  | }; | ||
|  | 
 | ||
|  | /** | ||
|  |  *  @brief Helps wrap a C++ functor taking a pair of scalar arguments as a broadcasting ufunc-like | ||
|  |  *         Python object. | ||
|  |  * | ||
|  |  *  Typical usage looks like this: | ||
|  |  *  @code | ||
|  |  *  struct CosSum  | ||
|  |  *  { | ||
|  |  *    typedef double first_argument_type; | ||
|  |  *    typedef double second_argument_type; | ||
|  |  *    typedef double result_type; | ||
|  |  *    double operator()(double input1, double input2) const { return std::cos(input1 + input2); } | ||
|  |  *  }; | ||
|  |  *   | ||
|  |  *  BOOST_PYTHON_MODULE(example)  | ||
|  |  *  { | ||
|  |  *    class_< CosSum >("CosSum") | ||
|  |  *      .def("__call__", binary_ufunc<CosSum>::make()); | ||
|  |  *  } | ||
|  |  *  @endcode | ||
|  |  *   | ||
|  |  */ | ||
|  | template <typename TBinaryFunctor,  | ||
|  |           typename TArgument1=typename TBinaryFunctor::first_argument_type, | ||
|  |           typename TArgument2=typename TBinaryFunctor::second_argument_type, | ||
|  |           typename TResult=typename TBinaryFunctor::result_type> | ||
|  | struct binary_ufunc  | ||
|  | { | ||
|  | 
 | ||
|  |   static object | ||
|  |   call(TBinaryFunctor & self, object const & input1, object const & input2, | ||
|  |        object const & output) | ||
|  |   { | ||
|  |     dtype in1_dtype = dtype::get_builtin<TArgument1>(); | ||
|  |     dtype in2_dtype = dtype::get_builtin<TArgument2>(); | ||
|  |     dtype out_dtype = dtype::get_builtin<TResult>(); | ||
|  |     ndarray in1_array = from_object(input1, in1_dtype, ndarray::ALIGNED); | ||
|  |     ndarray in2_array = from_object(input2, in2_dtype, ndarray::ALIGNED); | ||
|  |     multi_iter iter = make_multi_iter(in1_array, in2_array); | ||
|  |     ndarray out_array = (output != object()) | ||
|  |       ? from_object(output, out_dtype, ndarray::ALIGNED | ndarray::WRITEABLE) | ||
|  |       : zeros(iter.get_nd(), iter.get_shape(), out_dtype); | ||
|  |     iter = make_multi_iter(in1_array, in2_array, out_array); | ||
|  |     while (iter.not_done())  | ||
|  |     { | ||
|  |       TArgument1 * argument1 = reinterpret_cast<TArgument1*>(iter.get_data(0)); | ||
|  |       TArgument2 * argument2 = reinterpret_cast<TArgument2*>(iter.get_data(1)); | ||
|  |       TResult * result = reinterpret_cast<TResult*>(iter.get_data(2)); | ||
|  |       *result = self(*argument1, *argument2); | ||
|  |       iter.next(); | ||
|  |     }  | ||
|  |     return out_array.scalarize(); | ||
|  |   } | ||
|  | 
 | ||
|  |   static object make() | ||
|  |   { | ||
|  |     return make_function(call, default_call_policies(), | ||
|  | 			    (arg("input1"), arg("input2"), arg("output")=object())); | ||
|  |   } | ||
|  | 
 | ||
|  | }; | ||
|  | 
 | ||
|  | } // namespace boost::python::numpy | ||
|  | 
 | ||
|  | namespace converter  | ||
|  | { | ||
|  | 
 | ||
|  | NUMPY_OBJECT_MANAGER_TRAITS(numpy::multi_iter); | ||
|  | 
 | ||
|  | }}} // namespace boost::python::converter | ||
|  | 
 | ||
|  | #endif |