Python pour le calcul scientifique Exercices Pierre Navaro - IRMAR ENSAI le 25 mars 2016 1 1.1 Programmation Python Une fonction pour calculer la norme >>> def norme(a,b): """ calcul de la norme """ ... return sqrt(a*a+b*b) >>> from math import * >>> norme(3,4) 5.0 >>> help(norme) Help on function norme in module __main__: norme(a, b) calcul de la norme (END) Récrire cette fonction avec en entrée une liste python de taille quelconque. 1.2 Ecrire et lire un fichier avec le module pickle >>> import pickle >>> l=["camembert","coulommiers","brie"] >>> f=open("fromages","w") >>> pickle.dump(l,f) >>> f.close() >>> f=open("fromages","r") >>> lr=pickle.load(f) >>> f.close() >>> print lr ['camembert', 'coulommiers', 'brie'] Ajouter "munster" et "neuchatel" à la liste, trier la liste dans l’ordre alphabétique et modifier le fichier "fromages". 1.3 Programmation Objet Créer une classe Python nommée Vecteur, que l’on utilisera pour décrire un vecteur plan. Ces paramètres sont x et y . Implémenter l’addition __add__, la soustraction 1 __sub__, la multiplication __mul__ par un vecteur ou un scalaire et la représentation __repr__. Ne pas oublier le constructeur __init__. Attention votre multiplication n’est pas commutative par défaut, il faut implémenter la méthode __rmul__. Créer une classe nommée Particule, dont les paramètres sont les vecteurs position et vitesse. Implémenter une méthode "deplace(dt)" pour calculer la nouvelle position d’une particule après un pas de temps dt. Créer ensuite une classe Ion dérivant de Particule avec un paramètre supplémentaire "masse". >>> xp = Vecteur(3,4) >>> vp = Vecteur(1,2) >>> p = Particule(xp,vp) >>> p position (3 , 4); vitesse (1 , 2) >>> p.deplace(1.) >>> p position (4.0 , 6.0); vitesse (1 , 2) >>> i = Ion(xp,vp,1.) >>> i.deplace(-1.) >>> i position (2.0 , 2.0); vitesse (1 , 2) >>> i.energie 2.5 2 Numpy 2.1 Création d’un tableau numpy Créer les tableaux suivants : [100 101 102 103 104 105 106 107 108 109] avec numpy.arange [-2. -1.8 -1.6 -1.4 -1.2 -1. -0.8 -0.6 -0.4 -0.2 0. 0.2 0.4 0.6 0.8 1. 1.2 1.4 1.6 1.8] avec numpy.linspace [[ 0.001 0.00129155 0.0016681 0.00215443 0.00278256 0.003593810.00464159 0.00599484 0.00774264 0.01] avec numpy.logspace [[ [ [ [ [ [ [ 0. 0. 0. 0. 0. 0. 0. 0. -1. -1. -1.] 0. 0. -1. 1.] 0. 0. 0. -1.] 0. 0. 0. 0.] 0. 0. 0. 0.] 0. 0. 0. 0.] 0. 0. 0. 0.]] 2 avec numpy.tri, numpy.zeros, numpy.transpose [[ 0. 1. 2. 3. [-1. 0. 1. 2. [-1. -1. 0. 1. [-1. -1. -1. 0. [-1. -1. -1. -1. 4.] 3.] 2.] 1.] 0.]] avec numpy.ones, numpy.diag 2.2 Copie et référence Exécuter les scripts python suivants, quel est le problème ? comment le résoudre ? a = np.ones((4,3)) b = a.transpose() b[0,1] = 10 print a Script : https://perso.univ-rennes1.fr/pierre.navaro/python/numpy/ex6.py a = np.arange(12) b = a.reshape((4,3)) b[0,1] = 10 print a Script : https://perso.univ-rennes1.fr/pierre.navaro/python/numpy/ex7.py 2.3 Lire des données issues de fichiers texte Utiliser la fonction numpy.loadtxt pour charger les données contenues dans le fichier https://perso.univ-rennes1.fr/pierre.navaro/python/numpy/data8.txt. Script pour créer le fichier : https://perso.univ-rennes1.fr/pierre.navaro/python/numpy/ex8_gen.py Les donnees contenues dans : https://perso.univ-rennes1.fr/pierre.navaro/python/numpy/data9.txt ont été écrites de la manière suivante : La première ligne renseigne le nombre d’éléments pour chaque direction et la deuxième ligne contient les données. Ecrire le programme Python pour lire ces données et initialiser avec elles un tableau numpy 2D. Script pour créer le fichier de données : https://perso.univ-rennes1.fr/pierre.navaro/python/numpy/ex9_gen.py 2.4 Calculer une intégrale Evaluer l’intégrale suivante : Z ∞ 2 e−v dv I= −∞ — Analytiquement avec sympy. 3 >>> from sympy import * >>> v = symbols('v') >>> integrate(exp(-v*v),(v,-oo,oo)) pi**(1/2) — numériquement avec v ∈ [−10; 10] avec la méthode des trapèzes, écrire le code et tester avec N=20. — comparer avec le résultat de la fonction numpy.trapz 3 Matplotlib 3.1 Mode interactif Utiliser ipython -pylab pour exécuter les lignes suivantes : x = linspace(0,4*pi,1000) y = sin(x) + 0.2*sin(10*x) plot(x,y) xlabel(’x’) clf() plot(x,y,label=’hello’) xlabel(’$x$’) legend() title(’\LaTeX fonts like $\mu$ are available’) tics=arange(0,4*pi+0.1, step=pi/2) xticks(tics) tics_label=[’$0$’, ’$\pi/2$’] xticks(tics,tics_label) 3.2 Mode non interactif Reproduire le même graphique en insérant les lignes précédentes dans un fichier. Il existe une variable In dans ipython qui contient toutes les commandes déjà tapées dans Ipython. La commande "%hist -n" peut vous être utile également. 3.3 Tracer des données lues dans un fichier Récupérer les données des fichiers : https://perso.univ-rennes1.fr/pierre.navaro/python/matplotlib/foo_data.txt https://perso.univ-rennes1.fr/pierre.navaro/python/matplotlib/foo_axis.txt et tracer les données "data" en fonction des données "axis". 3.4 Tracer des données au format hdf5 Lire les données contenues dans le fichier https://perso.univ-rennes1.fr/pierre.navaro/python/matplotlib/foo.h5 Tracer "data" en fonction de "axis". 4 import h5py f = h5py.File(’foo.h5’, ’r’) x = f[’axis’].value y = f[’data’].value f.close() Script pour créer le fichier de données : https://perso.univ-rennes1.fr/pierre.navaro/python/matplotlib/ex5_gendata. py 3.5 Tracé 2D d’un champ scalaire Tracer les données nommées "data2d" lue dans le fichier : https://perso.univ-rennes1.fr/pierre.navaro/python/matplotlib/bar.h5 avec matplotlib.pyplot.contour et matplotlib.pyplot.imshow. Les coordonnées x et y sont nommées "x_axis" et "y_axis" dans les données hdf5. Script pour créer le fichier de données : https://perso.univ-rennes1.fr/pierre.navaro/python/matplotlib/ex7_gendata. py 4 Scipy 4.1 Interpolation Prenez le fichier https://perso.univ-rennes1.fr/pierre.navaro/python/scipy/ ex1.py. Comparer les courbes d’interpolations Krogh et Barycentric avec l’interpolation cubique. Utiliser les deux séries de points (x, y1 ) et (x, y2 ) calculées par les lignes python suivantes : a=sp.random.rand(10)-0.5 x=sp.linspace(-1,1,num=10) y1=(x-1.)*(x-0.5)*(x+0.5) y2=(x-1.)*(x-0.5)*(x+0.5)+a 4.2 Laplacien en deux dimensions On considère l’équation du laplacien : ∆u(x, y) = f (x, y), Afin que le problème soit bien posé, on spécifie des conditions aux limites sur le bord du domaine de type Dirichlet (u(x, y, t) connue sur le bord). Discrétisation spatiale : on utilise des différences finies d’ordre 2 et un maillage composé de Nx × Ny rectangles de taille ∆x × ∆y . ui,j−1 − 2uij + uij+1 ui−1,j − 2uij + ui+1j + = fij 2 ∆x ∆y 2 5 4.3 Assemblage de la matrice Construire la matrice creuse associée à cette discrétisation en tenant compte des conditions aux bords. Voici un exemple avec la fonction contenue dans : https://perso.univ-rennes1.fr/pierre.navaro/python/scipy/ComputeMatrix.py Assemblez la matrice en utilisant scipy.sparse.spdiags. 4.4 Résolution du problème Fichier https://perso.univ-rennes1.fr/pierre.navaro/python/scipy/laplacian2d. py Pour résoudre le système linéaire, nous avons utiliser la fonction numpy pour matrice pleine. Changer le programme pour utiliser l’algorithme du Gradient Conjugué. On pourra également utiliser un préconditionneur. 5 Python+Fortran 5.1 Exemple simple d’utilisation de f2py Soit le fichier fortran suivant pour le calcul de la norme : https://perso.univ-rennes1.fr/pierre.navaro/python/f2py/norme.f90 Création du module Python : $wget https://perso.univ-rennes1.fr/pierre.navaro/python/f2py/norme.f90 $f2py -m fortranmod -c norme.f90 Tester dans python : >>> >>> 4.0 >>> 5.0 >>> 5.2 import fortranmod fortranmod.norme(4.) fortranmod.norme([4,3]) print fortranmod.norme.__doc__ Modification d’un fichier signature : dgemm Nous allons créer la fonction f2py de DGEMM : la subroutine lapack permettant de calculer le produit de deux matrices. Générer votre fichier signature dgemm.pyf : $wget https://perso.univ-rennes1.fr/pierre.navaro/python/f2py/dgemm.f $f2py -h dgemm.pyf dgemm.f -m mylapack Modifier ce fichier pour que cette fonction soit utilisable dans Python avec les lignes suivantes. $f2py -c dgemm.pyf -llapack 6 >>> import numpy >>> import mylapack >>> a = numpy.array([[7,8],[3,4],[1,2]]) >>> b = numpy.array([[1,2,3],[4,5,6]]) >>> c = mylapack.dgemm(a,b) >>> print c [[ 39. 54. 69.] [ 19. 26. 33.] [ 9. 12. 15.]] 5.3 Création d’une fonction F2PY pour le solveur de Poisson Utilisons le programme Fortran suivant qui va permettre de calculer le laplacien. https://perso.univ-rennes1.fr/pierre.navaro/python/f2py/lap2d_fortran.f90 Compilation du module f2py $ wget https://perso.univ-rennes1.fr/pierre.navaro/python/f2py/lap2d_fortran.f90 $ f2py -c lap2d_fortran.f90 -m fortranmod On l’utilise pour résoudre l’équation de Poisson à l’aide du script python : Fichier :https://perso.univ-rennes1.fr/pierre.navaro/python/f2py/laplacian2d_ 1.py $ wget https://perso.univ-rennes1.fr/pierre.navaro/python/f2py/laplacian2d_1.py $ python laplacian2d_1.py 5.4 Module fortran A partir du fichier fortran précèdent, créer un module f90 nommé lap2d avec comme variables globales dx et dy . Modifier le fichier python pour utiliser ce module. 7 6 Interface avec le langage C 6.1 Fonction exponentielle exp(x) = lim n→+∞ 1+ x n n Créer la fonction python exp(x,n) dans un fichier exp_py.py. def exp(x,n): """Fonction exponentielle""" Pour tester les performances ajouter les lignes suivantes à votre fichier : if __name__ == '__main__': from timeit import Timer t = Timer("exp(1.,50)", "from __main__ import exp") print "1000000 calls : %s " % (t.timeit()) Puis taper : $ python exp_py.py 6.2 Fonction C Créer votre propre fonction C ou utiliser https://perso.univ-rennes1.fr/pierre.navaro/python/swig/exp_c.c 6.3 C-TYPES Compiler votre librairie dynamique : gcc -fPIC -shared -o exp_c.so -O3 exp_c.c Tester le package ctypes permettant d’appeler cette fonction avec le code suivant from timeit import Timer print "Ctypes..." setup_code = """ from ctypes import CDLL,c_double,c_int expDLL=CDLL('./exp_c.so') expDLL.exp_c.restype = c_double """ t = Timer("expDLL.exp_c(c_double(1),c_int(50))",setup_code) print "1,000,000 calls : %s " % t.timeit() Vous pouvez remarquer les fonctions de conversion C->Python 6.4 SWIG Créer le fichier d’interface SWIG et générer le module python exp_swig. 8 swig -python exp_c.i gcc ‘python2.7-config --cflags‘ -fPIC \ -shared -O3 -o _exp_swig.so exp_c_wrap.c exp_c.c On peut également générer le module en utilisant l’utilitaire scons, éditer un fichier nommé SConstruct et insérer les lignes suivantes : import distutils.sysconfig env = Environment(SWIGFLAGS=['-python'], CPPPATH=[distutils.sysconfig.get_python_inc()], SHLIBPREFIX="") env.SharedLibrary('_exp_swig.so', ['exp_c.c', 'exp_c.i']) Puis taper : $ scons Tester votre appel de fonction dans un fichier python. print "SWIG..." t = Timer("exp_swig.exp_c(1.,50)", "import exp_swig") print "1,000,000 calls : %s " % t.timeit() 6.5 Cython Transformer la fonction Python en Cython dans le fichier exp_cython.pyx. Compiler votre module avec le fichier setup.py suivant : from Cython.Distutils import build_ext from numpy.distutils.core import Extension, setup module_cython = Extension('exp_cython',['exp_cython.pyx']) setup( name='Exponentielle', version = '0.1', author = "Pierre Navaro", description = """ Cython example of exponential function """, ext_modules = [module_cython], cmdclass = {'build_ext':build_ext} ) avec la commande : $ python setup.py build_ext --inplace Puis tester votre module Cython avec : print "Cython..." t = Timer("exp_cython.exp_c(1.,50)", "import exp_cython") print "1,000,000 calls : %s " % t.timeit() 9 6.6 f2py Créer le module exp_f2py dans le fichier d’interface f2py nommé exp_f2py.pyf. Créer la fonction C modifiée dans le fichier exp_f2py.c : void exp_c(double x, int terms, double * res) { double power; double fact; int i; res[0] = 0.; power = 1.; fact = 1.; for (i=0;i<terms;i++) { res[0] += power/fact; power *= x; fact *= i+1; } } Compiler votre module avec la commande : $ f2py -m exp_f2py -c exp_f2py.c exp_f2py.pyf --opt=-O3 Tester votre module : print "F2PY..." t = Timer("exp_f2py.exp_c(1.,50)", "import exp_f2py") print "1,000,000 calls : %s " % t.timeit() 6.7 Performances Comparer tous les résultats avec la fonction exponentielle du package math. 7 Corrigés 1.1 https://perso.univ-rennes1.fr/pierre.navaro/python/exercices/ex1.py 1.2 https://perso.univ-rennes1.fr/pierre.navaro/python/exercices/ex2.py 1.3 https://perso.univ-rennes1.fr/pierre.navaro/python/exercices/ex3.py 2.1 https://perso.univ-rennes1.fr/pierre.navaro/python/numpy/ex1.py 2.1 https://perso.univ-rennes1.fr/pierre.navaro/python/numpy/ex2.py 2.1 https://perso.univ-rennes1.fr/pierre.navaro/python/numpy/ex3.py 2.1 https://perso.univ-rennes1.fr/pierre.navaro/python/numpy/ex4.py 2.1 https://perso.univ-rennes1.fr/pierre.navaro/python/numpy/ex5.py 2.2 https://perso.univ-rennes1.fr/pierre.navaro/python/numpy/ex6_correct. py 2.2 https://perso.univ-rennes1.fr/pierre.navaro/python/numpy/ex7_correct. py 2.3 https://perso.univ-rennes1.fr/pierre.navaro/python/numpy/ex8.py 10 2.3 https://perso.univ-rennes1.fr/pierre.navaro/python/numpy/ex9.py 2.4 https://perso.univ-rennes1.fr/pierre.navaro/python/numpy/ex10.py 3.2 https://perso.univ-rennes1.fr/pierre.navaro/python/matplotlib/ex2.py 3.3 https://perso.univ-rennes1.fr/pierre.navaro/python/matplotlib/ex3.py 3.4 https://perso.univ-rennes1.fr/pierre.navaro/python/matplotlib/ex5.py 3.5 https://perso.univ-rennes1.fr/pierre.navaro/python/matplotlib/ex7.py 4.1 https://perso.univ-rennes1.fr/pierre.navaro/python/scipy/ex2.py 4.3 https://perso.univ-rennes1.fr/pierre.navaro/python/scipy/mypackage/assemble. py 1 4.4 https://perso.univ-rennes1.fr/pierre.navaro/python/scipy/mypackage/laplace2d. py 5.2 https://perso.univ-rennes1.fr/pierre.navaro/python/f2py/dgemm.pyf 5.4 https://perso.univ-rennes1.fr/pierre.navaro/python/f2py/lap2d_module. f90 5.4 https://perso.univ-rennes1.fr/pierre.navaro/python/f2py/laplacian2d_ 2.py 6.1 https://perso.univ-rennes1.fr/pierre.navaro/python/swig/exp_py.py 6.4 https://perso.univ-rennes1.fr/pierre.navaro/python/swig/exp_c.i 6.5 https://perso.univ-rennes1.fr/pierre.navaro/python/swig/exp_cython.pyx 6.5 https://perso.univ-rennes1.fr/pierre.navaro/python/swig/setup.py 6.6 https://perso.univ-rennes1.fr/pierre.navaro/python/swig/exp_f2py.pyf 6.7 https://perso.univ-rennes1.fr/pierre.navaro/python/swig/exponentielle. py 1. Sylvain Faure Cours Scipy, ANGD Python en Calcul scientifique 11