casacore
PycArrayComCC.h
Go to the documentation of this file.
1 //# PycArrayCom.h: Common code to convert an Array to/from a Python array
2 //# Copyright (C) 2006
3 //# Associated Universities, Inc. Washington DC, USA.
4 //#
5 //# This library is free software; you can redistribute it and/or modify it
6 //# under the terms of the GNU Library General Public License as published by
7 //# the Free Software Foundation; either version 2 of the License, or (at your
8 //# option) any later version.
9 //#
10 //# This library is distributed in the hope that it will be useful, but WITHOUT
11 //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 //# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
13 //# License for more details.
14 //#
15 //# You should have received a copy of the GNU Library General Public License
16 //# along with this library; if not, write to the Free Software Foundation,
17 //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
18 //#
19 //# Correspondence concerning AIPS++ should be addressed as follows:
20 //# Internet email: aips2-request@nrao.edu.
21 //# Postal address: AIPS++ Project Office
22 //# National Radio Astronomy Observatory
23 //# 520 Edgemont Road
24 //# Charlottesville, VA 22903-2475 USA
25 
26 
27 #include <boost/python.hpp>
28 #include <numpy/arrayobject.h>
29 
30 #if PY_MAJOR_VERSION >= 3
31 #define IS_PY3K
32 #endif
33 
34 
35  Bool PycArrayCheck (PyObject* obj_ptr)
36  {
37  if (!PyArray_API) {
38  if (!isImported()) return False;
39  loadAPI();
40  }
41  return PyArray_Check (obj_ptr);
42  }
43 
45  {
46  using namespace boost::python;
47  // PySys_GetObject uses char* instead of const char*, so use a cast.
48  const char* modStr = "modules";
49  PyObject* mods = PySys_GetObject(const_cast<char*>(modStr));
50  dict d = extract<dict>(mods)();
51  return d.has_key(PYC_USE_PYARRAY);
52  }
53 
54  void loadAPI()
55  {
56  if (!PyArray_API) {
57  if (!importArray() || !PyArray_API) {
58  throw AipsError ("PycArray: failed to load the " PYC_USE_PYARRAY
59  " API");
60  }
61  }
62  }
63 
64 
65  template <typename T> struct TypeConvTraits {
66  typedef T casa_type;
67  typedef void* python_type;
68  static NPY_TYPES pyType()
69  { throw AipsError ("PycArray: unknown casa type"); }
70  };
71  template <> struct TypeConvTraits<casacore::Bool> {
73  typedef npy_bool python_type;
74  static NPY_TYPES pyType() { return NPY_BOOL; }
75  };
76  template <> struct TypeConvTraits<casacore::uChar> {
78  typedef npy_uint16 python_type; // Note: numarray uInt8 is Bool
79  static NPY_TYPES pyType() { return NPY_UINT16; }
80  };
81  template <> struct TypeConvTraits<casacore::Short> {
83  typedef npy_int16 python_type;
84  static NPY_TYPES pyType() { return NPY_INT16; }
85  };
86  template <> struct TypeConvTraits<casacore::uShort> {
88  typedef npy_uint16 python_type;
89  static NPY_TYPES pyType() { return NPY_UINT16; }
90  };
91  template <> struct TypeConvTraits<casacore::Int> {
93  typedef npy_int32 python_type;
94  static NPY_TYPES pyType() { return NPY_INT32; }
95  };
96  template <> struct TypeConvTraits<casacore::uInt> {
98  typedef npy_uint32 python_type;
99  static NPY_TYPES pyType() { return NPY_UINT32; }
100  };
101  template <> struct TypeConvTraits<casacore::Int64> {
103  typedef npy_int64 python_type;
104  static NPY_TYPES pyType() { return NPY_INT64; }
105  };
106  template <> struct TypeConvTraits<casacore::uInt64> {
108  typedef npy_uint64 python_type;
109  static NPY_TYPES pyType() { return NPY_UINT64; }
110  };
111  template <> struct TypeConvTraits<casacore::Float> {
113  typedef npy_float32 python_type;
114  static NPY_TYPES pyType() { return NPY_FLOAT32; }
115  };
116  template <> struct TypeConvTraits<casacore::Double> {
118  typedef npy_float64 python_type;
119  static NPY_TYPES pyType() { return NPY_FLOAT64; }
120  };
121  template <> struct TypeConvTraits<casacore::Complex> {
123  typedef npy_complex64 python_type;
124  static NPY_TYPES pyType() { return NPY_COMPLEX64; }
125  };
126  template <> struct TypeConvTraits<casacore::DComplex> {
128  typedef npy_complex128 python_type;
129  static NPY_TYPES pyType() { return NPY_COMPLEX128; }
130  };
131  template <> struct TypeConvTraits<casacore::String> {
133  typedef ::PyObject* python_type;
134  static NPY_TYPES pyType() { return NPY_OBJECT; }
135  };
136  // This one is only used to convert numpy BYTE and SBYTE to casa short.
137  // There is no back conversion, so an exception is thrown.
138  template <> struct TypeConvTraits<signed char> {
139  typedef signed char casa_type;
140  typedef npy_int8 python_type;
141  static NPY_TYPES pyType()
142  { throw AipsError ("PycArray: unknown casa type"); }
143  };
144 
145 
146  template <typename T>
147  void ArrayCopy<T>::toPy (void* to, const T* from, size_t nr)
148  {
149  if (sizeof(T) == sizeof(typename TypeConvTraits<T>::python_type)) {
150  ::memcpy (to, from, nr*sizeof(T));
151  } else {
152  typename TypeConvTraits<T>::python_type* dst =
153  static_cast<typename TypeConvTraits<T>::python_type*>(to);
154  for (size_t i=0; i<nr; i++) {
155  dst[i] = from[i];
156  }
157  }
158  }
159  template <typename T>
160  void ArrayCopy<T>::fromPy (T* to, const void* from, size_t nr)
161  {
162  if (sizeof(T) == sizeof(typename TypeConvTraits<T>::python_type)) {
163  ::memcpy (to, from, nr*sizeof(T));
164  } else {
165  const typename TypeConvTraits<T>::python_type* src =
166  static_cast<const typename TypeConvTraits<T>::python_type*>(from);
167  for (size_t i=0; i<nr; i++) {
168  to[i] = src[i];
169  }
170  }
171  }
172  template <typename T>
173  Array<T> ArrayCopy<T>::toArray (const IPosition& shape,
174  void* data, bool copy)
175  {
176  // If the python array was contiguous, etc., we can directly use
177  // its data because the Array used is only temporary.
178  // However, if a copy of the Python array was made in PycArray.cc,
179  // we cannot do that because the Python copy is out of scope when
180  // the Array object gets used.
181  if (!copy) {
182  if (sizeof(T) == sizeof(typename TypeConvTraits<T>::python_type)) {
183  return Array<T> (shape, static_cast<T*>(data), SHARE);
184  }
185  }
186  Array<T> arr(shape);
187  fromPy (arr.data(), data, arr.size());
188  return arr;
189  }
190 
191 
192  void ArrayCopy<Complex>::toPy (void* to, const Complex* from, size_t nr)
193  {
194  if (sizeof(Complex) != sizeof(TypeConvTraits<Complex>::python_type)) {
195  throw AipsError("PycArray: size of Complex data type mismatches");
196  }
197  ::memcpy (to, from, nr*sizeof(Complex));
198  }
199  void ArrayCopy<Complex>::fromPy (Complex* to, const void* from, size_t nr)
200  {
201  if (sizeof(Complex) != sizeof(TypeConvTraits<Complex>::python_type)) {
202  throw AipsError("PycArray: size of Complex data type mismatches");
203  }
204  ::memcpy (to, from, nr*sizeof(Complex));
205  }
206  Array<Complex> ArrayCopy<Complex>::toArray (const IPosition& shape,
207  void* data, bool copy)
208  {
209  if (!copy) {
210  if (sizeof(Complex) == sizeof(TypeConvTraits<Complex>::python_type)) {
211  return Array<Complex> (shape, static_cast<Complex*>(data), SHARE);
212  }
213  }
214  Array<Complex> arr(shape);
215  fromPy (arr.data(), data, arr.size());
216  return arr;
217  }
218 
219 
220  void ArrayCopy<DComplex>::toPy (void* to, const DComplex* from, size_t nr)
221  {
222  if (sizeof(DComplex) != sizeof(TypeConvTraits<DComplex>::python_type)) {
223  throw AipsError("PycArray: size of DComplex data type mismatches");
224  }
225  ::memcpy (to, from, nr*sizeof(DComplex));
226  }
227  void ArrayCopy<DComplex>::fromPy (DComplex* to, const void* from, size_t nr)
228  {
229  if (sizeof(DComplex) != sizeof(TypeConvTraits<DComplex>::python_type)) {
230  throw AipsError("PycArray: size of DComplex data type mismatches");
231  }
232  ::memcpy (to, from, nr*sizeof(DComplex));
233  }
234  Array<DComplex> ArrayCopy<DComplex>::toArray (const IPosition& shape,
235  void* data, bool copy)
236  {
237  if (!copy) {
238  if (sizeof(DComplex) == sizeof(TypeConvTraits<DComplex>::python_type)) {
239  return Array<DComplex> (shape, static_cast<DComplex*>(data), SHARE);
240  }
241  }
242  Array<DComplex> arr(shape);
243  fromPy (arr.data(), data, arr.size());
244  return arr;
245  }
246 
247 
248  void ArrayCopy<String>::toPy (void* to, const String* from, size_t nr)
249  {
250  PyObject** dst = static_cast<PyObject**>(to);
251  for (size_t i=0; i<nr; i++) {
252 #ifdef IS_PY3K
253  dst[i] = PyUnicode_FromString(from[i].chars());
254 #else
255  dst[i] = PyString_FromString(from[i].chars());
256 #endif
257  }
258  }
259  void ArrayCopy<String>::fromPy (String* to, const void* from, size_t nr)
260  {
261  using namespace boost::python;
262  PyObject** src = (PyObject**)from;
263  for (size_t i=0; i<nr; i++) {
264  handle<> py_elem_hdl(src[i]);
265  object py_elem_obj(py_elem_hdl);
266  extract<String> elem_proxy(py_elem_obj);
267  to[i] = elem_proxy();
268  }
269  }
270  Array<String> ArrayCopy<String>::toArray (const IPosition& shape,
271  void* data, bool)
272  {
273  Array<String> arr(shape);
274  fromPy (arr.data(), data, arr.size());
275  return arr;
276  }
277 
278  ValueHolder makeArray (PyObject* obj_ptr, Bool copyData)
279  {
280  if (! PycArrayCheck(obj_ptr)) {
281  throw AipsError ("PycArray: python object is not an array");
282  }
283  PyArrayObject* po = (PyArrayObject*)obj_ptr;
284  boost::python::object obj;
285  bool docopy = copyData; // copy data if wanted or needed
286  if (! PyArray_ISCONTIGUOUS(po)
287  || ! PyArray_ISALIGNED(po)
288  || PyArray_ISBYTESWAPPED(po)) {
289  boost::python::handle<> py_hdl(obj_ptr);
290  boost::python::object py_obj(py_hdl);
291  // incr refcount, because ~object decrements it
292  boost::python::incref(obj_ptr);
293  obj = py_obj.attr("copy")();
294  po = (PyArrayObject*)(obj.ptr());
295  docopy = true;
296  }
297  // Swap axes, because Casacore has row minor and Python row major order.
298  // A scalar is treated as a vector with length 1.
299  int nd = PyArray_NDIM(po);
300  IPosition shp(1, 1);
301  if (nd > 0) {
302  shp.resize (nd);
303  for (int i=0; i<nd; i++) {
304  shp[i] = PyArray_DIMS(po)[nd-i-1];
305  }
306  }
307  // Assert array is contiguous now.
308  // If the array is empty, numarray still sees it as non-contiguous.
309  if (shp.product() > 0) {
310  AlwaysAssert (PyArray_ISCONTIGUOUS(po)
312  && !PyArray_ISBYTESWAPPED(po), AipsError);
313  }
314  // Create the correct array.
315  switch (PyArray_TYPE(po)) {
316  case NPY_BOOL:
317  return ValueHolder (ArrayCopy<Bool>::toArray(shp, PyArray_DATA(po), docopy));
318  case NPY_INT16:
319  return ValueHolder (ArrayCopy<Short>::toArray(shp, PyArray_DATA(po), docopy));
320  case NPY_UINT16:
321  return ValueHolder (ArrayCopy<uShort>::toArray(shp, PyArray_DATA(po), docopy));
322  case NPY_INT32:
323  return ValueHolder (ArrayCopy<Int>::toArray(shp, PyArray_DATA(po), docopy));
324  case NPY_UINT32:
325  return ValueHolder (ArrayCopy<uInt>::toArray(shp, PyArray_DATA(po), docopy));
326  case NPY_INT64:
327  return ValueHolder (ArrayCopy<Int64>::toArray(shp, PyArray_DATA(po), docopy));
328  case NPY_FLOAT32:
329  return ValueHolder (ArrayCopy<Float>::toArray(shp, PyArray_DATA(po), docopy));
330  case NPY_FLOAT64:
331  return ValueHolder (ArrayCopy<Double>::toArray(shp, PyArray_DATA(po), docopy));
332  case NPY_COMPLEX64:
333  return ValueHolder (ArrayCopy<Complex>::toArray(shp, PyArray_DATA(po), docopy));
334  case NPY_COMPLEX128:
335  return ValueHolder (ArrayCopy<DComplex>::toArray(shp, PyArray_DATA(po), docopy));
336  case NPY_OBJECT:
337  return ValueHolder (ArrayCopy<String>::toArray(shp, PyArray_DATA(po), docopy));
338  default:
339  // Some types can be the same as other types, so they cannot
340  // be used in the switch (compiler complains).
341  // This is true for BYTE and SBYTE which can equal to BOOL in numarray.
342  // Similarly for STRING which exists for numpy and is set to
343  // INT for numarray.
344  if (PyArray_TYPE(po) == NPY_UINT64) {
345  Array<uInt64> arr = ArrayCopy<uInt64>::toArray(shp, PyArray_DATA(po), False);
346  Array<Int64> res(arr.shape());
347  convertArray (res, arr);
348  return ValueHolder(res);
349  } else if (PyArray_TYPE(po) == NPY_INT8) {
350  Array<signed char> arr = ArrayCopy<signed char>::toArray(shp, PyArray_DATA(po), False);
351  Array<Short> res(arr.shape());
352  convertArray (res, arr);
353  return ValueHolder(res);
354  } else if (PyArray_TYPE(po) == NPY_UINT8) {
355  // Copy using signed char, because uChar is mapped to Short in the Traits.
356  Array<signed char> arr = ArrayCopy<signed char>::toArray(shp, PyArray_DATA(po), False);
357  Array<Short> res(arr.shape());
358  void* varr = &arr;
359  Array<uChar>* uarr = static_cast<Array<uChar>*>(varr);
360  convertArray (res, *uarr);
361  return ValueHolder(res);
362  } else if (PyArray_TYPE(po) == NPY_STRING) {
363  size_t slen = 0;
364  if (nd > 0) {
365  slen = PyArray_STRIDES(po)[nd-1];
366  }
367  return ValueHolder (ArrayCopyStr_toArray(shp, PyArray_DATA(po), slen));
368  } else if (PyArray_TYPE(po) == NPY_UNICODE) {
369  size_t slen = 0;
370  if (nd > 0) {
371  slen = PyArray_STRIDES(po)[nd-1];
372  }
373  return ValueHolder (ArrayCopyUnicode_toArray(shp, PyArray_DATA(po), slen));
374  }
375  break;
376  }
377  throw AipsError ("PycArray: unknown python array data type");
378  }
379 
380 
381  // Instantiate the various templates.
382  template struct ArrayCopy<Bool>;
383  template struct ArrayCopy<signed char>;
384  template struct ArrayCopy<uChar>;
385  template struct ArrayCopy<Short>;
386  template struct ArrayCopy<uShort>;
387  template struct ArrayCopy<Int>;
388  template struct ArrayCopy<uInt>;
389  template struct ArrayCopy<Int64>;
390  template struct ArrayCopy<uInt64>;
391  template struct ArrayCopy<Float>;
392  template struct ArrayCopy<Double>;
393 
394  template boost::python::object makePyArrayObject
395  (casacore::Array<Bool> const& arr);
396  template boost::python::object makePyArrayObject
397  (casacore::Array<uChar> const& arr);
398  template boost::python::object makePyArrayObject
399  (casacore::Array<Short> const& arr);
400  template boost::python::object makePyArrayObject
401  (casacore::Array<uShort> const& arr);
402  template boost::python::object makePyArrayObject
403  (casacore::Array<Int> const& arr);
404  template boost::python::object makePyArrayObject
405  (casacore::Array<uInt> const& arr);
406  template boost::python::object makePyArrayObject
407  (casacore::Array<Int64> const& arr);
408  template boost::python::object makePyArrayObject
409  (casacore::Array<Float> const& arr);
410  template boost::python::object makePyArrayObject
411  (casacore::Array<Double> const& arr);
412  template boost::python::object makePyArrayObject
413  (casacore::Array<Complex> const& arr);
414  template boost::python::object makePyArrayObject
415  (casacore::Array<DComplex> const& arr);
#define AlwaysAssert(expr, exception)
These marcos are provided for use instead of simply using the constructors of assert_ to allow additi...
Definition: Assert.h:157
template boost::python::object makePyArrayObject(casacore::Array< Bool > const &arr)
Bool PycArrayCheck(PyObject *obj_ptr)
Check if the PyObject is an array object.
Definition: PycArrayComCC.h:35
Bool isImported()
Check if the API is or can be imported.
Definition: PycArrayComCC.h:44
ValueHolder makeArray(PyObject *obj_ptr, Bool copyData)
Convert the python array to a Casacore array in the ValueHolder.
void loadAPI()
Definition: PycArrayComCC.h:54
Array< String > ArrayCopyStr_toArray(const IPosition &shape, void *data, size_t slen)
Array< String > ArrayCopyUnicode_toArray(const IPosition &shape, void *data, size_t slen)
Bool importArray()
#define PYC_USE_PYARRAY
Definition: PycArrayNP.h:41
String: the storage and methods of handling collections of characters.
Definition: String.h:225
@ SHARE
Share means that the Array will just use the pointer (no copy), however the Array will NOT delete it ...
Definition: ArrayBase.h:62
this file contains all the compiler specific defines
Definition: mainpage.dox:28
unsigned char uChar
Definition: aipstype.h:47
const Bool False
Definition: aipstype.h:44
StatsData< AccumType > copy(const StatsData< AccumType > &stats)
short Short
Definition: aipstype.h:48
unsigned int uInt
Definition: aipstype.h:51
unsigned short uShort
Definition: aipstype.h:49
TableExprNode shape(const TableExprNode &array)
Function operating on any scalar or array resulting in a Double array containing the shape.
Definition: ExprNode.h:1987
long long Int64
Define the extra non-standard types used by Casacore (like proposed uSize, Size)
Definition: aipsxtype.h:38
float Float
Definition: aipstype.h:54
int Int
Definition: aipstype.h:50
bool Bool
Define the standard types used by Casacore.
Definition: aipstype.h:42
double Double
Definition: aipstype.h:55
unsigned long long uInt64
Definition: aipsxtype.h:39
Copy/convert the array data as needed.
Definition: PycArrayComH.h:49
static void toPy(void *to, const T *from, size_t nr)
static void fromPy(T *to, const void *from, size_t nr)
static Array< T > toArray(const IPosition &shape, void *data, bool copy)
void * python_type
Definition: PycArrayComCC.h:67
static NPY_TYPES pyType()
Definition: PycArrayComCC.h:68