Wie man diesen komplizierten Ausdruck mit numpy Slices ausdrückt

14

Ich möchte den folgenden Ausdruck in Python implementieren: wobei x und y numpy Arrays der Größe n sind und k ein numpy Array ist der Größe n × n . Die Größe n kann bis zu 10000 betragen, und die Funktion ist Teil einer inneren Schleife, die viele Male ausgewertet wird. Daher ist die Geschwindigkeit wichtig.

xi=j=1i1kij,jaijaj,
xynkn×nn

Idealerweise würde ich eine for-Schleife gerne ganz vermeiden, obwohl ich denke, dass es nicht das Ende der Welt ist, wenn es eine gibt. Das Problem ist, dass ich Probleme habe, es ohne ein paar verschachtelte Schleifen zu machen, und das wird es wahrscheinlich ziemlich langsam machen.

Kann jemand sehen, wie man die obige Gleichung mit numpy effizient und vorzugsweise auch lesbar ausdrückt? Was ist im Allgemeinen der beste Weg, um so etwas anzugehen?

Nathaniel
quelle
Ich hatte vor ein paar Tagen eine ähnliche Frage. Ich habe es bei stackoverflow nachgefragt. Schauen Sie sich diesen Beitrag an . Ich benutze scipy.weave anstelle von Cython. Weiß jemand, ob dies einen (erheblichen) Leistungsunterschied macht?
9.

Antworten:

17

Hier ist die Numba-Lösung. Auf meinem Computer ist die Numba-Version> 1000x schneller als die Python-Version ohne Dekorator (für eine 200x200-Matrix 'k' und einen 200-Längen-Vektor 'a'). Sie können auch den @autojit-Dekorator verwenden, der etwa 10 Mikrosekunden pro Aufruf hinzufügt, sodass derselbe Code mit mehreren Typen funktioniert.

from numba import jit, autojit

@jit('f8[:](f8[:,:],f8[:])')
#@autojit
def looped_ver(k, a):
    x = np.empty_like(a)
    for i in range(x.size):
        sm = 0.0
        for j in range(0, i+1):
            sm += k[i-j,j] * a[i-j] * a[j]
        x[i] = sm
    return x

Offenlegung: Ich bin einer der Numba-Entwickler.

Travis Oliphant
quelle
Danke, das sieht ziemlich einfach aus. Ich wusste nicht einmal über Numba Bescheid! Cython, PyPy, Numba ... es ist eine verwirrende Welt.
Nathaniel
3
Travis, sehr cool, macht es Ihnen etwas aus, der Antwort, dass Sie einer der numba-Entwickler sind, eine Offenlegung hinzuzufügen?
Aron Ahmadia
1
Mit ist die Cython-Version auch viel schneller als das geloopte Python (~ 700x für mich). Ich wäre gespannt, wie sich diese Leistung bei größeren Matrizen ändert und ob sie dieselben (Speicher-?) Engpässe aufweisen. n=200
Nat Wilson
@ NatWilson - wenn Sie dies als Frage zu scicomp stellen, würde ich gerne versuchen, es für Sie in Angriff zu nehmen :)
Aron Ahmadia
4

Hier ist ein Anfang. Erstens entschuldige ich mich für etwaige Fehler.

ichich-1

Bearbeiten: Nein, die Obergrenze war korrekt, wie in der Frage angegeben. Ich habe es so belassen, wie es hier ist, da eine andere Antwort jetzt denselben Code verwendet, aber die Korrektur ist einfach.

Zuerst eine geloopte Version:

def looped_ver(k, a):
    x = np.empty_like(a)
    for i in range(x.size):
        sm = 0
        for j in range(0, i+1):
            sm += k[i-j,j] * a[i-j] * a[j]
        x[i] = sm
    return x

Ich habe es zu einer einzigen Schleife mit numpy Slices gemacht:

def vectorized_ver(k, a):
    ktr = zeros_like(k)
    ar = zeros_like(k)
    sz = len(a)
    for i in range(sz):
        ktr[i,:i+1] = k[::-1].diagonal(-sz+i+1)
        a_ = a[:i+1]
        ar[i,:i+1] = a_[::-1] * a_
    return np.sum(ktr * ar, 1)

n=5000 .

Dann habe ich eine Cython-Version des (besser lesbaren) geloopten Codes geschrieben.

import numpy as np
import cython
cimport numpy as np

@cython.boundscheck(False)
@cython.wraparound(False)
def cyth_ver(double [:, ::1] k not None,
              double [:] a not None):
    cdef double[:] x = np.empty_like(a)
    cdef double sm
    cdef int i, j

    for i in range(len(a)):
        sm = 0.0
        for j in range(i+1):
            sm = sm + k[i-j,j] * a[i-j] * a[j]
        x[i] = sm
    return x

Auf meinem Laptop ist dieser etwa 200x schneller als die geloopte Version (und 8x schneller als die vektorisierte 1-Loop-Version). Ich bin sicher, andere können es besser machen.

Ich habe mit einer Julia-Version gespielt und es schien (wenn ich es richtig zeitlich abgestimmt habe) mit dem Cython-Code vergleichbar zu sein.

Nat Wilson
quelle
x0ich-1
Ah ich sehe. Ich habe das aus der ursprünglichen Summe zusammengetragen, war mir aber nicht sicher, ob das die Absicht war.
Nat Wilson
1

Was Sie wollen, scheint eine Faltung zu sein; Ich denke, der schnellste Weg, dies zu erreichen, ist die numpy.convolveFunktion.

Möglicherweise müssen Sie die Indizes entsprechend Ihren Anforderungen anpassen, aber ich denke, Sie möchten Folgendes ausprobieren:

import numpy as np
a = [1, 2, 3, 4, 5]
k = [2, 4, 6, 8, 10]

result = np.convolve(a, k*a[::-1])
Thomas Baruchel
quelle