#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, это работает отлично.