【发布时间】:2020-11-19 09:50:24
【问题描述】:
我有一些 C++ 代码可以生成和操作 Eigen 矩阵的数组。
最后我想在 python 中使用这些矩阵,并认为这可能是pybind11 的工作。
基本上我想要在 python 中返回的是两个嵌套列表/numpy 数组
mat_a(I, 4, 4) 和 mat_b(J, K, 4, 4)。
因为我必须在 C++ 中做很多线性代数的事情,所以我想使用 Eigen,我使用的数据结构是
std::array<std::array<Eigen::Matrix4f, 2>, 3>>> mat_b // for J=3, K=2。
现在的问题是如何有效地将它传递给python?
此外,我想对多个输入 x = [x_0, x_1, ..., x_N] 执行这些计算,结果是 mat_a(N, I, 4, 4) 和 mat_b(N, J, K, 4, 4)。每个 x_i 的计算都是独立的,但我认为在 C++ 中通过 x_i 编写这个循环可能会更快。另一方面,如果我们在 C++ 中只有固定大小的数组,任务会变得更容易,这个循环也可以转移到 python。
这是我的问题的一些虚拟代码(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);
}
我通过以下方式编译代码:
c++ -O3 -Wall -shared -std=c++14 -fPIC `python3 -m pybind11 --includes` example.cpp -o example`python3-config --extension-suffix`
比在 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])
如果我只想返回一个Eigen::Matrix,它会运行得很快,而且据我所知无需复制。但是当我尝试将Eigen:Matrices 与std::array/std::vector 嵌套时,pybind 返回一个嵌套的numpy 数组列表,而不是一个多维数组。
这正如预期的那样,实际上我对它的效果印象深刻,但对我来说似乎相当慢,尤其是随着数组尺寸的增长。
问题是如何改进这一点以获得多维 numpy 数组而无需不必要的复制。
我尝试了一些道路但没有奏效(对我来说,这并不意味着它们通常不起作用;我只是想不通):
- 使用
Eigen::Tensor代替Eigen:Matrix的数组 - 在 python 中创建矩阵并通过引用将其传递给 C++
- 为数组构建自定义包装器
, J>
【问题讨论】:
-
一个
numpy多维数组有一个一维数据缓冲区,并使用shape和strides来处理多维性。如果您的 c++ 结构没有兼容的布局,则无法避免复制。例如,我相信c矩阵通常是指向数组的指针数组。这更像是 Python 列表布局。 -
一种可能的解决方法是在 Python 端(nd-array)分配一块内存,而不是为这个同质的内存块(具有相应的偏移量)创建多个视图/映射(您的矩阵)在 Eigen/C++ 方面并在那里与他们合作。 eigen.tuxfamily.org/dox/group__TutorialMapClass.html
标签: python c++ numpy eigen pybind11