Подтвердить что ты не робот

Передача и возврат массивов numpy в методы С++ через Cython

Есть много вопросов об использовании numpy в cython на этом сайте, особенно полезным Простая упаковка кода C с помощью cython.

Однако интерфейс cython/numpy api по-видимому, немного изменился, в частности, обеспечив передачу массивов с непрерывной памятью.

Каков наилучший способ написать функцию-оболочку в цитоне, которая:

  • принимает массив numpy, который вероятно, но не обязательно смежный.
  • вызывает метод класса С++ с подписью double* data_in, double* data_out
  • возвращает массив numpy double*, который метод написал в?

Моя попытка:

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)

Этот код компилируется, но не обязательно оптимален. У вас есть предложения по улучшению фрагмента выше?

и (2) throws и "np не определяется в строке X_c = ..." ) при вызове во время выполнения. Точный код тестирования и сообщение об ошибке следующие:

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!

4b9b3361

Ответ 1

В основном вы все поняли. Во-первых, надеюсь, что оптимизация не должна быть большой проблемой. В идеале большая часть времени проводится внутри вашего ядра С++, а не в коде оболочки cythnon.

Есть несколько стилистических изменений, которые вы можете сделать, что упростит ваш код. (1) Перестройка между 1D и 2D массивами не требуется. Когда вы знаете макет памяти ваших данных (C-order vs. fortran order, striding и т.д.), Вы можете увидеть массив как просто кусок памяти, который вы собираетесь индексировать на С++, так что numpy ndim doesn ' на стороне С++ - он просто видит этот указатель. (2) Используя cython address-of operator &, вы можете получить указатель на начало массива немного более чистым способом - без явного приведения - с помощью &X[0,0].

Итак, это моя отредактированная версия вашего исходного фрагмента:

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