Cython: вызов метода cdef типа расширения из функции cdef, вызываемой python

#linux #cython

#linux #китон

Вопрос:

Я пытаюсь написать модуль Cython, который вычисляет попарные расстояния как часть более крупного класса хэшей, чувствительных к локальности. Вместо написания кода для каждого типа и каждой метрики расстояния я пытаюсь создать одну функцию cdef, которая принимает различные типы расширений, наследуемые от Metric:

 cdef class Metric:
    def __init__(self):
        pass

cdef class Euclidean(Metric):
    cdef numeric c_evaluate(self, numeric[:] x, numeric[:] y, int dims):
        ....

cdef numeric[:,:] pairwise(numeric[:] x, numeric[:] y, Metric func, bint symmetric):
    ...
    dm[i,j] = func.c_evaluate(x,y,dims)
    ...
 

Чтобы получить доступ к этой функции из Python:

 def py_pairwise(numeric[:,:] x, numeric[:,:] y, str func, bint symmetric = 1, **kwargs):
    cdef Metric mfunc = to_Metric(func, **kwargs)
    return pairwise(x, y, mfunc, symmetric)
 

Однако я продолжаю получать сообщение об ошибке «c_distance.объект [Metric] не имеет атрибута ‘c_evaluate'». Мне интересно, недоступен ли метод c_evaluate, потому что объект класса создается в коде python с помощью функции python to_Metric , хотя я думал, что функции def и cdef должны были свободно вызывать друг друга в модуле Cython. Метод работает, если я изменяю c_evaluate на метод cpdef, но я не уверен, устраняет ли это проблему, позволяя объекту cdef проходить через python в cython или просто использует более медленный метод python. Любые предложения (я также не нахожусь на своем домашнем компьютере, поэтому у меня сейчас нет всего кода. Будет обновляться позже / по запросу)?

Редактировать: Этой опечатки нет в исходных функциях (все еще могут быть другие):

 ctypedef fused floating:
    float
    double

cdef class Euclidean(Metric):
    cdef public floating c_evaluate(self, floating[:] x, floating[:] y, int dims):
        cdef int i
        cdef floating tmp, d = 0
        for i in range(dims):
            tmp = x[i]-y[i]
            d  = tmp*tmp
        return sqrt(d)

#@cython.boundscheck(False)
#@cython.wraparound(False)
def py_pairwise(numeric[:,::1] x, numeric[:,::1] y,str metric, bint symmetric,**kwargs):
    cdef Metric func = to_Metric(metric,**kwargs)   
    return pairwise(x,y,func,symmetric)

cdef numeric[:,::1] pairwise(numeric[:,::1] x,numeric[:,::1] y, Metric met, bint symmetric):#
    cdef int n,m,k,i,j
    n = x.shape[0]
    m = y.shape[0]
    dims = x.shape[1]
    if numeric in floating:
        mdtype = np.float
    else:
        mdtype = np.int
    #mdtype = np.float
    cdef numeric[:,::1] dm = (np.empty((n,m),dtype = mdtype)).fill(0)
    if symmetric:
        interval = lambda i,n,m: range(i 1,m)
    else:
        interval = lambda i,n,m: range(m)
    for i in range(n):
        for j in interval(i,n,m):
            dm[i,j] = met.c_evaluate(x[i,:],y[j,:],dims)
    return np.asarray(dm)
 

Кроме того, to_Metric:

 def to_Metric(str m, **kwargs):
    if len(kwargs) == 0:
        if m == 'euclidean':
            met = Euclidean()
        elif m in {'cos','cosine'}:
            met = Cosine()
        elif m in {'hamming','matching'}:
            met = Hamming()
        else:
            raise ValueError('Unrecognized metric {}'.format(''' m '''))
    else:
        if m in {'pnorm','p-norm'}:
            met = Pnorm(kwargs['p'])
        elif m == 'maximal':
            met = Maximal(kwargs['m1'],kwargs['m2'],kwargs['sep'])
        else:
            raise ValueError('Unrecognized metric {}'.format(''' m '''))
    return met
 

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

1. Может быть, это просто орфографическая ошибка?

Ответ №1:

Проблема в том , что c_evaluate он связан с классом Euclidean и из — за этого может использоваться только с объектами , которые , как известно , имеют тип Euclidean . Тем не менее, в pairwise вы объявляете тип met , который должен быть Metric .

Потому что вы объявили c_evaluate функцию такой cdef , какой она может быть найдена только во время компиляции. Если вы хотите c_evaluate , чтобы функция была найдена во время выполнения как стандартная функция Python, вы должны объявить ее как def .

Если вам нужно, чтобы функция была найдена во время компиляции (что ускоряет ее вызов), то вам следует либо сделать c_evaluate be функцией Metric объекта, либо заставить pairwise принимать только Euclidean объект.

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

1. Спасибо. Первоначально у меня был метод ‘c_evaluate’ в метрике с возвратом 0 в соответствии с примером в cython wiki , но я получил ошибки типа «ошибка: дублирующий элемент ‘__pyx_fuse_0c_evaluate’ » при компиляции и неправильно завершил этот пакет из-за невозможности перезаписать расширение типа cdef методы. Теперь я вижу, что это проблема с дублирующимися методами, созданными для каждого типа в объединенном числовом типе.

2. Обратите внимание, что в примере, на который вы ссылались cpdef , вместо cdef

3. Да, но я предполагал аналогичное использование для функций cdef, поскольку cpdef — это просто def и cdef, объединенные как объект python, и я был прав. Есть просто ошибка со слитными типами в методах cdef, которая afaict не была исправлена. Если я набираю все как double, это работает отлично.