pybind11 изменить массив numpy из C++

РЕДАКТИРОВАТЬ: теперь это работает, я не знаю, почему. Не думай, что я что-то изменил

Я хочу передать и изменить большой массив numpy с помощью pybind11. Поскольку он большой, я не хочу копировать его и возвращать новый.

Вот код:

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/numpy.h>
#include <vector>

// C++ code
void calc_sum_cost(float* ptr, int N, int M, float* ptr_cost) {
  for(int32_t i = 1; i < N; i++) {
    for(int32_t j = 1; j < M; j++) {
      float upc = ptr[(i-1) * M + j];
      float leftc = ptr[i * M + j - 1];
      float diagc = ptr[(i-1) * M + j - 1];
      float transition_cost = std::min(upc, std::min(leftc, diagc));
      if (transition_cost == diagc) {
        transition_cost += 2 * ptr_cost[i*M + j];
      } else {
        transition_cost += ptr_cost[i*M + j];
      }
      std::cout << transition_cost << std::endl;
      ptr[i * M + j] = transition_cost;
    }
  }
}

// Interface

namespace py = pybind11;

// wrap C++ function with NumPy array IO
py::object wrapper(py::array_t<float> array,
                  py::array_t<float> arrayb) {
  // check input dimensions
  if ( array.ndim()     != 2 )
    throw std::runtime_error("Input should be 2-D NumPy array");

  auto buf = array.request();
  auto buf2 = arrayb.request();
  if (buf.size != buf2.size) throw std::runtime_error("sizes do not match!");

  int N = array.shape()[0], M = array.shape()[1];

  float* ptr = (float*) buf.ptr;
  float* ptr_cost = (float*) buf2.ptr;
  // call pure C++ function
  calc_sum_cost(ptr, N, M, ptr_cost);
  return py::cast<py::none>(Py_None);
}

PYBIND11_MODULE(fast,m) {
  m.doc() = "pybind11 plugin";
  m.def("calc_sum_cost", &wrapper, "Calculate the length of an array of vectors");
}

Я думаю, что py::array::forcecast вызывает преобразование и поэтому оставляет входную матрицу без изменений (в python). Когда я удаляю это, хотя я получаю ошибку времени выполнения, когда я удаляю ::c_style, он запускается, но снова в python массив numpy такой же.

В основном мой вопрос заключается в том, как передать и изменить массив numpy с помощью pybind11?


person Nimitz14    schedule 20.02.2019    source источник
comment
Наивно я ожидал, что вам придется передавать аргументы вашей функции по ссылке...?! т.е. использовать py::array_t<float>& array.   -  person Tom de Geus    schedule 22.02.2019
comment
Аргументы не нужно передавать по ссылке, потому что типы py::array внутренне используют один и тот же буфер, если это возможно, т. е. когда типы элементов совпадают.   -  person Saul    schedule 27.09.2019


Ответы (1)


У меня была такая же проблема. Если из Python вы передаете массив numpy типа, соответствующего аргументу C++, то преобразование не происходит, и вы можете изменить данные на месте, т.е. для передачи аргумента py::array_t<float> в массиве numpy np.float32. Если вы передаете массив np.float64 (тип по умолчанию), то pybind11 выполняет преобразование из-за параметра шаблона py::array::forcecast (по умолчанию для py::array_t<T>), поэтому ваша функция C++ получает только преобразованную копию массива numpy, и любые изменения теряются после возвращения.

person Saul    schedule 07.03.2019
comment
Будет ли принудительно копироваться, если я передам срез numpy.array на C++? - person zhiqiu; 26.09.2019
comment
@zhiqiu, я не пробовал, но я считаю, что срез все равно будет передан как общий базовый буфер, если типы элементов совпадают, то есть без копирования. Массив py: использует один и тот же буфер, если это возможно, и нет необходимости принимать его по ссылке в C++. - person Saul; 27.09.2019