使用Pybind11将Eigen::Tensor暴露给Python

3

我试图使用pybind11将一个Eigen张量暴露给Python。我可以编译所有内容,也可以成功地将其导入Python。然而,数据无法转换为Python类型。我尝试了两种方法。一种是直接暴露数据,另一种是使用映射。但在Python环境中两者都失败了。

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/stl_bind.h>
#include <pybind11/numpy.h>
#include <pybind11/eigen.h>
#include <unsupported/Eigen/CXX11/Tensor>

class myclass{

    myclass(){
        m_data = new float[m_dim1*m_dim2*m_dim3]; // Contiguous data that represents a three dimensional array
        for(int i = 0; i<m_dim1*m_dim2*m_dim3; i++)
            m_data[i] = i;

        m_tensor = Eigen::TensorMap<Eigen::Tensor<float, 3>>(m_data, m_dim1, m_dim2, m_dim3);
    }

    Eigen::TensorMap<Eigen::Tensor<float, 3>>& getDataUsingMapping() { Eigen::TensorMap<Eigen::Tensor<float, 3>> temp(m_data, m_dim1, m_dim2, m_dim3);  return temp; }
    Eigen::Tensor<float, 3>& getDataWithoutUsingMapping() { return m_tensor};


private:
    Eigen::Tensor<float, 3> m_tensor;
    // In fact, m_data, m_dim1, m_dim2, m_dim3 all are
    // read from a data file but for this example let's 
    // assume some values.
    float* m_data; 
    int m_dim1 = 2, m_dim2 = 5, m_dim3 = 10;
}


PYBIND11_MODULE(example, m) {
    py::class_<myclass>(m, "myclass")
        .def(py::init<>())
        .def("getDataUsingMapping", &myClass::getDataUsingMapping, py::return_value_policy::reference)
        .def("getDataWithoutUsingMapping", &myClass::getDataWithoutUsingMapping, py::return_value_policy::reference);
}

我希望能够在Python中处理这个具有维度信息(m_dim1,m_dim2,m_dim3)的三维数组。

以下是尝试在Python中获取数据后出现的错误消息。

>>> import example
>>> d = example()
>>>
>>> DataInPython = d.getDataUsingMapping()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Unable to convert function return value to a Python type! The signature was
        (self: example) -> Eigen::TensorMap<Eigen::Tensor<float,3,0,__int64>,0,Eigen::MakePointer>
>>>
>>>
>>> DataInPython = d.getDataWithoutUsingMapping()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Unable to convert function return value to a Python type! The signature was
        (self: example) -> Eigen::Tensor<std::complex<float>,3,0,__int64>

Did you forget to `#include <pybind11/stl.h>`? Or <pybind11/complex.h>,
<pybind11/functional.h>, <pybind11/chrono.h>, etc. Some automatic
conversions are optional and require extra headers to be included
when compiling your pybind11 module.

我尝试包含所有pybdin11的include文件,但问题没有得到解决。能否有人友善地帮助我?


你遇到的错误是在转换复杂浮点数时出现的,我猜测 complex.h 没有被包含进来,即使你尝试了。你能解释一下你如何包含所有这些模块 (complex.h functional.h chrono.h) 吗? - Christian
1个回答

2

这段C++代码无法编译,而Python代码也不可能按照发布的方式运行。但是,在修复了这些问题并进行了逻辑上的更改后,结论仍然是pybind11不支持来自"unsupported/Eigen/CXX11/Tensor"的TensorMap,因为该类没有提供与其他Eigen映射类相同的接口。

我本来期望mapper caster的特化可以自动工作,但现在需要显式地这样做:

template<>
struct py::detail::type_caster<Eigen::TensorMap<Eigen::Tensor<float, 3>>, void>
    : py::detail::eigen_map_caster<Eigen::TensorMap<Eigen::Tensor<float, 3>>> {};

无法通过pybind11::detail::EigenProps的实例化,因为TensorMap没有提供其维度的cols/rows/stride。因此,SFINAE阻止了转换器的自动生成。
除了使用名为“unsupported”的目录中的头文件之外,是否还有其他选择?如果没有,最好将TensorMap的内容复制到numpy数组中,并在getDataUsingMapping的自定义中返回该数组:在SO上有几个如何进行此操作的示例,带或不带复制。 (对EigenProps的特殊化不起作用,除非您愿意扁平化张量,但您可以将其用作编写TensorMap的新通用类型转换器的示例。)

我的pybind11知识都是因为你。感谢你在繁忙的日程中抽出时间来帮助社区。 - afp_2008

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接