Einführung in numerische Simulation mit Python Alexander Schlemmer, Jan Schumann-Bischoff, Tariq Baig Max-Planck-Institut für Dynamik und Selbstorganisation, Biomedizinische Physik Nichtlineare Dynamik Praktikum
Inhaltsverzeichnis 1 Einleitung 2 3
Python Universelle Skriptsprache, meistens interpretiert Fokus auf gute Lesbarkeit von Sourcecode Open Source (Python Software Foundation License) Existiert seit 1991 Wird gepflegt und weiter entwickelt von der Python Software Foundation Aktuell zwei (z.t. inkompatible) Hauptversionen: Python 2.7 und Python 3.4
Python: Merkmale Große Anzahl an (frei) verfügbaren Bibliotheken Im wissenschaftlichen Bereich: numpy, scipy, matplotlib Dynamische Typisierung Objektorientierung Reduzierte, übersichtliche Syntax, wenige Schlüsselwörter Leicht zu lernen Als interpretierte Sprache eher langsam Höhere Geschwindigkeit durch Vektorisierung von numerischen Operationen Schlechte Unterstützung von Threads (Multithreading)
Python ausführen Python-Interpreter ausführen: python # Python 2 ( a uf den m e i s t e n Rechnern ) python3 Skript / Programm ausführen: python s k r i p t. py Äußerst praktische interaktive Shell: i p y t h o n Interaktives (Web-)Notebook: i p y t h o n notebook
Einfache Beispiele Erstes Beispiel >>> print("hello World!") Hello World! >>> # Das ist ein Kommentar >>> a = 42 # Variablendeklaration >>> print(a * 527 + abs(-0.1)) 22134.1
Variablendeklaration >>> v = 1 >>> type(v) <type int > >>> st = "str" >>> type(st) <type str > >>> y = 17 + 4j >>> type(y) <type complex > >>> tr = True >>> type(tr) <type bool > >>> x = 0.025 >>> type(x) <type float > Mehr zu Typen unter: https://docs.python.org/2/library/types.html
Mathematische Operationen (1) >>> # Einfache Ausdrücke: >>> 42 * 5 + 8-2 / (17 + 4) + a 260 >>> # Division: >>> 43.0 / 3 14.333333333333334 >>> 43 / 3 # Unterschied zw. Python 2 und 3! 14 >>> 43 // 3 # Ganzzahl-Division 14 >>> 43 % 3 # Modulo 1
Weitere Operatoren >>> # Potenzieren: >>> # Komplexe Zahlen: >>> 8**3 >>> c = 2 + 3j 512 >>> c.real >>> 9**(-2) 2.0 0.012345679012345678 >>> c.imag >>> # Vergleichen: 3.0 >>> 4 < 3 >>> c.conjugate() False (2-3j) >>> "str" == "str" >>> abs(c) True 3.605551275463989 >>> 68 >= 17*4 True >>> (not True == True) or (4 > 3 and 8!= 99) True
Mathematische Standardfunktionen >>> from math import sqrt, exp >>> from math import pi,sin,acos >>> sqrt(2) 1.4142135623730951 >>> exp(a*-7) 2.0769322043867094e-128 >>> sin(0.5*pi) 1.0 >>> acos(1) 0.0 >>> import cmath >>> cmath.exp(-1/2*pi*1j) (-1-1.2246467991473532e-16j) >>> cmath.log(32, 2) (5+0j) >>> cmath.log(-2).imag 3.141592653589793 https://docs.python.org/3/library/math.html https://docs.python.org/3/library/cmath.html
Listen >>> liste = [1, 3, 4, 5] >>> liste[0] >>> liste.append(7) 1 >>> liste.extend([2, 3]) >>> liste[6:8] >>> liste.reverse() [7, str ] >>> liste >>> liste[-4] [3, 2, 7, 5, 4, 3, 1] 5 >>> liste.sort() >>> liste[-3:] >>> liste [7, str, [1, 3]] [1, 2, 3, 3, 4, 5, 7] >>> liste[-1][1] * 72 >>> liste.append("str") 216 >>> liste.append([1, 3]) >>> liste [1, 2, 3, 3, 4, 5, 7, str, [1, 3]]
Bedingungen if-elif-else-block >>> if a < 7:... b = 17... print(a*7)... elif a > 7:... print(a/7)... else:... print(a)... 6
Schleifen (1) for-loop über Liste >>> for l in liste[0:2]:... print(l)... 1 2 for-loop über Range >>> for l in range(-2,2):... if l % 2 == 0:... print(l)... -2 0
Schleifen (2) while-loop >>> i = 0 >>> while(i < 7):... i += 2 # i = i + 2... >>> print(i) 8 Mehr zu Control-Flow: https://docs.python.org/3/tutorial/controlflow.html
Funktionen Funktionen definieren >>> def f(x, y):... print("funktionsaufruf")... b = a * x + y... return(b)... Funktionen aufrufen >>> f(23, -2) * 4 Funktionsaufruf 3856 Mehr zu Funktionen: https://docs.python.org/3/tutorial/ controlflow.html#defining-functions
Numpy ist elementares Python Paket für numerische Berechnungen in Python Numpy bietet: N-dimensionales Array Objekt Indizierung ganzer Array Bereiche Sehr schnelle Berechnung elementweiser Operationen (Vektorisierung) Viele Lineare Algebra Routinen wie Matrix Multiplikation, SVD, Eigewertberechnungen, Invertierung, Zufallszahlengeneratoren, Foiurier-Transformation,...
Indizierung von Python Arrays Indizierung in Python ist Null-basiert (wie C)! Numpy Arrays sind keine Listen oder Listen von Listen Listen können zu Numpy Arrays konvertiert werden. Eindimensionales Array (1) >>> import numpy as np >>> a = np.array([1., 4.5, 6.3, 4.1, 0.3, 9.4, 7.1]) >>> a array([ 1., 4.5, 6.3, 4.1, 0.3, 9.4, 7.1]) >>> a[2] 6.2999999999999998 >>> a.shape (7,)
Index von : bis : Schrittweite gibt Bereich im Array an Eindimensionales Array (2) >>> a = np.array([1., 4.5, 6.3, 4.1, 0.3, 9.4, 7.1]) >>> a[:3] # Das dritte Element ist NICHT enthalten! array([ 1., 4.5, 6.3]) >>> a[3:] # Das dritte Element IST enthalten! array([ 4.1, 0.3, 9.4, 7.1]) >>> a[1:-2] # Indizierung bis vorvorletztem Element array([ 4.5, 6.3, 4.1, 0.3]) >>> a[::2] # Indizierung mit Schrittweite 2 array([ 1., 6.3, 0.3, 7.1]) >>> a[1:5:2] array([ 4.5, 4.1]) >>> a[::-1] # Reihenfolge umdrehen array([ 7.1, 9.4, 0.3, 4.1, 6.3, 4.5, 1. ])
Zweidimensionale Arrays >>> a = np.array([[4.2, 8.1, 7.9], [2.3, -7.6, 0.3]]) >>> a array([[ 4.2, 8.1, 7.9], [ 2.3, -7.6, 0.3]]) >>> a.shape (2, 3) >>> a[:,2] array([ 7.9, 0.3]) >>> a[1,1:] array([-7.6, 0.3]) >>> a.t # transponieren array([[ 4.2, 2.3], [ 8.1, -7.6], [ 7.9, 0.3]])
Verändern von Elementen in Arrays >>> a = np.array([[4.2, 8.1, 7.9], [2.3, -7.6, 0.3]]) >>> a[0,2] = 1 >>> a array([[ 4.2, 8.1, 1. ], [ 2.3, -7.6, 0.3]]) >>> a[:,1] = [8, 9] >>> a array([[ 4.2, 8., 1. ], [ 2.3, 9., 0.3]]) >>> a[-1,:] = 3 >>> a array([[ 4.2, 8., 1. ], [ 3., 3., 3. ]])
Rechnen mit Arrays Elementweises Rechnen in Numpy deutlich schneller als mit eigenen Schleifen (wie in C, Fortran,... üblich). Quellcode sollte also vektorisiert sein. >>> a = np.array([[4, 8, 7], [2., -7, 0]]) >>> a + 3 array([[ 7., 11., 10.], [ 5., -4., 3.]]) >>> np.sin(a) array([[-0.7568025, 0.98935825, 0.6569866 ], [ 0.90929743, -0.6569866, 0. ]]) >>> b = np.array([[1, 4, 3], [5, 2, 4.1]]) >>> a * b # Achtung: elementweise Multiplikation! array([[ 4., 32., 21.], [ 10., -14., 0.]])
Spezielle Funktionen zum erzeugen von Arrays >>> a = np.zeros((4,3)) # Array mit Nullen >>> a = np.ones((4,3)) # Array mit Nullen >>> np.arange(3, 9, 2) # Werte mit gleichem Abstand array([3, 5, 7]) >>> np.diag([1, 2, 3]) # Diagonalmatrix array([[1, 0, 0], [0, 2, 0], [0, 0, 3]])
Lineare Algebra mit numpy Lineare Algebra Routinen befinden sich in np.linalg >>> a = np.array([[2, 1], [3, 4]]) >>> b = np.array([[4, 8, 7], [2., -7, 0]]) >>> np.dot(a,b) # Matrixmultiplikation array([[ 10., 9., 14.], [ 20., -4., 21.]]) U, S, V = np.linalg.svd(a) # Singulärwertzerlegung w, v = np.linalg.eig(a) # Eigenwertberechnung http://docs.scipy.org/doc/numpy/reference/routines. linalg.html
Kopieren und Referenzieren von Arrays >>> a = np.array([4.2, 8.1, 7.9, 2.3, -7.6, 0.3]) >>> b = a # Referenz auf gleichen Speicherbereich von a >>> a[1:4] = 4 >>> a array([ 4.2, 4., 4., 4., -7.6, 0.3]) >>> b array([ 4.2, 4., 4., 4., -7.6, 0.3]) >>> b = np.copy(a) # Kopie von a >>> a[-2:] = 3 >>> a array([ 4.2, 4., 4., 4., 3., 3. ]) >>> b array([ 4.2, 4., 4., 4., -7.6, 0.3])
Matplotlib ist quasi Standard zum graphischen darstellen von Daten Beherrscht 2D und 3D Plots, Histogramme und vieles mehr Gallery: http://www.matplotlib.org/gallery.html
2D Matplotlib - einzelner Plot (1) import matplotlib.pyplot as plt import numpy as np x = np.arange(0,10,0.01) y = np.sin(x) plt.plot(x,y) 1.0 0.5 0.0 0.5 plt.show() 1.0 0 2 4 6 8 10
2D Matplotlib - einzelner Plot (2) import matplotlib.pyplot as plt import numpy as np x = np.arange(0,10,0.01) y = np.sin(x) fig = plt.figure() ax = fig.add_subplot(1,1,1) ax.plot(x,y) ax.set_xlabel( $x$ ) ax.set_ylabel( y ) plt.savefig( sinusx2.pdf ) y 1.0 0.5 0.0 0.5 1.0 0 2 4 6 8 10 x
2D Matplotlib - Histogramm import matplotlib.pyplot as plt import numpy as np x = np.random.randn(10000) fig = plt.figure() ax = fig.add_subplot(1, 1, 1) ax.hist(x, 20) ax.set_xlabel( $x$ ) plt.savefig( histogramm.pdf ) 1600 1400 1200 1000 800 600 400 200 0 4 3 2 1 0 1 2 3 4 x
2D Matplotlib - subplots import matplotlib.pyplot as plt import numpy as np x = np.arange(0,10,0.01) y = np.sin(x) z = np.cos(x) fig = plt.figure() ax = fig.add_subplot(2,1,1) ax.plot(x,y) ax.set_ylabel( y ) ax = fig.add_subplot(2,1,2) ax.plot(x,z, --r ) ax.set_ylabel( z ) ax.set_xlabel( x ) plt.savefig( sincosx.pdf ) y z 1.0 0.5 0.0 0.5 1.0 0 2 4 6 8 10 1.0 0.5 0.0 0.5 1.0 0 2 4 x 6 8 10
3D Matplotlib from mpl_toolkits.mplot3d \ import Axes3D import matplotlib.pyplot as plt import numpy as np x = np.arange(0,50,0.1) y = np.sin(x) z = np.cos(x) fig = plt.figure() ax = fig.add_subplot(1,1,1, projection= 3d ) ax.plot(x,y,z,. ) ax.set_xlabel( x ) ax.set_ylabel( y ) ax.set_zlabel( z ) plt.savefig( sincosx3d.pdf ) 0 10 20 x 30 40 1.0 0.5 0.0 z 0.5 1.0 1.0 y 0.5 0.00.5 50 1.0
Enthält weitere numerische Funktionen Basiert auf Numpy Beispiele: Integrationsmethoden scipy.integrate Minimierung von Funktionen scipy.optimize Nächste Nachbarn Suche: scipy.spatial
DGL Integration Numerische Integratoren für gewöhnliche Differenzialgleichungen (DGLen) befinden sich im Paket scipy Gutes und genaues Integrationsverfahren ist Runge-Kutta45 ( dopri5 in scipy)
Beispiel: Lorenz63 model Definition der Modell Gleichungen def lorenz63(t,x): x1 = x[0] x2 = x[1] x3 = x[2] sigma = 10 rho = 28 beta = 8./3 F = np.zeros(3) F[0] = sigma*(x2-x1) F[1] = x1*(rho-x3)-x2 F[2] = x1*x2-beta*x3 return F
Beispiel: Lorenz63 model Integration der Modell Gleichungen from scipy.integrate import ode N = 2000 dt = 0.01 x = np.zeros((n,3)) t = np.arange(0,2000*dt,dt) x[0,:] = [-4.70, 0.10, 29.10] r = ode(lorenz63).set_integrator( dopri5 ) r.set_initial_value(x[0,:], 0) i = 0 while r.successful() and r.t < (N-1)*dt: r.integrate(r.t+dt) i = i + 1 x[i,:] = r.y
Attraktor des Lorenz63 Modells x3 10 15 20 25 30 35 40 45 5 20 15 10 5 x1 0 5 10 15 20 0 x2 102030 30 2010
Dictionaries Dictionary: Datenstruktur, um Key-Value-Paare zu speichern Erzeugung >>> d1 = {} # Erzeugt ein leeres Dictionary >>> # Dictionary mit einigen Key-Value-Paaren: >>> d2 = {"key1": "value1", "key2": 728,... "key3": [2, 3, 8]} Zugriff >>> d2["key1"] value1 >>> d2["key2"] = 17 Mehr zu Dictionaries: https://docs.python.org/2/tutorial/ datastructures.html#dictionaries
Serialisierung: Das Pickle-Modul... stellt Funktionen bereit, um (fast) beliebige komplexe Datenstrukturen auf der Festplatte zu speichern und von dort wiederherzustellen. Erzeugung >>> import pickle >>> pickle.dump(d2, open("pickled_d2.dat", "wb")) >>> new_d = pickle.load(open("pickled_d2.dat", "rb")) >>> print(new_d) { key3 : [2, 3, 8], key2 : 17, key1 : value1 }
Weiteres Dokumentationsmaterial Offizielles Python-Tutorial: https://docs.python.org/3/tutorial/index.html Stackoverflow: http://stackoverflow.com/questions/tagged/python Offizielles Numpy-Tutorial: http://wiki.scipy.org/tentative_numpy_tutorial -Cookbook: http://wiki.scipy.org/cookbook Offizielles SciPy-Tutorial: http://docs.scipy.org/doc/scipy/reference/ tutorial/index.html