2013-07-25 11 views
25

Ci sono molte domande sull'utilizzo di numpy in cython su questo sito, particolarmente utile per Simple wrapping of C code with cython.Passaggio e restituzione di array numpy a metodi C++ tramite Cython

Tuttavia, l'interfaccia cython/numpy api seems to have changed a bit, in particolare per garantire il passaggio di array di memoria contigui.

Qual è il modo migliore per scrivere una funzione wrapper in Cython che:

  • prende una matrice NumPy che è probabile, ma non necessariamente contigui
  • chiama un metodo di classe C++ con la firma double* data_in, double* data_out
  • restituisce una matrice numpy di double* a cui il metodo ha scritto?

La mia prova è qui sotto:

cimport numpy as np 
import numpy as np # as suggested by jorgeca 

cdef extern from "myclass.h": 
    cdef cppclass MyClass: 
     MyClass() except + 
     void run(double* X, int N, int D, double* Y) 

def run(np.ndarray[np.double_t, ndim=2] X): 
    cdef int N, D 
    N = X.shape[0] 
    D = X.shape[1] 

    cdef np.ndarray[np.double_t, ndim=1, mode="c"] X_c 
    X_c = np.ascontiguousarray(X, dtype=np.double) 

    cdef np.ndarray[np.double_t, ndim=1, mode="c"] Y_c 
    Y_c = np.ascontiguousarray(np.zeros((N*D,)), dtype=np.double) 

    cdef MyClass myclass 
    myclass = MyClass() 
    myclass.run(<double*> X_c.data, N, D, <double*> Y_c.data) 

    return Y_c.reshape(N, 2) 

Questo codice viene compilato, ma non è necessariamente ottimale. Hai qualche suggerimento su come migliorare lo snippet qui sopra?

e (2) tiri e "np non è definito sulla riga X_c = ...") quando lo si chiama in fase di esecuzione. Il codice di test e messaggio di errore esatto sono i seguenti:

import numpy as np 
import mywrapper 
mywrapper.run(np.array([[1,2],[3,4]], dtype=np.double)) 

# NameError: name 'np' is not defined [at mywrapper.pyx":X_c = ...] 
# fixed! 

+5

Hai ancora a 'importazione NumPy come np' nel file' .pyx' per utilizzare le funzioni di Numpy ('cimport NumPy come np' [ "è usato per importare speciali informazioni in fase di compilazione sul modulo numpy"] (http://docs.cython.org/src/tutorial/numpy.html#adding-types)). – jorgeca

+0

@jorgeca Immagino che il tuo commento risponda alla domanda OP ... –

+1

@SaulloCastro L'ho postato come commento perché pensavo fosse un ostacolo minore, ma non so quale sia il modo migliore per scrivere queste interfacce. – jorgeca

risposta

17

Hai fondamentalmente capito bene. In primo luogo, si spera che l'ottimizzazione non dovrebbe essere un grosso problema. Idealmente, la maggior parte del tempo viene impiegata all'interno del proprio kernel C++, non nel codice di cythnon wrapper.

Ci sono alcune modifiche stilistiche che è possibile apportare per semplificare il codice. (1) La rimodellazione tra array 1D e 2D non è necessaria. Quando conosci il layout di memoria dei tuoi dati (ordine C-order vs. fortran, striding, ecc), puoi vedere l'array come un semplice frammento di memoria che indichi te stesso in C++, quindi ndim di numpy non lo fa t importa sul lato C++ - sta solo vedendo quel puntatore. (2) Usando l'operatore address di cython &, è possibile ottenere il puntatore all'inizio dell'array in un modo leggermente più pulito, senza necessità di cast esplicito, utilizzando &X[0,0].

Quindi questa è la mia versione modificata del frammento originale:

cimport numpy as np 
import numpy as np 

cdef extern from "myclass.h": 
    cdef cppclass MyClass: 
     MyClass() except + 
     void run(double* X, int N, int D, double* Y) 

def run(np.ndarray[np.double_t, ndim=2] X): 
    X = np.ascontiguousarray(X) 
    cdef np.ndarray[np.double_t, ndim=2, mode="c"] Y = np.zeros_like(X) 

    cdef MyClass myclass 
    myclass = MyClass() 
    myclass.run(&X[0,0], X.shape[0], X.shape[1], &Y[0,0]) 

    return Y 
+1

Dire, questo può essere fatto con un memoryview digitato in Cython invece di passare l'array? Non ero sicuro se ciò avrebbe risparmiato un sovraccarico di memoria, ecc. – krishnab

Problemi correlati