numpycpp
 All Classes Namespaces Files Functions Typedefs Pages
numpy.cpp
Go to the documentation of this file.
1 /*
2  C *opyright (c) 2016 Michael Welter
3 
4  This file is part of numpycpp.
5 
6  numpycpp is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  numpycpp is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with numpycpp. If not, see <http://www.gnu.org/licenses/>.
18  */
19 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
20 #include "numpy.hpp"
21 #include "numpy/ndarrayobject.h"
22 #include "assert.h"
23 #include <stdexcept>
24 #include <cstring>
25 
26 namespace py = boost::python;
27 
28 namespace boost { namespace python { namespace numpy {
29 
30 namespace mw_py_impl
31 {
32 namespace np = boost::python::numpy;
33 
34 #if __cplusplus > 200000L
35 static_assert(np::MAX_DIM == NPY_MAXDIMS, "MAX_DIMS should be equal to NPY_MAXDIMS");
36 #endif
37 
38 /* Sometimes scalar numpy arrays end up as arguments to c++ function calls.
39  * For these cases we need automatic conversion functions such as the following.
40  */
41 template<class T>
43 {
44  static PyArray_Descr* descr;
45 
46  static void Register()
47  {
48  int itemtype = np::getItemtype<T>();
49  descr = PyArray_DescrFromType(itemtype);
50 
51  boost::python::converter::registry::push_back(
52  &convertible,
53  &construct,
54  boost::python::type_id<T>());
55  }
56 
57  static void* convertible(PyObject* obj_ptr)
58  {
59  if (!PyArray_CheckScalar(obj_ptr)) return NULL;
60  PyArray_Descr* objdescr = PyArray_DescrFromScalar(obj_ptr);
61  bool ok = PyArray_CanCastTypeTo(objdescr, descr, NPY_SAME_KIND_CASTING);
62  Py_DECREF(objdescr);
63  return ok ? obj_ptr : NULL;
64  }
65 
66  static void construct(
67  PyObject* obj_ptr,
68  boost::python::converter::rvalue_from_python_stage1_data* data)
69  {
70  void* storage = ((boost::python::converter::rvalue_from_python_storage<T>*)data)->storage.bytes;
71  data->convertible = storage;
72 
73  union
74  {
75  T data;
76  unsigned char buffer[sizeof(T)];
77  } buffer;
78  memset(buffer.buffer, 0, sizeof(T));
79 
80  PyArray_CastScalarToCtype(obj_ptr, buffer.buffer, descr);
81  new (storage) T(buffer.data);
82  }
83 };
84 
85 template<class T>
86 PyArray_Descr* from_numpy_scalar<T>::descr = NULL;
87 
88 
89 
90 template<class T>
91 struct to_arrayt
92 {
93  typedef np::arrayt<T> ArrayType;
94 
95  static void Register()
96  {
97  boost::python::converter::registry::push_back(
98  &convertible,
99  &construct,
100  boost::python::type_id<ArrayType>());
101  }
102 
103  static void* convertible(PyObject* obj_ptr)
104  {
105  bool ok = PyArray_Check(obj_ptr);
106  ok &= np::isCompatibleType<T>(PyArray_TYPE((PyArrayObject*)obj_ptr));
107  return ok ? obj_ptr : 0;
108  }
109 
110  static void construct(
111  PyObject* obj_ptr,
112  boost::python::converter::rvalue_from_python_stage1_data* data)
113  {
114  void* storage = ((boost::python::converter::rvalue_from_python_storage<ArrayType>*)data)->storage.bytes;
115  data->convertible = storage;
116 
117  new (storage) arrayt<T>(py::object(py::borrowed(obj_ptr)));
118  }
119 };
120 
121 
123 {
124  typedef np::arraytbase ArrayType;
125 
126  static void Register()
127  {
128  boost::python::converter::registry::push_back(
129  &convertible,
130  &construct,
131  boost::python::type_id<ArrayType>());
132  }
133 
134  static void* convertible(PyObject* obj_ptr)
135  {
136  bool ok = PyArray_Check(obj_ptr);
137  return ok ? obj_ptr : 0;
138  }
139 
140  static void construct(
141  PyObject* obj_ptr,
142  boost::python::converter::rvalue_from_python_stage1_data* data)
143  {
144  void* storage = ((boost::python::converter::rvalue_from_python_storage<ArrayType>*)data)->storage.bytes;
145  data->convertible = storage;
146  new (storage) arraytbase(py::object(py::borrowed(obj_ptr)));
147  }
148 };
149 
150 
152 {
153  typedef np::arraytbase TheType;
154 
155  static PyObject* convert(const TheType& arr)
156  {
157  return py::incref(arr.getObject().ptr());
158  }
159 
160  static void Register()
161  {
162  py::to_python_converter<TheType, from_arraytbase>();
163  }
164 };
165 
166 template<class T>
168 {
169  typedef np::arrayt<T> TheType;
170 
171  static PyObject* convert(const TheType& arr)
172  {
173  return py::incref(arr.getObject().ptr());
174  }
175 
176  static void Register()
177  {
178  py::to_python_converter<TheType, from_arrayt<T> >();
179  }
180 };
181 
182 
183 template<class T>
184 void RegisterAllConvertersForType()
185 {
189 }
190 
191 } // impl namespace
192 
193 #define CAST_TO_PPYARRAYOBJECT(p) ((PyArrayObject*)(p))
194 
195 
197 {
198  import_array1(); // this is from the numpy c-API and import the numpy module into python
199 
200  // these are boost python type conversions
201  mw_py_impl::RegisterAllConvertersForType<bool>();
202  mw_py_impl::RegisterAllConvertersForType<char>();
203  mw_py_impl::RegisterAllConvertersForType<short>();
204  mw_py_impl::RegisterAllConvertersForType<int>();
205  mw_py_impl::RegisterAllConvertersForType<long>();
206  mw_py_impl::RegisterAllConvertersForType<long long>();
207 
208  mw_py_impl::RegisterAllConvertersForType<unsigned char>();
209  mw_py_impl::RegisterAllConvertersForType<unsigned short>();
210  mw_py_impl::RegisterAllConvertersForType<unsigned int>();
211  mw_py_impl::RegisterAllConvertersForType<unsigned long>();
212  mw_py_impl::RegisterAllConvertersForType<unsigned long long>();
213 
214  mw_py_impl::RegisterAllConvertersForType<float>();
215  mw_py_impl::RegisterAllConvertersForType<double>();
216 
217  mw_py_impl::to_toarraytbase::Register();
218  mw_py_impl::from_arraytbase::Register();
219 
220  py::numeric::array::set_module_and_type("numpy", "ndarray"); // use numpy
221 }
222 
223 
224 object zeros(int rank, const Py_ssize_t *dims, int type)
225 {
226  npy_intp* tmp = const_cast<npy_intp*>(dims);
227  PyObject* p = PyArray_ZEROS(rank,tmp,type,true);
228  return py::object(handle<>(p));
229 }
230 
231 
232 object empty(int rank, const Py_ssize_t *dims, int type )
233 {
234  npy_intp* tmp = const_cast<npy_intp*>(dims);
235  PyObject* p = PyArray_EMPTY(rank, tmp, type, true);
236  return py::object(handle<>(p));
237 }
238 
239 
240 
241 int getItemtype(const object &a)
242 {
243  if (PyArray_Check(a.ptr()))
244  {
245  const PyArrayObject* p = CAST_TO_PPYARRAYOBJECT(a.ptr());
246  int t = PyArray_TYPE(p);
247  return t;
248  }
249  else
250  return -1;
251 }
252 
253 
254 arraytbase::arraytbase(const object& a_) :
255 obj(py::object()),
256  objptr(NULL)
257 {
258  construct(a_, -1);
259 }
260 
261 arraytbase::arraytbase(const object& a_, int typesize) :
262 obj(py::object()),
263  objptr(NULL)
264 {
265  construct(a_, typesize);
266 }
267 
268 
269 void arraytbase::construct(object const &a_, int typesize)
270 {
271  obj = a_;
272  if (obj.is_none()) return;
273 
274  if (!PyArray_Check(obj.ptr()))
275  throw std::invalid_argument("arrayt: attempted construction with something that is not derived from ndarray");
276 
277  objptr = obj.ptr();
278 
279  bool is_behaved = PyArray_ISBEHAVED(CAST_TO_PPYARRAYOBJECT(objptr));
280  if (!is_behaved)
281  throw std::invalid_argument("arrayt: numpy array is not behaved");
282 
283  if (typesize>0 && typesize != itemsize())
284  throw std::invalid_argument("arrayt: array itemsize does not match template argument");
285 }
286 
287 
288 int arraytbase::itemtype() const
289 {
290  return PyArray_TYPE(CAST_TO_PPYARRAYOBJECT(objptr));
291 }
292 
293 int arraytbase::itemsize() const
294 {
295  return PyArray_ITEMSIZE(CAST_TO_PPYARRAYOBJECT(objptr));
296 }
297 
298 int arraytbase::rank() const
299 {
300  return PyArray_NDIM(CAST_TO_PPYARRAYOBJECT(objptr));
301 }
302 
303 const Py_ssize_t* arraytbase::shape() const
304 {
305  return PyArray_DIMS(CAST_TO_PPYARRAYOBJECT(objptr));
306 }
307 
308 
309 const Py_ssize_t* arraytbase::strides() const
310 {
311  return PyArray_STRIDES(CAST_TO_PPYARRAYOBJECT(objptr));
312 }
313 
314 
315 bool arraytbase::isCContiguous() const
316 {
317  return PyArray_IS_C_CONTIGUOUS(CAST_TO_PPYARRAYOBJECT(objptr));
318 }
319 
320 
321 bool arraytbase::isWriteable() const
322 {
323  return PyArray_ISWRITEABLE(CAST_TO_PPYARRAYOBJECT(objptr));
324 }
325 
326 
327 bool arraytbase::isFContiguous() const
328 {
329  return PyArray_IS_F_CONTIGUOUS(CAST_TO_PPYARRAYOBJECT(objptr));
330 }
331 
332 
333 char* arraytbase::bytes()
334 {
335  return PyArray_BYTES(CAST_TO_PPYARRAYOBJECT(objptr));
336 }
337 
338 
339 template<class T>
340 bool isCompatibleType(int id)
341 {
342  return false;
343 }
344 
346 #define DEF_TYPE_COMPATIBILITY1(T, npyT1) \
347  template<> bool isCompatibleType<T>(int id) { return id == npyT1; }
348 #define DEF_TYPE_COMPATIBILITY2(T, npyT1, npyT2) \
349  template<> bool isCompatibleType<T>(int id) { return id == npyT1 || id == npyT2; }
350 #define DEF_TYPE_COMPATIBILITY3(T, npyT1, npyT2, npyT3) \
351  template<> bool isCompatibleType<T>(int id) { return id == npyT1 || id == npyT2 || id == npyT3; }
352 #define DEF_TYPE_COMPATIBILITY4(T, npyT1, npyT2, npyT3, npyT4) \
353  template<> bool isCompatibleType<T>(int id) { return id == npyT1 || id == npyT2 || id == npyT3 || id == npyT4; }
354 
355 
356 DEF_TYPE_COMPATIBILITY1(PyObject*, NPY_OBJECT)
357 DEF_TYPE_COMPATIBILITY1(bool, NPY_BOOL)
358 DEF_TYPE_COMPATIBILITY3(char, NPY_INT8, NPY_BYTE, NPY_BOOL)
359 DEF_TYPE_COMPATIBILITY3(unsigned char, NPY_UINT8, NPY_UBYTE, NPY_BOOL)
360 DEF_TYPE_COMPATIBILITY2(short, NPY_INT16, NPY_SHORT)
361 DEF_TYPE_COMPATIBILITY2(unsigned short, NPY_UINT16, NPY_USHORT)
362 DEF_TYPE_COMPATIBILITY2(int, NPY_INT32, NPY_INT) // NPY_INT is always 32 bit
363 DEF_TYPE_COMPATIBILITY2(unsigned int, NPY_UINT32, NPY_UINT)
364 // different sizes of type long, see here http://en.cppreference.com/w/cpp/language/types
365 #if NPY_BITSOF_LONG == 64 // defined in npy_common.h
366  DEF_TYPE_COMPATIBILITY3(unsigned long, NPY_UINT64, NPY_ULONG, NPY_ULONGLONG)
367  DEF_TYPE_COMPATIBILITY3(long, NPY_INT64, NPY_LONG, NPY_LONGLONG) // NPY_LONG can be NPY_INT or NPY_LONGLONG; NPY_LONGLONG and NPY_INT64 are always 64 bit
368 #elif NPY_BITSOF_LONG == 32
369  DEF_TYPE_COMPATIBILITY2(unsigned long, NPY_UINT32, NPY_UINT)
370  DEF_TYPE_COMPATIBILITY2(long, NPY_INT32, NPY_INT)
371 #endif
372 DEF_TYPE_COMPATIBILITY3(long long, NPY_INT64, NPY_LONG, NPY_LONGLONG)
373 DEF_TYPE_COMPATIBILITY3(unsigned long long, NPY_UINT64, NPY_ULONG, NPY_ULONGLONG)
374 DEF_TYPE_COMPATIBILITY1(float, NPY_FLOAT32)
375 DEF_TYPE_COMPATIBILITY2(double, NPY_FLOAT64, NPY_DOUBLE)
376 
377 #define DEF_ARRY_TYPE2ID(t,id)\
378 template<> int getItemtype<t>() {\
379 return id; \
380 };
381 
382 DEF_ARRY_TYPE2ID(PyObject*, NPY_OBJECT);
383 DEF_ARRY_TYPE2ID(float,NPY_FLOAT32);
384 DEF_ARRY_TYPE2ID(double,NPY_FLOAT64);
385 DEF_ARRY_TYPE2ID(int,NPY_INT);
386 DEF_ARRY_TYPE2ID(long,NPY_LONG);
387 DEF_ARRY_TYPE2ID(long long ,NPY_LONGLONG);
388 DEF_ARRY_TYPE2ID(short,NPY_SHORT);
389 DEF_ARRY_TYPE2ID(char,NPY_BYTE);
390 DEF_ARRY_TYPE2ID(bool,NPY_BOOL); // lets hope that bool is one byte long
391 DEF_ARRY_TYPE2ID(unsigned int,NPY_UINT);
392 DEF_ARRY_TYPE2ID(unsigned long,NPY_ULONG);
393 DEF_ARRY_TYPE2ID(unsigned short,NPY_USHORT);
394 DEF_ARRY_TYPE2ID(unsigned char,NPY_UBYTE);
395 DEF_ARRY_TYPE2ID(unsigned long long ,NPY_ULONGLONG);
398 } } } // namespace
int getItemtype(const object &a)
Obtain numpy's data type number from an boost-python object which should hold a ndarray.
Definition: numpy.cpp:241
This is the base class from which type specific variants are derived.
Definition: numpy.hpp:248
object empty(int rank, const Py_ssize_t *dims, int type)
Create a new ndarray with uninitialized memory.
Definition: numpy.cpp:232
void importNumpyAndRegisterTypes()
Initializes things.
Definition: numpy.cpp:196
This class defines operators () and [] to allow for direct memory access to array elements of type T...
Definition: numpy.hpp:354
bool isCompatibleType(int id)
Determines if a numpy data type identified by the type number id is binary compatible with the c-type...
Definition: numpy.cpp:340
object zeros(int rank, const Py_ssize_t *dims, int type)
Creates a new ndarray filled with zeros.
Definition: numpy.cpp:224
Things are in here.