【问题标题】:Is there any way to use pythonappend with SWIG's new builtin feature?有什么方法可以将 pythonappend 与 SWIG 的新内置功能一起使用?
【发布时间】:2012-03-05 10:01:13
【问题描述】:

我有一个与 SWIG 完美配合的小项目。特别是,我的一些函数返回std::vectors,它被翻译成Python 中的元组。现在,我做了很多数字,所以我只是让 SWIG 在它们从 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 引用返回并且我使用向量的向量(不要开始!)。这些差异很大,我不希望其他人在试图找出细微差别时磕磕绊绊。

%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 的方法 (explained here) 来解决。

【问题讨论】:

  • 我认为如果不编写类型映射并在 C 端执行此操作,您将无法做到这一点,这正是因为 -builtin 删除了通常放置 pythonappend 的代码。你确定 -builtin 更快(即分析是否引导你使用它?)我很想使用两个模块,一个有 -builtin 一个没有。
  • 我很惊讶没有警告-builtin 忽略 pythonappend。我无法应对将std::vectors 类型映射到 numpy 数组的挑战。我做了配置文件,它显着加速了我界面中最烦人的循环(没有足够长的时间来休息;太长而不能经常等待)。但我也意识到我可以将这个循环移动到我的 c++ 代码中——虽然有点尴尬。所以我就这样走了。不过,您的“两个模块”建议很有趣,并且可能在其他情况下有用。
  • 您是否使用 -Wall 调用了 SWIG?我认为在这种情况下它会发出警告。
  • 没有警告,即使是-Wall(尽管这是一个足够大的忽略,我认为它甚至不需要这样做)。
  • 尝试用 Cython 包装 Data 方法?

标签: python numpy swig


【解决方案1】:

我同意你的观点,使用 typemap 会有点混乱,但这是完成这项任务的正确方法。你也对,SWIG 文档没有直接说%pythonappend-builtin 不兼容,但强烈暗示:%pythonappend 添加到 Python 代理类,并且 Python 代理与-builtin 标志一起使用的类根本不存在。

之前,您所做的是让 SWIG 将 C++ std::vector 对象转换为 Python 元组,然后将这些元组传递回 numpy - 它们再次被转换。

您真正想要做的是在 C 级别将它们转换一次。

这里有一些代码会将所有 std::vector&lt;int&gt; 对象转换为 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++ 输出文件中
  • 导致在加载 Python 模块时调用 import_array(否则,所有 NumPy 方法都会出现段错误)
  • std::vector&lt;int&gt; 的任何返回值映射到带有typemap 的NumPy 数组中

这段代码应该放在之前%import包含返回std::vector&lt;int&gt;的函数的标题。除了这个限制之外,它是完全独立的,所以它不应该给你的代码库添加太多主观的“混乱”。

如果您需要其他向量类型,您只需更改NPY_INT 以及所有int*int 位,否则重复上述功能。

【讨论】:

  • 太棒了!我拥有用于​​类型图的所有元素,但我还没有将它们正确地组合在一起。虽然我还没有正式在我的项目中使用这个功能,但我已经通过构建一个更简单的模块进行了非常彻底的测试。非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-12
  • 2016-09-27
  • 1970-01-01
  • 1970-01-01
  • 2016-09-23
  • 2020-04-07
相关资源
最近更新 更多