我可以使用 pybind11 将 numpy 数组传递给接受 Eigen::Tensor 的函数吗?
Can I use pybind11 to pass a numpy array to a function accepting a Eigen::Tensor?
我可以使用 pybind1
将三维 numpy 数组传递给接受 Eigen::Tensor
作为参数的 c++ 函数吗?例如,考虑以下 C++ 函数:
Eigen::Tensor<double, 3> addition_tensor(Eigen::Tensor<double, 3> a,
Eigen::Tensor<double, 3> b) {
return a + b;
}
编译函数后,将其导入 python 并将 numpy 数组 np.ones((1, 2, 2))
传递给它,我收到以下错误消息:
TypeError: addition_tensor(): incompatible function arguments. The following argument types are supported:
1. (arg0: Eigen::Tensor<double, 3, 0, long>, arg1: Eigen::Tensor<double, 3, 0, long>) -> Eigen::Tensor<double, 3, 0, long>
我对无法传递三维 numpy 数组感到特别惊讶,因为我可以将二维 numpy array
传递给接受 Eigen::MatrixXd
的函数,如:
Eigen::MatrixXd addition(Eigen::MatrixXd a, Eigen::MatrixXd b) { return a + b; }
我在这个例子中使用的全部代码是:
#include <eigen-git-mirror/Eigen/Dense>
#include <eigen-git-mirror/unsupported/Eigen/CXX11/Tensor>
#include "pybind11/include/pybind11/eigen.h"
#include "pybind11/include/pybind11/pybind11.h"
Eigen::MatrixXd addition(Eigen::MatrixXd a, Eigen::MatrixXd b) { return a + b; }
Eigen::Tensor<double, 3> addition_tensor(Eigen::Tensor<double, 3> a,
Eigen::Tensor<double, 3> b) {
return a + b;
}
PYBIND11_MODULE(example, m) {
m.def("addition", &addition, "A function which adds two numbers");
m.def("addition_tensor", &addition_tensor,
"A function which adds two numbers");
}
我用g++ -shared -fPIC `python3 -m pybind11 --includes` example.cpp -o example`python3-config --extension-suffix`
编译了上面的代码。有人知道如何将三维 numpy
数组转换为接受三维 Eigen::Tensor
的函数吗?
不直接支持,这里有一些讨论(如果您想将其添加到您的项目中,包括一些用于映射的代码):https://github.com/pybind/pybind11/issues/1377
感谢@John Zwinck 的回答,我可以实现我正在寻找的东西。如果有人感兴趣,这里是复制:
#include <eigen-git-mirror/Eigen/Dense>
#include <eigen-git-mirror/unsupported/Eigen/CXX11/Tensor>
#include "pybind11/include/pybind11/eigen.h"
#include "pybind11/include/pybind11/numpy.h"
#include "pybind11/include/pybind11/pybind11.h"
Eigen::Tensor<double, 3, Eigen::RowMajor> getTensor(
pybind11::array_t<double> inArray) {
// request a buffer descriptor from Python
pybind11::buffer_info buffer_info = inArray.request();
// extract data an shape of input array
double *data = static_cast<double *>(buffer_info.ptr);
std::vector<ssize_t> shape = buffer_info.shape;
// wrap ndarray in Eigen::Map:
// the second template argument is the rank of the tensor and has to be
// known at compile time
Eigen::TensorMap<Eigen::Tensor<double, 3, Eigen::RowMajor>> in_tensor(
data, shape[0], shape[1], shape[2]);
return in_tensor;
}
pybind11::array_t<double> return_array(
Eigen::Tensor<double, 3, Eigen::RowMajor> inp) {
std::vector<ssize_t> shape(3);
shape[0] = inp.dimension(0);
shape[1] = inp.dimension(1);
shape[2] = inp.dimension(2);
return pybind11::array_t<double>(
shape, // shape
{shape[1] * shape[2] * sizeof(double), shape[2] * sizeof(double),
sizeof(double)}, // strides
inp.data()); // data pointer
}
pybind11::array_t<double> addition(pybind11::array_t<double> a,
pybind11::array_t<double> b) {
Eigen::Tensor<double, 3, Eigen::RowMajor> a_t = getTensor(a);
Eigen::Tensor<double, 3, Eigen::RowMajor> b_t = getTensor(b);
Eigen::Tensor<double, 3, Eigen::RowMajor> res = a_t + b_t;
return return_array(res);
}
PYBIND11_MODULE(example, m) {
m.def("addition", &addition, "A function which adds two numbers");
}
与 John 提到的 link 中的建议相反,我不介意对 Eigen::Tensor
使用 RowMajor
存储顺序。我也看到这个存储顺序在 tensorflow
代码中被多次使用。我不知道上面的代码是否不必要地复制了数据。
我可以使用 pybind1
将三维 numpy 数组传递给接受 Eigen::Tensor
作为参数的 c++ 函数吗?例如,考虑以下 C++ 函数:
Eigen::Tensor<double, 3> addition_tensor(Eigen::Tensor<double, 3> a,
Eigen::Tensor<double, 3> b) {
return a + b;
}
编译函数后,将其导入 python 并将 numpy 数组 np.ones((1, 2, 2))
传递给它,我收到以下错误消息:
TypeError: addition_tensor(): incompatible function arguments. The following argument types are supported:
1. (arg0: Eigen::Tensor<double, 3, 0, long>, arg1: Eigen::Tensor<double, 3, 0, long>) -> Eigen::Tensor<double, 3, 0, long>
我对无法传递三维 numpy 数组感到特别惊讶,因为我可以将二维 numpy array
传递给接受 Eigen::MatrixXd
的函数,如:
Eigen::MatrixXd addition(Eigen::MatrixXd a, Eigen::MatrixXd b) { return a + b; }
我在这个例子中使用的全部代码是:
#include <eigen-git-mirror/Eigen/Dense>
#include <eigen-git-mirror/unsupported/Eigen/CXX11/Tensor>
#include "pybind11/include/pybind11/eigen.h"
#include "pybind11/include/pybind11/pybind11.h"
Eigen::MatrixXd addition(Eigen::MatrixXd a, Eigen::MatrixXd b) { return a + b; }
Eigen::Tensor<double, 3> addition_tensor(Eigen::Tensor<double, 3> a,
Eigen::Tensor<double, 3> b) {
return a + b;
}
PYBIND11_MODULE(example, m) {
m.def("addition", &addition, "A function which adds two numbers");
m.def("addition_tensor", &addition_tensor,
"A function which adds two numbers");
}
我用g++ -shared -fPIC `python3 -m pybind11 --includes` example.cpp -o example`python3-config --extension-suffix`
编译了上面的代码。有人知道如何将三维 numpy
数组转换为接受三维 Eigen::Tensor
的函数吗?
不直接支持,这里有一些讨论(如果您想将其添加到您的项目中,包括一些用于映射的代码):https://github.com/pybind/pybind11/issues/1377
感谢@John Zwinck 的回答,我可以实现我正在寻找的东西。如果有人感兴趣,这里是复制:
#include <eigen-git-mirror/Eigen/Dense>
#include <eigen-git-mirror/unsupported/Eigen/CXX11/Tensor>
#include "pybind11/include/pybind11/eigen.h"
#include "pybind11/include/pybind11/numpy.h"
#include "pybind11/include/pybind11/pybind11.h"
Eigen::Tensor<double, 3, Eigen::RowMajor> getTensor(
pybind11::array_t<double> inArray) {
// request a buffer descriptor from Python
pybind11::buffer_info buffer_info = inArray.request();
// extract data an shape of input array
double *data = static_cast<double *>(buffer_info.ptr);
std::vector<ssize_t> shape = buffer_info.shape;
// wrap ndarray in Eigen::Map:
// the second template argument is the rank of the tensor and has to be
// known at compile time
Eigen::TensorMap<Eigen::Tensor<double, 3, Eigen::RowMajor>> in_tensor(
data, shape[0], shape[1], shape[2]);
return in_tensor;
}
pybind11::array_t<double> return_array(
Eigen::Tensor<double, 3, Eigen::RowMajor> inp) {
std::vector<ssize_t> shape(3);
shape[0] = inp.dimension(0);
shape[1] = inp.dimension(1);
shape[2] = inp.dimension(2);
return pybind11::array_t<double>(
shape, // shape
{shape[1] * shape[2] * sizeof(double), shape[2] * sizeof(double),
sizeof(double)}, // strides
inp.data()); // data pointer
}
pybind11::array_t<double> addition(pybind11::array_t<double> a,
pybind11::array_t<double> b) {
Eigen::Tensor<double, 3, Eigen::RowMajor> a_t = getTensor(a);
Eigen::Tensor<double, 3, Eigen::RowMajor> b_t = getTensor(b);
Eigen::Tensor<double, 3, Eigen::RowMajor> res = a_t + b_t;
return return_array(res);
}
PYBIND11_MODULE(example, m) {
m.def("addition", &addition, "A function which adds two numbers");
}
与 John 提到的 link 中的建议相反,我不介意对 Eigen::Tensor
使用 RowMajor
存储顺序。我也看到这个存储顺序在 tensorflow
代码中被多次使用。我不知道上面的代码是否不必要地复制了数据。