Pybind NumPy访问2D/ND数组

8
新手使用pybind - 阅读了文档,但不知道如何将其应用于二维数组。
我有两个存储3D坐标的数组shape =(10,3)
a = np.zeros(shape=(10,3))
b = np.ones(shape=(10,3)) * 3
c = a + b

现在,使用pybind,在C++中如何操作numpy数组进行此操作?
在一些文档中,我读到要使用[]运算符访问元素,在其他文档中则使用()。如何分配3D向量?如何获取指向数组元素的指针以使用步长进行分配 - 或者它是否有一个运算符?

我不明白你在问什么。这可能取决于如何使用C++来处理它们,例如作为std-vector或基于Eigen的矩阵/数组。它们都接受不同风格的索引。你的例子也只包含2D数组。你所说的3D向量(在数学上听起来也很奇怪)是什么? - sascha
2个回答

19

PyBind很棒,向作者/维护者致敬!你可以在这里找到一个几乎可用的示例。

根据你的问题进行调整后,它可能会给出类似以下的答案(在El Dude的评论后进行了编辑):

#include <iostream>
#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>


namespace py = pybind11;


py::array_t<double> add_arrays(py::array_t<double> input1, py::array_t<double> input2) {
  py::buffer_info buf1 = input1.request();
  py::buffer_info buf2 = input2.request();

  if (buf1.size != buf2.size) {
    throw std::runtime_error("Input shapes must match");
  }

  /*  allocate the buffer */
  py::array_t<double> result = py::array_t<double>(buf1.size);

  py::buffer_info buf3 = result.request();

  double *ptr1 = (double *) buf1.ptr,
         *ptr2 = (double *) buf2.ptr,
         *ptr3 = (double *) buf3.ptr;
  int X = buf1.shape[0];
  int Y = buf1.shape[1];

  for (size_t idx = 0; idx < X; idx++) {
    for (size_t idy = 0; idy < Y; idy++) {
      ptr3[idx*Y + idy] = ptr1[idx*Y+ idy] + ptr2[idx*Y+ idy];
    }
  }
 
  // reshape array to match input shape
  result.resize({X,Y});

  return result;
}


PYBIND11_MODULE(example, m) {
        m.doc() = "Add two vectors using pybind11"; // optional module docstring

        m.def("add_arrays", &add_arrays, "Add two NumPy arrays");
}

我在Linux上使用Python2.7和GCC v5.4构建(由于没有找到Python.h,因此我必须使用略有不同的命令,因此我添加了指向Python 2.7的链接)

c++ -O3 -Wall -shared -std=c++11 -fPIC -I/usr/include/python2.7 -lpython2.7 `python -m pybind11 --includes` example.cpp -o example`python-config --extension-suffix

你可以通过Python调用它:

import numpy as np
import example # [bad] name I chose for my compiled module

a = np.zeros((10,3))
b = np.ones((10,3)) * 3 
c = example.add_arrays(a, b)

print c

希望能对您有帮助。


编辑 - 我创建了一个GitHub存储库,其中包含一些基于PyBind11的完整示例,可在所有平台上编译。


重塑应该可以通过调整缓冲对象中的大小和步幅来实现吗? - El Dude
你说得对,我漏掉了,但是array_t本身有一个resize函数,我一直在尝试查看buffer_info。我会编辑我的答案进行更改,谢谢。 - Christian
嗯,我在帖子之前找到了示例代码和解决方案...但是现在你来了。 - El Dude
我在这一行遇到了编译错误:py::buffer_info result = py::array_t<double>(buf1.size); - Yonatan Simson
抱歉,这是我的错误,应该是 py::array_t<double> result = py::array_t<double>(buf1.size); 我会编辑答案。你可以在我的Github页面找到这段代码。 - Christian
显示剩余4条评论

1
使用缓冲区类是关键。虽然在文档和示例中隐藏得很好/复杂,但它在 @Christian 的帖子中提到过。
缓冲区包含指向数据的指针以及步幅和其他数组参数。本质上是通过 request 方法访问的 numpy 标头。从那里开始使用很容易,但找到它有点麻烦,因为示例使用美丽的 C11 auto 类型来解释这种用法。

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