]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/boost/python/numpy/ufunc.hpp
update sources to v12.2.3
[ceph.git] / ceph / src / boost / boost / python / numpy / ufunc.hpp
1 // Copyright Jim Bosch 2010-2012.
2 // Copyright Stefan Seefeld 2016.
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
6
7 #ifndef boost_python_numpy_ufunc_hpp_
8 #define boost_python_numpy_ufunc_hpp_
9
10 /**
11 * @brief Utilities to create ufunc-like broadcasting functions out of C++ functors.
12 */
13
14 #include <boost/python.hpp>
15 #include <boost/python/numpy/numpy_object_mgr_traits.hpp>
16 #include <boost/python/numpy/dtype.hpp>
17 #include <boost/python/numpy/ndarray.hpp>
18 #include <boost/python/numpy/config.hpp>
19
20 namespace boost { namespace python { namespace numpy {
21
22 /**
23 * @brief A boost.python "object manager" (subclass of object) for PyArray_MultiIter.
24 *
25 * multi_iter is a Python object, but a very low-level one. It should generally only be used
26 * in loops of the form:
27 * @code
28 * while (iter.not_done()) {
29 * ...
30 * iter.next();
31 * }
32 * @endcode
33 *
34 * @todo I can't tell if this type is exposed in Python anywhere; if it is, we should use that name.
35 * It's more dangerous than most object managers, however - maybe it actually belongs in
36 * a detail namespace?
37 */
38 class BOOST_NUMPY_DECL multi_iter : public object
39 {
40 public:
41
42 BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(multi_iter, object);
43
44 /// @brief Increment the iterator.
45 void next();
46
47 /// @brief Check if the iterator is at its end.
48 bool not_done() const;
49
50 /// @brief Return a pointer to the element of the nth broadcasted array.
51 char * get_data(int n) const;
52
53 /// @brief Return the number of dimensions of the broadcasted array expression.
54 int get_nd() const;
55
56 /// @brief Return the shape of the broadcasted array expression as an array of integers.
57 Py_intptr_t const * get_shape() const;
58
59 /// @brief Return the shape of the broadcasted array expression in the nth dimension.
60 Py_intptr_t shape(int n) const;
61
62 };
63
64 /// @brief Construct a multi_iter over a single sequence or scalar object.
65 BOOST_NUMPY_DECL multi_iter make_multi_iter(object const & a1);
66
67 /// @brief Construct a multi_iter by broadcasting two objects.
68 BOOST_NUMPY_DECL multi_iter make_multi_iter(object const & a1, object const & a2);
69
70 /// @brief Construct a multi_iter by broadcasting three objects.
71 BOOST_NUMPY_DECL multi_iter make_multi_iter(object const & a1, object const & a2, object const & a3);
72
73 /**
74 * @brief Helps wrap a C++ functor taking a single scalar argument as a broadcasting ufunc-like
75 * Python object.
76 *
77 * Typical usage looks like this:
78 * @code
79 * struct TimesPI
80 * {
81 * typedef double argument_type;
82 * typedef double result_type;
83 * double operator()(double input) const { return input * M_PI; }
84 * };
85 *
86 * BOOST_PYTHON_MODULE(example)
87 * {
88 * class_< TimesPI >("TimesPI")
89 * .def("__call__", unary_ufunc<TimesPI>::make());
90 * }
91 * @endcode
92 *
93 */
94 template <typename TUnaryFunctor,
95 typename TArgument=typename TUnaryFunctor::argument_type,
96 typename TResult=typename TUnaryFunctor::result_type>
97 struct unary_ufunc
98 {
99
100 /**
101 * @brief A C++ function with object arguments that broadcasts its arguments before
102 * passing them to the underlying C++ functor.
103 */
104 static object call(TUnaryFunctor & self, object const & input, object const & output)
105 {
106 dtype in_dtype = dtype::get_builtin<TArgument>();
107 dtype out_dtype = dtype::get_builtin<TResult>();
108 ndarray in_array = from_object(input, in_dtype, ndarray::ALIGNED);
109 ndarray out_array = ! output.is_none() ?
110 from_object(output, out_dtype, ndarray::ALIGNED | ndarray::WRITEABLE)
111 : zeros(in_array.get_nd(), in_array.get_shape(), out_dtype);
112 multi_iter iter = make_multi_iter(in_array, out_array);
113 while (iter.not_done())
114 {
115 TArgument * argument = reinterpret_cast<TArgument*>(iter.get_data(0));
116 TResult * result = reinterpret_cast<TResult*>(iter.get_data(1));
117 *result = self(*argument);
118 iter.next();
119 }
120 return out_array.scalarize();
121 }
122
123 /**
124 * @brief Construct a boost.python function object from call() with reasonable keyword names.
125 *
126 * Users will often want to specify their own keyword names with the same signature, but this
127 * is a convenient shortcut.
128 */
129 static object make()
130 {
131 return make_function(call, default_call_policies(), (arg("input"), arg("output")=object()));
132 }
133 };
134
135 /**
136 * @brief Helps wrap a C++ functor taking a pair of scalar arguments as a broadcasting ufunc-like
137 * Python object.
138 *
139 * Typical usage looks like this:
140 * @code
141 * struct CosSum
142 * {
143 * typedef double first_argument_type;
144 * typedef double second_argument_type;
145 * typedef double result_type;
146 * double operator()(double input1, double input2) const { return std::cos(input1 + input2); }
147 * };
148 *
149 * BOOST_PYTHON_MODULE(example)
150 * {
151 * class_< CosSum >("CosSum")
152 * .def("__call__", binary_ufunc<CosSum>::make());
153 * }
154 * @endcode
155 *
156 */
157 template <typename TBinaryFunctor,
158 typename TArgument1=typename TBinaryFunctor::first_argument_type,
159 typename TArgument2=typename TBinaryFunctor::second_argument_type,
160 typename TResult=typename TBinaryFunctor::result_type>
161 struct binary_ufunc
162 {
163
164 static object
165 call(TBinaryFunctor & self, object const & input1, object const & input2,
166 object const & output)
167 {
168 dtype in1_dtype = dtype::get_builtin<TArgument1>();
169 dtype in2_dtype = dtype::get_builtin<TArgument2>();
170 dtype out_dtype = dtype::get_builtin<TResult>();
171 ndarray in1_array = from_object(input1, in1_dtype, ndarray::ALIGNED);
172 ndarray in2_array = from_object(input2, in2_dtype, ndarray::ALIGNED);
173 multi_iter iter = make_multi_iter(in1_array, in2_array);
174 ndarray out_array = !output.is_none()
175 ? from_object(output, out_dtype, ndarray::ALIGNED | ndarray::WRITEABLE)
176 : zeros(iter.get_nd(), iter.get_shape(), out_dtype);
177 iter = make_multi_iter(in1_array, in2_array, out_array);
178 while (iter.not_done())
179 {
180 TArgument1 * argument1 = reinterpret_cast<TArgument1*>(iter.get_data(0));
181 TArgument2 * argument2 = reinterpret_cast<TArgument2*>(iter.get_data(1));
182 TResult * result = reinterpret_cast<TResult*>(iter.get_data(2));
183 *result = self(*argument1, *argument2);
184 iter.next();
185 }
186 return out_array.scalarize();
187 }
188
189 static object make()
190 {
191 return make_function(call, default_call_policies(),
192 (arg("input1"), arg("input2"), arg("output")=object()));
193 }
194
195 };
196
197 } // namespace boost::python::numpy
198
199 namespace converter
200 {
201
202 NUMPY_OBJECT_MANAGER_TRAITS(numpy::multi_iter);
203
204 }}} // namespace boost::python::converter
205
206 #endif