Использование Cython для обертывания библиотеки, которая обертывает другую библиотеку

#cython

#cython

Вопрос:

Моя цель — использовать Cython для обертывания библиотеки Apohenia, библиотеки C для научных вычислений.

Это попытка не перестраивать колесо, и сама Apophenia пытается сделать то же самое, основывая свои структуры на структурах из научной библиотеки GNU:

 typedef struct {
  gsl_vector *vector;
  gsl_matrix *matrix;
  gsl_vector *weights;
  apop_names *names;
  ...
} apop_data;
  

Apophenia предоставляет множество векторных / матричных операций, которые GSL либо не предоставляет, либо предоставляет немного неуклюже, но если у GSL есть функция, нет смысла ее переписывать. Вы должны быть в состоянии писать код на C, который переключается между apop_data набором в целом и его частями GSL так часто, как это необходимо, например:

 apop_data *dataset = apop_text_to_data("infile.csv"); //fill the matrix element
gsl_vector *minv = apop_matrix_inverse(dataset->matrix);
apop_data *dinv = apop_matrix_to_data(minv);
apop_data *identity_matrix = apop_dot(dataset, dinv); // I = D * D^-1
dataset->vector = gsl_vector_alloc(10);
gsl_vector_set_all(dataset->vector, 1);
  

Я не уверен, как обернуть это в Cython. Типичный метод, по-видимому, заключается в предоставлении структуры на стороне Python, которая включает внутреннюю копию обертываемой структуры C:

 """I'm omitting the Cython declarations of the C structs and functions,
 which are just translations of the C declarations. Let those be in c_apop."""

cdef class apop_data:
   cdef c_apop.apop_data *d

   def set(self, row, col, val):
       c_apop.apop_data_set(self.d, row, col, val)

   def get(self, row, col):
       c_apop.apop_data_get(self.d, row, col)

   [et cetera]


cdef class gsl_vector:
   cdef c_apop.gsl_vector *v

   def set(self, row, val):
       c_apop.gsl_vector_set(self.v, row)

   def get(self, row):
       c_apop.gsl_vector_get(self.v, row)

   [et cetera]
  

Но теперь мы застряли, потому что если бы мы должны были получить векторный элемент из набора данных,

  pyd = apop_data(10)
 v = pyd.d.vector
  

v это необработанный C gsl_vector , а не объект python, поэтому следующей строкой не может быть v.get(0) или v.set(0, 1) .

Мы могли бы добавить методы к apop_data классу с именем vector_get and vector_set , которые вернут обертку в python gsl_vector , но это создает свои собственные проблемы: если пользователь перераспределяет вектор C, лежащий в основе py-vector из pyv = pyd.get_vector() , как мы гарантируем, что pyd.d.vector он будет перераспределен вместе с ним?

Я попробовал пару вещей, и мне кажется, что я каждый раз упускаю суть. Есть предложения о том, как наилучшим образом спроектировать классы Cython для этой ситуации?

Ответ №1:

Структура C никогда не должна быть доступна стороне python. Я быстро взглянул на библиотеку, и, похоже, в ней нет ничего необычного.

Единственная ситуация, которую вам нужно отслеживать, — это когда библиотека фактически перераспределяет базовый вектор. Для этих функций обычно требуется указатель на указатель и они обновляют значение указателя до новой выделенной структуры.

Зачем вам нужно предоставлять pyd.get_vector ?