I have some C++ code that generates and manipulates arrays of Eigen matrices.
In the end I want to use those matrices in python and thought this might be a job for pybind11.
Basically what I want back in python are two nested lists / numpy arrays
mat_a(I, 4, 4) and mat_b(J, K, 4, 4).
Because I have to do a lot of linear algebra stuff in C++ I wanted to use Eigen and the data structure I used is
std::array<std::array<Eigen::Matrix4f, 2>, 3>>> mat_b // for J=3, K=2.
The problem now is how to get this to python efficiently?
Additionally I want to perform those calculations for multiple inputs x = [x_0, x_1, ..., x_N] and than expect mat_a(N, I, 4, 4) and mat_b(N, J, K, 4, 4) as result. The calculations for each x_i are independent but I thought maybe it is faster to write this loop over x_i in C++ as well. If on the other hand the task gets easier if we only have fixed sized arrays in C++ this loop can also move to python.
Here is some dummy code of my problem (I=5, J=3, K=2) :
// example.cpp
#include <pybind11/pybind11.h>
#include <pybind11/eigen.h>
#include <pybind11/stl.h>
#include <pybind11/functional.h>
#include <pybind11/stl_bind.h>
#include <array>
#include <vector>
#include <Eigen/Dense>
Eigen::Matrix4f get_dummy(){
Eigen::Matrix4f mat_a;
mat_a << 1, 2, 3, 4,
5, 6, 7, 8,
9, 8, 7, 6,
5, 4, 3, 2;
return mat_a;
}
std::pair< std::vector<std::array<Eigen::Matrix4f, 5> >,
std::vector<std::array<std::array<Eigen::Matrix4f, 2>, 3> > > get_matrices(std::vector<float> & x){
std::vector<std::array<Eigen::Matrix4f, 5> > mat_a(x.size());
std::vector< std::array< std::array< Eigen::Matrix4f, 2>, 3> > mat_b(x.size());
// for (u_int i=0; i< x.size(); i++)
// do_stuff(x[i], mat_a[i], mat_b[i]);
mat_a[0][0] = get_dummy();
return std::make_pair(mat_a, mat_b);
}
PYBIND11_MODULE(example, m) {
m.def("get_dummy", &get_dummy, pybind11::return_value_policy::reference_internal);
m.def("get_matrices", &get_matrices, pybind11::return_value_policy::reference_internal);
}
I compile the code via:
c++ -O3 -Wall -shared -std=c++14 -fPIC `python3 -m pybind11 --includes` example.cpp -o example`python3-config --extension-suffix`
And than use it in python:
import numpy as np
import example
x = np.zeros(1000)
mat_a, mat_b = get_matrices(x)
print(np.shape(mat_a))
print(np.shape(mat_b))
print(mat_a[0][0])
If I just want to return a single Eigen::Matrix it works fast and as far as I can tell without copying. But when I try to nest the Eigen:Matrices with std::array/std::vector pybind returns a nested list of numpy arrays instead of one multidimensional array.
This is as expected and I am actually impressed how well this works but it seems rather slow to me especially as the dimensions of the arrays grow.
The question is how can I improve this to get multidimensional numpy arrays without unnecessary copying.
Some roads I tried but did not work (for me, what doesn't mean that they do not work in general; I just could not figure it out):
- use
Eigen::Tensorinstead of the arrays ofEigen:Matrix - create the matrices in python and pass it to C++ by reference
- building a custom wrapper for array<array<Matrix4f, K>, J>