-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Convert vector of basic type to numpy array instead of list
-Port numpy_proxy class from nda
- Loading branch information
Showing
4 changed files
with
260 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
#include "numpy_proxy.hpp" | ||
|
||
namespace cpp2py { | ||
|
||
// Make a new view_info | ||
PyObject *numpy_proxy::to_python() { | ||
|
||
// Apparently we can not get rid of this | ||
_import_array(); | ||
|
||
#ifdef PYTHON_NUMPY_VERSION_LT_17 | ||
int flags = NPY_BEHAVED & ~NPY_OWNDATA; | ||
#else | ||
int flags = NPY_ARRAY_BEHAVED & ~NPY_ARRAY_OWNDATA; | ||
#endif | ||
// make the array read only | ||
if (is_const) flags &= ~NPY_ARRAY_WRITEABLE; | ||
PyObject *result = | ||
PyArray_NewFromDescr(&PyArray_Type, PyArray_DescrFromType(element_type), rank, extents.data(), strides.data(), data, flags, NULL); | ||
if (not result) return nullptr; // the Python error is set | ||
|
||
if (!PyArray_Check(result)) { | ||
PyErr_SetString(PyExc_RuntimeError, "The python object is not a numpy array"); | ||
return nullptr; | ||
} | ||
|
||
PyArrayObject *arr = (PyArrayObject *)(result); | ||
#ifdef PYTHON_NUMPY_VERSION_LT_17 | ||
arr->base = base; | ||
assert(arr->flags == (arr->flags & ~NPY_OWNDATA)); | ||
#else | ||
int r = PyArray_SetBaseObject(arr, base); | ||
//EXPECTS(r == 0); | ||
//EXPECTS(PyArray_FLAGS(arr) == (PyArray_FLAGS(arr) & ~NPY_ARRAY_OWNDATA)); | ||
#endif | ||
base = nullptr; // ref is stolen by the new object | ||
|
||
return result; | ||
} | ||
|
||
// ---------------------------------------------------------- | ||
|
||
// Extract a view_info from python | ||
numpy_proxy make_numpy_proxy(PyObject *obj) { | ||
|
||
// Apparently we can not get rid of this | ||
_import_array(); | ||
|
||
if (obj == NULL) return {}; | ||
if (not PyArray_Check(obj)) return {}; | ||
|
||
numpy_proxy result; | ||
|
||
// extract strides and lengths | ||
PyArrayObject *arr = (PyArrayObject *)(obj); | ||
|
||
#ifdef PYTHON_NUMPY_VERSION_LT_17 | ||
result.rank = arr->nd; | ||
#else | ||
result.rank = PyArray_NDIM(arr); | ||
#endif | ||
|
||
result.element_type = PyArray_TYPE(arr); | ||
result.extents.resize(result.rank); | ||
result.strides.resize(result.rank); | ||
result.data = PyArray_DATA(arr); | ||
// base is ignored, stays at nullptr | ||
|
||
#ifdef PYTHON_NUMPY_VERSION_LT_17 | ||
for (long i = 0; i < result.rank; ++i) { | ||
result.extents[i] = size_t(arr->dimensions[i]); | ||
result.strides[i] = std::ptrdiff_t(arr->strides[i]); | ||
} | ||
#else | ||
for (size_t i = 0; i < result.rank; ++i) { | ||
result.extents[i] = size_t(PyArray_DIMS(arr)[i]); | ||
result.strides[i] = std::ptrdiff_t(PyArray_STRIDES(arr)[i]); | ||
} | ||
#endif | ||
|
||
//PRINT(result.rank); | ||
//PRINT(result.element_type); | ||
//PRINT(result.data); | ||
|
||
return result; | ||
} | ||
|
||
// ---------------------------------------------------------- | ||
|
||
PyObject *make_numpy_copy(PyObject *obj, int rank, long element_type) { | ||
|
||
if (obj == nullptr) return nullptr; | ||
|
||
// From obj, we ask the numpy library to make a numpy, and of the correct type. | ||
// This handles automatically the cases where : | ||
// - we have list, or list of list/tuple | ||
// - the numpy type is not the one we want. | ||
// - adjust the dimension if needed | ||
// If obj is an array : | ||
// - if Order is same, don't change it | ||
// - else impose it (may provoque a copy). | ||
// if obj is not array : | ||
// - Order = FortranOrder or SameOrder - > Fortran order otherwise C | ||
|
||
int flags = 0; //(ForceCast ? NPY_FORCECAST : 0) ;// do NOT force a copy | (make_copy ? NPY_ENSURECOPY : 0); | ||
//if (!(PyArray_Check(obj) )) | ||
//flags |= ( IndexMapType::traversal_order == indexmaps::mem_layout::c_order(rank) ? NPY_C_CONTIGUOUS : NPY_F_CONTIGUOUS); //impose mem order | ||
#ifdef PYTHON_NUMPY_VERSION_LT_17 | ||
flags |= (NPY_C_CONTIGUOUS); //impose mem order | ||
flags |= (NPY_ENSURECOPY); | ||
#else | ||
flags |= (NPY_ARRAY_C_CONTIGUOUS); // impose mem order | ||
flags |= (NPY_ARRAY_ENSURECOPY); | ||
#endif | ||
return PyArray_FromAny(obj, PyArray_DescrFromType(element_type), rank, rank, flags, NULL); // new ref | ||
} | ||
|
||
} // namespace nda::python |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
#pragma once | ||
#include <Python.h> | ||
#include <numpy/arrayobject.h> | ||
|
||
#include <vector> | ||
#include <complex> | ||
|
||
namespace cpp2py { | ||
|
||
using v_t = std::vector<long>; | ||
|
||
// the basic information for a numpy array | ||
struct numpy_proxy { | ||
int rank = 0; | ||
long element_type = 0; | ||
void *data = nullptr; | ||
bool is_const = false; | ||
v_t extents, strides; | ||
PyObject *base = nullptr; // The ref. counting guard typically | ||
|
||
// Returns a new ref (or NULL if failure) with a new numpy. | ||
// If failure, return null with the Python exception set | ||
PyObject *to_python(); | ||
}; | ||
|
||
// From a numpy, extract the info. Better than a constructor, I want to use the aggregate constructor of the struct also. | ||
numpy_proxy make_numpy_proxy(PyObject *); | ||
|
||
// Make a copy in Python with the given rank and element_type | ||
// If failure, return null with the Python exception set | ||
PyObject *make_numpy_copy(PyObject *obj, int rank, long elements_type); | ||
|
||
// | ||
template <typename T> constexpr long npy_type = -1; | ||
template <typename T> constexpr bool has_npy_type = (npy_type<T> == -1); | ||
|
||
#define NPY_CONVERT(C, P) template <> constexpr long npy_type<C> = P; | ||
NPY_CONVERT(bool, NPY_BOOL) | ||
NPY_CONVERT(char, NPY_STRING) | ||
NPY_CONVERT(signed char, NPY_BYTE) | ||
NPY_CONVERT(unsigned char, NPY_UBYTE) | ||
NPY_CONVERT(short, NPY_SHORT) | ||
NPY_CONVERT(unsigned short, NPY_USHORT) | ||
NPY_CONVERT(int, NPY_INT) | ||
NPY_CONVERT(unsigned int, NPY_UINT) | ||
NPY_CONVERT(long, NPY_LONG) | ||
NPY_CONVERT(unsigned long, NPY_ULONG) | ||
NPY_CONVERT(long long, NPY_LONGLONG) | ||
NPY_CONVERT(unsigned long long, NPY_ULONGLONG) | ||
NPY_CONVERT(float, NPY_FLOAT) | ||
NPY_CONVERT(double, NPY_DOUBLE) | ||
NPY_CONVERT(long double, NPY_LONGDOUBLE) | ||
NPY_CONVERT(std::complex<float>, NPY_CFLOAT) | ||
NPY_CONVERT(std::complex<double>, NPY_CDOUBLE) | ||
NPY_CONVERT(std::complex<long double>, NPY_CLONGDOUBLE) | ||
#undef NPY_CONVERT | ||
|
||
} // namespace cpp2py |