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