code

SWIG의 새로운 내장 기능과 함께 pythonappend를 사용하는 방법이 있습니까?

codestyles 2020. 10. 15. 07:49
반응형

SWIG의 새로운 내장 기능과 함께 pythonappend를 사용하는 방법이 있습니까?


SWIG와 아름답게 작동하는 작은 프로젝트가 있습니다. 특히 내 함수 중 일부는 std::vectors를 반환 하는데, 이는 Python에서 튜플로 변환됩니다. 이제 저는 많은 숫자를 수행하므로 C ++ 코드에서 반환 된 후 SWIG가이를 numpy 배열로 변환하도록합니다. 이를 위해 SWIG에서 다음과 같은 것을 사용합니다.

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

(사실, Data라는 이름의 함수가 여러 개 있는데, 그중 일부는 float를 반환하기 때문에 val실제로 튜플 인지 확인합니다 .) 이것은 아름답게 작동합니다.

하지만 -builtin현재 사용 가능한 플래그 도 사용하고 싶습니다 . 이러한 데이터 함수에 대한 호출은 드물고 대부분 대화 형이므로 느림은 문제가되지 않지만 내장 옵션을 사용하면 속도가 크게 빨라지는 다른 느린 루프가 있습니다.

문제는 해당 플래그를 사용할 때 pythonappend 기능이 자동으로 무시된다는 것입니다. 이제 데이터는 튜플을 다시 반환합니다. 여전히 numpy 배열을 반환 할 수있는 방법이 있습니까? 나는 typemap을 사용해 보았지만 그것은 거대한 혼란으로 바뀌었다.

편집하다:

Borealid는 질문에 매우 훌륭하게 대답했습니다. 완전성을 위해 저는 const 참조로 반환하고 벡터의 벡터를 사용하기 때문에 필요한 몇 가지 관련되지만 미묘하게 다른 typemap을 포함합니다 (시작하지 마세요!). 이것들은 다른 사람이 사소한 차이점을 알아 내려고 애쓰는 것을 원하지 않을 정도로 충분히 다릅니다.

%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);
}

편집 2 :

내가 찾던 내용은 아니지만 @MONK의 접근 방식을 사용하여 유사한 문제를 해결할 수도 있습니다 ( 여기에서 설명 ).


사용 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);
}

이것은 C 수준의 numpy 함수를 사용하여 배열을 구성하고 반환합니다. 순서대로 :

  • NumPy의 arrayobject.h파일이 C ++ 출력 파일에 포함되도록합니다.
  • 원인 import_array파이썬 모듈이로드 될 때 호출 할 (그렇지 않으면 모든 NumPy와 방법은 세그 폴트합니다)
  • 모든 반환 std::vector<int>을 NumPy 배열에 매핑합니다.typemap

이 코드는 배치해야 하기 전에 당신이 %import반환하는 기능을 포함하는 헤더를 std::vector<int>. 그 제한 외에는 완전히 독립적이므로 코드베이스에 너무 많은 주관적인 "메쉬"를 추가해서는 안됩니다.

If you need other vector types, you can just change the NPY_INT and all the int* and int bits, otherwise duplicating the function above.

참고URL : https://stackoverflow.com/questions/9270052/is-there-any-way-to-use-pythonappend-with-swigs-new-builtin-feature

반응형