Open Galfurian opened 3 years ago
Hi,
Thanks for this very detailed explanation. xtensor-python
already provides C++ bindings for Numpy arrays, which can operates in place: pyarray
(for dynamic number of dimensions) and pytensor
(for static number of dimensions). They handle for you the conversion of strides and the auto assign problem, and any change from the python side will reflect on the C++ side (and vice versa).
#include <xtensor-python/pyarray.hpp>
pyarray<double> do_something(const pyarray<double>& arg)
{
// ...
// Will print the strides in the 'xtensor format' (i.e. strides
// in number of elements instead of number of bytes).
std::cout << xt::adapt(arg.strides()) << std::endl;
// Fine:
arg = arg;
return arg + 1.;
}
PYBIND11_MODULE(pytensor, m)
{
m.def("do_something", do_something);
}
a = np.array([1., 2.], [3., 4.])
b = do_sometihng(a)
print(b)
This should print the strides of a
(from the C++ side) and b
which is [[2., 3.], [4., 5]]
.
The main drawback is that pyarray
is different form xarray
. You can assign a pyarray
object to an xarray
object (and vice versa) but this results in a copy. Also we don't have bindings for views. Which makes me think we could implement your idea of a memory view of a buffer in-memory and have a pyadaptor
, that would allow to directly provide xtensor
structures to Python. That would actually be awesome!
Is that something you would like to implement?
Thank you very much for the reply.
I get the usefulness of both pyarray
and pytensor
compared to a more `handmade'' solution, and I know that
xt::adapt` is quite useful (like a swiss army knife).
Anyway, I will gladly investigate how to write a pyadaptor
. I might need some pointers to start with, and I will definitely need to check your coding guidelines.
Regards,
Enrico
Hi @JohanMabille,
When you said:
Also we don't have bindings for views.
in https://github.com/xtensor-stack/xtensor-python/issues/254#issuecomment-817560282
Did you mean for numpy views or xtensor views ?
I am experiencing some strange behaviour when I pass numpy views (resulting from some array indexing, e.g. arr[:, 99:]
) to my bindings function (expecting a pytensor
), and I am wondering whether this is because you don't have bindings for numpy views ? Or if passing numpy views for pytensor
parameters should work ?
Thanks.
Hi @JohanMabille and @Galfurian
Which makes me think we could implement your idea of a memory view of a buffer in-memory and have a pyadaptor, that would allow to directly provide xtensor structures to Python.
I would be incredibly interested in a pyadaptor that can take convert numpy arrays to xtensors/xarrays without copying. Did anything happen regarding this feature? If not: Do you have some pointers on how to start implementing this? E.g. Is there an adaptor in the xtensor-stack that could be used as a template?
This is a question/idea concerning the xtensor/python wrapping.
I was wondering if the following way I'm currently interfacing
xt::xarray
to numpy arrays in python is somehow valid and not an abomination. I'm writing down here the ideas, but I also wrote a self-contained and completely commented "example" project attached to this issue.What this wrapping method allowed me to do:
xarray
andxstrided_view
;Full Example: pytensor.zip
C++ to Python
First I had to take care of transforming xtensor strides to numpy strides:
Once I had a way of adapting the strides, I transformed the xarray into a
pybind11::array_t
, as follows:I've tested, and the changes made in python are correctly registered in C++. The only thing I know I must avoid is overriding the array with itself in python, otherwise, it will be a mess (more on this inside the attached full example).
A this point I thought, maybe I can do the same with views? So I wrote the following function:
I mean, that's all from this side.
Python to C++
The interfacing in the other direction is done in a similar fashion.
I needed some "adaptation" functions.
First, for the shape:
Second, for the strides:
Once I had both those adaptation function, I could easily modify the xt::xarray:
Then, I've done the same for the views:
Error To Avoid
Let us take an extract of the example I attached to the issue. Here is the C++ registration:
In python I need to avoid overriding the same array with itself:
This will output:
So except for this, it is behaving nicely the binding. I can also provided a solution to this problem:
Meh.. this requires a copy, is not that nice but it works.
Conclusion
I'm no expert, I started working with xtensor a couple of months ago.
I needed to interface something that was already using
xt::xtensor
to python, without changing everything toxt::pyarray
. I'm lazy, and also I didn't know how to make all thestrided_view
inside the code work with thext::pyarray
.I might be doing something nasty with this code, but I don't know all the meticulous details of how xtensor and python are actually handling the memory. I only know that both numpy and xtensor use the row-major ordered arrays and I'm hoping that's enough.
Thank you very much for reading through my explanation.
Cheers,
Enrico