2012-02-14 21 views
75

Tôi có một dự án nhỏ hoạt động tốt với SWIG. Cụ thể, một số chức năng của tôi trả về std::vector s, được dịch thành các bộ dữ liệu bằng Python. Bây giờ, tôi làm rất nhiều số, vì vậy tôi chỉ có SWIG chuyển đổi chúng thành các mảng numpy sau khi chúng được trả về từ mã C++. Để làm điều này, tôi sử dụng một cái gì đó như sau trong SWIG.Có cách nào để sử dụng pythonappend với tính năng nội trang mới của SWIG không?

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

(Trên thực tế, có một số chức năng có tên Data, một số trong đó trở nổi, đó là lý do tôi kiểm tra xem val thực sự là một tuple). Điều này hoạt động giống đẹp.

Nhưng, tôi cũng muốn sử dụng cờ -builtin hiện có sẵn. Các cuộc gọi đến các chức năng Dữ liệu này rất hiếm và chủ yếu là tương tác, do đó sự chậm chạp của chúng không phải là vấn đề, nhưng có các vòng chậm khác tăng tốc đáng kể với tùy chọn dựng sẵn.

Vấn đề là khi tôi sử dụng cờ đó, tính năng pythonappend sẽ bị bỏ qua. Bây giờ, dữ liệu chỉ trả về một tuple một lần nữa. Có cách nào tôi vẫn có thể trở lại mảng numpy? Tôi đã thử sử dụng các bản đồ, nhưng nó biến thành một mớ hỗn độn khổng lồ.

Chỉnh sửa:

Borealid đã trả lời câu hỏi rất độc đáo. Chỉ cần cho đầy đủ, tôi bao gồm một vài bản đồ có liên quan nhưng tinh tế khác nhau mà tôi cần bởi vì tôi trở lại bằng tham chiếu const và tôi sử dụng vectơ của vectơ (không bắt đầu!). Đây là đủ khác nhau mà tôi sẽ không muốn bất cứ ai khác stumbling xung quanh cố gắng tìm ra sự khác biệt nhỏ.

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

Sửa 2:

Mặc dù không hoàn toàn những gì tôi đang tìm kiếm, các vấn đề tương tự cũng có thể được giải quyết bằng phương pháp @ Monk (explained here).

+4

Tôi không nghĩ rằng bạn có thể làm điều này mà không cần viết một typemap và làm nó ở phía C, chính xác vì -builtin loại bỏ mã nơi pythonappend bình thường được đặt. Bạn có chắc chắn -builtin là nhanh hơn nhiều (tức là đã profiling dẫn bạn sử dụng nó?) Tôi muốn bị cám dỗ để sử dụng hai mô-đun, một với và một mà không -builtin. – Flexo

+0

Tôi ngạc nhiên không có cảnh báo rằng '-builtin' bỏ qua pythonappend. Tôi không phải là thách thức của việc đánh máy 'std :: vector' vào mảng numpy. Tôi đã làm hồ sơ, và nó đáng kể tăng tốc vòng lặp gây phiền nhiễu nhất trong giao diện của tôi (không đủ dài để nghỉ ngơi, quá lâu để chờ đợi thường xuyên). Nhưng tôi cũng nhận ra rằng tôi có thể di chuyển vòng lặp này vào mã C++ của tôi - mặc dù hơi lúng túng. Vì vậy, đó là cách tôi sẽ đi. Tuy nhiên, đề xuất 'hai mô-đun' của bạn thú vị và có thể hữu ích trong các trường hợp khác. – Mike

+0

Bạn có gọi SWIG bằng -Wall không? Tôi cho rằng nó sẽ cảnh báo trong trường hợp đó. – Flexo

Trả lời

6

Tôi đồng ý với bạn rằng việc sử dụng typemap hơi lộn xộn, nhưng đó là cách phù hợp để thực hiện tác vụ này. Bạn cũng đúng rằng tài liệu SWIG không trực tiếp nói rằng %pythonappend không tương thích với -builtin, nhưng nó ngụ ý rõ ràng: %pythonappendthêm vào lớp proxy Python và lớp proxy Python không tồn tại cùng với -builtin cờ.

Trước đó, những gì bạn đang làm là có SWIG chuyển đổi các đối tượng C++ std::vector thành tuples Python và sau đó chuyển các bộ đó xuống số numpy - nơi chúng được chuyển đổi lại.

Điều bạn thực sự muốn làm là chuyển đổi chúng một lần, ở cấp C.

Dưới đây là một số mã mà sẽ biến tất cả std::vector<int> đối tượng vào mảng số nguyên 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); 
} 

này sử dụng các chức năng NumPy C cấp để xây dựng và trả về một mảng.Theo thứ tự, nó:

  • Đảm bảo tập tin arrayobject.h NumPy được bao gồm trong C++ file output
  • Nguyên nhân import_array để được gọi khi các mô-đun Python được nạp (nếu không, tất cả các phương pháp NumPy sẽ segfault)
  • Maps bất kỳ lợi nhuận của std::vector<int> vào mảng NumPy với một typemap

mã này nên được đặt trước bạn %import tiêu đề wh ich chứa các hàm trả về std::vector<int>. Khác với hạn chế đó, nó hoàn toàn độc lập, vì vậy nó không nên thêm quá nhiều "lộn xộn" chủ quan vào codebase của bạn.

Nếu bạn cần các loại véc tơ khác, bạn chỉ có thể thay đổi NPY_INT và tất cả các bit int*int, nếu không sao chép hàm ở trên.

+0

Thật tuyệt vời! Tôi đã có tất cả các yếu tố bạn có cho typemap, nhưng tôi đã không hoàn toàn đặt chúng lại với nhau đúng cách. Mặc dù tôi chưa chính thức làm việc này với dự án của mình, tôi đã thực hiện một bài kiểm tra khá kỹ lưỡng bằng cách xây dựng một mô-đun đơn giản hơn. Cảm ơn rất nhiều! – Mike

Các vấn đề liên quan