summaryrefslogtreecommitdiff
path: root/PVCM/cama/en/ma62 Gradient conjugué -- Exercice.ipynb
diff options
context:
space:
mode:
Diffstat (limited to 'PVCM/cama/en/ma62 Gradient conjugué -- Exercice.ipynb')
-rw-r--r--PVCM/cama/en/ma62 Gradient conjugué -- Exercice.ipynb409
1 files changed, 409 insertions, 0 deletions
diff --git a/PVCM/cama/en/ma62 Gradient conjugué -- Exercice.ipynb b/PVCM/cama/en/ma62 Gradient conjugué -- Exercice.ipynb
new file mode 100644
index 0000000..0bca368
--- /dev/null
+++ b/PVCM/cama/en/ma62 Gradient conjugué -- Exercice.ipynb
@@ -0,0 +1,409 @@
+{
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.12"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4,
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Programmer le gradient conjugué\n",
+ "\n",
+ "A partir de ce [cours sur le gradient conjugué](http://perso.unifr.ch/ales.janka/numeroptim/07_conjgrad.pdf) programmez en Python + Numpy le gradient conjugué en exploitant les astuces mathématiques indiquées pour optimiser\n",
+ "votre code.\n",
+ "\n",
+ "* Effectuez des tests pour valider votre code. \n",
+ "* Comparez la vitesse de convergence à celle du gradient avec μ optimal. Tracez des courbes de convergence (cf la feuille qui en parle)\n",
+ "* Comparez les temps de calcul.\n",
+ "\n",
+ "\n",
+ "Note : Veuillez écrire des fonctions les plus propres possibles, en particulier qui n'utilisent pas des variables globales comme c'est le cas dans ma correction du gradient (ma33)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "import numpy.linalg as lin\n",
+ "import matplotlib.pylab as plt\n",
+ "\n",
+ "%matplotlib inline\n",
+ "%config InlineBackend.figure_format = 'retina'"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def make_system(N):\n",
+ " A = 1.0 * np.random.randint(-10, 10, size=(N,N))\n",
+ " A[np.diag_indices(N)] = 0.1 + np.abs(A).sum(axis=0) # diag dominante\n",
+ " A = A + A.T # symétrique\n",
+ " A = A / np.abs(A).sum(axis=0).mean()\n",
+ " b = 1.0 * np.random.randint(-10,10,size=(N))\n",
+ " return A, b\n",
+ "\n",
+ "A, b = make_system(2)\n",
+ "print(A, \"\\n\\n\", b)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def gradient_conjugué(A, x0, b, error=1E-9, convergence=False):\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# vérfions que ca marche\n",
+ "\n",
+ "# combien faut-il d'itérations\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def compute_error(N, method=gradient_conjugué):\n",
+ " A, b = make_system(N)\n",
+ " x = method(A, np.zeros(N), b)\n",
+ " err = A @ x - b\n",
+ " return np.sqrt(err @ err)\n",
+ "\n",
+ "compute_error(10)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Comparons avec le gradient simple"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def gradient(A, x0, b, e = 1E-9, convergence=False, max_iterations=1000):\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# vérifions que ca marche\n",
+ "\n",
+ "compute_error(10, method=gradient)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Perfs"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# comparons les performances\n",
+ "\n",
+ "seed = 123\n",
+ "np.random.seed(seed)\n",
+ "\n",
+ "%timeit compute_error(1000, method=gradient)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "seed = 123\n",
+ "np.random.seed(seed)\n",
+ "\n",
+ "%timeit compute_error(1000, method=gradient_conjugué)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Nombre d'iteration dans les 2 cas\n",
+ "\n",
+ "On teste avec N=1000 puis N=10000."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "N = 1000\n",
+ "A,b = make_system(N)\n",
+ "x0 = np.zeros(N)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Pour le gradient simple"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "err = gradient(A, x0, b, convergence=True)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "#plot"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Pour le gradient conjugué"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "err = gradient_conjugué(A, x0, b, convergence=True)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "#plot"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Un cas réel\n",
+ "\n",
+ "Logiquement vous devriez être décu aussi on va tester avec un problème réel qui correspond à cet exemple\n",
+ "de l'[équation de Poisson](https://doc.freefem.org/tutorials/poisson.html). Le système matriciel de ce problème est téléchargeable \n",
+ "[ici](https://www.lrde.epita.fr/~ricou/cama/data/Ab.npz). Une fois le fichier sauvé, pour récupérer\n",
+ "A et b faites :\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "npz = np.load('/tmp/Ab.npz',allow_pickle=True)\n",
+ "A = npz['A']\n",
+ "b = npz['b']"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "* Faites une étude rapide de A, indiquez quel pourcentage des valeurs de A est différent de 0. Afficher l'image de la matrice avec `plt.imshow(A)` (faire une grande image pour voir quelque chose).\n",
+ "* Refaites la comparaison entre les deux méthodes avec ce système matriciel.\n",
+ "* Regardez la documentation de `lin.solve` (en particulier les options) et comparer `lin.solve` à vos deux algorithmes."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Comparaison gradient simple et conjugué"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%time err = gradient_conjugué(A, np.zeros(len(A)), b, convergence=True)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "#plot"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# le gradient simple\n",
+ "\n",
+ "%time err = gradient(A, np.zeros(len(A)), b, convergence=True, max_iterations=10000)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# plot"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "On voit la supériorité du gradient conjugué tant en nombre d'itérations (175 contre 7800) qu'en temps de calcul (environ 40 fois plus rapide)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Comparaison avec `lin.solve` de Scipy\n",
+ "\n",
+ "Le solveur de Scipy a des option que celui de Numpy n'a pas."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# regarder la doc pour avoir les options optimales\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# calculer le résidu\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "On note aussi `lin.solve` est plus rapide et sa solution est nettement meilleure... `lin.solve` utilise une méthode directe ici. Cela est dû au fait que Scipy utilise la bibliothèque Lapack (qui est imbatable)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Le gradient conjugué de Scipy (avec Lapack)\n",
+ "\n",
+ "Le gradient conjugué à tout son sens pour les matrices creuses aussi il est dans la partie \"sparse\" de Scipy.\n",
+ "On a vu que notre matrice à plus de 99 % de valeur nulles ce qui en fait bien une matrice creuse. Aussi je\n",
+ "la charge dans le format COO (téléchargez __[Acoo.npz](https://www.lrde.epita.fr/~ricou/cama/data/Acoo.npz)__) qui ne stocke que les valeurs non nulles et"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import scipy.sparse as sparse\n",
+ "from scipy.sparse.linalg import cg\n",
+ "\n",
+ "Ac = sparse.load_npz('/tmp/Acoo.npz')\n",
+ "%time x = cg(Ac, b)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "On gagne un ordre de grandeur."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ]
+} \ No newline at end of file