2012-02-14 7 views
75

Ho un piccolo progetto che funziona perfettamente con SWIG. In particolare, alcune delle mie funzioni restituiscono std::vector s, che vengono convertite in tuple in Python. Ora faccio un sacco di numeri, quindi ho appena convertito SWIG in array numpy dopo che sono stati restituiti dal codice C++. Per fare questo, uso qualcosa come il seguente in SWIG.C'è un modo per usare pythonappend con la nuova funzione integrata di SWIG?

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

(In realtà, ci sono diverse funzioni denominate dati, alcuni dei quali ritornano galleggianti, che è il motivo per cui verifico che val è in realtà una tupla). Questo funziona meravigliosamente.

Ma, mi piacerebbe anche utilizzare il flag -builtin che è ora disponibile. Le chiamate a queste funzioni dati sono rare e per lo più interattive, quindi la loro lentezza non è un problema, ma ci sono altri cicli lenti che accelerano significativamente con l'opzione integrata.

Il problema è che quando utilizzo questo flag, la funzione pythonappend viene silenziosamente ignorata. Ora, Data restituisce di nuovo una tupla. C'è un modo in cui potrei ancora restituire gli array numpy? Ho provato a usare i typemaps, ma è diventato un gran casino.

Edit:

Borealid ha risposto alla domanda molto bene. Solo per completezza, includo un paio di typemaps correlati ma sottilmente diversi di cui ho bisogno perché ritorno con riferimento const e utilizzo i vettori di vettori (non iniziare!). Sono abbastanza diversi da non desiderare che qualcun altro inciampi cercando di capire le piccole differenze.

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

Anche se non è proprio quello che stavo cercando, problemi simili possono essere risolti usando @ approccio del monaco (explained here).

+4

Non penso che si possa fare questo senza scrivere una typemap e farlo sul lato C, proprio perché -builtin rimuove il codice dove normalmente viene piazzato pythonappend. Sei sicuro che -builtin sia molto più veloce (cioè il profiling ti ha portato ad usarlo?) Sarei tentato di usare due moduli, uno con e uno senza -builtin. – Flexo

+0

Sono sorpreso che non ci sia alcun avviso che '-builtin' ignora pythonappend. Non sono all'altezza della sfida del typemapping di 'std :: vector's in array numpy. Ho fatto il profilo e questo ha velocizzato in modo significativo il ciclo più fastidioso dell'interfaccia (non abbastanza lungo per fare una pausa, troppo a lungo per aspettare di frequente). Ma ho anche capito che posso spostare questo ciclo nel mio codice C++, anche se un po 'goffamente. Ecco come andrò. Tuttavia, il tuo suggerimento sui "due moduli" è interessante e potrebbe essere utile in altri casi. – Mike

+0

Hai chiamato SWIG con -Wall? Presumo che avrebbe avvertito in quel caso. – Flexo

risposta

6

Sono d'accordo con voi sul fatto che l'utilizzo di typemap diventa un po 'disordinato, ma è il modo giusto per eseguire questa operazione. Hai anche ragione che la documentazione SWIG non dice direttamente che %pythonappend non è compatibile con -builtin, ma è fortemente implicito: %pythonappendaggiunge alla classe proxy Python, e la classe proxy Python non esiste affatto insieme allo -builtin bandiera.

Prima, quello che stavi facendo stava avendo SWIG convertire gli oggetti C++ std::vector in tuple Python, e poi passando quelle tuple giù a numpy - dove sono stati convertiti nuovo.

Quello che vuoi veramente è convertirli una volta, al livello C.

Ecco un codice che trasformerà tutti std::vector<int> oggetti in array interi 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); 
} 

Questo utilizza le funzioni NumPy C-livello per costruire e restituire una matrice.Al fine, essa:

  • Assicura il file di NumPy arrayobject.h è incluso nel file di output C++
  • cause import_array da chiamare quando viene caricato il modulo Python (in caso contrario, tutti i metodi NumPy saranno segfault)
  • Mappe qualsiasi ritorni di std::vector<int> in array NumPy con typemap

Questo codice deve essere inserito prima di si %import le intestazioni wh che contiene le funzioni che restituiscono std::vector<int>. Oltre a questa limitazione, è completamente autonomo, quindi non dovrebbe aggiungere troppi "problemi" soggettivi alla base di codice.

Se sono necessari altri tipi di vettore, è sufficiente modificare lo NPY_INT e tutti i valori e int, altrimenti duplicare la funzione precedente.

+0

Stupendo! Avevo tutti gli elementi che hai per la typemap, ma non li avevo messi abbastanza bene insieme. Anche se non ho ancora ufficialmente lavorato con il mio progetto, ho fatto un test piuttosto approfondito costruendo un modulo più semplice. Grazie mille! – Mike

Problemi correlati