casacore
Loading...
Searching...
No Matches
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: casa-feedback@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> {
122 typedef casacore::Complex casa_type;
123 typedef npy_complex64 python_type;
124 static NPY_TYPES pyType() { return NPY_COMPLEX64; }
125 };
126 template <> struct TypeConvTraits<casacore::DComplex> {
127 typedef casacore::DComplex casa_type;
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:155
template boost::python::object makePyArrayObject(casacore::Array< Bool > const &arr)
Bool PycArrayCheck(PyObject *obj_ptr)
Check if the PyObject is an array object.
Bool isImported()
Check if the API is or can be imported.
ValueHolder makeArray(PyObject *obj_ptr, Bool copyData)
Convert the python array to a Casacore array in the ValueHolder.
void loadAPI()
Array< String > ArrayCopyUnicode_toArray(const IPosition &shape, void *data, size_t slen)
Array< String > ArrayCopyStr_toArray(const IPosition &shape, void *data, size_t slen)
Bool importArray()
#define PYC_USE_PYARRAY
Definition PycArrayNP.h:39
String: the storage and methods of handling collections of characters.
Definition String.h:223
this file contains all the compiler specific defines
Definition mainpage.dox:28
unsigned char uChar
Definition aipstype.h:45
short Short
Definition aipstype.h:46
unsigned int uInt
Definition aipstype.h:49
unsigned short uShort
Definition aipstype.h:47
long long Int64
Define the extra non-standard types used by Casacore (like proposed uSize, Size)
Definition aipsxtype.h:36
float Float
Definition aipstype.h:52
int Int
Definition aipstype.h:48
bool Bool
Define the standard types used by Casacore.
Definition aipstype.h:40
double Double
Definition aipstype.h:53
unsigned long long uInt64
Definition aipsxtype.h:37
Copy/convert the array data as needed.
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)
static NPY_TYPES pyType()