Есть ли способ, чтобы использовать pythonappend с новой встроенной функцией СВИГ это?


у меня есть небольшой проект, который прекрасно работает с SWIG. В частности, некоторые из моих функций возвращают std::vectors, которые переводятся в Кортежи на Python. Теперь я делаю много цифр, поэтому у меня просто есть SWIG преобразовать их в массивы numpy после того, как они возвращаются из кода c++. Для этого я использую что-то вроде следующего в SWIG.

%feature("pythonappend") My::Cool::Namespace::Data() const %{ if isinstance(val, tuple) : val = numpy.array(val) %}

(на самом деле, есть несколько функций с именем Data, некоторые из которых возвращают поплавки, поэтому я проверяю это val - это на самом деле кортеж.) Это работает просто прекрасно.

но, я также хотел бы использовать -builtin флаг, который теперь доступен. Вызовы этих функций данных редки и в основном интерактивны, поэтому их медленность не является проблемой, но есть и другие медленные циклы, которые значительно ускоряются с помощью встроенной опции.

проблема в том, что когда я использую этот флаг, функция pythonappend молча игнорируется. Теперь Data просто возвращает кортеж снова. Есть ли способ я все еще могу вернуться массивов numpy? Я попытался использовать типографские карты, но это превратилось в гигантский беспорядок.

Edit:

Бореалид ответил на вопрос очень хорошо. Просто для полноты я включаю пару связанных, но тонко разных типов карт, которые мне нужны, потому что я возвращаюсь по ссылке const и использую векторы векторов (не начинайте!). Они достаточно разные, чтобы я не хотел, чтобы кто-то еще спотыкался, пытаясь выяснить незначительные различия.

%typemap(out) std::vector<int>& {
  npy_intp result_size = ->size();
  npy_intp dims[1] = { result_size };
  PyArrayObject* npy_arr = (PyArrayObject*)PyArray_SimpleNew(1, dims, NPY_INT);
  int* dat = (int*) PyArray_DATA(npy_arr);
  for (size_t i = 0; i < result_size; ++i) { dat[i] = (*)[i]; }
  $result = PyArray_Return(npy_arr);
}
%typemap(out) std::vector<std::vector<int> >& {
  npy_intp result_size = ->size();
  npy_intp result_size2 = (result_size>0 ? (*)[0].size() : 0);
  npy_intp dims[2] = { result_size, result_size2 };
  PyArrayObject* npy_arr = (PyArrayObject*)PyArray_SimpleNew(2, dims, NPY_INT);
  int* dat = (int*) PyArray_DATA(npy_arr);
  for (size_t i = 0; i < result_size; ++i) { for (size_t j = 0; j < result_size2; ++j) { dat[i*result_size2+j] = (*)[i][j]; } }
  $result = PyArray_Return(npy_arr);
}

изменить 2:

хотя и не совсем то, что я искал, подобные проблемы также могут быть решены с помощью подхода @MONK (пояснил, что).

1 75

1 ответ:

я согласен с вами, что использование typemap становится немного грязным, но это правильный способ выполнить эту задачу. Вы также правы, что в документации SWIG прямо не говорится, что %pythonappend несовместимо с -builtin, но это подразумевалось: %pythonappend добавляет в класс прокси Python, и прокси-класс Python вообще не существует в сочетании с -builtin флаг.

раньше то, что вы делали, было SWIG convert The C++ std::vector объекты в кортежи Python, а затем передача этих кортежей обратно в numpy - где они были снова преобразованы.

то, что вы действительно хотите сделать, это преобразовать их один раз, на уровне C.

вот некоторый код, который превратит все std::vector<int> объекты в целочисленные массивы NumPy:

%{
#include "numpy/arrayobject.h"
%}

%init %{
    import_array();
%}

%typemap(out) std::vector<int> {
    npy_intp result_size = .size();

    npy_intp dims[1] = { result_size };

    PyArrayObject* npy_arr = (PyArrayObject*)PyArray_SimpleNew(1, dims, NPY_INT);
    int* dat = (int*) PyArray_DATA(npy_arr);

    for (size_t i = 0; i < result_size; ++i) {
        dat[i] = [i];
    }

    $result = PyArray_Return(npy_arr);
}

это использует функции numpy уровня C для построения и возврата массива. По порядку, это:

  • обеспечивает и NumPy это arrayobject.h файл включен в C++ выходной файл
  • причины import_array вызывается при загрузке модуля Python (в противном случае все методы NumPy будут segfault)
  • отображает любые возвраты std::vector<int> в массивы NumPy с typemap

этот код должен быть помещен до вы %import заголовки, которые содержат функции, возвращающие std::vector<int>. Помимо этого ограничения, он полностью самодостаточен, поэтому он не должен добавлять слишком много субъективного "беспорядка" к вашему кодовая база.

Если вам нужны другие типы векторов, вы можете просто изменить NPY_INT и int* и int бит, в противном случае дублирование функции выше.