2013-04-16 11 views
15

Próbuję wykonać obliczenia w języku Cython, które opierają się w dużej mierze na niektórych numpy/scipy funkcji matematycznych, takich jak numpy.log. Zauważyłem, że jeśli zadzwonię wielokrotnie NumPy/funkcje scipy w pętli w Cython istnieją ogromne koszty ogólne, np:Jak wywoływać funkcje numpy/scipy C bezpośrednio w Cython, bez obciążania wywołaniami Pythona?

import numpy as np 
cimport numpy as np 
np.import_array() 
cimport cython 

def myloop(int num_elts): 
    cdef double value = 0 
    for n in xrange(num_elts): 
    # call numpy function 
    value = np.log(2) 

To jest bardzo drogie, przypuszczalnie dlatego np.log przechodzi Pythonie zamiast wywoływania funkcji numpy C bezpośrednio. Jeśli zamieniam tę linię na:

from libc.math cimport log 
... 
# calling libc function 'log' 
value = log(2) 

to znacznie szybciej. Jednak, gdy próbuję przekazać tablicę numpy do libc.math.log:

cdef np.ndarray[long, ndim=1] foo = np.array([1, 2, 3]) 
log(foo) 

daje ten błąd:

TypeError: only length-1 arrays can be converted to Python scalars 

Moje pytania to:

  1. Czy jest możliwe wywołać funkcję C i przekazać jej tablicę numpy? Czy może on być użyty tylko na wartościach skalarnych, które wymagałyby napisania pętli (np. Gdybym chciał zastosować ją do tablicy foo powyżej).
  2. Czy istnieje analogiczny sposób wywoływania funkcji scipy z C bezpośrednio bez Python narzut? Jak mogę zaimportować bibliotekę funkcji C scipy?

przykład betonu: że chcesz wywołać wiele scipy-tych lub przydatnych funkcji statystycznych NumPy'S (np scipy.stats.*) na wartościach skalarnych wewnątrz for pętli w Cython? Szalenie polega na ponownym zaimplementowaniu wszystkich tych funkcji w języku Cython, więc ich wersje C muszą zostać wywołane. Na przykład, wszystkie funkcje związane z pdf/cdf i próbkowaniem z różnych rozkładów statystycznych (np. Zobacz http://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.rv_continuous.pdf.html#scipy.stats.rv_continuous.pdf i http://www.johndcook.com/distributions_scipy.html) Jeśli wywołasz te funkcje z narzutem Pythona w pętli, będzie to zdecydowanie spowolnione.

dzięki.

+0

Plik 'scipy.stats' pdf itp.funkcje są głównie wdrażane w Pythonie. Możesz uniknąć narzutu, przetwarzając wiele liczb naraz. –

Odpowiedz

2

Nie można zastosować funkcji C, takich jak loguj zliczone tablice, a numpy nie ma biblioteki funkcji C, którą można wywołać z cython.

Funkcje Numpy są już zoptymalizowane do wywoływania w niewymaganych tablicach. Jeśli nie masz wyjątkowego sposobu użycia, nie będziesz czerpać większych korzyści z ponownego wdrażania funkcji numpy jako funkcji języka C. (Możliwe, że niektóre funkcje w numpy nie są dobrze zaimplementowane, w takim przypadku rozważ przesłanie swoich importów jako łatek.) Jednak masz rację.

# A 
from libc.math cimport log 
for i in range(N): 
    r[i] = log(foo[i]) 

# B 
r = np.log(foo) 

# C 
for i in range(n): 
    r[i] = np.log(foo[i]) 

Ogólnie A i B powinny mieć podobne czasy pracy, ale należy unikać C i będzie znacznie wolniej.

Aktualizacja

Oto kod dla scipy.stats.norm.pdf, jak widać to napisany w Pythonie z numpy i połączeń scipy. Nie ma wersji C tego kodu, musisz go nazwać "przez pythona". Jeśli to cię powstrzymuje, będziesz musiał ponownie wszczepić go w C/Cythonie, ale najpierw spędziłbym trochę czasu bardzo starannie profilując kod, aby sprawdzić, czy są jakieś niższe wiszące owoce, które trzeba najpierw wykonać.

def pdf(self,x,*args,**kwds): 
    loc,scale=map(kwds.get,['loc','scale']) 
    args, loc, scale = self._fix_loc_scale(args, loc, scale) 
    x,loc,scale = map(asarray,(x,loc,scale)) 
    args = tuple(map(asarray,args)) 
    x = asarray((x-loc)*1.0/scale) 
    cond0 = self._argcheck(*args) & (scale > 0) 
    cond1 = (scale > 0) & (x >= self.a) & (x <= self.b) 
    cond = cond0 & cond1 
    output = zeros(shape(cond),'d') 
    putmask(output,(1-cond0)+np.isnan(x),self.badvalue) 
    if any(cond): 
     goodargs = argsreduce(cond, *((x,)+args+(scale,))) 
     scale, goodargs = goodargs[-1], goodargs[:-1] 
     place(output,cond,self._pdf(*goodargs)/scale) 
    if output.ndim == 0: 
     return output[()] 
    return output 
+1

Ale co jeśli potrzebuję napisać pętlę for w kodzie, który nie może być wektoryzowany i chcę użyć pewnych numpy funkcji (na wartościach skalarnych, nie wektorach) w tej pętli? Czy będę musiał ponownie wdrożyć te numpy funkcje w Cython? Wydaje się to bardzo głupie. Mogę to zrobić tylko z opcją C, która jak zauważyłeś, nie jest dobra. – user248237dfsf

+0

W takim przypadku najlepszym rozwiązaniem może być ponowne zaimplementowanie tych funkcji w cytoncie i użycie opcji A. Jeśli nie masz nic przeciwko , które funkcje numpy musisz wywołać wewnątrz pętli. –

+2

Różne funkcje związane ze statystyką. Poprawiłem moją odpowiedź, aby uzyskać więcej szczegółów. Na przykład wszystkie funkcje związane z pdf/cdf i próbkowaniem z różnych dystrybucji statystycznych (np. Patrz http://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.rv_continuous.pdf.html#scipy. stats.rv_continuous.pdf i http://www.johndcook.com/distributions_scipy.html). Nie wydaje się właściwe, aby ponownie wdrożyć wszystkie te pliki pdf przy użyciu prymitywów z libc.math, aby uzyskać go w Cython ... musi być lepszy sposób? – user248237dfsf