有什么方法可以将pythonappend与SWIG的新内置功能一起使用?


77

我有一个与SWIG完美搭配的小项目。特别是,我的某些函数返回std::vectors,在Python中将其转换为元组。现在,我做了很多数字运算,因此我只是将它们从C ++代码返回后,将它们转换为numpy数组。为此,我在SWIG中使用了以下内容。

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

(实际上,有几个名为Data的函数,其中一些返回浮点数,这就是为什么我检查它val实际上是一个元组的原因。)这工作得很漂亮。

但是,我也想使用-builtin现在可用的标志。很少调用这些Data函数,并且大多数都是交互式的,因此它们的缓慢性不是问题,但是使用内置选项可以显着加快其他缓慢循环的速度。

问题是当我使用该标志时,pythonappend功能将被静默忽略。现在,Data再次返回一个元组。有什么办法我仍然可以返回numpy数组吗?我尝试使用类型映射,但结果变得一团糟。

编辑:

Borealid很好地回答了这个问题。仅出于完整性考虑,我包括了一些相关但略微不同的类型映射,因为我是通过const引用返回的,并且我使用了vectors的向量(不要开始!)。这些差异是如此之大,以至于我不希望其他人为找出细微的差异而绊脚石。

%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的方法(在此说明)解决类似的问题。


4
我认为您不必编写类型映射并在C端进行操作就不会这样做,这恰恰是因为-builtin删除了通常放置pythonappend的代码。您确定-builtin更快(即通过分析使您可以使用它吗?)我很想使用两个模块,一个带有-builtin,一个带有-builtin。
Flexo

我很惊讶没有警告会-builtin忽略pythonappend。我无法将std::vectors映射为numpy数组。我做了配置文件,它极大地加速了我界面中最烦人的循环(时间不够长,无法休息;时间太长,无法频繁等待)。但是我也意识到我可以将此循环移入我的c ++代码中-尽管有些尴尬。所以这就是我要去的方式。尽管如此,您的“两个模块”建议还是很有趣的,在其他情况下可能会有用。
Mike

您是否用-Wall呼叫SWIG?我以为会在这种情况下发出警告。
Flexo

即使有,也没有警告-Wall(尽管这是一个足够大的忽略,我认为它甚至不需要这样做)。
Mike

尝试Data用Cython包装方法吗?
Paul Price

Answers:


8

我同意您的看法,使用起来typemap有些混乱,但这是完成此任务的正确方法。您也很对,SWIG文档没有直接说与%pythonappend兼容-builtin,但是强烈暗示:%pythonappend 将其添加到Python代理类中,并且与该-builtin标记一起根本不存在Python代理类。

之前,您要做的是让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加载Python模块时被调用的原因(否则,所有NumPy方法都将发生segfault)
  • 将任何返回值映射std::vector<int>到NumPy数组中,并带有typemap

这段代码应该放在之前%import包含返回功能的标题std::vector<int>。除了该限制之外,它完全是独立的,因此不应在代码库中添加过多的主观“混乱”。

如果你需要其他的载体类型,你可以改变NPY_INT,所有的int*int位,否则重复上面的功能。


高超!我拥有类型映射所需的所有元素,但是我没有将它们正确地组合在一起。虽然我尚未正式将其用于我的项目,但我通过构建一个更简单的模块进行了相当全面的测试。非常感谢!
Mike
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.