File: PycArrayComCC.h

package info (click to toggle)
casacore 3.8.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 51,912 kB
  • sloc: cpp: 471,569; fortran: 16,372; ansic: 7,416; yacc: 4,714; lex: 2,346; sh: 1,865; python: 629; perl: 531; sed: 499; csh: 201; makefile: 32
file content (415 lines) | stat: -rw-r--r-- 15,007 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
//# PycArrayCom.h: Common code to convert an Array to/from a Python array
//# Copyright (C) 2006
//# Associated Universities, Inc. Washington DC, USA.
//#
//# This library is free software; you can redistribute it and/or modify it
//# under the terms of the GNU Library General Public License as published by
//# the Free Software Foundation; either version 2 of the License, or (at your
//# option) any later version.
//#
//# This library is distributed in the hope that it will be useful, but WITHOUT
//# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
//# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
//# License for more details.
//#
//# You should have received a copy of the GNU Library General Public License
//# along with this library; if not, write to the Free Software Foundation,
//# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
//#
//# Correspondence concerning AIPS++ should be addressed as follows:
//#        Internet email: casa-feedback@nrao.edu.
//#        Postal address: AIPS++ Project Office
//#                        National Radio Astronomy Observatory
//#                        520 Edgemont Road
//#                        Charlottesville, VA 22903-2475 USA


#include <boost/python.hpp>
#include <numpy/arrayobject.h>

#if PY_MAJOR_VERSION >= 3
#define IS_PY3K
#endif


  Bool PycArrayCheck (PyObject* obj_ptr)
  {
    if (!PyArray_API) {
      if (!isImported()) return False;
      loadAPI();
    }
    return PyArray_Check (obj_ptr);
  }

  Bool isImported()
  {
    using namespace boost::python;
    // PySys_GetObject uses char* instead of const char*, so use a cast.
    const char* modStr = "modules";
    PyObject* mods = PySys_GetObject(const_cast<char*>(modStr));
    dict d =  extract<dict>(mods)();
    return d.has_key(PYC_USE_PYARRAY);
  }

  void loadAPI()
  {
    if (!PyArray_API) {
      if (!importArray()  ||  !PyArray_API) {
	throw AipsError ("PycArray: failed to load the " PYC_USE_PYARRAY
			 " API");
      }
    }
  }


  template <typename T> struct TypeConvTraits {
    typedef T     casa_type;
    typedef void* python_type;
    static NPY_TYPES pyType()
      { throw AipsError ("PycArray: unknown casa type"); }
  };
  template <> struct TypeConvTraits<casacore::Bool> {
    typedef casacore::Bool   casa_type;
    typedef npy_bool     python_type;
    static NPY_TYPES pyType() { return NPY_BOOL; }
  };
  template <> struct TypeConvTraits<casacore::uChar> {
    typedef casacore::uChar  casa_type;
    typedef npy_uint16   python_type;    // Note: numarray uInt8 is Bool
    static NPY_TYPES pyType() { return NPY_UINT16; }
  };
  template <> struct TypeConvTraits<casacore::Short> {
    typedef casacore::Short  casa_type;
    typedef npy_int16    python_type;
    static NPY_TYPES pyType() { return NPY_INT16; }
  };
  template <> struct TypeConvTraits<casacore::uShort> {
    typedef casacore::uShort casa_type;
    typedef npy_uint16   python_type;
    static NPY_TYPES pyType() { return NPY_UINT16; }
  };
  template <> struct TypeConvTraits<casacore::Int> {
    typedef casacore::Int    casa_type;
    typedef npy_int32    python_type;
    static NPY_TYPES pyType() { return NPY_INT32; }
  };
  template <> struct TypeConvTraits<casacore::uInt> {
    typedef casacore::uInt   casa_type;
    typedef npy_uint32   python_type;
    static NPY_TYPES pyType() { return NPY_UINT32; }
  };
  template <> struct TypeConvTraits<casacore::Int64> {
    typedef casacore::Int64  casa_type;
    typedef npy_int64    python_type;
    static NPY_TYPES pyType() { return NPY_INT64; }
  };
  template <> struct TypeConvTraits<casacore::uInt64> {
    typedef casacore::uInt64 casa_type;
    typedef npy_uint64   python_type;
    static NPY_TYPES pyType() { return NPY_UINT64; }
  };
  template <> struct TypeConvTraits<casacore::Float> {
    typedef casacore::Float  casa_type;
    typedef npy_float32  python_type;
    static NPY_TYPES pyType() { return NPY_FLOAT32; }
  };
  template <> struct TypeConvTraits<casacore::Double> {
    typedef casacore::Double casa_type;
    typedef npy_float64  python_type;
    static NPY_TYPES pyType() { return NPY_FLOAT64; }
  };
  template <> struct TypeConvTraits<casacore::Complex> {
    typedef casacore::Complex casa_type;
    typedef npy_complex64 python_type;
    static NPY_TYPES pyType() { return NPY_COMPLEX64; }
  };
  template <> struct TypeConvTraits<casacore::DComplex> {
    typedef casacore::DComplex casa_type;
    typedef npy_complex128 python_type;
    static NPY_TYPES pyType() { return NPY_COMPLEX128; }
  };
  template <> struct TypeConvTraits<casacore::String> {
    typedef casacore::String casa_type;
    typedef ::PyObject*  python_type;
    static NPY_TYPES pyType() { return NPY_OBJECT; }
  };
  // This one is only used to convert numpy BYTE and SBYTE to casa short.
  // There is no back conversion, so an exception is thrown.
  template <> struct TypeConvTraits<signed char> {
    typedef signed char casa_type;
    typedef npy_int8 python_type;
    static NPY_TYPES pyType()
      { throw AipsError ("PycArray: unknown casa type"); }
  };


  template <typename T>
  void ArrayCopy<T>::toPy (void* to, const T* from, size_t nr)
  {
    if (sizeof(T) == sizeof(typename TypeConvTraits<T>::python_type)) {
      ::memcpy (to, from, nr*sizeof(T));
    } else {
      typename TypeConvTraits<T>::python_type* dst =
	static_cast<typename TypeConvTraits<T>::python_type*>(to);
      for (size_t i=0; i<nr; i++) {
	dst[i] = from[i];
      }
    }
  }
  template <typename T>
  void  ArrayCopy<T>::fromPy (T* to, const void* from, size_t nr)
  {
    if (sizeof(T) == sizeof(typename TypeConvTraits<T>::python_type)) {
      ::memcpy (to, from, nr*sizeof(T));
    } else {
      const typename TypeConvTraits<T>::python_type* src =
	static_cast<const typename TypeConvTraits<T>::python_type*>(from);
      for (size_t i=0; i<nr; i++) {
	to[i] = src[i];
      }
    }
  }
  template <typename T>
  Array<T> ArrayCopy<T>::toArray (const IPosition& shape,
				  void* data, bool copy)
  {
    // If the python array was contiguous, etc., we can directly use
    // its data because the Array used is only temporary.
    // However, if a copy of the Python array was made in PycArray.cc,
    // we cannot do that because the Python copy is out of scope when
    // the Array object gets used.
    if (!copy) {
      if (sizeof(T) == sizeof(typename TypeConvTraits<T>::python_type)) {
	return Array<T> (shape, static_cast<T*>(data), SHARE);
      }
    }
    Array<T> arr(shape);
    fromPy (arr.data(), data, arr.size());
    return arr;
  }


  void ArrayCopy<Complex>::toPy (void* to, const Complex* from, size_t nr)
  {
    if (sizeof(Complex) != sizeof(TypeConvTraits<Complex>::python_type)) {
      throw AipsError("PycArray: size of Complex data type mismatches");
    }
    ::memcpy (to, from, nr*sizeof(Complex));
  }
  void ArrayCopy<Complex>::fromPy (Complex* to, const void* from, size_t nr)
  {
    if (sizeof(Complex) != sizeof(TypeConvTraits<Complex>::python_type)) {
      throw AipsError("PycArray: size of Complex data type mismatches");
    }
    ::memcpy (to, from, nr*sizeof(Complex));
  }
  Array<Complex> ArrayCopy<Complex>::toArray (const IPosition& shape,
					      void* data, bool copy)
  {
    if (!copy) {
      if (sizeof(Complex) == sizeof(TypeConvTraits<Complex>::python_type)) {
	return Array<Complex> (shape, static_cast<Complex*>(data), SHARE);
      }
    }
    Array<Complex> arr(shape);
    fromPy (arr.data(), data, arr.size());
    return arr;
  }


  void ArrayCopy<DComplex>::toPy (void* to, const DComplex* from, size_t nr)
  {
    if (sizeof(DComplex) != sizeof(TypeConvTraits<DComplex>::python_type)) {
      throw AipsError("PycArray: size of DComplex data type mismatches");
    }
    ::memcpy (to, from, nr*sizeof(DComplex));
  }
  void ArrayCopy<DComplex>::fromPy (DComplex* to, const void* from, size_t nr)
  {
    if (sizeof(DComplex) != sizeof(TypeConvTraits<DComplex>::python_type)) {
      throw AipsError("PycArray: size of DComplex data type mismatches");
    }
    ::memcpy (to, from, nr*sizeof(DComplex));
  }
  Array<DComplex> ArrayCopy<DComplex>::toArray (const IPosition& shape,
						void* data, bool copy)
  {
    if (!copy) {
      if (sizeof(DComplex) == sizeof(TypeConvTraits<DComplex>::python_type)) {
	return Array<DComplex> (shape, static_cast<DComplex*>(data), SHARE);
      }
    }
    Array<DComplex> arr(shape);
    fromPy (arr.data(), data, arr.size());
    return arr;
  }


  void ArrayCopy<String>::toPy (void* to, const String* from, size_t nr)
  {
    PyObject** dst = static_cast<PyObject**>(to);
    for (size_t i=0; i<nr; i++) {
#ifdef IS_PY3K
      dst[i] = PyUnicode_FromString(from[i].chars());
#else
      dst[i] = PyString_FromString(from[i].chars());
#endif
    }
  }
  void ArrayCopy<String>::fromPy (String* to, const void* from, size_t nr)
  {
    using namespace boost::python;
    PyObject** src = (PyObject**)from;
    for (size_t i=0; i<nr; i++) {
      handle<> py_elem_hdl(src[i]);
      object py_elem_obj(py_elem_hdl);
      extract<String> elem_proxy(py_elem_obj);
      to[i] = elem_proxy();
    }
  }
  Array<String> ArrayCopy<String>::toArray (const IPosition& shape,
					    void* data, bool)
  {
    Array<String> arr(shape);
    fromPy (arr.data(), data, arr.size());
    return arr;
  }

  ValueHolder makeArray (PyObject* obj_ptr, Bool copyData)
  {
    if (! PycArrayCheck(obj_ptr)) {
      throw AipsError ("PycArray: python object is not an array");
    }
    PyArrayObject* po = (PyArrayObject*)obj_ptr;
    boost::python::object obj;
    bool docopy = copyData;               // copy data if wanted or needed
    if (! PyArray_ISCONTIGUOUS(po)
	||  ! PyArray_ISALIGNED(po)
	||  PyArray_ISBYTESWAPPED(po)) {
      boost::python::handle<> py_hdl(obj_ptr);
      boost::python::object py_obj(py_hdl);
      // incr refcount, because ~object decrements it
      boost::python::incref(obj_ptr);
      obj = py_obj.attr("copy")();
      po = (PyArrayObject*)(obj.ptr());
      docopy = true;
    }
    // Swap axes, because Casacore has row minor and Python row major order.
    // A scalar is treated as a vector with length 1.
    int nd = PyArray_NDIM(po);
    IPosition shp(1, 1);
    if (nd > 0) {
      shp.resize (nd);
      for (int i=0; i<nd; i++) {
	shp[i] = PyArray_DIMS(po)[nd-i-1];
      }
    }
    // Assert array is contiguous now.
    // If the array is empty, numarray still sees it as non-contiguous.
    if (shp.product() > 0) {
      AlwaysAssert (PyArray_ISCONTIGUOUS(po)
		    ///&&  PyArray_ISALIGNED(po)   fails on MIPS (see issue 531)
		    &&  !PyArray_ISBYTESWAPPED(po), AipsError);
    }
    // Create the correct array.
    switch (PyArray_TYPE(po)) {
    case NPY_BOOL:
      return ValueHolder (ArrayCopy<Bool>::toArray(shp, PyArray_DATA(po), docopy));
    case NPY_INT16:
      return ValueHolder (ArrayCopy<Short>::toArray(shp, PyArray_DATA(po), docopy));
    case NPY_UINT16:
      return ValueHolder (ArrayCopy<uShort>::toArray(shp, PyArray_DATA(po), docopy));
    case NPY_INT32:
      return ValueHolder (ArrayCopy<Int>::toArray(shp, PyArray_DATA(po), docopy));
    case NPY_UINT32:
      return ValueHolder (ArrayCopy<uInt>::toArray(shp, PyArray_DATA(po), docopy));
    case NPY_INT64:
      return ValueHolder (ArrayCopy<Int64>::toArray(shp, PyArray_DATA(po), docopy));
    case NPY_FLOAT32:
      return ValueHolder (ArrayCopy<Float>::toArray(shp, PyArray_DATA(po), docopy));
    case NPY_FLOAT64:
      return ValueHolder (ArrayCopy<Double>::toArray(shp, PyArray_DATA(po), docopy));
    case NPY_COMPLEX64:
      return ValueHolder (ArrayCopy<Complex>::toArray(shp, PyArray_DATA(po), docopy));
    case NPY_COMPLEX128:
      return ValueHolder (ArrayCopy<DComplex>::toArray(shp, PyArray_DATA(po), docopy));
    case NPY_OBJECT:
      return ValueHolder (ArrayCopy<String>::toArray(shp, PyArray_DATA(po), docopy));
    default:
      // Some types can be the same as other types, so they cannot
      // be used in the switch (compiler complains).
      // This is true for BYTE and SBYTE which can equal to BOOL in numarray.
      // Similarly for STRING which exists for numpy and is set to
      // INT for numarray.
      if (PyArray_TYPE(po) == NPY_UINT64) {
	Array<uInt64> arr = ArrayCopy<uInt64>::toArray(shp, PyArray_DATA(po), False);
	Array<Int64> res(arr.shape());
	convertArray (res, arr);
	return ValueHolder(res);
      } else if (PyArray_TYPE(po) == NPY_INT8) {
	Array<signed char> arr = ArrayCopy<signed char>::toArray(shp, PyArray_DATA(po), False);
	Array<Short> res(arr.shape());
	convertArray (res, arr);
	return ValueHolder(res);
      } else if (PyArray_TYPE(po) == NPY_UINT8) {
	// Copy using signed char, because uChar is mapped to Short in the Traits.
	Array<signed char> arr = ArrayCopy<signed char>::toArray(shp, PyArray_DATA(po), False);
	Array<Short> res(arr.shape());
        void* varr = &arr;
        Array<uChar>* uarr = static_cast<Array<uChar>*>(varr);
	convertArray (res, *uarr);
	return ValueHolder(res);
      } else if (PyArray_TYPE(po) == NPY_STRING) {
	size_t slen = 0;
	if (nd > 0) {
	  slen = PyArray_STRIDES(po)[nd-1];
	}
	return ValueHolder (ArrayCopyStr_toArray(shp, PyArray_DATA(po), slen));
      } else if (PyArray_TYPE(po) == NPY_UNICODE) {
	size_t slen = 0;
	if (nd > 0) {
	  slen = PyArray_STRIDES(po)[nd-1];
	}
	return ValueHolder (ArrayCopyUnicode_toArray(shp, PyArray_DATA(po), slen));
      }
      break;
    }
    throw AipsError ("PycArray: unknown python array data type");
  }


  // Instantiate the various templates.
  template struct ArrayCopy<Bool>;
  template struct ArrayCopy<signed char>;
  template struct ArrayCopy<uChar>;
  template struct ArrayCopy<Short>;
  template struct ArrayCopy<uShort>;
  template struct ArrayCopy<Int>;
  template struct ArrayCopy<uInt>;
  template struct ArrayCopy<Int64>;
  template struct ArrayCopy<uInt64>;
  template struct ArrayCopy<Float>;
  template struct ArrayCopy<Double>;

  template boost::python::object makePyArrayObject
    (casacore::Array<Bool> const& arr);
  template boost::python::object makePyArrayObject
    (casacore::Array<uChar> const& arr);
  template boost::python::object makePyArrayObject
    (casacore::Array<Short> const& arr);
  template boost::python::object makePyArrayObject
    (casacore::Array<uShort> const& arr);
  template boost::python::object makePyArrayObject
    (casacore::Array<Int> const& arr);
  template boost::python::object makePyArrayObject
    (casacore::Array<uInt> const& arr);
  template boost::python::object makePyArrayObject
    (casacore::Array<Int64> const& arr);
  template boost::python::object makePyArrayObject
    (casacore::Array<Float> const& arr);
  template boost::python::object makePyArrayObject
    (casacore::Array<Double> const& arr);
  template boost::python::object makePyArrayObject
    (casacore::Array<Complex> const& arr);
  template boost::python::object makePyArrayObject
    (casacore::Array<DComplex> const& arr);