1. ホーム
  2. python

SWIGの新しいビルトイン機能でpythonappendを使う方法はないですか?

2023-10-10 03:12:29

質問

私は、SWIGで美しく動作する小さなプロジェクトを持っています。 特に、私の関数のいくつかは std::vector を返すのですが、これはPythonのタプルに変換されます。 私は多くの数値を扱うので、c++のコードから返された後、SWIGにこれらをnumpy配列に変換させています。 これを行うために、私はSWIGで次のようなものを使っています。

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

(実際には、Dataという名前の関数がいくつかあり、そのうちのいくつかは浮動小数点数を返すので、それをチェックするために val が実際にはタプルであることを確認するためです)。 これは見事に動作します。

しかし、私はまた、このように -builtin フラグが使えるようになりました。 これらの Data 関数の呼び出しはまれで、ほとんどが対話型なので、その遅さは問題ではありませんが、他にも遅いループがあり、組み込みオプションで大幅に高速化されます。

問題は、私がそのフラグを使用するとき、pythonappend 機能が黙って無視されることです。 今、データは単にタプルを再び返します。 まだ numpy 配列を返すことができる何らかの方法はありますか? 私はタイプマップを使用しようとしましたが、それは巨大な混乱に変わりました。

編集してください。

Borealidが非常にうまく質問に答えてくれています。 私はconst参照で戻り、vectorのvectorを使うので必要な、関連するが微妙に異なる2つの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 がPythonモジュールがロードされたときに呼び出されるようにします(そうでなければ、すべてのNumPyメソッドはセグメンテーションフォールトを起こします)。
  • のすべての戻り値をマップします。 std::vector<int> をNumPyの配列にマッピングする。 typemap

このコードを配置する の前に あなたは %import を返す関数を含むヘッダは std::vector<int> . この制限の他は、完全に自己完結しているので、コードベースに主観的な混乱を加えることはないでしょう。

もし他のベクトル型が必要なら、単に NPY_INT を変更し、すべての int*int のビットがあり、それ以外は上記の関数と重複しています。