Pertanyaan Apakah ada cara untuk menggunakan pythonappend dengan fitur builtin baru SWIG?


Saya memiliki proyek kecil yang bekerja dengan baik dengan SWIG. Secara khusus, beberapa fungsi saya kembali std::vectors, yang diterjemahkan ke tuples dengan Python. Sekarang, saya melakukan banyak numerik, jadi saya hanya perlu mengubah SWIG ini menjadi array numpy setelah mereka kembali dari kode c ++. Untuk melakukan ini, saya menggunakan sesuatu seperti yang berikut di SWIG.

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

(Sebenarnya, ada beberapa fungsi bernama Data, beberapa di antaranya mengapung kembali, itulah sebabnya saya memeriksanya val sebenarnya adalah tuple.) Ini bekerja dengan sangat indah.

Tapi, saya juga ingin menggunakan -builtin bendera itu sekarang tersedia. Panggilan ke fungsi Data ini jarang dan sebagian besar bersifat interaktif, sehingga kelambatannya bukan masalah, tetapi ada loop lambat lainnya yang mempercepat secara signifikan dengan opsi bawaan.

Masalahnya adalah ketika saya menggunakan bendera itu, fitur pythonappend diam-diam diabaikan. Sekarang, Data hanya mengembalikan tuple lagi. Apakah ada cara saya masih bisa mengembalikan array numpy? Saya mencoba menggunakan typemaps, tetapi itu berubah menjadi kekacauan besar.

Edit:

Borealid telah menjawab pertanyaan itu dengan sangat baik. Hanya untuk kelengkapan, saya menyertakan beberapa tipemaps yang berhubungan tetapi subtly berbeda yang saya butuhkan karena saya kembali dengan referensi const dan saya menggunakan vektor vektor (jangan mulai!). Ini cukup berbeda sehingga saya tidak ingin orang lain tersandung untuk mencoba mencari perbedaan kecil.

%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:

Meskipun tidak cukup apa yang saya cari, masalah serupa juga dapat diselesaikan dengan menggunakan pendekatan @ MONK (dijelaskan di sini).


75
2018-02-14 00:19


asal


Jawaban:


Saya setuju dengan Anda yang menggunakan typemap sedikit berantakan, tetapi itu adalah cara yang tepat untuk menyelesaikan tugas ini. Anda juga benar bahwa dokumentasi SWIG tidak secara langsung mengatakan itu %pythonappend tidak kompatibel dengan -builtin, tetapi sangat tersirat: %pythonappend  menambah kelas proksi Python, dan kelas proxy Python tidak ada sama sekali dengan -builtin bendera.

Sebelumnya, apa yang Anda lakukan adalah memiliki SWIG mengubah C ++ std::vector objek ke dalam tupel Python, lalu meneruskan tuple itu kembali ke numpy - di mana mereka dikonversi lagi.

Yang benar-benar ingin Anda lakukan adalah mengubahnya sekali, pada level C.

Berikut beberapa kode yang akan mengubah semuanya std::vector<int> objek ke dalam NumPy integer array:

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

Ini menggunakan fungsi numpy level C untuk membangun dan mengembalikan array. Agar, itu:

  • Pastikan Numpy arrayobject.h file termasuk dalam file output C ++
  • Penyebab import_array dipanggil ketika modul Python dimuat (jika tidak, semua metode NumPy akan segfault)
  • Peta setiap pengembalian std::vector<int> ke dalam array NumPy dengan typemap

Kode ini harus ditempatkan sebelum kamu %import header yang berisi fungsi yang kembali std::vector<int>. Selain pembatasan itu, itu sepenuhnya mandiri, jadi seharusnya tidak menambahkan terlalu banyak "kekacauan" subjektif ke basis kode Anda.

Jika Anda membutuhkan jenis vektor lainnya, Anda dapat mengubah NPY_INT dan semua int* dan int bit, jika tidak menduplikasi fungsi di atas.


6
2018-01-10 17:18