{ "cells": [ { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Python version 3.8.5 (default, Jul 28 2020, 12:59:40) \n", "[GCC 9.3.0]\n" ] } ], "source": [ "import sys #only needed to determine Python version number\n", "print('Python version ' + sys.version) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# M62 TP 3 - Implémentation de schémas\n", "\n", "Compléter le notebook en ajoutant l'implémentation des schémas indiqués et en vérifiant l'impléméntation sur l'exemple donné." ] }, { "cell_type": "markdown", "metadata": { "toc": true }, "source": [ "

Table of Contents

\n", "
" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "WyG-bTRQE3f6", "slideshow": { "slide_type": "slide" } }, "source": [ "## Implémentation des schémas" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "On écrit les schémas numériques : \n", "+ les $N+1$ nœuds d'intégration $[t_0,t_1,\\dots,t_{N}]$ sont contenus dans le vecteur `tt`\n", "+ les $N+1$ valeurs $[u_0,u_1,\\dots,u_{N}]$ pour chaque méthode sont contenues dans le vecteur `uu`.\n", "\n", "Comme `len(tt)` $=N+1$ et `range(1,N)` produit 1,2,3,N-1 : \n", "- pour un schéma à un pas $u_{n+1}=F(u_n)$ on initialise $u_0$ et on calcule $u_{n+1}$ pour $n$ de $0$ jusqu'à $N-1$, autrement dit `n in range(N)` soit encore `n in range(len(tt)-1)`\n", "- pour un schéma à deux pas $u_{n+1}=F(u_n,u_{n-1})$ on initialise $u_0$ et $u_1$ et on calcule $u_{n+1}$ pour $n$ de $1$ jusqu'à $N-1$, autrement dit `n in range(1,N)` soit encore `n in range(1,len(tt)-1)`\n", "- pour un schéma à trois pas $u_{n+1}=F(u_n,u_{n-1},u_{n-2})$ on initialise $u_0$, $u_1$ et $u_2$ et on calcule $u_{n+1}$ pour $n$ de $2$ jusqu'à $N-1$, autrement dit `n in range(2,N)` soit encore `n in range(2,len(tt)-1)`\n", "- etc.\n" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "IPython.notebook.set_autosave_interval(300000)" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Autosaving every 300 seconds\n" ] } ], "source": [ "%reset -f\n", "%matplotlib inline\n", "%autosave 300\n", "\n", "from matplotlib.pylab import *\n", "from scipy.optimize import fsolve" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "### Schémas explicites" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "y69SGZjfIDo9", "slideshow": { "slide_type": "slide" } }, "source": [ "#### Schéma d'Euler progressif = de Adam-Bashforth à 1 pas\n", "$$\n", "\\begin{cases}\n", "u_0=y_0,\\\\\n", "u_{n+1}=u_n+h\\varphi(t_n,u_n)& n=0,1,2,\\dots N-1\n", "\\end{cases}\n", "$$" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "colab": { "autoexec": { "startup": false, "wait_interval": 0 } }, "colab_type": "code", "id": "_Bgo6mNyIQgu", "scrolled": true, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "def euler_progressif(phi,tt,y0):\n", " h = tt[1]-tt[0]\n", " uu = [y0]\n", " for i in range(len(tt)-1):\n", " uu.append(uu[i]+h*phi(tt[i],uu[i]))\n", " return uu" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "bJ2pbhejIQM2", "slideshow": { "slide_type": "slide" } }, "source": [ "#### Schéma de Adam-Bashforth à 2 pas\n", "$$\n", "\\begin{cases}\n", "u_0=y_0,\\\\\n", "u_{1}=u_0+h\\varphi(t_0,u_0),\\\\\n", "u_{n+1}=u_n+\\frac{h}{2}\\Bigl(3\\varphi(t_n,u_n)-\\varphi(t_{n-1},u_{n-1})\\Bigr)& n=1,2,3,4,5,\\dots N-1\n", "\\end{cases}\n", "$$" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "nI8swyc6RxIR", "slideshow": { "slide_type": "slide" } }, "source": [ "#### Schéma de Adam-Bashforth à 3 pas\n", "$$\n", "\\begin{cases}\n", "u_0=y_0,\\\\\n", "u_{1}=u_0+h\\varphi(t_0,u_0),\\\\\n", "u_{2}=u_1+\\frac{h}{2}\\Bigl(3\\varphi(t_1,u_1)-\\varphi(t_{0},u_{0})\\Bigr),\\\\\n", "u_{n+1}=u_n+\\frac{h}{12}\\Bigl(23\\varphi(t_n,u_n)-16\\varphi(t_{n-1},u_{n-1})+5\\varphi(t_{n-2},u_{n-2})\\Bigr)& n=2,3,4,5,\\dots N-1\n", "\\end{cases}\n", "$$" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "awcWzBp7SXvQ", "slideshow": { "slide_type": "slide" } }, "source": [ "#### Schéma de Adam-Bashforth à 4 pas\n", "$$\n", "\\begin{cases}\n", "u_0=y_0,\\\\\n", "u_{1}=u_0+h\\varphi(t_0,u_0),\\\\\n", "u_{2}=u_1+\\frac{h}{2}\\Bigl(3\\varphi(t_1,u_1)-\\varphi(t_{0},u_{0})\\Bigr),\\\\\n", "u_{3}=u_2+\\frac{h}{12}\\Bigl(23\\varphi(t_2,u_2)-16\\varphi(t_{1},u_{1})+5\\varphi(t_{0},u_{0})\\Bigr),\\\\\n", "u_{n+1}=u_n+\\frac{h}{24}\\Bigl(55\\varphi(t_n,u_n)-59\\varphi(t_{n-1},u_{n-1})+37\\varphi(t_{n-2},u_{n-2})-9\\varphi(t_{n-3},u_{n-3})\\Bigr)& n=3,4,5,\\dots N-1\n", "\\end{cases}\n", "$$" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "heLmvMe_S0y6", "slideshow": { "slide_type": "slide" } }, "source": [ "#### Schéma de Adam-Bashforth à 5 pas\n", "$$\n", "\\begin{cases}\n", "u_0=y_0,\\\\\n", "u_{1}=u_0+h\\varphi(t_0,u_0),\\\\\n", "u_{2}=u_1+\\frac{h}{2}\\Bigl(3\\varphi(t_1,u_1)-\\varphi(t_{0},u_{0})\\Bigr),\\\\\n", "u_{3}=u_2+\\frac{h}{12}\\Bigl(23\\varphi(t_2,u_2)-16\\varphi(t_{1},u_{1})+5\\varphi(t_{0},u_{0})\\Bigr),\\\\\n", "u_{4}=u_3+\\frac{h}{24}\\Bigl(55\\varphi(t_3,u_3)-59\\varphi(t_{2},u_{2})+37\\varphi(t_{1},u_{1})-9\\varphi(t_{0},u_{0})\\Bigr),\\\\\n", "u_{n+1}=u_n+\\frac{h}{720}\\Bigl(1901\\varphi(t_n,u_n)-2774\\varphi(t_{n-1},u_{n-1})+2616\\varphi(t_{n-2},u_{n-2})-1274\\varphi(t_{n-3},u_{n-3})+251\\varphi(t_{n-4},u_{n-4})\\Bigr)& n=4,5,\\dots N-1\n", "\\end{cases}\n", "$$" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "OldEmxFfTJfq", "slideshow": { "slide_type": "slide" } }, "source": [ "#### Schéma de Nylström à 2 pas\n", "$$\n", "\\begin{cases}\n", "u_0=y_0,\\\\\n", "u_{1}=u_0+h\\varphi(t_0,u_0),\\\\\n", "u_{n+1}=u_{n-1}+2h\\varphi(t_{n},u_{n})& n=1,2,3,4,5,\\dots N-1\n", "\\end{cases}\n", "$$" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "dADQEhyYTVQz", "slideshow": { "slide_type": "slide" } }, "source": [ "#### Schéma de Nylström à 3 pas\n", "$$\n", "\\begin{cases}\n", "u_0=y_0,\\\\\n", "u_{1}=u_0+h\\varphi(t_0,u_0),\\\\\n", "u_{2}=u_{0}+2h\\varphi(t_{1},u_{1}),\\\\\n", "u_{n+1}=u_{n-1}+\\frac{h}{3}\\Bigl(7\\varphi(t_{n},u_{n})-2\\varphi(t_{n-1},u_{n-1})+\\varphi(t_{n-2},u_{n-2})\\Bigr)& n=2,3,4,5,\\dots N-1\n", "\\end{cases}\n", "$$" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "LsgdqQnfTf66", "slideshow": { "slide_type": "slide" } }, "source": [ "#### Schéma de Nylström à 4 pas\n", "$$\n", "\\begin{cases}\n", "u_0=y_0,\\\\\n", "u_{1}=u_0+h\\varphi(t_0,u_0),\\\\\n", "u_{2}=u_{0}+2h\\varphi(t_{1},u_{1}),\\\\\n", "u_{3}=u_{1}+\\frac{h}{3}\\Bigl(7\\varphi(t_{2},u_{2})-2\\varphi(t_{1},u_{1})+\\varphi(t_{0},u_{0})\\Bigr),\\\\\n", "u_{n+1}=u_{n-1}+\\frac{h}{3}\\Bigl(8\\varphi(t_{n},u_{n})-5\\varphi(t_{n-1},u_{n-1})+4\\varphi(t_{n-2},u_{n-2})-\\varphi(t_{n-3},u_{n-3})\\Bigr)& n=3,4,5,\\dots N-1\n", "\\end{cases}\n", "$$" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "_50Xo95mT9tC", "slideshow": { "slide_type": "slide" } }, "source": [ "#### Schéma de Runke-Kutta RK4\n", "$$\\begin{cases}\n", "u_0=y(t_0)=y_0,\\\\\n", "\\tilde u_{n+1/2}=u_n+\\frac{h}{2} \\varphi(t_{n},u_{n}),\\\\\n", "\\check u_{n+1/2}=u_n+\\frac{h}{2} \\varphi(t_{n}+\\frac{h}{2},\\tilde u_{n+1/2}),\\\\\n", "\\hat u_{n+1}=u_n+h\\varphi(t_{n+1},\\check u_{n+1/2}),\\\\\n", "u_{n+1}=u_n+\\frac{h}{6} \\left(\\varphi(t_{n},u_{n})+2\\varphi(t_{n}+\\frac{h}{2},\\tilde u_{n+1/2} )+2\\varphi(t_{n}+\\frac{h}{2}, \\check u_{n+1/2})+\\varphi(t_{n+1},\\hat u_{n+1} \\right)& n=0,1,2,\\dots N-1\n", "\\end{cases}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Schémas implicites" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "y69SGZjfIDo9", "slideshow": { "slide_type": "slide" } }, "source": [ "#### Schéma d'Euler régressif\n", "$$\n", "\\begin{cases}\n", "u_0=y_0,\\\\\n", "u_{n+1}=u_n+h\\varphi(t_{n+1},u_{n+1})& n=0,1,2,\\dots N-1\n", "\\end{cases}\n", "$$\n", "avec $u_{n+1}$ zéro de la fonction $$x\\mapsto -x+u_n+h\\varphi(t_{n+1},x)$$" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "def euler_regressif(phi,tt,y0):\n", " h = tt[1]-tt[0]\n", " uu = [y0]\n", " for i in range(len(tt)-1):\n", " temp = fsolve(lambda x: -x+uu[i]+h*phi(tt[i+1],x), uu[i])\n", " uu.append(temp)\n", " return uu" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "23PyYGzhQwuo", "slideshow": { "slide_type": "slide" } }, "source": [ "#### Schéma de Crank-Nicolson (AM-1)\n", "$$\n", "\\begin{cases}\n", "u_0=y_0,\\\\\n", "u_{n+1}=u_n+\\frac{h}{2}\\Bigl(\\varphi(t_n,u_n)+\\varphi(t_{n+1},u_{n+1})\\Bigr)& n=0,1,2,\\dots N-1\n", "\\end{cases}\n", "$$\n", "avec $u_{n+1}$zéro de la fonction $$x\\mapsto -x+u_n+\\frac{h}{2}(\\varphi(t_n,u_n)+\\varphi(t_{n+1},x))$$" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "23PyYGzhQwuo", "slideshow": { "slide_type": "slide" } }, "source": [ "#### Schéma de AM-2\n", "$$\n", "\\begin{cases}\n", "u_0=y_0,\\\\\n", "u_1=u_0+\\frac{h}{2}\\Bigl(\\varphi(t_1,u_1)+\\varphi(t_{0},u_{0})\\Bigr),\\\\\n", "u_{n+1}=u_n+\\frac{h}{12}\\Bigl(5\\varphi(t_{n+1},u_{n+1})+8\\varphi(t_n,u_n)-\\varphi(t_{n-1},u_{n-1})\\Bigr)& n=1,2,\\dots N-1\n", "\\end{cases}\n", "$$" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "23PyYGzhQwuo", "slideshow": { "slide_type": "slide" } }, "source": [ "#### Schéma de AM-3\n", "$$\n", "\\begin{cases}\n", "u_0=y_0,\\\\\n", "u_1=u_0+\\frac{h}{2}\\Bigl(\\varphi(t_1,u_1)+\\varphi(t_{0},u_{0})\\Bigr),\\\\\n", "u_{2}=u_n+\\frac{h}{12}\\Bigl(5\\varphi(t_{2},u_{2})+8\\varphi(t_1,u_1)-\\varphi(t_{0},u_{0})\\Bigr),\\\\\n", "u_{n+1}=u_n+\\frac{h}{24}\\Bigl(9\\varphi(t_{n+1},u_{n+1})+19\\varphi(t_n,u_n)-5\\varphi(t_{n-1},u_{n-1})+\\varphi(t_{n-2},u_{n-2})\\Bigr)& n=2,3,\\dots N-1\n", "\\end{cases}\n", "$$" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "23PyYGzhQwuo", "slideshow": { "slide_type": "slide" } }, "source": [ "#### Schéma de AM-4\n", "$$\n", "\\begin{cases}\n", "u_0=y_0,\\\\\n", "u_1=u_0+\\frac{h}{2}\\Bigl(\\varphi(t_1,u_1)+\\varphi(t_{0},u_{0})\\Bigr),\\\\\n", "u_{2}=u_1+\\frac{h}{12}\\Bigl(5\\varphi(t_{2},u_{2})+8\\varphi(t_1,u_1)-\\varphi(t_{0},u_{0})\\Bigr),\\\\\n", "u_{3}=u_2+\\frac{h}{24}\\Bigl(9\\varphi(t_{3},u_{3})+19\\varphi(t_2,u_2)-5\\varphi(t_{1},u_{1})+\\varphi(t_{0},u_{0})\\Bigr),\\\\\n", "u_{n+1}=u_n+\\frac{h}{720}\\Bigl(251\\varphi(t_{n+1},u_{n+1})+646\\varphi(t_n,u_n)-264\\varphi(t_{n-1},u_{n-1})+106\\varphi(t_{n-2},u_{n-2})-19\\varphi(t_{n-3},u_{n-3})\\Bigr)& n=3,4,\\dots N-1\n", "\\end{cases}\n", "$$" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "23PyYGzhQwuo", "slideshow": { "slide_type": "slide" } }, "source": [ "#### Schéma BDF2\n", "$$\n", "\\begin{cases}\n", "u_0=y_0,\\\\\n", "u_1=u_0+h\\varphi(t_1,u_1),\\\\\n", "u_{n+1}=\\frac{4}{3}u_n-\\frac{1}{3}u_{n-1}+\\frac{2}{3}\\varphi(t_{n+1},u_{n+1})& n=1,2,\\dots N-1\n", "\\end{cases}\n", "$$" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "23PyYGzhQwuo", "slideshow": { "slide_type": "slide" } }, "source": [ "#### Schéma BDF3\n", "$$\n", "\\begin{cases}\n", "u_0=y_0,\\\\\n", "u_1=u_0+h\\varphi(t_1,u_1),\\\\\n", "u_{2}=\\frac{4}{3}u_1-\\frac{1}{3}u_{0}+\\frac{2}{3}h\\varphi(t_{2},u_{2}),\\\\\n", "u_{n+1}=\\frac{18}{11}u_n-\\frac{9}{11}u_{n-1}+\\frac{2}{11}u_{n-2}+\\frac{6}{11}h\\varphi(t_{n+1},u_{n+1})& n=2,3,\\dots N-1\n", "\\end{cases}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Schémas predicteur-correcteur" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "p4f0txAsIwNG", "slideshow": { "slide_type": "slide" } }, "source": [ "#### Schéma d'Euler modifié\n", "$$\n", "\\begin{cases}\n", "u_0=y_0,\\\\\n", "\\tilde u = u_n+\\frac{h}{2}\\varphi(t_n,u_n),\\\\\n", "u_{n+1}=u_n+h\\varphi\\left(t_n+\\frac{h}{2},\\tilde u\\right)& n=0,1,2,\\dots N-1\n", "\\end{cases}\n", "$$" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "23PyYGzhQwuo", "slideshow": { "slide_type": "slide" } }, "source": [ "#### Schéma de Heun\n", "$$\n", "\\begin{cases}\n", "u_0=y_0,\\\\\n", "\\tilde u = u_n+h\\varphi(t_n,u_n)\\\\\n", "u_{n+1}=u_n+\\frac{h}{2}\\Bigl(\\varphi(t_n,u_n)+\\varphi(t_{n+1},\\tilde u)\\Bigr)& n=0,1,2,\\dots N-1\n", "\\end{cases}\n", "$$" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "23PyYGzhQwuo", "slideshow": { "slide_type": "slide" } }, "source": [ "#### Schéma AM-2 AB-1\n", "$$\n", "\\begin{cases}\n", "u_0=y_0,\\\\\n", "u_1=u_0+h\\varphi(t_0,u_0),\\\\\n", "\\tilde u=u_n+h\\varphi(t_n,u_n),\\\\\n", "u_{n+1}=u_n+\\frac{h}{12}\\Bigl(5\\varphi(t_{n+1},\\tilde u)+8\\varphi(t_n,u_n)-\\varphi(t_{n-1},u_{n-1})\\Bigr)& n=1,2,\\dots N-1\n", "\\end{cases}\n", "$$" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "23PyYGzhQwuo", "slideshow": { "slide_type": "slide" } }, "source": [ "#### Schéma AM-3 AB-2\n", "$$\n", "\\begin{cases}\n", "u_0=y_0,\\\\\n", "u_1=u_0+h\\varphi(t_0,u_0),\\\\\n", "u_2=u_0+\\frac{h}{2}(3\\varphi(t_1,u_1)-\\varphi(t_{0},u_{0})),\\\\\n", "\\tilde u=u_n+\\frac{h}{2}(3\\varphi(t_n,u_n)-\\varphi(t_{n-1},u_{n-1})),\\\\\n", "u_{n+1}=u_n+\\frac{h}{24}\\Bigl(9\\varphi(t_{n+1},\\tilde u)+19\\varphi(t_n,u_n)-5\\varphi(t_{n-1},u_{n-1})+\\varphi(t_{n-2},u_{n-2})\\Bigr)& n=2,3,\\dots N-1\n", "\\end{cases}\n", "$$" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Comparaison sur un exemple\n", "Considérons le problème de Cauchy\n", ">trouver la fonction $y \\colon I\\subset \\mathbb{R} \\to \\mathbb{R}$ définie sur l'intervalle $I=[0,1]$ telle que\n", "$$\\begin{cases}\n", "y'(t) = \\sin(t)+y(t), &\\forall t \\in I=[0,1],\\\\\n", "y(0) = 0\n", "\\end{cases}$$\n", "\n", "1. Calculer la solution exacte en utilisant le module `sympy`.\n", "1. Calculer la solution approchée obtenue avec la méthode d'Euler explicite avec $h=1/N$ et $N=8$ (pour bien visualiser les erreurs);\n", "2. même exercice pour les méthodes *multipas explicites* (AB$_2$, AB$_3$, AB$_4$, AB$_5$, N$_2$, N$_3$, N$_4$);\n", "3. même exercice pour les méthodes *multipas implicites* (Euler implicite, Crank-Nicolson, AM$_1$, AM$_2$, AM$_3$, AM$_4$)\n", "2. même exercice pour les méthodes *multipas explicites Runge-Kutta* (RK$_4$);\n", "3. même exercice pour les méthodes *multipas implicites BDF* (BDF$_2$, BDF$_3$)\n", "4. même exercice pour les méthodes *predictor-corrector* (Euler modifié, Heun, AM$_2$-AB$_1$, AM$_3$-AB$_2$).\n", "5. Pour chaque méthode, afficher solution exacte *vs* solution approchée ainsi que le maximum de la valeur absolue de l'erreur." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "colab": { "collapsed_sections": [], "default_view": {}, "name": "EdoExplicites.ipynb", "provenance": [], "version": "0.3.2", "views": {} }, "kernelspec": { "display_name": "Python 3", "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.8.5" }, "latex_envs": { "LaTeX_envs_menu_present": true, "autoclose": true, "autocomplete": true, "bibliofile": "biblio.bib", "cite_by": "apalike", "current_citInitial": 1, "eqLabelWithNumbers": true, "eqNumInitial": 1, "hotkeys": { "equation": "Ctrl-E", "itemize": "Ctrl-I" }, "labels_anchors": false, "latex_user_defs": false, "report_style_numbering": true, "user_envs_cfg": true }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": true, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": true, "toc_position": {}, "toc_section_display": true, "toc_window_display": false } }, "nbformat": 4, "nbformat_minor": 4 }