Pasar y devolver arrays numpy a métodos C++ a través de Cython


Hay muchas preguntas sobre el uso de numpy en cython en este sitio, una particularmente útil es Simple envoltura de código C con cython.

Sin embargo, la api de interfaz cython/numpy parece haber cambiado un poco, en particular para garantizar el paso de matrices contiguas de memoria.

¿Cuál es la mejor manera de escribir una función wrapper en cython que:

  • toma una matriz numpy que es probable pero no necesariamente contigua
  • llamadas un método de clase C++ con la firma double* data_in, double* data_out
  • devuelve una matriz numpy del double* que el método escribió?

Mi intento está abajo:

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)

Este código compila pero no es necesariamente óptimo. ¿Tiene alguna sugerencia para mejorar el fragmento de código anterior?

and (2) lanza y "np no está definido en línea X_c = ...") cuando lo llama en tiempo de ejecución. El código de prueba exacto y el mensaje de error son los siguientes:

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!

Author: Community, 2013-07-25

1 answers

Básicamente lo has hecho bien. Primero, esperemos que la optimización no sea un gran problema. Idealmente, la mayor parte del tiempo se pasa dentro de su kernel de C++, no en el código de envoltura de cythnon.

Hay algunos cambios estilísticos que puede hacer que simplificarán su código. (1) No es necesario remodelar entre matrices 1D y 2D. Cuando conoce el diseño de memoria de sus datos (orden C vs. orden fortran, zancadas, etc.), puede ver la matriz como solo un trozo de memoria que va a indexar tú mismo en C++, así que el ndim de numpy no importa en el lado de C++ 's es solo ver ese puntero. (2) Usando la dirección del operador de cython &, puede obtener el puntero al inicio de la matriz de una manera un poco más limpia no no es necesario un cast explícito using usando &X[0,0].

Así que esta es mi versión editada de tu fragmento original:

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
 18
Author: Robert T. McGibbon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-11 21:12:48