[PYTHON] Beschleunigung numerischer Berechnungen mit NumPy: Basics

Ziel

Es ist für Anfänger von Python und NumPy geschrieben. Es kann besonders nützlich sein für diejenigen, die eine numerische Berechnung physikalischer Systeme anstreben, die "Ich kann C-Sprache verwenden, habe aber kürzlich Python gestartet" oder "Ich verstehe nicht, wie man wie Python schreibt" entspricht. Vielleicht.

Außerdem kann es aufgrund meines mangelnden Studiums zu falschen Beschreibungen kommen. Bitte verzeihen Sie mir.

Zusammenfassung

Python verfügt über eine sehr umfangreiche Bibliothek numerischer Berechnungen und ist einfach zu verwenden. Viele von ihnen sind in Fortran geschriebene Heritage-Wrapper, aber sie sind viel einfacher als das Aufrufen von C / C ++ usw. Wenn Sie beispielsweise LAPACK [^ 1] aus C / C ++ aufrufen und versuchen, das Eigenwertproblem zu berechnen

info = LAPACKE_zheevr(LAPACK_COL_MAJOR, 'V', 'A', 'L', H, (lapack_complex_double*)a, lda, vl, vu, il, iu, abstol, &mm, w, (lapack_complex_double*)z, ldz, isuppz);

Es gibt zu viele Argumente und es ist kompliziert. Obwohl je mehr Argumente, desto mehr Rechenfreiheit, sieht es nicht wie ein Anfänger aus. Außerdem unterstützt C / C ++ Komplexe nicht nativ. Da Typen wie "lapack_complex_double" im Wrapper definiert sind, kann dies verwirrend sein. Andererseits ist es in Python sehr präzise:

w, v = numpy.linalg.eig(H)

Argumente wie die in LAPACK werden ebenfalls ordnungsgemäß vorbereitet [^ 2]. Da jedoch der Standardwert festgelegt ist, können Sie dies einfach tun, wenn Sie ihn nur lösen möchten, ohne über die Details nachzudenken. Einfach und gut Übrigens scheinen verschiedene Löser in SciPy eine höhere Leistung zu haben als in NumPy.

In diesem Fall ist es verlockend, Python verschiedene numerische Berechnungen zu überlassen, aber dieses Kind hat auch Nachteile. ** Denn Schleifen sind tödlich langsam. ** Bei sogenannten Matrixoperationen treten häufig Dreifachschleifen auf. Da die numerische Berechnung jedoch auf "Feindifferenzierung und Schleifenverarbeitung" basiert, ist sie ziemlich fatal [^ 3]. Obwohl es sich möglicherweise nicht um eine Kompilierungssprache handelt, scheint sie langsamer zu sein als andere LL-Sprachen. Für Matrixoperationen

for i in range(N):
	for j in range(N):
		for k in range(N):
			c[i][j] = a[i][k] * b[k][j]

Ich habe es geschrieben und es ist vorbei.

NumPy integrierte Funktionen und Operationen

Nutzen Sie also die universelle Funktion von NumPy. ** Die integrierte Funktion von NumPy funktioniert für jedes Element des NumPy-Arrays "ndarray" und gibt ein neues "ndarray" zurück. . ** Dies ist sehr wichtig und kann bei Tensoroperationen zu Schleifen führen. Beispielsweise kann math.sin (cmath.sin) nur Skalare übergeben, aber numpy.sin

>>> theta = numpy.array([numpy.pi/6, numpy.pi/4, numpy.pi])
>>> numpy.sin(theta) 
array([  5.00000000e-01,   7.07106781e-01,   1.22464680e-16])

Das Werfen eines NumPy-Arrays bewirkt, dass numpy.sin auf jedes Element einwirkt, und diese Art der Verarbeitung ist häufig so schnell wie C / C ++. Die Hauptimplementierung von NumPy ist C. Dies liegt daran, dass BLAS [^ 4] / LAPACK bei linearen Operationen sein Bestes gibt.

Ebenso schnell und vielfältig sind die vier von "ndarray" definierten Betriebsregeln.

Teil 1: Vektor

Schreiben wir eine lineare Operation in Bezug auf einen Vektor ohne for-Schleife. Da erwartet wird, dass Funktionen wie das innere Produkt und das äußere Produkt richtig vorbereitet sind, wie werden die Summe, das Produkt, der Quotient usw. für "ndarray" hauptsächlich unten beschrieben? Mal sehen, ob es definiert ist. ** Die Operation auf ndarray unterscheidet sich völlig von der Operation auf list, die in Python erstellt wurde ** Danach wird der Alias wie "import numpy as np" gesetzt.

Vektor und Skalar

Ein eindimensionales "ndarray" kann als eine Art Vektor betrachtet werden. Scalar (complex, float, int) funktioniert mit allen Elementen von ndarray:

>>> a = np.array([1, 2, 3])
>>> a + 2
array([3, 4, 5])
>>> a * (3 + 4j)
array([ 3. +4.j,  6. +8.j,  9.+12.j])

Vektoren

ndarray arbeitet mit Elementen mit übereinstimmenden Indizes:

>>> a = np.array([1, 2, 3])
>>> b = -np.array([1, 2, 3])
>>> a + b
array([0, 0, 0])
>>> a * b
array([-1, -4, -9])
>>> a / b
array([-1., -1., -1.])

Es ist interessant, dass es sich weder um ein inneres noch um ein äußeres Produkt handelt. Es ist möglich, eine gleichmäßige Teilung zu definieren. Es wird häufig verwendet, wenn eine Differenzialoperation für eine bestimmte differenzierte Funktion ausgeführt wird. Die Summe / das Produkt von Ndarrays mit unterschiedlichen Dimensionen ist nicht definiert.

Umgestaltete Ndarrays

ndarray hat eine Methode namens reshape. Sie können beispielsweise einen 3x1-Vektor in einen 1x3-Vektor umordnen. Es ist wie ein horizontaler Vektor in einen vertikalen Vektor, aber ein Produkt (*). Es gibt keine einfache Entsprechung, da sich die Definition von von der des Vektorraums unterscheidet. Das Produkt mit diesem "Umform" -Vektor ist sehr interessant:

>>> c = a.reshape(3, 1)
>>> c
array([[1],
       [2],
       [3]])
>>> c + b
array([[ 0, -1, -2],
       [ 1,  0, -1],
       [ 2,  1,  0]])
>>> c * b
array([[-1, -2, -3],
       [-2, -4, -6],
       [-3, -6, -9]])

Mit Regeln wie (3 × 1) + oder * (1 × 3) = (3 × 3), aber wie bei der vorherigen Operation, ** ist diese Operation machbar **. Sie erscheint auf den ersten Blick seltsam. Es mag sein, aber wenn Sie einen Blick darauf werfen, können Sie sehen, dass dies anhand der Regeln im vorherigen Abschnitt erklärt werden kann. Damit besteht die Möglichkeit, dass selbst bei der Initialisierung der ** -Matrix die for-Schleife nicht verwendet werden muss. Masu **. Es ist sehr nützlich für Aufgaben wie das Initialisieren einer großen Matrix bei jeder Änderung der Parameter und das Fortfahren mit der Berechnung.

Ich persönlich mag die sehr flexiblen Spezifikationen, die nicht an diese Typen gebunden sind [^ 5]. Ich denke, sie sind intuitiver als andere Sprachen.

Vektorinitialisierung

Es ist keine Operation, aber es macht keinen Sinn, eine for-Schleife zu verwenden, um einen Vektor vorzubereiten. Es gibt viele Funktionen, die "ndarray" erstellen, aber hier sind einige der Funktionen, die ich am häufigsten verwende:

>>> L = 1
>>> N = 10
>>> np.linspace(-L/2, L/2, N)
array([-0.5       , -0.38888889, -0.27777778, -0.16666667, -0.05555556,
        0.05555556,  0.16666667,  0.27777778,  0.38888889,  0.5       ])

>>> dL = 0.2
>>> np.arange(-L/2, L/2, dL)
array([-0.5, -0.3, -0.1,  0.1,  0.3])

>>> np.logspace(0, 1 ,10, base=np.e)
array([ 1.        ,  1.11751907,  1.24884887,  1.39561243,  1.5596235 ,
        1.742909  ,  1.94773404,  2.17662993,  2.43242545,  2.71828183])

Wenn Sie Operationen mit Skalaren und Vektoren kombinieren, scheint dies auch ohne for-Schleife der Fall zu sein.

Teil 2: Matrix

Der Ablauf der Geschichte ist fast der gleiche wie der des Vektors. Da das Matrixprodukt eine dedizierte Funktion hat, ist das Folgende auch die Geschichte der Operation. Ich denke, dass das Operationsergebnis vorhergesagt werden kann.

Matrix und Skalar

>>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
>>> a
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])
>>> a + (2 - 3j)
array([[  3.-3.j,   4.-3.j,   5.-3.j],
       [  6.-3.j,   7.-3.j,   8.-3.j],
       [  9.-3.j,  10.-3.j,  11.-3.j]])
>>> a * 0.2
array([[ 0.2,  0.4,  0.6],
       [ 0.8,  1. ,  1.2],
       [ 1.4,  1.6,  1.8]])

Matrix und Vektor

>>> b = np.array([1, 2, 3])
>>> a + b
array([[ 2,  4,  6],
       [ 5,  7,  9],
       [ 8, 10, 12]])
>>> a * b
array([[ 1,  4,  9],
       [ 4, 10, 18],
       [ 7, 16, 27]])
>>> a / b
array([[ 1. ,  1. ,  1. ],
       [ 4. ,  2.5,  2. ],
       [ 7. ,  4. ,  3. ]])

Matrix und Matrix

>>> b = np.array([[-1, 2, -3], [4, -5, 6], [-7, 8, -9]])
>>> a + b
array([[ 0,  4,  0],
       [ 8,  0, 12],
       [ 0, 16,  0]])
>>> a * b
array([[ -1,   4,  -9],
       [ 16, -25,  36],
       [-49,  64, -81]])

Matrixinitialisierung

Es gibt auch nützliche Funktionen für Matrizen. Nachfolgend sind nur einige aufgeführt:

>>> np.identity(4)
array([[ 1.,  0.,  0.,  0.],
       [ 0.,  1.,  0.,  0.],
       [ 0.,  0.,  1.,  0.],
       [ 0.,  0.,  0.,  1.]])

>>> np.eye(4, 3)
array([[ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.],
       [ 0.,  0.,  0.]])

>>> c = np.array([1, 2, 3, 4])
>>> np.diag(c)
array([[1, 0, 0, 0],
       [0, 2, 0, 0],
       [0, 0, 3, 0],
       [0, 0, 0, 4]])

abschließend

Ich denke, andere Leute haben bessere Artikel über die praktischen Funktionen und detaillierten arithmetischen Regeln von NumPy geschrieben. Wenn Sie sich diesen Artikel ansehen und ihn wie NumPy schreiben, können Sie verschiedene Berechnungen durchführen, ohne for-Schleifen zu verwenden. Wir hoffen, dass Sie das Gefühl haben, dass Sie es schaffen können. Wenn Sie diese verwenden, werden die numerischen Berechnungsalgorithmen, die Studenten der Naturwissenschaften im Programmierunterricht lernen, mit einer Geschwindigkeit ausgeführt, die mit C / C ++ vergleichbar ist. Viele Handalgorithmen sind bereits in SciPy und anderen verfügbar.

Es ist etwas lang her, daher möchte ich die spezifische Anwendung für die numerische Berechnung in einem anderen Artikel zusammenfassen. Vielen Dank.

** Nachtrag (25.11.2016): Fortsetzung unten ** Beschleunigung der numerischen Berechnung mit NumPy / SciPy: Anwendung 1 Beschleunigung der numerischen Berechnung mit NumPy / SciPy: Anwendung 2

[^ 1]: LAPACK (Linear Algebra PACKage) ist eine Bibliothek von linearen Algebraoperationen, die in Fortran90 geschrieben wurden. Einige Wrapper für C / C ++ heißen Lapacke. Da es eine MKL-kompatible Schnittstelle hat, Das MKL-Handbuch wird hilfreich sein. [^ 2]: Weitere Informationen finden Sie unter NumPy und SciPy. Bitte beachten Sie das Handbuch 1 / reference /). [^ 3]: Außerdem ist der Overhead des Funktionsaufrufs auch ein großer Eindruck. Im Fall von Procon kommt es häufig vor, dass "es passiert, wenn es mit einer dynamischen Planungsmethode geschrieben wird, aber TLE in der Wiederholung von Gedenkstätten". [^ 4]: Eine Bibliothek, die einen grundlegenderen Satz linearer Operationen als LAPACK enthält. In LAPACK nennen wir BLAS. [^ 5]: Einige Leute mögen "kein Typ" nicht. Im Gegensatz zu Programmen, die "bestimmte Spezifikationen" erfüllen, sind numerische Berechnungen in der Physik so einfach zu überprüfen, ob die Ausgabe korrekt ist. Dies ist nicht der Fall. Und es ist noch schwieriger zu debuggen, wenn während der Programmausführung "implizite Änderungen des Variablentyps" zulässig sind. Da der beobachtbare Betrag auf Float festgelegt ist, führt die statische Typisierung zu Fehlern. Ich verstehe die Behauptung, dass es schwierig ist, aber ich mag Python.

Recommended Posts

Beschleunigung numerischer Berechnungen mit NumPy: Basics
Beschleunigung der numerischen Berechnung mit NumPy / SciPy: Anwendung 2
Beschleunigung der numerischen Berechnung mit NumPy / SciPy: Anwendung 1
Beschleunigung der numerischen Berechnung mit NumPy / SciPy: Aufnehmen gefallener Ohren
Sehen Sie, wie schnell Sie mit NumPy / SciPy beschleunigen können
NumPy-Grundlagen
# Python-Grundlagen (#Numpy 1/2)
# Python-Grundlagen (#Numpy 2/2)
Python #Numpy Basics
Ergebnisse bei der Beschleunigung numerischer Berechnungen mit Python und Numba
Hinweise zur Beschleunigung des Python-Codes mit Numba
Laden / Anzeigen und Beschleunigen von GIF mit Python [OpenCV]
Python Basic 8 Numpy Test
Gleitender Durchschnitt mit Numpy
Erste Schritte mit Numpy
Lernen Sie mit Chemo Informatics NumPy
Verkettung von Matrizen mit Numpy
Summencode mit Numpy
Führen Sie eine Regressionsanalyse mit NumPy durch
Erweitern Sie NumPy mit Rust
Numerische Berechnung mit Python