2016-10-14 18 views
0

より大きなクラスの地域依存ハッシュの一部としてペア間距離を計算するCythonモジュールを作成しようとしています。Cython:pythonによって呼び出されるcdef関数から拡張型cdefメソッドを呼び出す

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) 
代わりに、それぞれのタイプとメトリックの各距離のためにコードを書くのは、私はメトリックを継承し、様々な拡張子の種類を取る1つのCDEF関数を作成しようとしています

しかし、私は "c_distance。[Metric]オブジェクトには属性 'c_evaluate'がないというエラーが発生しています。"私はc_evaluateメソッドにアクセスできないのだろうかと疑問に思っているのは、クラスオブジェクトがPython関数to_Metricを介してPythonコードで作成されているからです。しかし、def関数とcdef関数はCythonモジュール内で自由に呼び出せると考えられます。このメソッドは、cpefメソッドにc_evaluateを変更しても機能しますが、cdefオブジェクトがpythonをcythonに渡すことを許可するか、単に低速のpythonメソッドを使用することで問題が解決するかどうかはわかりません。すべての提案(私は自宅のコンピュータではないので、今はすべてのコードを持っていません。後で更新する予定ですか?)また、to_Metric

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) 

編集:それタイプミスが本来の機能ではありませんが(まだ他の人があるかもしれない)

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 
+0

これは単なるスペルミスですか? – hpaulj

答えて

0

問題がc_evaluateが関連付けられているということですクラスEuclideanであり、のタイプは、Euclideanであることが知られているオブジェクトで使用できます。ただし、pairwiseでは、タイプmetMetricと宣言します。

c_evaluateは、cdefとして機能すると宣言しているため、コンパイル時にしか見つかりません。標準のPython関数のように実行時にc_evaluateを見つけたい場合は、defと宣言する必要があります。

あなたが(速く、それを呼び出しますする)コンパイル時に発見される機能が必要な場合は、あなたがc_evaluateMetricオブジェクトの関数で作るべきか、あなたはpairwiseだけEuclideanオブジェクトを取る行う必要があります。

+0

ありがとうございます。私はもともと、[cython wiki](http://cython.readthedocs.io/en/latest/src/tutorial/cdef_classes.html)の例にあるように、返り値0のMetricで 'c_evaluate'メソッドを持っていましたが、コンパイル時に "error:duplicate member '__pyx_fuse_0c_evaluate' "のようなエラーが発生し、拡張タイプのcdefメソッドを上書きできないため、このwadが間違って終了しました。今は、融合型数値内の各タイプに対して作成された重複メソッドの問題です。 –

+0

リンクされた例は 'cdef'の代わりに' cpdef'を使用しています。 – DavidW

+0

はい、cpdefは単にdefとcdefがPythonオブジェクトとして組み合わされているので、同様の使用を前提としています。 afaictが修正されていないcdefメソッドに融合型のバグがあります。私が二重にすべてをタイプすれば、それは完全に働く。 –

関連する問題