{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "bd894fee",
   "metadata": {},
   "source": [
    "# Séance 6\n",
    "\n",
    "\n",
    "```{admonition} Objectifs\n",
    "* Nombres réels\n",
    "* Notion de précision\n",
    "* Affichage graphique\n",
    "```\n",
    "\n",
    "Nous avons vu à la séance précédente que Sagemath permettait de manipuler des valeurs exactes. Voyons maintenant comment il traite le **calcul numérique**, c'est-à-dire le **calcul sur des valeurs approchées**.\n",
    "\n",
    "\n",
    "## Représentation des nombres réels\n",
    "\n",
    "### Création et classe parente\n",
    "\n",
    "Les **nombres réels** sont stockés avec une certaine **précision** dans Sagemath. On parle généralement de **nombre flottant**, ou **à virgule flottante**, pour se distinguer du concept mathématique. On verra que l'on pourra **choisir la précision** de stockage d'un nombre flottant, mais elle est de $53$ bits significatifs par défaut (c'est le standard de précision double `binary64`, voir détails [ici](https://en.wikipedia.org/wiki/Double-precision_floating-point_format)).\n",
    "\n",
    "Voyons un exemple."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5537ed2d",
   "metadata": {},
   "outputs": [],
   "source": [
    "a = 1.2\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "379dc27b",
   "metadata": {},
   "source": [
    "Dans Sagemath, un nombre flottant réel a pour classe parente `RealField`, le corps des nombres réels. Ce corps est disponible dans la variable `RR`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "29713cdb",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(parent(a))\n",
    "print(RR)\n",
    "print(RealField())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "47d3c090",
   "metadata": {},
   "source": [
    "On observe que l'affichage ci-dessus donne la précision de stockage. Voyons ce que cela signifie concrètement.\n",
    "\n",
    "\n",
    "\n",
    "### Virgule flottante\n",
    "\n",
    "\n",
    "Pour comprendre cela, il faut s'intéresser à la manière de **représenter un nombre en binaire**. Si le nombre est un **entier** $n \\in \\mathbb{N}$, on a la représentation binaire classique :\n",
    "\n",
    "$$\n",
    "    n = \\sum_{i=0}^k n_i 2^i    \\quad \\longrightarrow \\quad n \\;\\text{ s'écrit } \\quad (n_k n_{n-1} \\dots n_1 n_0)_2\n",
    "$$\n",
    "\n",
    "Par exemple, l'entier $13$ s'écrit $(1101)_2$ en base $2$. C'est ainsi qu'il est stocké sur une machine.\n",
    "\n",
    "\n",
    "Pour les nombres qui ne sont pas des entiers, on peut choisir une **représentation par virgule flottante**. Commençons par un exemple. On considère le nombre décimal $x = 18,\\!625$. On peut l'écrire sous la forme d'un produit entre un entier et la puissance d'une base. En base $10$ par exemple :\n",
    "\n",
    "$$\n",
    "x = 18625 \\times 10^{-3}\\,.\n",
    "$$\n",
    "\n",
    "L'entier $n = 18625$ est appelé **la mantisse** (ici en base $10$), et l'entier $-3$ **l'exposant**. Pour **stocker un nombre décimal quelconque**, il suffit donc de stocker sa mantisse et son exposant comme deux entiers, comme par exemple ci-dessus en base $10$.\n",
    "\n",
    "Sur un ordinateur, en revanche, **on représente des objets avec des bits**. Il faut reprendre la méthode ci-dessus en base $2$. On obtient\n",
    "\n",
    "$$\n",
    "x = 149 \\times 2^{-3}\\,.\n",
    "$$\n",
    "\n",
    "avec en binaire\n",
    "\n",
    "$$\n",
    "\\text{mantisse}(x) = (10010101)_2   \\quad \\text{ et} \\quad \\text{ exposant}(x) = (-11)_2\n",
    "$$\n",
    "\n",
    "\n",
    "Pour stocker le nombre $x$ de manière exacte, il faut donc au moins $8$ bits pour la mantisse, $2$ bits pour l'exposant, et un bit pour le signe ($+$ ou $-$).\n",
    "\n",
    "\n",
    "\n",
    "Voyons un autre exemple : $y = \\frac{1}{3} = 0,\\!3333333\\dots$ La décomposition en binaire de $y$ est infinie :\n",
    "\n",
    "$$\n",
    "    y = 1/4 + 1/16 + 1/64 + \\dots + 1/2^{2N} + \\dots = \\sum_{i=1}^{+\\infty} 2^{-2i}\n",
    "$$\n",
    "\n",
    "Sur une machine finie, on **ne peut donc pas** stocker $y$ de manière exacte en binaire, avec la méthode précédente. Il faut donc **tronquer** la représentation de la mantisse à un certain ordre, qu'on appelle la **précision**. \n",
    "\n",
    "En pratique, si l'on se donne une précision $N$ pour stocker $y$, on va donc chercher le **nombre le plus proche de $y$ dont la mantisse peut être représentée sur $N$ bits**. Par exemple, dans le cas de $y = 1/3$ et $N=1$, ce nombre le plus proche est $y' = 1/4$. On stocke donc $y = 1/3$ avec $1$ bit de précision comme $y' = 1 \\times 2^{-2}$ :\n",
    "\n",
    "$$\n",
    "\\text{mantisse}(y') = (1)_2   \\quad \\text{ et} \\quad \\text{ exposant}(x) = (-10)_2\n",
    "$$\n",
    "\n",
    "\n",
    "Voyons cela sur un exemple :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4e45e25b",
   "metadata": {},
   "outputs": [],
   "source": [
    "y = 1/3\n",
    "R1 = RealField(1)     # pour créer le corps des nombres flottants à 1 bit de précision\n",
    "print(R1)\n",
    "\n",
    "y1 = R1(y)            # pour créer le nombre flottant 1/3 avec 1 bit de précision\n",
    "print(y1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8c547001",
   "metadata": {},
   "source": [
    "Pour conclure, stocker des nombres avec $53$ bits de précision signifie donc qu'on les approche par des nombres à virgule flottante dont les mantisses sont de taille maximale $53$ bits.\n",
    "\n",
    "\n",
    "\n",
    "### Erreurs de précision\n",
    "\n",
    "Pour certains nombres, la mantisse est donc tronquée à certain ordre. Cela signifie que les approximations effectuées peuvent mener à des **erreurs de calculs**.\n",
    "\n",
    "\n",
    "**Exemple :** ajouter une valeur très petite mais non-nulle ne modifie rien."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b520de67",
   "metadata": {},
   "outputs": [],
   "source": [
    "y = 1.0\n",
    "z = y + 2**(-53)     # il faudrait 54 bits de précision pour stocker z de manière exacte\n",
    "print(y)\n",
    "print(z)\n",
    "print(y == z)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3c4d17a5",
   "metadata": {},
   "source": [
    "Pour obtenir davantage de précision, on doit **donner explicitement** la précision de stockage désirée, et effectuer une **coercion**. Comme on l'a vu à la précédente séance, cette coercion se fait par la classe parente :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "def18ce4",
   "metadata": {},
   "outputs": [],
   "source": [
    "R54 = RealField(54)\n",
    "print(R54)\n",
    "\n",
    "y = R54(1)\n",
    "z = y + 2**(-53)\n",
    "print(y)\n",
    "print(z)\n",
    "print(y == z)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4b8e77dd",
   "metadata": {},
   "source": [
    "Remarquez que **l'affichage en base $10$** ne permet parfois pas de se rendre compte que deux nombres sont différents...\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "### Écriture décimale de constantes\n",
    "\n",
    "\n",
    "Pour obtenir l' **écriture décimale** de certaines constantes classiques ($\\pi$, $e$, etc...), on peut donc utiliser le formalisme ci-dessus.\n",
    "\n",
    "Si on souhaite obtenir le nombre $\\pi$ avec une précision d'au moins $100$ chiffres après la virgule, on peut donc choisir une représentation avec $101$ chiffres décimaux de précision, soit environ $336$ bits de précision."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "36889dee",
   "metadata": {},
   "outputs": [],
   "source": [
    "R336 = RealField(336)\n",
    "R336(pi)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7cac891e",
   "metadata": {},
   "source": [
    "### Nombres complexes flottants\n",
    "\n",
    "\n",
    "Dans Sagemath, on peut également manipuler des **nombres complexes** avec une précision arbitraire. Pour cela, la classe parente est `ComplexField()`, avec comme argument de construction optionnel la précision de stockage. Comme pour les réels, la précision par défaut est de $53$ bits, et cette classe est prédéfinie dans la variable `CC`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6f85dc0b",
   "metadata": {},
   "outputs": [],
   "source": [
    "x = 2.0 + I\n",
    "print(x)\n",
    "print(parent(x))\n",
    "print(x in CC)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dc1cb4f2",
   "metadata": {},
   "source": [
    "## Affichage graphique avec Sagemath : premiers pas\n",
    "\n",
    "### Afficher une série de points dans le plan\n",
    "\n",
    "Avec Sagemath, il y a plusieurs manières d'afficher une série de points dans le plan. Toutes prennent en entrée une **liste `L`** qui décrit les points du plan à affciher. Donc, à la différence  de python et `matplotlib.pyplot`, il **ne faut pas déclarer séparément la liste des abscisses et celle des ordonnées**.\n",
    "\n",
    "Les trois fonctions principales qui permettent d'afficher graphiquement une liste de points sont `scatter_plot`, `list_plot` et `points`. Dans ce cours, nous allons voir le fonctionnement de `points`.\n",
    "\n",
    "La syntaxe est la suivante : \n",
    "```\n",
    "points(L)\n",
    "```\n",
    "où `L` est une liste de \"points\" qui peuvent être décrits sous la forme de :\n",
    "1. liste $[x, y]$ composée de l'abscisse et de l'ordonnée\n",
    "2. couple $(x, y)$, c'est-à-dire *tuple* de taille 2, aussi composé d'une abscisse et d'une ordonnée\n",
    "3. nombre complexe $z$,\n",
    "4. vecteur de dimension 2 (on verra cette notion plus tard)\n",
    "5. ... (il y a encore d'autres possibilités) ...\n",
    "\n",
    "Voyons ce que cela donne sur un **premier exemple** : on souhait afficher les $100$ premiers termes de la suite de terme général $(-1)^n/(n+1)$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "81030cc6",
   "metadata": {},
   "outputs": [],
   "source": [
    "L = [ [n, (-1)**n * 1/(n+1)] for n in range(100) ]\n",
    "points(L).show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "149ff880",
   "metadata": {},
   "source": [
    "**Second exemple :** afficher des nombres complexes sur le cercle unité, espacés par $10$ degrés $= \\pi/36$ radians."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "219c5b9c",
   "metadata": {},
   "outputs": [],
   "source": [
    "C = [ exp(2*k*I*pi/36) for k in range(36) ]\n",
    "points(C).show(aspect_ratio=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a2c3703e",
   "metadata": {},
   "source": [
    "Il y a de nombreuses **options** pour améliorer l'affichage. En voici quelques unes (ce n'est pas à retenir).\n",
    "\n",
    "\n",
    "\n",
    "|   Option (exemple)   | Description |\n",
    "|:---:|:---:|\n",
    "| `xmin=2` et `xmax=5` | Précise que l'affichage doit commencer à $x=2$ (à gauche) et terminer à $x=5$ (à droite) |\n",
    "| `ymin=-1` et `ymax=7` | Précise que l'affichage doit commencer à $y=-1$ (en bas) et terminer à $y=7$ (en haut) |\n",
    "| `color=\"red\"` | Précise que les points doivent s'afficher en rouge |\n",
    "| `scale=\"loglog\"` | Produit un affichage en double échelle logarithmique |\n",
    "| `frame=True`| Rajoute une grille à l'affichage |\n",
    "| `axes=False`| Retire les axes de l'affichage |\n",
    "| `size=20`| Affiche les points avec un diamètre de $20$pts (unité graphique de base) |\n",
    "\n",
    "\n",
    "En général, ces options sont à ajouter comme arguments optionnels des fonctions `points`, `list_plot` et `scatter_plot`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2d8710a5",
   "metadata": {},
   "outputs": [],
   "source": [
    "P = points(L, color=\"red\", frame=True, size=20, axes=False, ymin=-0.2, ymax=0.2)\n",
    "P.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8560b188",
   "metadata": {},
   "source": [
    "### Afficher une fonction prédéfinie\n",
    "\n",
    "\n",
    "Sagemath permet également d'afficher une fonction réelle prédéfinie **sans avoir à précalculer de liste de points**. Pour cela, on peut très simplement utiliser la fonction `plot`.\n",
    "\n",
    "Exemple avec la fonction exponentielle :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "57d9f305",
   "metadata": {},
   "outputs": [],
   "source": [
    "plot(exp)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7bfa5e5e",
   "metadata": {},
   "source": [
    "On peut bien entendu recadrer l'affichage :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "46acbb5f",
   "metadata": {},
   "outputs": [],
   "source": [
    "plot(exp, xmin=-3, xmax=3)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f66f8da2",
   "metadata": {},
   "source": [
    "Pour afficher une fonction d'une variable réelle \"$t$\", il faut prédéfinir cette variable abstraite. Pour cela, on utilise le mot-clef `var` d'une manière peu conforme : l'instruction `var('t')` va déclarer dans Sagemath une variable `t` abstraite."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dde2b261",
   "metadata": {},
   "outputs": [],
   "source": [
    "var('t')\n",
    "print(t)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3e98cb03",
   "metadata": {},
   "source": [
    "```{note}\n",
    "La variable `x` est déjà prédéfinie pas défaut dans Sagemath, mais il n'est pas coûteux de réécrire `var('x')`, notamment dans le cas où vous auriez précédent utilisé le nom `x` pour d'autres types de calculs.\n",
    "```\n",
    "\n",
    "\n",
    "\n",
    "Cela nous permet de créer des fonctions que nous pourrons ensuite afficher. Par exemple :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "de4620d5",
   "metadata": {},
   "outputs": [],
   "source": [
    "f = t^2 - t\n",
    "plot(f, xmin=-2, xmax=2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7073cfe0",
   "metadata": {},
   "source": [
    "### Affichage multiple\n",
    "\n",
    "\n",
    "Enfin, commme avec `matplotlib`, il est possible de superposer des graphiques. La syntaxe est différente de `matplotlib`, mais elle est presque plus simple : il suffit **d'additionner** les graphiques avec l'opérateur `+`. Exemple :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "30052599",
   "metadata": {},
   "outputs": [],
   "source": [
    "L = [ (i/4, sin(i/4)) for i in range(40) ]\n",
    "plot(sin, xmin=0, xmax=10) + points(L, color='red', size=30)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Sagemath",
   "language": "python",
   "name": "sagemath"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
