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#include <boost/python.hpp>
27#include <numpy/arrayobject.h>
28
29#if PY_MAJOR_VERSION >= 3
30#define IS_PY3K
31#endif
32
33Bool PycArrayCheck(PyObject* obj_ptr) {
34 if (!PyArray_API) {
35 if (!isImported()) return False;
36 loadAPI();
37 }
38 return PyArray_Check(obj_ptr);
39}
40
41Bool isImported() {
42 using namespace boost::python;
43 // PySys_GetObject uses char* instead of const char*, so use a cast.
44 const char* modStr = "modules";
45 PyObject* mods = PySys_GetObject(const_cast<char*>(modStr));
46 dict d = extract<dict>(mods)();
47 return d.has_key(PYC_USE_PYARRAY);
48}
49
50void loadAPI() {
51 if (!PyArray_API) {
52 if (!importArray() || !PyArray_API) {
53 throw AipsError("PycArray: failed to load the " PYC_USE_PYARRAY " API");
54 }
55 }
56}
57
58template <typename T>
60 typedef T casa_type;
61 typedef void* python_type;
62 static NPY_TYPES pyType() { throw AipsError("PycArray: unknown casa type"); }
63};
64template <>
65struct TypeConvTraits<casacore::Bool> {
67 typedef npy_bool python_type;
68 static NPY_TYPES pyType() { return NPY_BOOL; }
69};
70template <>
71struct TypeConvTraits<casacore::uChar> {
73 typedef npy_uint16 python_type; // Note: numarray uInt8 is Bool
74 static NPY_TYPES pyType() { return NPY_UINT16; }
75};
76template <>
77struct TypeConvTraits<casacore::Short> {
79 typedef npy_int16 python_type;
80 static NPY_TYPES pyType() { return NPY_INT16; }
81};
82template <>
83struct TypeConvTraits<casacore::uShort> {
85 typedef npy_uint16 python_type;
86 static NPY_TYPES pyType() { return NPY_UINT16; }
87};
88template <>
91 typedef npy_int32 python_type;
92 static NPY_TYPES pyType() { return NPY_INT32; }
93};
94template <>
95struct TypeConvTraits<casacore::uInt> {
97 typedef npy_uint32 python_type;
98 static NPY_TYPES pyType() { return NPY_UINT32; }
99};
100template <>
101struct TypeConvTraits<casacore::Int64> {
103 typedef npy_int64 python_type;
104 static NPY_TYPES pyType() { return NPY_INT64; }
105};
106template <>
107struct TypeConvTraits<casacore::uInt64> {
109 typedef npy_uint64 python_type;
110 static NPY_TYPES pyType() { return NPY_UINT64; }
111};
112template <>
113struct TypeConvTraits<casacore::Float> {
115 typedef npy_float32 python_type;
116 static NPY_TYPES pyType() { return NPY_FLOAT32; }
117};
118template <>
119struct TypeConvTraits<casacore::Double> {
121 typedef npy_float64 python_type;
122 static NPY_TYPES pyType() { return NPY_FLOAT64; }
123};
124template <>
125struct TypeConvTraits<casacore::Complex> {
126 typedef casacore::Complex casa_type;
127 typedef npy_complex64 python_type;
128 static NPY_TYPES pyType() { return NPY_COMPLEX64; }
129};
130template <>
131struct TypeConvTraits<casacore::DComplex> {
132 typedef casacore::DComplex casa_type;
133 typedef npy_complex128 python_type;
134 static NPY_TYPES pyType() { return NPY_COMPLEX128; }
135};
136template <>
137struct TypeConvTraits<casacore::String> {
139 typedef ::PyObject* python_type;
140 static NPY_TYPES pyType() { return NPY_OBJECT; }
141};
142// This one is only used to convert numpy BYTE and SBYTE to casa short.
143// There is no back conversion, so an exception is thrown.
144template <>
145struct TypeConvTraits<signed char> {
146 typedef signed char casa_type;
147 typedef npy_int8 python_type;
148 static NPY_TYPES pyType() { throw AipsError("PycArray: unknown casa type"); }
149};
150
151template <typename T>
152void ArrayCopy<T>::toPy(void* to, const T* from, size_t nr) {
153 if (sizeof(T) == sizeof(typename TypeConvTraits<T>::python_type)) {
154 ::memcpy(to, from, nr * sizeof(T));
155 } else {
156 typename TypeConvTraits<T>::python_type* dst =
157 static_cast<typename TypeConvTraits<T>::python_type*>(to);
158 for (size_t i = 0; i < nr; i++) {
159 dst[i] = from[i];
160 }
161 }
162}
163template <typename T>
164void ArrayCopy<T>::fromPy(T* to, const void* from, size_t nr) {
165 if (sizeof(T) == sizeof(typename TypeConvTraits<T>::python_type)) {
166 ::memcpy(to, from, nr * sizeof(T));
167 } else {
168 const typename TypeConvTraits<T>::python_type* src =
169 static_cast<const typename TypeConvTraits<T>::python_type*>(from);
170 for (size_t i = 0; i < nr; i++) {
171 to[i] = src[i];
172 }
173 }
174}
175template <typename T>
176Array<T> ArrayCopy<T>::toArray(const IPosition& shape, void* data, bool copy) {
177 // If the python array was contiguous, etc., we can directly use
178 // its data because the Array used is only temporary.
179 // However, if a copy of the Python array was made in PycArray.cc,
180 // we cannot do that because the Python copy is out of scope when
181 // the Array object gets used.
182 if (!copy) {
183 if (sizeof(T) == sizeof(typename TypeConvTraits<T>::python_type)) {
184 return Array<T>(shape, static_cast<T*>(data), SHARE);
185 }
186 }
187 Array<T> arr(shape);
188 fromPy(arr.data(), data, arr.size());
189 return arr;
190}
191
192void ArrayCopy<Complex>::toPy(void* to, const Complex* from, size_t nr) {
193 if (sizeof(Complex) != sizeof(TypeConvTraits<Complex>::python_type)) {
194 throw AipsError("PycArray: size of Complex data type mismatches");
195 }
196 ::memcpy(to, from, nr * sizeof(Complex));
197}
198void ArrayCopy<Complex>::fromPy(Complex* to, const void* from, size_t nr) {
199 if (sizeof(Complex) != sizeof(TypeConvTraits<Complex>::python_type)) {
200 throw AipsError("PycArray: size of Complex data type mismatches");
201 }
202 ::memcpy(to, from, nr * sizeof(Complex));
203}
204Array<Complex> ArrayCopy<Complex>::toArray(const IPosition& shape, void* data, bool copy) {
205 if (!copy) {
206 if (sizeof(Complex) == sizeof(TypeConvTraits<Complex>::python_type)) {
207 return Array<Complex>(shape, static_cast<Complex*>(data), SHARE);
208 }
209 }
210 Array<Complex> arr(shape);
211 fromPy(arr.data(), data, arr.size());
212 return arr;
213}
214
215void ArrayCopy<DComplex>::toPy(void* to, const DComplex* from, size_t nr) {
216 if (sizeof(DComplex) != sizeof(TypeConvTraits<DComplex>::python_type)) {
217 throw AipsError("PycArray: size of DComplex data type mismatches");
218 }
219 ::memcpy(to, from, nr * sizeof(DComplex));
220}
221void ArrayCopy<DComplex>::fromPy(DComplex* to, const void* from, size_t nr) {
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}
227Array<DComplex> ArrayCopy<DComplex>::toArray(const IPosition& shape, void* data, bool copy) {
228 if (!copy) {
229 if (sizeof(DComplex) == sizeof(TypeConvTraits<DComplex>::python_type)) {
230 return Array<DComplex>(shape, static_cast<DComplex*>(data), SHARE);
231 }
232 }
233 Array<DComplex> arr(shape);
234 fromPy(arr.data(), data, arr.size());
235 return arr;
236}
237
238void ArrayCopy<String>::toPy(void* to, const String* from, size_t nr) {
239 PyObject** dst = static_cast<PyObject**>(to);
240 for (size_t i = 0; i < nr; i++) {
241#ifdef IS_PY3K
242 dst[i] = PyUnicode_FromString(from[i].c_str());
243#else
244 dst[i] = PyString_FromString(from[i].c_str());
245#endif
246 }
247}
248void ArrayCopy<String>::fromPy(String* to, const void* from, size_t nr) {
249 using namespace boost::python;
250 PyObject** src = (PyObject**)from;
251 for (size_t i = 0; i < nr; i++) {
252 handle<> py_elem_hdl(src[i]);
253 object py_elem_obj(py_elem_hdl);
254 extract<String> elem_proxy(py_elem_obj);
255 to[i] = elem_proxy();
256 }
257}
258Array<String> ArrayCopy<String>::toArray(const IPosition& shape, void* data, bool) {
259 Array<String> arr(shape);
260 fromPy(arr.data(), data, arr.size());
261 return arr;
262}
263
264ValueHolder makeArray(PyObject* obj_ptr, Bool copyData) {
265 if (!PycArrayCheck(obj_ptr)) {
266 throw AipsError("PycArray: python object is not an array");
267 }
268 PyArrayObject* po = (PyArrayObject*)obj_ptr;
269 boost::python::object obj;
270 bool docopy = copyData; // copy data if wanted or needed
271 if (!PyArray_ISCONTIGUOUS(po) || !PyArray_ISALIGNED(po) || PyArray_ISBYTESWAPPED(po)) {
272 boost::python::handle<> py_hdl(obj_ptr);
273 boost::python::object py_obj(py_hdl);
274 // incr refcount, because ~object decrements it
275 boost::python::incref(obj_ptr);
276 obj = py_obj.attr("copy")();
277 po = (PyArrayObject*)(obj.ptr());
278 docopy = true;
279 }
280 // Swap axes, because Casacore has row minor and Python row major order.
281 // A scalar is treated as a vector with length 1.
282 int nd = PyArray_NDIM(po);
283 IPosition shp(1, 1);
284 if (nd > 0) {
285 shp.resize(nd);
286 for (int i = 0; i < nd; i++) {
287 shp[i] = PyArray_DIMS(po)[nd - i - 1];
288 }
289 }
290 // Assert array is contiguous now.
291 // If the array is empty, numarray still sees it as non-contiguous.
292 if (shp.product() > 0) {
293 AlwaysAssert(PyArray_ISCONTIGUOUS(po)
295 && !PyArray_ISBYTESWAPPED(po),
296 AipsError);
297 }
298 // Create the correct array.
299 switch (PyArray_TYPE(po)) {
300 case NPY_BOOL:
301 return ValueHolder(ArrayCopy<Bool>::toArray(shp, PyArray_DATA(po), docopy));
302 case NPY_INT16:
303 return ValueHolder(ArrayCopy<Short>::toArray(shp, PyArray_DATA(po), docopy));
304 case NPY_UINT16:
305 return ValueHolder(ArrayCopy<uShort>::toArray(shp, PyArray_DATA(po), docopy));
306 case NPY_INT32:
307 return ValueHolder(ArrayCopy<Int>::toArray(shp, PyArray_DATA(po), docopy));
308 case NPY_UINT32:
309 return ValueHolder(ArrayCopy<uInt>::toArray(shp, PyArray_DATA(po), docopy));
310 case NPY_INT64:
311 return ValueHolder(ArrayCopy<Int64>::toArray(shp, PyArray_DATA(po), docopy));
312 case NPY_FLOAT32:
313 return ValueHolder(ArrayCopy<Float>::toArray(shp, PyArray_DATA(po), docopy));
314 case NPY_FLOAT64:
315 return ValueHolder(ArrayCopy<Double>::toArray(shp, PyArray_DATA(po), docopy));
316 case NPY_COMPLEX64:
317 return ValueHolder(ArrayCopy<Complex>::toArray(shp, PyArray_DATA(po), docopy));
318 case NPY_COMPLEX128:
319 return ValueHolder(ArrayCopy<DComplex>::toArray(shp, PyArray_DATA(po), docopy));
320 case NPY_OBJECT:
321 return ValueHolder(ArrayCopy<String>::toArray(shp, PyArray_DATA(po), docopy));
322 default:
323 // Some types can be the same as other types, so they cannot
324 // be used in the switch (compiler complains).
325 // This is true for BYTE and SBYTE which can equal to BOOL in numarray.
326 // Similarly for STRING which exists for numpy and is set to
327 // INT for numarray.
328 if (PyArray_TYPE(po) == NPY_UINT64) {
329 Array<uInt64> arr = ArrayCopy<uInt64>::toArray(shp, PyArray_DATA(po), False);
330 Array<Int64> res(arr.shape());
331 convertArray(res, arr);
332 return ValueHolder(res);
333 } else if (PyArray_TYPE(po) == NPY_INT8) {
334 Array<signed char> arr = ArrayCopy<signed char>::toArray(shp, PyArray_DATA(po), False);
335 Array<Short> res(arr.shape());
336 convertArray(res, arr);
337 return ValueHolder(res);
338 } else if (PyArray_TYPE(po) == NPY_UINT8) {
339 // Copy using signed char, because uChar is mapped to Short in the Traits.
340 Array<signed char> arr = ArrayCopy<signed char>::toArray(shp, PyArray_DATA(po), False);
341 Array<Short> res(arr.shape());
342 void* varr = &arr;
343 Array<uChar>* uarr = static_cast<Array<uChar>*>(varr);
344 convertArray(res, *uarr);
345 return ValueHolder(res);
346 } else if (PyArray_TYPE(po) == NPY_STRING) {
347 size_t slen = 0;
348 if (nd > 0) {
349 slen = PyArray_STRIDES(po)[nd - 1];
350 }
351 return ValueHolder(ArrayCopyStr_toArray(shp, PyArray_DATA(po), slen));
352 } else if (PyArray_TYPE(po) == NPY_UNICODE) {
353 size_t slen = 0;
354 if (nd > 0) {
355 slen = PyArray_STRIDES(po)[nd - 1];
356 }
357 return ValueHolder(ArrayCopyUnicode_toArray(shp, PyArray_DATA(po), slen));
358 }
359 break;
360 }
361 throw AipsError("PycArray: unknown python array data type");
362}
363
364// Instantiate the various templates.
365template struct ArrayCopy<Bool>;
366template struct ArrayCopy<signed char>;
367template struct ArrayCopy<uChar>;
368template struct ArrayCopy<Short>;
369template struct ArrayCopy<uShort>;
370template struct ArrayCopy<Int>;
371template struct ArrayCopy<uInt>;
372template struct ArrayCopy<Int64>;
373template struct ArrayCopy<uInt64>;
374template struct ArrayCopy<Float>;
375template struct ArrayCopy<Double>;
376
377template boost::python::object makePyArrayObject(casacore::Array<Bool> const& arr);
378template boost::python::object makePyArrayObject(casacore::Array<uChar> const& arr);
379template boost::python::object makePyArrayObject(casacore::Array<Short> const& arr);
380template boost::python::object makePyArrayObject(casacore::Array<uShort> const& arr);
381template boost::python::object makePyArrayObject(casacore::Array<Int> const& arr);
382template boost::python::object makePyArrayObject(casacore::Array<uInt> const& arr);
383template boost::python::object makePyArrayObject(casacore::Array<Int64> const& arr);
384template boost::python::object makePyArrayObject(casacore::Array<Float> const& arr);
385template boost::python::object makePyArrayObject(casacore::Array<Double> const& arr);
386template boost::python::object makePyArrayObject(casacore::Array<Complex> const& arr);
387template boost::python::object makePyArrayObject(casacore::Array<DComplex> const& arr);
#define AlwaysAssert(expr, exception)
These marcos are provided for use instead of simply using the assert_ function directly.
Definition Assert.h:133
template boost::python::object makePyArrayObject(casacore::Array< Bool > const &arr)
Bool PycArrayCheck(PyObject *obj_ptr)
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:40
String: the storage and methods of handling collections of characters.
Definition String.h:355
For temporary backward namespace compatibility, use casa as alias for casacore.
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
static void fromPy(Complex *to, const void *from, size_t nr)
static void fromPy(DComplex *to, const void *from, size_t nr)
static void fromPy(String *to, const void *from, size_t nr)
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()