{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Les expressions de la bibliothèque Sympy\n",
    "\n",
    "# Syntaxe, arbres syntaxiques, dérivation symbolique\n",
    "\n",
    "Marc Lorenzi\n",
    "\n",
    "8 mai 2019"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sympy import *\n",
    "import matplotlib.pyplot as plt\n",
    "init_printing()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.rcParams['figure.figsize'] = (16, 6)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "SymPy est une bibliothèque Python qui permet de faire du calcul symbolique, c'est à dire du calcul __exact__. Dans ce notebook nous allons parler des objets sans doute les plus importants définis par cette bibliothèque : les __expressions__.\n",
    "\n",
    "Sympy définit un grand nombre de classes et de fonctions, nous n'aborderons dans ce notebook qu'une toute petite partie. Pour tout savoir, rien ne vaut la documentation officielle qui contient entre autres un excellent tutoriel. Vous la trouverez à l'adresse [https://docs.sympy.org](https://docs.sympy.org)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. Notion d'expression"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.1 Symboles"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Pour un mathématicien, une \"expression\" est quelque chose du genre $x+y$, ou $3x+2y+z\\sin^2z$, où $x,y,z$ sont des \"variables\". Avant de définir précisément ce qu'est une expression, parlons des __symboles__."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = Symbol('x')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La ligne ci-dessus vient de définir une nouvelle __variable Python__ dont le nom est $x$ (il s'agit du $x$ à gauche du signe $=$). Après affectation, cette variable contient un objet du type \"symbole\", et le nom de ce symbole est $x$ (le 'x' entre parenthèses à droite du signe $=$). Un petit test ?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Évidemment ceci n'est pas très spectaculaire. Tentons autre chose :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = Symbol('y')\n",
    "x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Eh oui, maintenant la variable $x$ contient un symbole, mais le nom de ce symbole est $y$. Ce n'est pas très judicieux mais c'est faisable. Faisons-nous pour la suite un petit stock de symboles. La fonction `symbols` (sans majuscule) permet de définir plusieurs symboles à la fois."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x, y, z, t = symbols('x y z t')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x, y, z, t"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Histoire d'insister sur le fait que je ne parlerai que d'une petite partie des possibilités de SymPy, voici les __méthodes__ que possède l'objet $x$. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(dir(x))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Effectivement :-) ... Par exemple :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x.is_Symbol"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__A retenir__ : Un __symbole__ est créé par la fonction `Symbol`. On le stocke dans une __variable Python__. L'usage est (la plupart du temps) de donner le même nom à la variable et au symbole. On peut aussi créer plusieurs symboles à la fois avec la fonction `symbols`.\n",
    "\n",
    "__Remarque__ : lorsqu'on crée un symbole, on peut y adjoindre des hypothèses sur ce symbole. Par exemple, c'est un entier, un réel positif, etc. Je n'en parlerai pas ici, consultez la documentation."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.2 Premières expressions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Voici notre première expression autre qu'un symbole."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "expr = x ** 2 + sqrt(y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Affichons `expr`. Il y a au moins trois façons de voir cette expression.\n",
    "\n",
    "D'abord la vision \"Python\". On utilise la fonction `print`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(expr)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Deuxième façon, taper juste `expr`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "expr"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cette fois-ci l'affichage obtenu est plus conforme à nos habitudes mathématiques. Il existe une troisième méthode qui nous sera fort utile : la fonction `srepr` renvoie une chaîne de caractères."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(srepr(expr))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Qu'est-ce que c'est que ça ? \n",
    "\n",
    "Nous voyons là quelque chose qui se rapproche beaucoup de la structure interne des expressions SymPy. `Pow`, `Add`, `Integer`, etc, sont des fonctions définies par `sympy` (en fait des constructeurs de classe). Et vous pouvez si le coeur vous en dit les utiliser pour créér des expressions : "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Mul(Add(x, y), Add(z, t))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cela revient au même que"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "(x + y) * (z + t)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Remarquez que ce qui est affiché ne correspond pas __exactement__ à ce que nous avons entré. Par défaut, `sympy` considère que l'addition et la multiplication sont commutatives. On ne peut pas être certain de la façon dont `sympy` va traiter l'expression que l'on tape. Sympy effectue également certaines simplifications \"triviales\" :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x + x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "y * 1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.3 Les nombres, les constantes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Certaines expressions SymPy sont juste des nombres. Si vous voulez créer une expression constante, égale à 2 par exemple, il vous faut entrer `Integer(2)`. Pourquoi ? Tentons une expérience."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "2 / 3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Mais enfin, SymPy est une bibliothèque de calcul symbolique, il devrait donc nous renvoyer la fraction $\\frac 2 3$ ? Certes, mais où voyez-vous SymPy dans la cellule ci-dessus ? SymPy n'a pas pris le contrôle de notre machine, ce que nous avons entré c'est une ligne de __Python__. Et la réponse est une __réponse de Python__. En revanche :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Integer(2) / 3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cette fois-ci, nous avons calculé le quotient de __l'entier Sympy__ 2 par l'entier 3, et SymPy nous renvoie la valeur \"espérée\" : les objets de la classe Integer savent comment se diviser par 3 :-)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Le rationnel $\\frac 2 3$ renvoyé ci-dessus est donc une expression. Quel genre d'expression, précisément ? "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "srepr(Integer(2) / 3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notez le constructeur `Rational`, vous pouvez évidemment vous en servir pour manipuler des rationnels."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Rational(1, 3) + Rational(5, 7)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.4 Classes, objets, champs, méthodes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Python est un langage __orienté objets__. Dans un tel langage on définit des __classes__ qui sont, sans entrer dans les détails, des \"types de données\". Définir une classe, c'est définir le comportement des __objets__ de la classe, que l'on appelle aussi les __instances__ de la classe. \n",
    "\n",
    "- Classe = type de données\n",
    "- instance de la classe = objet ayant le type en question\n",
    "\n",
    "Un objet possède\n",
    "\n",
    "- des __champs__, qui sont des valeurs associées à l'objet.\n",
    "- des __méthodes__, qui sont des fonctions qui permettent à l'objet de se modifier, d'interagir avec d'autres objets, etc.\n",
    "\n",
    "Par exemple, si vous entrez"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "w = 3 + 1j * 2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "vous fabriquez un objet de la classe `complex` et vous l'affectez à la variable $w$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "w.__class__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cet objet possède deux champs :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(w.real, w.imag)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "L'objet $z$ possède un certain nombre de méthodes. Lesquelles ? La fonction malnommée `dir` nous le dit :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(dir(w))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Par exemple,"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "w.__abs__()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Lorsque vous tapez `abs(w)`, Python appelle la méthode `__abs__` de l'objet $w$.\n",
    "\n",
    "Encore un petit exemple, celui des listes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "s = [1, 2, 3]\n",
    "print(dir(s))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Vous avez sûrement reconnu ci-dessus un certain nombre des méthodes de la liste $s$, comme `append`, `reverse` ou `sort` ...\n",
    "\n",
    "Comme ce notebook n'est pas un cours de programmation objet, je n'en dirai pas plus. Je ferai tout au plus quelques remarques par-ci par-là."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.5 Soustraction et division"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La soustraction des expressions c'est sûrement `Sub` ? Et la division c'est `Div` ? __Pas du tout__. La soustraction et la division cela n'existe pas, ouvrez votre cours de maths."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "srepr(x - y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Pour `sympy`, $x-y$ c'est $x + (-1)\\times y$, ce en quoi il n'a pas tort."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "srepr(x / y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Et $\\frac x y$ c'est $x\\times y^{-1}$. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Nous maîtrisons donc les 5 opérations de l'arithmétique :\n",
    "\n",
    "- $+$ c'est la fonction `Add`.\n",
    "- $\\times$  c'est la fonction `Mul`.\n",
    "- L'exponentiation c'est la fonction `Pow`.\n",
    "- $-$ et $/$ sont transformées en sommes, produits et puissances."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.6 Des expressions plus compliquées"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Contrairement au dicton, quand on peut faire simple on peut faire compliqué :-). SymPy connaît les \"fonctions\" usuelles (remarquez les guillemets, on y revient plus loin), sinus, cosinus, exponentielle, etc. Et aussi des fonctions moins usuelles (pour un étudiant de prépa) comme la fonction $\\Gamma$, la fonction $\\zeta$ ou les fonctions hypergéométriques.\n",
    "\n",
    "Voici une expression \"compliquée\"."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "expr = (x + y ** 2) * sin(1 / z) + t * atan(t / 2)\n",
    "expr"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Quelle est sa représentation en tant qu'objet SymPy ? Si vous comprenez la réponse vous avez fait un grand pas."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "srepr(expr)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Voici une fonction qui renvoie des expressions compliquées."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def compliquee(n, x):\n",
    "    expr = x\n",
    "    for k in range(n):\n",
    "        expr = sqrt (1 + expr)\n",
    "    return expr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "compliquee(10, x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(compliquee(10, x))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(srepr(compliquee(10, x)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.7 Finalement, c'est quoi une expression ?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Les programmeurs de SymPy ont défini dans cette bibliothèque une __classe__ `Expr`, qui est la classe __ancêtre__ de toutes les expressions. Les __objets__ qui sont des __instances__ de la classe `Expr` __sont__ les expressions. SymPy définit en fait toute une __hiérarchie__ de classes, dont celles qui nous intéressent __descendent__ de la classe `Expr`. Par exemple, les classes `Mul`, `Add`, `Symbol`, `Integral`, etc. Tout objet de la classe `Add` est aussi par __héritage__ un objet de la classe `Expr`, et est donc une expression de plein droit. Et de même pour toutes les classes qui héritent de la classe `Expr`.\n",
    "\n",
    "Si nous voulons rester à un niveau un peu moins concret que l'implémentation réelle des expressions, nous pouvons dire qu'une expression est :\n",
    "\n",
    "- un Symbole\n",
    "\n",
    "ou\n",
    "\n",
    "- Un Entier\n",
    "\n",
    "ou\n",
    "\n",
    "- Un Rationnel\n",
    "\n",
    "ou\n",
    "\n",
    "- Add(expression, Expression, ..., Expression)\n",
    "\n",
    "ou\n",
    "\n",
    "- Mul(Expression, Expression, ..., Expression)\n",
    "\n",
    "ou\n",
    "\n",
    "- Pow(Expression, Expression)\n",
    "\n",
    "ou \n",
    "\n",
    "- Bien d'autres choses, qu'il est hors de question d'examiner ici de façon exhaustive."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Voici les __classes filles__ de la classe `Expr`, c'est à les classes qui sont ses héritières directes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(Expr.__subclasses__())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " Si vous voulez avoir une idée de __toutes__ les classes qui __héritent__ de la classe `Expr`, c'est à dire les sous-classes, les sous-sous-classes, etc., voici une fonction qui permet de les obtenir. Elle effectue un __parcours de graphe__ en appelant récursivement la méthode `__subclasses__`, que possède toute classe Python."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def heritieres(classe):\n",
    "    sous_classes = set()\n",
    "    s = [classe]\n",
    "    while s != []:\n",
    "        c = s.pop()\n",
    "        for enfant in c.__subclasses__():\n",
    "            if enfant not in sous_classes:\n",
    "                sous_classes.add(enfant)\n",
    "                s.append(enfant)\n",
    "    return sous_classes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(heritieres(Expr))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Surprise : nous voyons par exemple que `exp` est une classe ! Et nous qui pensions que c'était une fonction ! Lorsque vous tapez `exp(x)`, SymPy crée un objet de la classe `exp`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "expr = exp(x)\n",
    "print(expr.__class__)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Et voici les champs et méthodes de `expr`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(dir(expr))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__Exercice__ : Quelles sont les classes qui héritent de la classe `Symbol` ? De la classe `Integer` ?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Au cas où vous vous poseriez la question, voici une fonction qui renvoie les __ancêtres__ d'une classe. Toute classe possède un champ `__bases__` qui est la liste de ses classes parentes (Python autorisant __l'héritage multiple__, une classe peut avoir plusieurs mères :-))."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def ancetres(classe):\n",
    "    sur_classes = set()\n",
    "    s = [classe]\n",
    "    while s != []:\n",
    "        c = s.pop()\n",
    "        for parent in c.__bases__:\n",
    "            if parent not in sur_classes:\n",
    "                sur_classes.add(parent)\n",
    "                s.append(parent)\n",
    "    return sur_classes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Integer.__bases__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Rational.__bases__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Number.__bases__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Et caetera :-)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ancetres(Integer)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ancetres(exp)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ancetres(Symbol)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Remarquez que dans tous ces exemples, `Expr` est dans la liste des ancêtres.\n",
    "\n",
    "Si vous avez de bons yeux, vous avez également remarqué la classe `object`, qui est est dans toutes les listes. En fait, toutes les classes Python ont la classe `object` pour ancêtre. Sauf une : laquelle à votre avis ???"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ancetres(list), ancetres(int), ancetres(complex), ancetres(object)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. Analyse syntaxique des expressions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Chaque expression SymPy possède des __champs__ qui permettent d'analyser cette expression. Voici les deux plus importants pour nous."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.1 Les champs `func` et `args`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Chaque expression possède un champ `func` qui est le \"type\" de l'expression."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "expr = (x + y) * (z + 2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "expr.func"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "L'expression ci-dessus est en effet un produit de deux expressions plus simples. Lesquelles ? C'est là qu'intervient le champ `args`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "expr.args"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`expr.args` est le $n$-uplet des __sous-expressions__ de `expr`. La documentation `sympy` garantit que pour __TOUTE__ expression `expr` on a\n",
    "\n",
    "$$expr = expr.fun(*expr.args)$$\n",
    "\n",
    "Rappelons que si $f$ est une fonction Python et $s$ est, par exemple, le triplet $(x,y,z)$, alors $f(*s)=f(x,y,z)$. L'étoile est essentielle, car $f(s)=f((x,y,z))$, avec DEUX paires de parenthèses."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.2 Expressions atomiques"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Que se passe-t-il si notre expression est un symbole ou un nombre ?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x.func"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Logique. Et les arguments de $x$ ?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x.args"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Pas d'arguments. Normal, puisque $x$ n'est pas une fonction. Mais comment récupérer le fait que $x$ c'est 'x' ?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "repr(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "C'était facile. Ceci marche aussi sur les nombres."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "expr = Integer(17)\n",
    "expr.func, expr.args, srepr(expr)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.3 Hauteur d'une expression"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Comment mesurer la complexité d'une expression $e$ ? Une bonne indication de cette complexité est la __hauteur__ $h(e)$ de l'expression, que nous pouvons définir récursivement.\n",
    "\n",
    "- Si $e$ est un symbole ou nombre, $h(e)=1$.\n",
    "- Sinon, soient $e_0,\\ldots,e_{n-1}$ les arguments de $e$. On pose\n",
    "\n",
    "$$h(e) = 1 +\\max(h(e_0),\\ldots,h(e_{n-1}))$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def hauteur(expr):\n",
    "    gs = expr.args\n",
    "    if len(gs) == 0: return 1\n",
    "    else:\n",
    "        return 1 + max([hauteur(g) for g in gs])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Voici quelques exemples."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hauteur(y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hauteur(x ** 2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hauteur(x ** 2 + y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hauteur(cos(x) ** 2 + sin(x) ** 2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hauteur(compliquee(10, x))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Je vous laisse essayer d'autres exemples. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.4 Représentation arborescente des expressions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Soit $e$ une expression. Nous allons définir __l'arbre syntaxique__ de $e$, $T(e)$ ($T$ comme \"tree\") récursivement.\n",
    "\n",
    "- Si $e$ est une expression atomique comme un entier ou un symbole, $T(e)$ a une racine étiquetée `Integer` ou `Symbol`. Sous cette racine se trouve une feuille, étiquetée par la valeur de l'expression, du genre 32 ou 'x'.\n",
    "- Si $e=F(e_0,\\ldots,e_{n-1})$ est une expression composée, $T(e)$ a une racine étiquetée par $F$ ($F$ est ce que renvoie `e.func`). Sous la racine se trouvent $n$ fils, qui sont les arbres syntaxiques $T(e_0), T(e_1),\\ldots,T(e_{n-1})$. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La fonction `dessin_arbre` ci-dessous prend une expression en paramètre et dessine son arbre syntaxique. Je ne la détaillerai pas, libre à vous d'examiner son code. Je signale simplement l'utilité des deux paramètres optionnels.\n",
    "\n",
    "- `bornes` est un quadruplet qui contient les coordonnées minimales et maximales du rectangle dans lequel on dessine l'arbre.\n",
    "- $d$ est la distance entre deux niveaux de l'arbre. On l'évalue en calculant la hauteur de l'expression."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def dessin_arbre(expr, bornes=(-1, 1, -1, 1), d=None):\n",
    "    xmin, xmax, ymin, ymax = bornes\n",
    "    if d == None:\n",
    "        d = (ymax - ymin) / hauteur(expr)\n",
    "    plt.axis('off')\n",
    "    f = expr.func\n",
    "    gs = expr.args\n",
    "    xc = (xmin + xmax) / 2\n",
    "    plt.text(xc, ymax + 0.1 * d, f.__name__, fontsize=12, horizontalalignment='center')\n",
    "    n = len(gs)\n",
    "    if n == 0:\n",
    "        plt.plot([xc, xc], [ymax, ymax - d], 'b')\n",
    "        plt.text(xc, ymax - 1.2 * d, repr(expr), fontsize=12, horizontalalignment='center')\n",
    "    for k in range(n):\n",
    "        x1 = xmin + k * (xmax - xmin) / n\n",
    "        x2 = xmin + (k + 1) * (xmax - xmin) / n\n",
    "        plt.plot([xc, (x1 + x2) / 2], [ymax, ymax - d], 'b')\n",
    "        dessin_arbre(gs[k], (x1, x2, ymin, ymax - d), d)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dessin_arbre(cos(x) ** 2 - sin(x) ** 2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "expr = Integral(exp(-x **2), (x, 0, oo))\n",
    "expr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dessin_arbre(expr)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "expr = integrate(1 / (x ** 3 + 1), x)\n",
    "expr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dessin_arbre(expr)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Essayez avec d'autres expressions ..."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. Dérivation symbolique"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`sympy` permet évidemment d'effectuer des dérivations symboliques grâce à la fonction `diff` :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "diff(sqrt(1 + x ** 2), x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Nous allons dans cette section écrire notre propre fonction de dérivation. Appelons cette fonction `derivee`. Cette fonction prendra deux paramètres : une expression `f` et un symbole `x` et renverra la dérivée de $f$ par rapport à $x$."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.1 Les dérivées usuelles"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Avant toutes choses, ouvrons notre cours de maths et stockons dans un dictionnaire les dérivées des \"fonctions\" usuelles."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "derivees_usuelles = {sin: lambda t: cos(t), \n",
    "                     cos: lambda t: -sin(t), \n",
    "                     tan: lambda t: 1 / cos(t) ** 2, \n",
    "                     exp: lambda t: exp(t), \n",
    "                     log: lambda t: 1 / t, \n",
    "                     sqrt: lambda t: 1/(2 * sqrt(t)),\n",
    "                     asin: lambda t: 1 / sqrt(1 - t ** 2),\n",
    "                     acos: lambda t: -1 / sqrt(1 - t ** 2),\n",
    "                     atan: lambda t: 1 / (t ** 2 + 1),\n",
    "                     sinh: lambda t: cosh(t),\n",
    "                     cosh: lambda t: sinh(t),\n",
    "                     tanh: lambda t: 1 / cosh(t) ** 2,\n",
    "                     asinh: lambda t: 1 / sqrt(t ** 2 + 1),\n",
    "                     acosh: lambda t: 1 / sqrt(t ** 2 - 1),\n",
    "                     atanh: lambda t: 1 / (1 - t ** 2)}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Une clé du dictionnaire est un \"nom de fonction\" $f$. `derivees_usuelles[f]` est une fonction Python qui prend un paramètre $t$ et renvoie l'expression $f'(t)$. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.2 La fonction de dérivation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Comment dériver l'expression $f$ par rapport au symbole $x$ ? Il suffit de considérer un certain nombre de cas :\n",
    "\n",
    "- Si $f$ est le symbole $x$, on renvoie 1.\n",
    "- Si $f$ est un nombre ou un symbole autre que $x$, on renvoie 0.\n",
    "- Si $f$ est une somme, ou un produit, ou une puissance, on appelle une fonction adaptée (voir plus loin !).\n",
    "- Si $f$ est une \"fonction\" (une composée, en fait), on appelle aussi une fonction adaptée. Nous supposerons que $f$ est une fonction d'une seule variable. Sinon les choses deviennent un peu plus compliquées. Restons raisonnables ...\n",
    "\n",
    "Mais comment savoir dans quel cas on se trouve ? Rappelons-nous, les expressions de SymPy sont des objets qui sont des instances de classes. Ces objets contiennent des champs qui permettent de savoir de quel genre ils sont, ou ne sont pas. Le champ `is_Symbol` de l'expression $f$, par exemple, vaut `True` si $f$ est un symbole, et `False` sinon.\n",
    "\n",
    "Le code de la fonction `derivee` est maintenant évident."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def derivee(f, x): \n",
    "    if f.is_Symbol and repr(f) == repr(x): return Integer(1)\n",
    "    elif f.is_Number or f.is_NumberSymbol or f.is_Symbol: return Integer(0)\n",
    "    elif f.is_Add: return derivee_somme(f.args, x)\n",
    "    elif f.is_Mul: return derivee_produit(f.args, x)     \n",
    "    elif f.is_Pow: return derivee_puissance(f.args[0], f.args[1], x)\n",
    "    elif f.is_Function: return derivee_fonction(f.func, f.args[0], x) # <-- f.args[0] car une seule variable !\n",
    "    else: raise Exception('Not Implemented')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Si vous vous posez une question à propos des tests de la troisième ligne, voici l'explication :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pi.is_NumberSymbol"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "E.is_NumberSymbol"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Pour SymPy, $\\pi$ et $e$ ne sont pas des nombres mais des __symboles de nombres__.\n",
    "\n",
    "Évidemment, pour l'instant on ne peut dériver que des constantes et des variables."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "derivee(Rational(3, 5), x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "derivee(x, x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "derivee(x, t)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.3 Dérivée d'une somme"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Dériver une somme, c'est facile :\n",
    "\n",
    "$$\\left(\\sum_{k=0}^{n-1}g_k\\right)'=\\sum_{k=0}^{n-1}g'_k$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def derivee_somme(gs, x):\n",
    "    s = Integer(0)\n",
    "    for g in gs:\n",
    "        s = Add(s, derivee(g, x))\n",
    "    return s"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Testons."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "derivee(x + y + z, x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Peut-on dériver $x + x$ ? Non. Pourquoi ?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x + x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Eh oui, SymPy transforme automatiquement $x+x$ en $2x$, et notre fonction ne sait pas encore dériver les produits. Alors apprenons-lui comment faire.\n",
    "\n",
    "__Remarque__ : Il est possible d'interdire à SymPy d'évaluer automatiquement ce genre d'expression. Je n'en parlerai pas dans ce notebook."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.4 Dérivée d'un produit"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Tout le monde le sait, $(uv)'=u'v+uv'$. Et $(uvw)'$ ? Eh bien\n",
    "\n",
    "$$(uvw)'=u'vw+uv'w+uvw'$$\n",
    "\n",
    "Cela se généralise facilement par récurrence sur le nombre de facteurs :\n",
    "\n",
    "$$(g_0g_1\\ldots g_{n-1})'=g'_0g_1\\ldots g_{n-1}+g_0g'_1\\ldots g_{n-1}+\\ldots+g_0g_1\\ldots g'_{n-1}$$\n",
    "\n",
    "c'est à dire une somme de $n$ termes où dans chaque terme, on dérive un et un seul des facteurs.\n",
    "\n",
    "La fonction ci-dessous fait le travail. Elle prend en paramètre une liste d'expressions et renvoie la dérivée du produit de ces expressions. Remarquons tout de même les deux boucles imbriquées, qui nous promettent une complexité en $O(n^2)$ où $n$ est le nombre de facteurs. telle quelle, cette fonction n'est pas efficace mais nous nous en contenterons."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def derivee_produit(gs, x):\n",
    "    s = Integer(0)\n",
    "    for k in range(len(gs)):\n",
    "        g1 = derivee(gs[k], x)\n",
    "        p = Integer(1)\n",
    "        for j in range(len(gs)):\n",
    "            if j != k: p = Mul(p, gs[j])\n",
    "            else: p = p * g1\n",
    "        s = s + p\n",
    "    return s"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Maintenant, on peut dériver $x + x$ !"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "derivee(x + x, x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "derivee(x * y * z, x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Remarquons que nous ne savons pas encore dériver $x \\times x$ :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x * x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La fonction `derivee_produit` est suffisamment compliquée pour demander à être testée plus en profondeur. À cet effet, définissons pour $x$ réel et $n$ entier naturel la $n$ième __puissance descendante__ de $x$ : $x^{\\underline 0}=1$ et, si $n\\ge 1$ :\n",
    "\n",
    "$$x^{\\underline n}=x(x-1)(x-2)\\ldots(x-n+1)$$\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def power_dn(x, n):\n",
    "    p = 1\n",
    "    for k in range(n): p *= x - k\n",
    "    return p"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Voici $x^{\\underline{10}}$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p = power_dn(x, 10)\n",
    "p"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Dérivons."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p1 = derivee(p, x)\n",
    "p1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Puis développons avec la fonction `expand` de SymPy."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p1 = expand(derivee(p, x))\n",
    "p1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "maintenant faisons le \"contraire\" : développons d'abord $p$, __puis__ dérivons. On devrait trouver le même résultat. Enfin, cela reste pour l'instant un __voeu pieux__ :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "expand(p)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Eh oui, il y a des puissances dans ce que l'on veut dériver. Alors réglons le cas des puissances."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.5 Dérivée d'une puissance"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Soient $g$ et $h$ deux \"fonctions\". On a\n",
    "\n",
    "$$g^h=e^{h\\ln g}$$\n",
    "\n",
    "et donc\n",
    "\n",
    "$$(g^h)'=(h'\\ln g + hg'\\frac 1 g)g^h$$\n",
    "\n",
    "D'où la fonction `derivee_puissance`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def derivee_puissance(g, h, x):\n",
    "    g1 = derivee(g, x)\n",
    "    h1 = derivee(h, x)\n",
    "    return Mul(Add(Mul(h1, log(g)), Mul(h, g1, Pow(g, Integer(-1)))), Pow(g, h))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Maintenant, on peut dériver $x\\times x$ :-)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "derivee(x * x, x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Et aussi $\\sqrt x$. En effet :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "srepr(sqrt(x))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "derivee(sqrt(x), x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Et aussi des choses beaucoup moins triviales"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "derivee(sqrt(x ** 2 + 1), x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "derivee(x ** (x ** 2 - x), x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "derivee((x ** 2 * (2 - x)) ** Rational(1, 3), x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "factor(_)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Et nous pouvons aussi finir de tester notre fonction qui dérive les produits !"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p1 = expand(derivee(p, x))\n",
    "p2 = derivee(expand(p), x)\n",
    "p1, p2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p1 - p2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.6 Dérivée d'une composée"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Étant données deux fonctions $g$ et $h$, on a\n",
    "\n",
    "$$(g\\circ h)'=(g'\\circ h)\\times h'$$\n",
    "\n",
    "D'où le code ci-dessous. On cherche la dérivée de $g$ dans le dictionnaire des dérivées usuelles. \n",
    "\n",
    "- Si $g$ est une clé du dictionnaire, aucun problème.\n",
    "\n",
    "- Si $g$ n'est pas une clé du dictionnaire la fonction renvoie une expression \"formelle\". Précisément, elle construit la dérivée \"non évaluée\" $\\frac d {dt}g(t)$ (fonction `Derivative` de SymPy), puis elle remplace $t$ par $h$ (la méthode `subs` permet de faire cela). Elle multiplie ensuite par $h'$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def derivee_fonction(g, h, x):\n",
    "    if not (g in derivees_usuelles):\n",
    "        return Mul(Derivative(g(t), t).subs(t, h), derivee(h, x))\n",
    "    else:    \n",
    "        return Mul(derivees_usuelles[g](h), derivee(h, x))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Maintenant, plus aucune (?) dérivée ne nous résiste :-)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "derivee(exp(-x ** 2), x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "derivee(x ** sin(x), x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "derivee(sin(cos(tan(sin(cos(tan(x)))))) , x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "expr = integrate(1 / (x ** 4 + 1), x)\n",
    "expr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "derivee(expr, x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "simplify(_)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__Remarque__ : La simplification des expressions est un sujet à part entière, et c'est un sujet compliqué. Nous avons ci-dessus utilisé la fonction `simplify`, qui est une sorte de __fonction magique__. Si vous voulez plus de détails sur le sujet de la simplification, consultez la documentation de SymPy."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Un dernier exemple. Créons deux fonctions \"abstraites\" $f$ et $g$. Notre fonction `derivee` ne sait pas les dériver. Elle renvoie tout de même des résultats cohérents ci-dessous :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = Function('f')\n",
    "g = Function('g')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "derivee(f(x ** 2 + x), x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "derivee(g(asin(f(x))), x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.7 Rajouter des dérivées usuelles"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Comment faire pour booster notre fonction `derivee` ? Imaginons qu'une nouvelle fonction usuelle (ou pas) devienne pour nous très importante : il suffit de la rajouter au dictionnaire des dérivées usuelles. Prenons deux exemples."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Une fonction importante en probabilités est la primitive de $x\\mapsto e^{-x^2}$ qui s'annule en 0, ou plus exactement $\\frac 2 {\\sqrt\\pi}$ fois cette primitive. Appelons la $\\psi$."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Tout d'abord, disons à SymPy que $\\psi$ est une __fonction__. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "psi = Function('psi')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "On peut maintenant considérer des expressions du genre : "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cos(psi(x ** 2 + 1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ensuite, rajoutons $\\psi$  dans le dictionnaire. On a \n",
    "\n",
    "$$\\psi'(t)=\\frac{2}{\\sqrt\\pi}e^{-t^2}$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "derivees_usuelles[psi] = lambda t: 2 / sqrt(pi) * exp(-t ** 2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Et voilà. Quelques tests ?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "derivee(psi(x), x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__Remarque__ : Dans SymPy, cette fonction s'appelle `erf`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "diff(erf(x), x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "derivee(erf(x), x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ben oui, notre fonction ne sait pas que `erf`, c'est $\\psi$."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Quelle est la dérivée de $\\ln\\psi$ ?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "derivee(log(psi(x)), x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Et sa dérivée cinquième ?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "expr = log(psi(x))\n",
    "for k in range(5):\n",
    "    expr = derivee(expr, x)\n",
    "expr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "factor(_)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Prenons un autre exemple. Pour $x\\ne 0$, posons\n",
    "\n",
    "$$\\phi(x)=\\frac 1 x\\int_0^x\\frac{\\arctan t}{t}dt$$\n",
    "\n",
    "On vérifie facilement que pour tout $x$ non nul\n",
    "\n",
    "$$\\phi'(x)=\\frac 1 x \\left(\\frac{\\arctan x}{x}-\\phi(x)\\right)$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "phi = Function('phi')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La variable $\\phi$ contient une expression SymPy qui est une fonction au sens mathématique du terme. En clair, $\\phi(x)$ est elle-même une expression."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "derivees_usuelles[phi] = lambda t: (atan(t) / t - phi(t)) / t"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Que vaut $\\phi''(x)$ ?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "derivee(derivee(phi(x), x), x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "factor(_)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Que vaut $x^2\\phi'(x)+x\\phi(x)$ ?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x ** 2 * derivee(phi(x), x) + x * phi(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Simplifions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "simplify(_)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$\\phi$ est donc solution de l'équation différentielle\n",
    "\n",
    "$$x^2y'+xy=\\arctan x$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.8 Et maintenant ?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notre fonction de dérivation ne dérive évidemment pas toutes les expressions que l'on peut fabriquer avec SymPy. Pour ne prendre qu'un exemple :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = Function('f')\n",
    "g = Function('g')\n",
    "h = Function('h')\n",
    "expr = Integral(f(t, x), (t, g(x), h(x)))\n",
    "expr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "isinstance(expr, Integral)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "SymPy sait dériver cela."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "diff(expr, x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Si nous voulons que notre fonction `derivee` puisse dériver ce genre d'expression, il faut reprendre son code et rajouter un cas, celui où l'expression à dériver est du genre \"intégrale\". C'est tout à fait possible.\n",
    "\n",
    "__Exercice__ : Faites-le, apprenez à la fonction `derivee` à dériver par rapport à $x$ des expressions du genre $\\int_g^hf$ où $g, h, f$ sont des expressions. Adaptez pour cela la formule écrite juste au-dessus.\n",
    "\n",
    "Ce qui est aussi tout à fait certain c'est qu'au fil du temps nous nous apercevrons que d'autres types d'expressions ne sont pas dérivables avec notre fonction. À chaque fois il faudra réécrire le code de la fonction `derivee` qui va s'allonger, s'allonger .... Ceci est évidemment problématique.\n",
    "\n",
    "La philosophie du code source de SymPy est tout à fait différente. Ce code est __orienté objet__ : chaque nouveau genre d'expression donne lieu à la définition d'une nouvelle __classe__. Et c'est à l'intérieur de cette classe qu'est définie la __méthode__ permettant aux objets de \"se dériver\". Je n'en dirai pas plus. Si vous voulez en savoir plus allez sur le site de SymPy [https://www.sympy.org](https://www.sympy.org). Vous y trouverez un lien vers le code source de SymPy. Allez par exemple dans le répertoire `sympy/core` et regardez le fichier `add.py`. Puis cherchez la ligne\n",
    "\n",
    "`def _eval_derivative(self, s):`\n",
    "\n",
    "Votre navigateur peut la trouver automatiquement !\n",
    "\n",
    "__Exercice__ : Faites de même avec les classes `Mul` et `Pow`. La façon dont la dérivée d'un produit est calculée par SymPy est-elle meilleure que la nôtre ?\n",
    "\n",
    "__Exertcice__ : Allez voir dans le répertoire `sympy/integrals` le fichier `integrals.py`. Comment SymPy dérive-t-il une intégrale ? Comparez avec ce que vous avez fait dans l'exercice ci-dessus, que vous avez forcément fait.\n",
    "\n",
    "Bonne lecture :-). "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "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.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
