2012-02-14 2 views
75

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

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

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

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

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

Edit:

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

%typemap(out) std::vector<int>& { 
    npy_intp result_size = $1->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] = (*$1)[i]; } 
    $result = PyArray_Return(npy_arr); 
} 
%typemap(out) std::vector<std::vector<int> >& { 
    npy_intp result_size = $1->size(); 
    npy_intp result_size2 = (result_size>0 ? (*$1)[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] = (*$1)[i][j]; } } 
    $result = PyArray_Return(npy_arr); 
} 

Edit 2:

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

+4

Я не думаю, что вы можете сделать это, не написав типовую карту и не сделав ее на стороне С, именно потому, что -builtin удаляет код, в котором обычно размещается pythonappend. Вы уверены, что -builtin намного быстрее (т. Е. Профилирование привело вас к его использованию?) У меня возникло бы желание использовать два модуля: один с одним без -builtin. – Flexo

+0

Я удивлен, что нет предупреждения, что '-builtin' игнорирует pythonappend. Я не подхожу к вызову typemapping 'std :: vector' в массивы numpy. Я сделал профиль, и это значительно ускорило самый раздражающий цикл в моем интерфейсе (недостаточно долго, чтобы сделать паузу, слишком долго ждать часто). Но я также понял, что могу переместить этот цикл в свой код на C++, хотя и несколько неловко. Так вот как я пойду. Тем не менее, предложение «двух модулей» является интересным и может быть полезным в других случаях. – Mike

+0

Вы звонили в SWIG -Wall? Я предположил, что в этом случае будет предупреждать. – Flexo

ответ

6

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

Прежде, чем вы занимались, SWIG конвертировал объекты 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 = $1.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] = $1[i]; 
    } 

    $result = PyArray_Return(npy_arr); 
} 

Это использует Numpy функции C-уровня для построения и возвращает массив.Для того, что:

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

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

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

+0

Превосходно! У меня были все элементы, которые у вас есть для типовой карты, но я не совсем правильно их сочетал. Пока я официально не работал с моим проектом, я провел довольно тщательный тест, построив более простой модуль. Огромное спасибо! – Mike

Смежные вопросы