C++ 2D vector to 2D pybind11 array

Viewed 1252

I'm fairly new to C++ and struggling with pybind. I simply can't figure out how to convince pybind to convert a 2D vector from C++ to a python-readable format.

That's the code I have in mind:

py::array_t<float> to_matrix(std::vector<std::vector<float>> &vals)
{
    int N = vals.size();
    int M = 6;
    py::array_t<float>({N, M}) arr;
    for (int i = 0; (i < N); i++)
    {
        for (int j = 0; (j < M); j++)
        {
            arr[i][j] = vals[i][j];
        };
    };
    return arr;
};

The input from C++ is a vector of vectors with N rows and 6 columns, simply a very long list of data points. Ideally I would like to have the output as a numpy array, but any python datastructure is fine (e.g., list of lists).

The documentation makes it sound so easy, but I can't figure it out. What am I doing wrong?

Thanks for any help in advance.

1 Answers

There are a few things going on here, but let's start by getting a minimal example working. The following function will create a 2d array from a hard-coded std::vector<std::vector<float>>

py::array_t<float> to_matrix()
{
    std::vector<std::vector<float>> vals = {
        {1, 2, 3, 4, 5},
        {6, 7, 8, 9, 10},
        {11, 12, 13, 14, 15}
    };

    size_t N = vals.size();
    size_t M = vals[0].size();

    py::array_t<float, py::array::c_style> arr({N, M});

    auto ra = arr.mutable_unchecked();

    for (size_t i = 0; i < N; i++)
    {
        for (size_t j = 0; j < M; j++)
        {
            ra(i, j) = vals[i][j];
        };
    };

    return arr;
};

PYBIND11_MODULE(foo, m)
{
    m.def("to_matrix", &to_matrix);
}

Two things to note, first the array shape is a constructor argument to the array. Second is the use of mutable_unchecked to obtain a proxy object that can be used to make the assignments.

In your case the vector of vectors would be coming from elsewhere in your C++ code.

But note that pybind11 also provides boilerplates for wrapping containers like std::vector. These are available in the header pybind11/stl_bind.h, and allow you to do this

std::vector<std::vector<float>> make_vector()
{
    std::vector<std::vector<float>> vals = {
        {1, 2, 3, 4, 5},
        {6, 7, 8, 9, 10},
        {11, 12, 13, 14, 15}
    };

    return vals;

}

PYBIND11_MODULE(foo, m)
{
    py::bind_vector<std::vector<std::vector<float>>>(m, "FloatVector2D");
    m.def("make_vector", &make_vector);
}

Such an object won't be completely equivalent to a numpy array though (has no shape attribute etc)

Related