{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "*ENSAM-Bordeaux, Mathématiques et informatique. Date : le 23/10/19. Auteur : Éric Ducasse. Version : 1.1*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
*On pourra faire exécuter ce notebook cellule par cellule $\\left(\\mathtt{Maj+Entrée}\\right)$ ou intégralement par $\\mathtt{\\;Kernel\\rightarrow Restart\\;\\&\\;Run\\;All}$.*
" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%matplotlib inline" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import sympy as sb\n", "sb.init_printing() # Pour avoir de jolies formules mathématiques à l'écran" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(f\"Version de sympy : {sb.__version__}\") " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# **Manipulation avancée d'expressions**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
***Non exigible dans le cadre de l'UEF MINI.***
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Documentation **sympy** sur le sujet :" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "https://docs.sympy.org/latest/modules/core.html#module-sympy.core.basic" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1. Écriture fonctionnelle des expressions et outils " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### La fonction sympy.srepr " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cette fonction permet de visualiser une expression telle qu'elle est stockée en mémoire et non pas telle qu'elle s'affiche habituellement." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x,y = sb.symbols(\"x,y\")\n", "k,n = sb.symbols(\"k,n\", integer=True, positive=True)\n", "f = sb.Function(\"f\")\n", "xp = sb.S(3)/5*f(x**k) + sb.cos(2*sb.pi*k*y) ; xp" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sb.srepr(xp)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* *Remarque au passage sur la définition des fractions* :" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sb.srepr( sb.S(1)/5 )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Propriétés d'un symbole : l'attribut assumptions0 " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x.assumptions0" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "k.assumptions0" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "f.assumptions0" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.assumptions0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Symboles et sous-espressions présents dans une expression : la méthode atoms " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### * Tous *" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.atoms()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### * Les nombres usuels (non transcendants) *" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.atoms(sb.Number)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "(sb.S(2)/5*sb.sqrt(3)).atoms(sb.Number)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sb.srepr(sb.S(2)/5*sb.sqrt(3))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### * Les nombres transcendants *" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.atoms(sb.NumberSymbol)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### * « vrais » symboles *" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.atoms(sb.Symbol)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### * Extraction de sous-expressions *" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.atoms(sb.Function)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.atoms(sb.Mul)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.atoms(sb.Pow)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.atoms(sb.cos)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Plus évolué : l'extraction de coefficient(s) à partir d'un modèle (pattern) dans une expression :
la méthode match et les symboles indéfinis sympy.Wild " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "pattern : *modèle*, *motif*, *patron*, *prototype* " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X = sb.Wild(\"X\") ; Y = sb.Wild(\"Y\") ; Z = sb.Wild(\"Z\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp # pour rappel" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.match(X+Y)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.match(X+sb.cos(Y))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.match(X*f(Y)+sb.cos(Z))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. Réécriture d'expressions par substitution " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.1 La méthode replace " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(sb.Add.replace.__doc__[:248]+\"[...]\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### * Remplacement d'un symbole par une expression *" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sb.srepr(xp)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp # pour rappel" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.replace(x,0)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.replace(y,sb.S(1)/2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### * Remplacement d'une fonction par une autre fonction *" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.replace(f,lambda t:t**2).replace(sb.cos,lambda t:sb.sin(t/2))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Variante (voir paragraphe suivant) :\n", "X = sb.Wild(\"X\")\n", "xp.replace(f(X),X**2).replace(sb.cos(X),sb.sin(X/2))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.replace(sb.Add,sb.Mul)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.replace(sb.Pow,sb.Mul)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.replace(sb.Mul,sb.Add)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.replace(sb.Mul,sb.Add,map=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### * Remplacement d'un motif par un autre motif *" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X,Y,Z = sb.Wild(\"X\"),sb.Wild(\"Y\"),sb.Wild(\"Z\")\n", "a = sb.symbols(\"a\")\n", "xp.replace(X+Y,X+a*Y)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.replace(f(X**Y),(X+a*Y))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.replace(f(X**Y),X+a*Y, map=True)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.replace(sb.cos(k*X),(-1)**k, map=True)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Fonctionne :\n", "omega = sb.symbols(\"omega\", positive=True)\n", "xp.replace(2*sb.pi*k*y,omega*y)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Ne fonctionne pas :\n", "xp.replace(2*sb.pi*k,omega)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Documentation de xreplace : « Replacements occur only if an entire node in the expression tree is matched »" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sb.srepr(sb.cos(2*sb.pi*k*y))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### * Remplacement de symboles vérifiant une hypothèse *" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.replace(lambda n:n.is_integer,lambda n:a*n)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.replace(lambda n:n.is_integer,lambda n:a*n,map=True)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.replace(lambda n:n.is_Mul,lambda n:a*n,map=True)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.replace(sb.Mul,lambda *args:a*sb.Mul(*args),map=True) # Variante" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### * Une anomalie observée sur la documentation : *" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "S = sb.sin(x)/x ; S" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "[S.replace(x,0),S.xreplace({x:0})] # Étonnant, non ? (et en contradiction avec la documentation...)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "S.replace(x,0,simultaneous=False) # Subtilité expliquée ci-dessous" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "https://docs.sympy.org/latest/modules/core.html#sympy.core.basic.Basic.replace :
« Traverses an expression tree and performs replacement of matching subexpressions from the bottom to the top of the tree. The default approach is to do the replacement in a simultaneous fashion so changes made are targeted only once. If this is not desired or causes problems, simultaneous can be set to False. »
** Il semble que ce soit l'inverse qui est programmé, ou que le terme $\\mathtt{simultaneous}$ soit un faux-ami...**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"Représentation de S en mémoire, avec une structure arborescente :\")\n", "instru = sb.srepr(S) ; instru" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "deuxieme_avant = sb.sympify(\"Mul(Pow(Symbol('x'), Integer(-1)), sin(0))\") # simultaneous=True, semble-t-il\n", "deuxieme_avant" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pas_logique = instru.replace(\"Symbol('x')\",\"0\") # simultaneous=False ?\n", "ensemble = sb.sympify(pas_logique) \n", "(pas_logique, sb.sympify(pas_logique)) " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "premier_avant = sb.sympify(\"Mul(Pow(0, Integer(-1)), sin(Symbol('x')))\") # Autre possibilité pour simultaneous=False\n", "(premier_avant,premier_avant.replace(x,0))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.2 La méthode xreplace " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(sb.Add.xreplace.__doc__[:700]+\"[...]\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.xreplace({x**k:a,2*sb.pi*k*y:omega*k})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Les substitutions sont ordonnées du particulier au général" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.xreplace({x**k:a,k:omega*k/(2*sb.pi*y)})" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "i,j = sb.symbols(\"i,j\", integer=True, positive=True)\n", "(k+k**2).xreplace({k**2:j,k:i})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Sinon, c'est l'ordre lexical qui compte" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.xreplace({k:j,k:i})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### * Contrairement à replace, ne fonctionne pas sur les fonctions : *" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.xreplace({f:lambda t:t**2,sb.cos:lambda t:sb.sin(t/2)})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### * ...ni sur les motifs : *" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X = sb.Wild(\"X\")\n", "xp.xreplace({f(X):X**2,sb.cos(X):sb.sin(X/2)})" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "Y = sb.Wild(\"Y\")\n", "xp.xreplace({X+Y:X*Y})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### * Tous les remplacements sont simultanés, contrairement à la méthode subs, ci-après : *" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "(x/y).xreplace({x:y,y:x})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.3 La méthode subs " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### *Les substitutions sont effectuées dans un ordre donné et en cascade :*" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a,b,c,d = sb.symbols(\"a,b,c,d\")\n", "a.subs( ((a,b),(b,c),(c,d)) )" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "[a.xreplace( {a:b, b:c, c:d} ),a.xreplace( {a:b, b:c, c:d} ).xreplace( {a:b, b:c, c:d} )]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(sb.Add.subs.__doc__[:946]+\"[...]\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "(x/y).subs({x:y,y:x})" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "(x/y).subs({x:y,y:x}, simultaneous=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### * Contrairement à la méthode xreplace, on peut imposer l'ordre de remplacement : *" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "(x/(x+y)).subs({y:x,x:2*y}) # Un dictionnaire n'étant pas ordonné, c'est l'ordre lexical qui compte" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "(x/(x+y)).subs( ((y,x), (x,2*y)) ) # Un tuple ou une liste est ordonnée" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.subs({k:j,j:i}) # j --> i avant k --> j" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xp.subs( ((k,j), (j,i)) ) # k --> j avant j --> i" ] }, { "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.5" } }, "nbformat": 4, "nbformat_minor": 2 }