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 struct py_converter<nda::basic_array_view<T, R, Layout, Algebra>> {
27 using view_t = nda::basic_array_view<T, R, Layout, Algebra>;
28 using U = std::decay_t<T>;
29 static_assert(has_npy_type<U>,
"Logical Error");
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");
86 static view_t py2c(PyObject *obj) {
87 auto p = make_numpy_proxy(obj);
88 EXPECTS(p.extents.size() >= R);
89 EXPECTS(p.element_type == npy_type<T> or p.extents.size() > R);
90 EXPECTS((numpy_check_layout<R, Layout>(obj)));
92 std::array<long, R> extents, strides;
93 for (
int u = 0; u < R; ++u) {
94 extents[u] = p.extents[u];
95 strides[u] = p.strides[u] /
sizeof(T);
97 return view_t{{extents, strides},
static_cast<T *
>(p.data)};
104 template <
typename T,
int R,
char Algebra>
105 struct py_converter<nda::basic_array<T, R, nda::C_layout, Algebra, nda::heap<>>> {
108 static_assert(not std::is_same_v<T, pyref>,
"Not implemented");
109 static_assert(not std::is_same_v<T, PyObject *>,
"Not implemented");
111 using array_t = nda::basic_array<T, R, nda::C_layout, Algebra, nda::heap<>>;
112 using view_t = nda::basic_array_view<T, R, nda::C_layout, Algebra>;
113 using converter_T = py_converter<std::decay_t<T>>;
114 using converter_view_T = py_converter<view_t>;
115 using converter_view_pyobject = py_converter<nda::basic_array_view<PyObject *, R, nda::C_layout, Algebra>>;
119 static std::string tp_name() {
120 std::ostringstream out;
121 out <<
"ndarray[" << python_typename<T>() <<
", " << R <<
"]";
127 template <
typename A>
128 static PyObject *c2py(A &&src) {
129 static_assert(std::is_same_v<std::decay_t<A>, nda::basic_array<T, R, nda::C_layout, Algebra, nda::heap<>>>,
130 "Logic Error in array c2py conversion");
131 auto p = nda::python::make_numpy_proxy_from_array_or_view(std::forward<A>(src));
132 return p.to_python();
137 static PyObject *make_numpy(PyObject *obj) {
138 return PyArray_FromAny(obj, PyArray_DescrFromType(npy_type<T>), R, R, NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_ENSURECOPY, NULL);
141 static bool is_convertible(PyObject *obj,
bool raise_python_exception) {
143 if (not PyArray_Check(obj) or (PyArray_Check(obj) and has_npy_type<T> and (PyArray_TYPE((PyArrayObject *)(obj)) != npy_type<T>))) {
144 c2py::pyref numpy_obj = make_numpy(obj);
145 if (PyErr_Occurred()) {
146 if (!raise_python_exception) PyErr_Clear();
149 return is_convertible(numpy_obj, raise_python_exception);
152 if constexpr (has_npy_type<T>) {
154 return converter_view_T::is_convertible(obj, raise_python_exception,
false ,
false );
158 bool res = converter_view_pyobject::is_convertible(obj, raise_python_exception,
true );
159 if (not res)
return false;
166 auto p = make_numpy_proxy(obj);
167 std::array<long, R> shape;
168 for (
int i = 0; i < R; ++i) shape[i] = p.extents[i];
169 auto l = [obj](
auto... i) ->
bool {
170 pyref subobj = PyObject_GetItem(obj, pyref::make_tuple(PyLong_FromLong(i)...));
171 return converter_T::is_convertible(subobj,
false);
173 res =
sum(nda::array_adapter{shape, l});
175 if (!res and raise_python_exception) PyErr_SetString(PyExc_TypeError,
"Cannot convert to array. One element can not be converted to C++.");
182 static array_t py2c(PyObject *obj) {
185 if (not PyArray_Check(obj) or (PyArray_Check(obj) and has_npy_type<T> and (PyArray_TYPE((PyArrayObject *)(obj)) != npy_type<T>))) {
187 c2py::pyref numpy_obj = make_numpy(obj);
188 EXPECTS(not PyErr_Occurred());
189 return py2c(numpy_obj);
192 if constexpr (has_npy_type<T>) {
193 if (not numpy_check_layout<R, nda::C_layout>(obj)) {
194 c2py::pyref obj_c_order = make_numpy(obj);
195 return array_t{converter_view_T::py2c(obj_c_order)};
197 return converter_view_T::py2c(obj);
199 auto p = make_numpy_proxy(obj);
200 std::array<long, R> shape;
201 for (
int i = 0; i < R; ++i) shape[i] = p.extents[i];
202 auto l = [obj](
auto... i) {
203 pyref subobj = PyObject_GetItem(obj, pyref::make_tuple(PyLong_FromLong(i)...));
204 return converter_T::py2c(subobj);
206 array_t res = nda::array_adapter{shape, l};
207 if (PyErr_Occurred()) PyErr_Print();
213 template <
typename T,
int R,
char Algebra>
214 struct py_converter<nda::basic_array<T, R, nda::C_layout, Algebra, nda::heap<>> const &> {
215 using array_t = nda::basic_array<T, R, nda::C_layout, Algebra, nda::heap<>>;
219 template <
typename T,
int R,
char Algebra>
220 struct py_converter<nda::basic_array<T, R, nda::C_layout, Algebra, nda::heap<>> &> {
221 using array_t = nda::basic_array<T, R, nda::C_layout, Algebra, nda::heap<>>;
222 using view_t = nda::basic_array_view<T, R, nda::C_layout, Algebra>;
223 static PyObject *c2py(array_t &a) {
return cxx2py(view_t(a)); }
226 template <
typename E>
228 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.
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.