Как передать многомерные массивы Numpy в функцию C с помощью CFFI?

#python #c #numpy #cffi

#python #c #numpy #cffi

Вопрос:

Я пытаюсь ускорить свою программу на Python, реализовав функцию на C и внедрив ее в свой код с использованием CFFI. Функция принимает два массива размером 3×3 и вычисляет расстояние. Код Python выглядит следующим образом:

 import cffi
import numpy as np

ffi = cffi.FFI()
ffi.cdef("""
    extern double dist(const double s[3][3], const double t[3][3]);
""")
lib = ffi.dlopen("./dist.so")

S = np.array([[-1.63538,  0.379116, -1.16372],[-1.63538, 0.378137, -1.16366 ],[-1.63193, 0.379116, -1.16366]], dtype=np.float32)
T = np.array([[-1.6467834, 0.3749715, -1.1484985],[-1.6623441, 0.37410975, -1.1647063 ],[-1.6602284, 0.37400728, -1.1496595 ]], dtype=np.float32)
Sp = ffi.cast("double(*) [3]", S.ctypes.data)
Tp = ffi.cast("double(*) [3]", T.ctypes.data)

dd = lib.dist(Sp,Tp);
  

Это решение работает не так, как предполагалось. Действительно, аргументы, печатаемые функцией C, являются:

  Sp=[[0.000002,  -0.270760,  -0.020458]
    [0.000002,  0.000000,  0.000000]
    [0.000000,  0.000000,  0.000000]]
 Tp=[[0.000002,  -0.324688,  -0.020588]
    [0.000002,  0.000000,  0.000000]
    [0.000000,  0.000000,  -nan]]
  

Я также попробовал следующее для инициализации указателей:

 Sp = ffi.new("double *[3]")
for i in range(3):
    Sp[i] = ffi.cast("double *", S[i].ctypes.data)
Tp = ffi.new("double *[3]")
for i in range(3):
    Tp[i] = ffi.cast("double *", T[i].ctypes.data)
dd = lib.dist(Sp,Tp);
  

Но это решение вызывает ошибку в dist(Sp,Tp) :

 TypeError: initializer for ctype 'double(*)[3]' must be a pointer to same type, not cdata 'double *[3]'
  

У вас есть какие-либо идеи о том, как заставить это работать? Спасибо.

Ответ №1:

Типы double[3][3] и double *[3] не эквивалентны. Первый представляет собой 2D-массив double 3×3, хранящийся как 9 непрерывных double. Последний представляет собой массив из 3 двойных указателей, который не является тем, как 2D статические массивы реализованы в C или C .

Как массивы numpy, так и статические массивы C представлены в памяти в виде непрерывного блока элементов, это просто double *[3] тип в середине, который бросает гаечный ключ в работу. Что бы вы хотели, так это использовать double[3][3] правильное или double[3]* (указатель на строку из трех двойников). Обратите внимание, что если вы используете последнее, вам может потребоваться изменить прототип функции на take double [][3] .

Комментарии:

1. Спасибо. Действительно, приведение к double[3][3] сработало. В любом случае мне также пришлось изменить тип numpy на float64