2015-05-25 21 views
15

ich bin für den meisten speichereffizienten Weg, um den absoluten Quadrat-Wert eines komplexen numpy ndarrayDie meisten speichereffiziente Art und Weise abs zu berechnen() ** 2 komplexe numpy ndarray

arr = np.empty((250000, 150), dtype='complex128') # common size 
zu berechnen

Ich habe kein ufunc gefunden, das genau np.abs()**2 tun würde.

Da ein Array dieser Größe und Art etwa eine halbe GB benötigt, suche ich nach einer primär speichereffizienten Methode.

Ich möchte auch, dass es tragbar ist, also idealerweise eine Kombination von ufuncs.

Bisher mein Verständnis ist, dass diese

über die besten
result = np.abs(arr) 
result **= 2 

sollte es unnötig (**0.5)**2 berechnen wird, soll aber **2 an Ort und Stelle berechnen. Insgesamt ist der Spitzenspeicherbedarf nur die ursprüngliche Array-Größe + Array-Ergebnisgröße, die 1,5 * Original-Array-Größe sein sollte, da das Ergebnis real ist.

Wenn ich loswerden der nutzlos **2 Anruf bekommen wollte würde ich so etwas wie dieses

result = arr.real**2 
result += arr.imag**2 

zu tun, aber wenn ich mich nicht irre, bedeutet dies, ich werde für Speicher zuweisen haben beide die Berechnung von Real- und Imaginärteil, so würde die maximale Speichernutzung 2.0 * ursprüngliche Array-Größe sein. Die arr.real-Eigenschaften geben auch ein nicht zusammenhängendes Array zurück (das ist jedoch von geringerer Bedeutung).

Gibt es etwas, was ich vermisse? Gibt es bessere Möglichkeiten, dies zu tun?

EDIT 1: Es tut mir leid für die nicht klar zu machen, will ich nicht, arr zu überschreiben, so kann ich es nicht so aus verwenden.

Antwort

4

Dank numba.vectorize in neuere Versionen von numba, erstellen eine numpy universelle Funktion für die Aufgabe ist sehr einfach:

@numba.vectorize([numba.float64(numba.complex128),numba.float32(numba.complex64)]) 
def abs2(x): 
    return x.real**2 + x.imag**2 

Auf meinem Rechner finde ich eine dreifache Beschleunigung auf eine reine numpy Version verglichen, die Zwischen Arrays erstellt:

>>> x = np.random.randn(10000).view('c16') 
>>> y = abs2(x) 
>>> np.all(y == x.real**2 + x.imag**2) # exactly equal, being the same operation 
True 
>>> %timeit np.abs(x)**2 
10000 loops, best of 3: 81.4 µs per loop 
>>> %timeit x.real**2 + x.imag**2 
100000 loops, best of 3: 12.7 µs per loop 
>>> %timeit abs2(x) 
100000 loops, best of 3: 4.6 µs per loop 
+0

Ich möchte das als eine Antwort akzeptieren, aber ich bin mir nicht sicher, wie tragbar es ist. Numba ist heutzutage mit Anaconda auf den meisten Rechnern ziemlich einfach zu installieren, aber ich bin mir nicht sicher, wie portierbar die LLVM-Bindungen über Architekturen sind. Vielleicht könnten Sie einige Informationen über die Portabilität dieser Antwort hinzufügen. –

+0

Nun, ich bin LLVM Experte, aber die Dokumentation der aktuellen Version (0.31.0) sagt: Unterstützt werden Linux, Windows 7 und OS X 10.9 und höher. – burnpanck

1

arr.real und arr.imag sind nur Ansichten in das komplexe Array. Daher wird kein zusätzlicher Speicher zugewiesen.

+2

aber es wird zugewiesen, wenn ich 'arr.real berechnen ** 2'. –

1

Wenn Ihr primäres Ziel ist, Speicher zu sparen, nehmen NumPy ufuncs einen optionalen out Parameter, mit dem Sie die Ausgabe auf ein Array Ihrer Wahl richten können. Es kann nützlich sein, wenn Sie Operationen an Ort und Stelle durchführen möchten.

Wenn Sie diese geringfügige Änderung an Ihre ersten Methode machen, dann können Sie den Vorgang auf arr vollständig an Ort und Stelle durchführen: konnten

np.abs(arr, out=arr) 
arr **= 2 

Ein gewundener Weg, der nur verwendet einen wenig zusätzlichen Speicher Ändern Sie an Ort und Stelle, berechnen Sie das neue Array von realen Werten und stellen Sie dann arr wieder her.

Dies bedeutet, Informationen über die Zeichen zu speichern (es sei denn, Sie wissen, dass Ihre komplexen Zahlen alle positive Real- und Imaginärteile haben). Für das Vorzeichen jedes reellen oder imaginären Werts wird nur ein einziges Bit benötigt. Daher wird 1/16 + 1/16 == 1/8 der Speicher arr verwendet (zusätzlich zu dem neuen Array von Floats, das Sie erstellen).

>>> signs_real = np.signbit(arr.real) # store information about the signs 
>>> signs_imag = np.signbit(arr.imag) 
>>> arr.real **= 2 # square the real and imaginary values 
>>> arr.imag **= 2 
>>> result = arr.real + arr.imag 
>>> arr.real **= 0.5 # positive square roots of real and imaginary values 
>>> arr.imag **= 0.5 
>>> arr.real[signs_real] *= -1 # restore the signs of the real and imagary values 
>>> arr.imag[signs_imag] *= -1 

Auf Kosten der Speicherung signbits ist arr unverändert und result hält die Werte, die wir wollen.

+0

danke, aber ich möchte Arr nicht überschreiben, Entschuldigung dafür, das nicht klar zu machen. –

+0

Ich sehe ... Ich kann mir keinen Weg vorstellen, genau das zu tun, was Sie wollen, dass (a) "arr" erhält und (b) nur ein neues Array von float-Werten (mit der gleichen Form wie 'arr') zuweist). Möglicherweise ist ein benutzerdefiniertes ufunc erforderlich (dies kann sich jedoch auf die Portabilität auswirken). –

+0

Vielen Dank für Ihr verschachteltes Beispiel. Ich muss am Ende mit numexpr enden. –

0

BEARBEITEN: Diese Lösung hat den doppelten Mindestspeicherbedarf und ist nur geringfügig schneller. Die Diskussion in den Kommentaren dient jedoch als Referenz.

Hier ist eine schnellere Lösung, mit dem Ergebnis, gespeichert in res:

import numpy as np 
res = arr.conjugate() 
np.multiply(arr,res,out=res) 

, wo wir die Eigenschaft des abs einer komplexen Zahl ausgebeutet, dh abs(z) = sqrt(z*z.conjugate), so dass abs(z)**2 = z*z.conjugate

+0

Ich habe auch darüber nachgedacht, aber das hat das Problem, dass das Ergebnis immer noch komplex ist. Darüber hinaus beträgt der Speicherspitzenverbrauch 2,0 * ursprüngliche Arraygröße. Ich könnte einfach den reellen Teil nehmen (da der Imag-Teil sehr nahe bei 0 liegen sollte), aber das würde entweder den Spitzenspeicherverbrauch weiter erhöhen oder mir ein nicht zusammenhängendes Array geben. Auch die Multiplikation komplexer Zahlen führt viele unnötige Multiplikationen und Additionen durch, von denen wir bereits wissen, dass sie keinen Nutzen haben (da sie sich auf 0 aufheben). –

+0

1) das Ergebnis ist reellwertig, mit einem komplexen 'dtype', der anders ist; 2) der Speicherverbrauch ist nicht zweimal, wir ordnen nur einmal für res, was unvermeidlich ist, und dann verwenden out für multiply() '; 3) beachte, dass "all (res.imag == 0) -> True" ist, so dass es KEINEN imaginären Teil überhaupt gibt; 4) Sie können nicht von komplexen zu komplexen Multiplikationen als 4 real-reelle Multiplikationen denken und schlussfolgern, dass es zeitraubende Berechnungen gibt. Der Code ist schneller als mit 'abs()' und das ist gefragt. Wenn Sie sich fragen, warum das so ist, läuft das wahrscheinlich darauf hinaus, wie CPUs die Multiplikation komplexer Zahlen implementieren. – gg349

+0

Obwohl es (in der Theorie) reellwertig ist, belegt es immer noch Speicher für alle imaginären Nullteile. Ich habe darüber gesprochen, wie viel Speicher ich brauche, um das endgültige (echte) Ergebnis zu erhalten, vorausgesetzt, ich denke nicht will arr. überschreiben Das Minimum ist 1,5 * arr Größe. Dein Vorschlag ist 2.0, weil er auch Speicher für die Null-Imaginärteile aufnimmt. Sich auf CPU-Optimierungen zu verlassen, ist nicht sehr portabel (obwohl es schwierig wäre, einen PC zu finden, der heutzutage kein Theme hätte). –