9#include "./make_numpy_proxy_from_array.hpp"
17 template <
int R,
typename Layout>
18 bool numpy_check_layout(PyObject *obj) {
19 EXPECTS(PyArray_Check(obj));
20 PyArrayObject *arr = (PyArrayObject *)(obj);
21 return Layout::template mapping<R>::is_stride_order_valid(PyArray_DIMS(arr), PyArray_STRIDES(arr));
24 template <
typename T,
int R,
typename Layout,
char Algebra>
25 requires(has_npy_type<std::decay_t<T>>)
26 struct py_converter<nda::basic_array_view<T, R, Layout, Algebra>> {
28 using view_t = nda::basic_array_view<T, R, Layout, Algebra>;
29 using U = std::decay_t<T>;
30 static_assert(not std::is_same_v<U, pyref>,
"Not implemented");
35 static std::string tp_name() {
36 std::ostringstream out;
37 out <<
"ndarray[" << python_typename<T>() <<
", " << R <<
"]";
43 static PyObject *c2py(view_t v) {
44 auto p = nda::python::make_numpy_proxy_from_array_or_view(v);
50 static bool is_convertible(PyObject *obj,
bool raise_python_exception,
bool allow_lower_rank =
false,
bool require_c_order =
true) {
53 if (not PyArray_Check(obj)) {
54 if (raise_python_exception) PyErr_SetString(PyExc_TypeError,
"Cannot convert to array_view : Python object is not a Numpy array");
57 PyArrayObject *arr = (PyArrayObject *)(obj);
59 auto r = PyArray_NDIM(arr);
60 if (allow_lower_rank ? r < R : r != R) {
61 if (raise_python_exception)
69 if (not allow_lower_rank && r == R) {
70 if (has_npy_type<U> && (PyArray_TYPE(arr) != npy_type<U>)) {
71 if (raise_python_exception) PyErr_SetString(PyExc_TypeError,
"Cannot convert to array_view : Type mismatch");
76 if (require_c_order and not numpy_check_layout<R, Layout>(obj)) {
77 if (raise_python_exception) PyErr_SetString(PyExc_TypeError,
"Cannot convert to array_view : Numpy array is not in C order");
81 if constexpr (not std::is_const_v<T>) {
82 if (not PyArray_ISWRITEABLE(arr)) {
83 if (raise_python_exception) PyErr_SetString(PyExc_TypeError,
"Cannot convert to mutable array_view : Numpy array is read-only");
93 static view_t py2c(PyObject *obj) {
94 auto p = make_numpy_proxy(obj);
95 EXPECTS(p.extents.size() >= R);
96 EXPECTS(p.element_type == npy_type<T> or p.extents.size() > R);
97 EXPECTS((numpy_check_layout<R, Layout>(obj)));
99 std::array<long, R> extents, strides;
100 for (
int u = 0; u < R; ++u) {
101 extents[u] = p.extents[u];
102 strides[u] = p.strides[u] /
sizeof(T);
104 return view_t{{extents, strides},
static_cast<T *
>(p.data)};
111 template <
typename T,
int R,
char Algebra>
112 struct py_converter<nda::basic_array<T, R, nda::C_layout, Algebra, nda::heap<>>> {
115 static_assert(not std::is_same_v<T, pyref>,
"Not implemented");
116 static_assert(not std::is_same_v<T, PyObject *>,
"Not implemented");
118 using array_t = nda::basic_array<T, R, nda::C_layout, Algebra, nda::heap<>>;
119 using view_t = nda::basic_array_view<T, R, nda::C_stride_layout, Algebra>;
120 using converter_T = py_converter<std::decay_t<T>>;
121 using converter_view_T = py_converter<view_t>;
122 using converter_view_pyobject = py_converter<nda::basic_array_view<PyObject *, R, nda::C_stride_layout, Algebra>>;
126 static std::string tp_name() {
127 std::ostringstream out;
128 out <<
"ndarray[" << python_typename<T>() <<
", " << R <<
"]";
134 template <
typename A>
135 static PyObject *c2py(A &&src) {
136 static_assert(std::is_same_v<std::decay_t<A>, nda::basic_array<T, R, nda::C_layout, Algebra, nda::heap<>>>,
137 "Logic Error in array c2py conversion");
138 auto p = nda::python::make_numpy_proxy_from_array_or_view(std::forward<A>(src));
139 return p.to_python();
144 static PyObject *make_numpy(PyObject *obj) {
145 return PyArray_FromAny(obj, PyArray_DescrFromType(npy_type<T>), R, R, NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_ENSURECOPY, NULL);
148 static bool is_convertible(PyObject *obj,
bool raise_python_exception) {
150 if (not PyArray_Check(obj) or (PyArray_Check(obj) and has_npy_type<T> and (PyArray_TYPE((PyArrayObject *)(obj)) != npy_type<T>))) {
151 c2py::pyref numpy_obj = make_numpy(obj);
152 if (PyErr_Occurred()) {
153 if (!raise_python_exception) PyErr_Clear();
156 return is_convertible(numpy_obj, raise_python_exception);
159 if constexpr (has_npy_type<T>) {
162 using const_view_t = nda::basic_array_view<const T, R, nda::C_stride_layout, Algebra>;
163 return py_converter<const_view_t>::is_convertible(obj, raise_python_exception,
false ,
false );
167 bool res = converter_view_pyobject::is_convertible(obj, raise_python_exception,
true );
168 if (not res)
return false;
175 auto p = make_numpy_proxy(obj);
176 std::array<long, R> shape;
177 for (
int i = 0; i < R; ++i) shape[i] = p.extents[i];
178 auto l = [obj](
auto... i) ->
bool {
179 pyref subobj = PyObject_GetItem(obj, pyref::make_tuple(PyLong_FromLong(i)...));
180 return converter_T::is_convertible(subobj,
false);
183 for (
int i = 0; i < R; ++i) n_elements *= shape[i];
184 long n_convertible =
sum(nda::array_adapter{shape, l});
186 if (n_convertible != n_elements) {
187 if (raise_python_exception) PyErr_SetString(PyExc_TypeError,
"Cannot convert to array. One element can not be converted to C++.");
196 static array_t py2c(PyObject *obj) {
199 if (not PyArray_Check(obj) or (PyArray_Check(obj) and has_npy_type<T> and (PyArray_TYPE((PyArrayObject *)(obj)) != npy_type<T>))) {
201 c2py::pyref numpy_obj = make_numpy(obj);
202 EXPECTS(not PyErr_Occurred());
203 return py2c(numpy_obj);
206 if constexpr (has_npy_type<T>) {
207 if (not numpy_check_layout<R, nda::C_layout>(obj) or not PyArray_ISWRITEABLE((PyArrayObject *)(obj))) {
208 c2py::pyref obj_copy = make_numpy(obj);
209 return array_t{converter_view_T::py2c(obj_copy)};
211 return converter_view_T::py2c(obj);
213 auto p = make_numpy_proxy(obj);
214 std::array<long, R> shape;
215 for (
int i = 0; i < R; ++i) shape[i] = p.extents[i];
216 auto l = [obj](
auto... i) {
217 pyref subobj = PyObject_GetItem(obj, pyref::make_tuple(PyLong_FromLong(i)...));
218 return converter_T::py2c(subobj);
220 array_t res = nda::array_adapter{shape, l};
221 if (PyErr_Occurred()) PyErr_Print();
227 template <
typename T,
int R,
char Algebra>
228 requires(has_npy_type<std::decay_t<T>>)
229 struct py_converter<nda::basic_array<T, R, nda::C_layout, Algebra,
nda::heap<>>
const &> {
230 using array_t = nda::basic_array<T, R, nda::C_layout, Algebra, nda::heap<>>;
231 static PyObject *c2py(array_t
const &a, [[maybe_unused]] PyObject *guardian) {
236 template <
typename T,
int R,
char Algebra>
237 requires(has_npy_type<std::decay_t<T>>)
238 struct py_converter<nda::basic_array<T, R, nda::C_layout, Algebra,
nda::heap<>> &> {
239 using array_t = nda::basic_array<T, R, nda::C_layout, Algebra, nda::heap<>>;
240 using view_t = nda::basic_array_view<T, R, nda::C_layout, Algebra>;
241 static PyObject *c2py(array_t &a, [[maybe_unused]] PyObject *guardian) {
242 return cxx2py(view_t(a));
247 template <
typename E>
249 struct py_converter<E> {
auto sum(A const &a)
Sum all the elements of an nda::Array object.
decltype(auto) make_regular(A &&a)
Make a given object regular.
auto make_const_view(basic_array< T, R, LP, A, CP > const &a)
Make an nda::basic_array_view with a const value type from a given nda::basic_array.
constexpr bool is_expression
Constexpr variable that is true if type A is a lazy expression type.
heap_basic< mem::mallocator< AdrSp > > heap
Alias template of the nda::heap_basic policy using an nda::mem::mallocator.
std::string to_string(std::array< T, R > const &a)
Get a string representation of a std::array.
Includes all relevant headers for the core nda library.