{
"cells": [
{
"cell_type": "markdown",
"id": "40540b19",
"metadata": {},
"source": [
"# Objets\n",
"\n",
"## Vecteur\n",
"\n",
"Créer une classe Vecteur3D. Chaque vecteur aura 3 attributs : x, y, z\n",
"\n",
"* Écrire une méthode norme qui renvoie la norme.\n",
"* Écrire la méthode ``__add__`` pour faire la somme entre deux veteurs\n",
"* Écrire la méthode ``__mul__`` pour faire soit le produit par un scalaire ($2\\vec{u}$) ou le produit scalaire ($\\vec{u}\\cdot\\vec{v}$)."
]
},
{
"cell_type": "markdown",
"id": "a4d45511",
"metadata": {},
"source": [
"## Bibliographie\n",
"\n",
"\n",
"Un livre est décrit par son titre, auteur et année de publication (pour faire les choses simplements). Écrire une classe ``Livre`` qui enregistre ces informations. Ecrire la méthode ``__repr__`` et ``__str__``.\n",
"\n",
"Une bibliographe est une liste de livre. Écrire la classe ``Bibliographie`` qui enregistre une liste de livre (on stockera la liste de livre sous forme d'une liste qui sera un attribut de la bibliographie).\n",
"\n",
"L'objectif final est de pouvoir faire ceci :: \n",
"\n",
" livre1 = Livre(\"A very nice book\", \"F. Dupont\", 2014)\n",
" livre2 = Livre(\"A very smart book\", \"A. Einstein\", 1923)\n",
" livre3 = Livre(\"A very stupid comic\", \"D. Duck\", 1937)\n",
"\n",
" bibliographie = Bibliographie([book1, book2, book3])\n",
"\n",
"Maintenant que tout est fait sous forme d'objet, on peut imaginer écrire plusieurs méthode : \n",
"\n",
"* Écrire une méthode ``filter_by_year`` qui fait une nouvelle bibliographie ne contenant que les livres d'une année donée.\n",
"* Écrire une méthode ``to_html`` qui formate correctement la bibliographie. La méthode de la classe Bibliographie devra appeler une méthode pour chaque Livre.\n",
"\n",
"Et en HTML :: \n",
"\n",
"
\n",
" \n",
" Auteur | Titre | Année |
\n",
" \n",
" \n",
" F. Dupont | 2014 | A very nice book |
\n",
" A. Einstein | 1923 | A very smart book |
\n",
" D. Duck | 1937 | A very stupid comic |
\n",
" \n",
"
\n",
"\n",
"Remarque : si un objet possède une méthode ``_repr_html_``, alors le jupyter notebook utilsera automatiquement la représentation en HTML. Rajouter cette méthode (qui appelera to_html)."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "aa25a1f6",
"metadata": {},
"outputs": [],
"source": [
"class Livre():\n",
" def __init__(self, titre, auteur, annee):\n",
" self.titre = titre\n",
" self.auteur = auteur\n",
" self.annee = annee\n",
" \n",
" def __repr__(self):\n",
" return f\"Livre({self.titre!r}, {self.auteur!r}, {self.annee!r})\"\n",
"\n",
" def to_html_table_line(self):\n",
" return f\"{self.auteur} | {self.titre} | {self.annee} |
\"\n",
" \n",
" \n",
"bibio_html_template=\"\"\"\n",
"\n",
"Auteur | Titre | Année |
\n",
"\n",
"\n",
"{content}\n",
"\n",
"
\"\"\"\n",
"\n",
"\n",
"class Bibliographie():\n",
" def __init__(self, liste_des_livres):\n",
" self._liste_livres = liste_des_livres\n",
" \n",
" def __getitem__(self, key):\n",
" return self._liste_livres[key]\n",
" \n",
" def __repr__(self):\n",
" return f\"Bibliographie({self._liste_livres!r})\"\n",
" \n",
" def to_html_table(self):\n",
" content = '\\n'.join([livre.to_html_table_line() for livre in self._liste_livres])\n",
" return bibio_html_template.format(content=content)\n",
" \n",
" _repr_html_ = to_html_table"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "bc4dafb1",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"Auteur | Titre | Année |
\n",
"\n",
"\n",
"F. Dupont | A very nice book | 2014 |
\n",
"A. Einstein | A very smart book | 1923 |
\n",
"D. Duck | A very stupid comic | 1937 |
\n",
"\n",
"
"
],
"text/plain": [
"Bibliographie([Livre('A very nice book', 'F. Dupont', 2014), Livre('A very smart book', 'A. Einstein', 1923), Livre('A very stupid comic', 'D. Duck', 1937)])"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"livre1 = Livre(\"A very nice book\", \"F. Dupont\", 2014)\n",
"livre2 = Livre(\"A very smart book\", \"A. Einstein\", 1923)\n",
"livre3 = Livre(\"A very stupid comic\", \"D. Duck\", 1937)\n",
"\n",
"bibliographie = Bibliographie([livre1, livre2, livre3])\n",
"bibliographie"
]
},
{
"cell_type": "markdown",
"id": "157d8474",
"metadata": {},
"source": [
"## Système de calcul formel\n",
"\n",
"*Cet exercice est à but purement pédagogique. Pour utiliser un système de calcul formel sous Python, la librairie ``sympy`` existe et fonctionnera bien mieux que ce que l'on va faire !*\n",
"\n",
"\n",
"L'objectif de ce TD est de réaliser un système de calcul formel qui permettra de manipuler des expressions algébriques simples et de réaliser des opérations simples. Par exemple, on souhaite pouvoir effectuer ::\n",
"\n",
"```\n",
"x = Symbol('x')\n",
"y = Symbol('y')\n",
"\n",
"s = 2*x*y + sin(x)*y\n",
"\n",
"print(s.diff(x)) # Dérivée par rapport à x\n",
"```\n",
"\n",
"Chaque expression sera représentée par un arbre. Les feuilles de l'arbre seront soit les symboles soit les constantes numériques. Les noeuds seront des fonctions à un ou plusieurs argument (sinus, somme, opposé, ...). Le nom de la classe du noeud désignera la fonction. Les \"enfants\" du noeud seront les arguments de la fonction. Par exemple l'expression ci dessus correspondra à l'objet suivant ::\n",
"\n",
"```\n",
"# sA : 2*x*y\n",
"sA = Prod(Prod(Number(2), Symbol('x')), Symbol('y'))\n",
"# sB : sin(x)*y\n",
"sB = Prod(Sin(Symbol('x')), Symbol('y'))\n",
"\n",
"s = Sum(sA, sB)\n",
"```\n",
"\n",
"### Structure du programme\n",
"\n",
"Voici la structure de base ::\n",
"\n",
"```\n",
"class Expr(object):\n",
" pass\n",
"\n",
"class Node(Expr):\n",
" pass\n",
"\n",
"class Leave(Expr):\n",
" pass\n",
"```\n",
"\n",
"Pour les feuilles ::\n",
"\n",
"```\n",
"class Symbol(Leave):\n",
" pass\n",
"\n",
"class Number(Leave):\n",
" pass\n",
"```\n",
"\n",
"Ensuite on définit les fonctions ::\n",
"\n",
"```\n",
"class Function(Node):\n",
" \"\"\" Function with an arbitrary number of arguments \"\"\"\n",
" pass\n",
"```\n",
"\n",
"Les opérateurs sont des fonctions comme les autres, mais elle seront simplement affichées différemment ::\n",
"\n",
"```\n",
"class BinaryOperator(Function):\n",
" pass\n",
"\n",
"class Sum(BinaryOperator):\n",
" pass \n",
"# Idem pour Sub, Div, Prod, Pow\n",
"\n",
"class UnitaryOperator(Function):\n",
" pass\n",
"\n",
"class Neg(UnitaryOperateor):\n",
" pass\n",
"```\n",
"\n",
"Les fonction mathématiques, qui prennent un seul argument ::\n",
"\n",
"```\n",
"class MathFunction(Function):\n",
" pass\n",
"\n",
"class Sin(MathFunction):\n",
" pass\n",
"```\n",
"\n",
"### Questions\n",
"\n",
"On va procéder étape par étape. Il sera plus facile de commencer par les feuilles avant d'écrire la structure globale. \n",
"\n",
"\n",
"1. Ecrire le ``__init__`` de la classe Symbol et Number"
]
},
{
"cell_type": "code",
"execution_count": 55,
"id": "ac648e4c",
"metadata": {},
"outputs": [],
"source": [
"class Expr(object):\n",
" def __add__(self, other):\n",
" return Sum(self, other)\n",
" \n",
" def __mul__(self, other):\n",
" return Prod(self, other)\n",
" \n",
" def __repr__(self):\n",
" return self.display()\n",
"\n",
"class Node(Expr):\n",
" pass\n",
"\n",
"class Leave(Expr):\n",
" pass\n",
"\n",
"class Symbol(Leave):\n",
" pass\n",
"\n",
"class Number(Leave):\n",
" pass\n",
"\n",
"class Function(Node):\n",
" \"\"\" Function with an arbitrary number of arguments \"\"\"\n",
" pass\n",
"\n",
"class BinaryOperator(Function):\n",
" pass\n",
"\n",
"class Sum(BinaryOperator):\n",
" pass \n",
"# Idem pour Sub, Div, Prod, Pow\n",
"\n",
"class UnitaryOperator(Function):\n",
" pass\n",
"\n",
"class Neg(UnitaryOperator):\n",
" pass\n",
"\n",
"class MathFunction(Function):\n",
" pass\n",
"\n",
"class Sin(MathFunction):\n",
" pass"
]
},
{
"cell_type": "code",
"execution_count": 56,
"id": "7024a95d",
"metadata": {},
"outputs": [],
"source": [
"import numbers\n",
"\n",
"class Symbol(Leave):\n",
" def __init__(self, symbole):\n",
" self.symb = symbole\n",
" \n",
"class Number(Leave):\n",
" def __init__(self, nombre):\n",
" if not isinstance(nombre, numbers.Number):\n",
" raise ValueError\n",
" self.nbre = nombre\n",
" "
]
},
{
"cell_type": "markdown",
"id": "fa8ea3f4",
"metadata": {},
"source": [
"2. Ecrire une méthode ``display`` sur ces classes afin de renvoyer une chaîne de caractère contenant le symbole ou le nombre"
]
},
{
"cell_type": "code",
"execution_count": 57,
"id": "a3bf2374",
"metadata": {},
"outputs": [],
"source": [
"class Symbol(Leave):\n",
" def __init__(self, symbole):\n",
" self.symb = symbole\n",
"\n",
" def display(self, *args):\n",
" return self.symb\n",
"\n",
"class Number(Leave):\n",
" def __init__(self, nombre):\n",
" if not isinstance(nombre, numbers.Number):\n",
" raise ValueError\n",
" self.nbre = nombre\n",
" \n",
" def display(self):\n",
" return str(self.nbre)\n",
" "
]
},
{
"cell_type": "markdown",
"id": "1fe3cfba",
"metadata": {},
"source": [
"3. Ecrire le ``__init__`` de la class Sin ainsi que le display. Le display devra appeler le display de l'argument. Par exemple ceci devra fonctionner :: \n",
"\n",
"```\n",
">>> x = Symbol('x')\n",
">>> Sin(x).display()\n",
"sin(x)\n",
">>> Sin(Sin(x)).display()\n",
"sin(sin(x))\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 58,
"id": "549bb6c4",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'Sin(Sin(x))'"
]
},
"execution_count": 58,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"class Sin(MathFunction):\n",
" def __init__(self, arg):\n",
" self.arg = arg\n",
" \n",
" def display(self):\n",
" return f'Sin({self.arg.display()})'\n",
" \n",
"x = Symbol('x')\n",
"Sin(Sin(x)).display()"
]
},
{
"cell_type": "markdown",
"id": "bef74c73",
"metadata": {},
"source": [
"4. Généraliser le __init__ et le display de ``Sin`` afin de le mettre dans la class MathFunction. On rajoutera un attribut de classe à chaque sous classe de MathFunction :: \n",
"\n",
"```\n",
"class Sin(MathFunction):\n",
" funtion_name = 'sin'\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 59,
"id": "731aa1af",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'sin(sin(x))'"
]
},
"execution_count": 59,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"class MathFunction(Function):\n",
" def display(self):\n",
" return f'{self.function_name}({self.arg.display()})'\n",
"\n",
"\n",
"class Sin(MathFunction):\n",
" function_name = 'sin'\n",
" def __init__(self, arg):\n",
" self.arg = arg\n",
" \n",
" \n",
"x = Symbol('x')\n",
"Sin(Sin(x)).display()"
]
},
{
"cell_type": "markdown",
"id": "5c963e24",
"metadata": {},
"source": [
"5. Faire de même pour les opérateurs binaires. On pourra commencer par simplement le faire pour Sum, puis généraliser avec un attribut de classe :: \n",
"\n",
"```\n",
"class Sum(BinaryOperator):\n",
" operator_name = '+'\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 60,
"id": "25af5e5a",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'(x) + (sin((x) * (y)))'"
]
},
"execution_count": 60,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"class Function(Node):\n",
" \"\"\" Function with an arbitrary number of arguments \"\"\"\n",
" def __init__(self, *args):\n",
" self.args = args\n",
"\n",
"class BinaryOperator(Function):\n",
" def __init__(self, arg1, arg2):\n",
" self.args = (arg1, arg2)\n",
" \n",
" def display(self):\n",
" return f'({self.args[0].display()}) {self.operator_name} ({self.args[1].display()})'\n",
" \n",
"class Prod(BinaryOperator):\n",
" operator_name = '*'\n",
"\n",
" \n",
"class Sum(BinaryOperator):\n",
" operator_name = '+'\n",
"\n",
"x = Symbol('x')\n",
"y = Symbol('y')\n",
"Sum(x, Sin(Prod(x, y))).display() "
]
},
{
"cell_type": "markdown",
"id": "1e18c4b8",
"metadata": {},
"source": [
"6. A ce stade quelque chose comme ceci devrait fonctionner ::\n",
"\n",
"```\n",
"x = Symbol('x')\n",
"y = Symbol('y')\n",
"Sum(x, Sin(Prod(x, y)))\n",
"```\n",
"\n",
"Rajouter les méthodes ``__add__``, ``__mul__``, etc à la classe Expr afin de pouvoir écrire :\n",
"\n",
"```\n",
">>> x + Sin(x*y)\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 61,
"id": "d9521ed8",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(x) + (sin((x) * (y)))"
]
},
"execution_count": 61,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"x + Sin(x*y)"
]
},
{
"cell_type": "markdown",
"id": "87f98e37",
"metadata": {},
"source": [
"7. Ecrire les méthodes ``evaluate`` afin de calculer la valeur numérique d'une expression. Cette méthode fonctionnera de la sorte : \n",
"\n",
"```\n",
">>> expr = x + Sin(x*y)\n",
">>> expr.evaluate(x=1, y=3)\n",
"```\n",
"\n",
"On aura donc le protocole suivant ::\n",
" \n",
"```\n",
"def evaluate(self, **kwd):\n",
" pass\n",
"```\n",
"\n",
"Le dictionnaire kwd sera passé récursivement jusqu'aux feuilles et sera utilisé pour évaluer les symboles. \n",
"\n",
"Les opérateurs binaires numériques sont définis dans le module ``operator`` et les fonctions dans le module ``math``. Afin de factoriser le code, on rajoutera donc simplement un attribut de classe du type ``operator_function = operator.add`` pour les opérateurs binaires et ``math_function = math.sin`` pour les fonctions. "
]
},
{
"cell_type": "code",
"execution_count": 66,
"id": "c2a80b38",
"metadata": {},
"outputs": [],
"source": [
"import math\n",
"import numbers \n",
"import operator\n",
"\n",
"class Expr(object):\n",
" def __add__(self, other):\n",
" return Sum(self, other)\n",
" \n",
" def __mul__(self, other):\n",
" return Prod(self, other)\n",
" \n",
" def __repr__(self):\n",
" return self.display()\n",
"\n",
"class Node(Expr):\n",
" pass\n",
"\n",
"class Leave(Expr):\n",
" pass\n",
"\n",
"\n",
"class UnitaryOperator(Function):\n",
" pass\n",
"\n",
"class Neg(UnitaryOperator):\n",
" pass\n",
"\n",
"\n",
"class Symbol(Leave):\n",
" def __init__(self, symbole):\n",
" self.symb = symbole\n",
"\n",
" def display(self, *args):\n",
" return self.symb\n",
" \n",
" def evaluate(self, **kwd):\n",
" try:\n",
" return kwd[self.symb]\n",
" except KeyError:\n",
" raise Exception(\"La valeur de {} n'est pas définie\".format(self.symb))\n",
"\n",
"class Number(Leave):\n",
" def __init__(self, nombre):\n",
" if not isinstance(nombre, numbers.Number):\n",
" raise ValueError\n",
" self.nbre = nombre\n",
" \n",
" def display(self):\n",
" return str(self.nbre)\n",
"\n",
" def evaluate(self, **kwd):\n",
" return self.nbre\n",
" \n",
"class Function(Node):\n",
" \"\"\" Function with an arbitrary number of arguments \"\"\"\n",
" def __init__(self, *args):\n",
" self.args = args\n",
"\n",
" def evaluate(self, **kwd):\n",
" evaluated_args = [elm.evaluate(**kwd) for elm in self.args]\n",
" return self.math_function(*evaluated_args)\n",
" \n",
"class BinaryOperator(Function):\n",
" def __init__(self, arg1, arg2):\n",
" self.args = (arg1, arg2)\n",
" \n",
" def display(self):\n",
" return f'({self.args[0].display()}) {self.operator_name} ({self.args[1].display()})'\n",
" \n",
"class Prod(BinaryOperator):\n",
" operator_name = '*'\n",
" math_function = operator.mul\n",
" \n",
"class Sum(BinaryOperator):\n",
" operator_name = '+'\n",
" math_function = operator.add\n",
"\n",
"class MathFunction(Function):\n",
" def display(self):\n",
" return f'{self.function_name}({self.args[0].display()})'\n",
"\n",
"\n",
"class Sin(MathFunction):\n",
" function_name = 'sin'\n",
" math_function = math.sin\n",
" def __init__(self, arg):\n",
" self.args = (arg,)\n",
" "
]
},
{
"cell_type": "code",
"execution_count": 68,
"id": "2ccb31aa",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(x) + (sin((x) * (y)))\n"
]
},
{
"data": {
"text/plain": [
"2.4121184852417565"
]
},
"execution_count": 68,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"x = Symbol('x')\n",
"y = Symbol('y')\n",
"expr = x + Sin(x*y)\n",
"print(expr)\n",
"expr.evaluate(x=2, y=4.5)"
]
},
{
"cell_type": "markdown",
"id": "d9967332",
"metadata": {},
"source": [
"8. Maintenant que vous avez compris le principe, il devrait être facile d'écrire une méthode ``diff`` qui effectue la dérivée par rapport à une variable !\n",
"\n",
"9. Reste à simplifier les expressions. Une technique consiste à créer des règles de simplifications sous forme de méthode que l'on regroupe ensuite dans une liste ::\n",
"\n",
"```\n",
"class Sum(BinaryOperator):\n",
" operator_name = '+'\n",
" operator_function = operator.add\n",
"\n",
" def simplication_de_deux_nombres(self):\n",
" if isinstance(self.arg1, Number) and \n",
" isinstance(self.arg2, Number):\n",
" return Number(self.arg1.value + self.arg2.value)\n",
"\n",
" def simplication_addition_avec_zero(self):\n",
" pass\n",
"\n",
" liste_simplication = ['simplication_de_deux_nombres', \n",
" 'simplication_addition_avec_zero'] \n",
"```\n",
"\n",
"Ensuite, il faut réussir à appeler correctement et de façon recursive ces méthodes...\n",
"\n",
"10. Pour l'affichage des opérateurs binaires, les règles de priorité peuvent être utilisées pour éviter de mettre trop de parenthèses. Par exemple, dans le cas ``a*(b+c)``, la multiplication appelle le display de l'addition. Comme elle est prioritaire, l'addition va renvoyer le résulat avec des parenthèses. Dans le cas inverse ``a + b*c``, c'est inutile. Il faut donc que le display d'un opérateur passe sa priorité à ses enfants lors de l'appel de display. Implémenter ce principe."
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "d32dc57f",
"metadata": {},
"outputs": [],
"source": [
"import numbers\n",
"import operator\n",
"import math\n",
"\n",
"class Expr(object):\n",
"\n",
" def binary_operator(self, other, operator):\n",
" if isinstance(other, numbers.Number):\n",
" other = Number(other)\n",
" if isinstance(other, Expr):\n",
" return operator(self, other)\n",
" return NotImplemented\n",
"\n",
" def reversed_binary_operator(self, other, operator):\n",
" if isinstance(other, numbers.Number):\n",
" other = Number(other)\n",
" if isinstance(other, Expr):\n",
" return operator(other, self)\n",
" return NotImplemented\n",
"\n",
" def __add__(self, other):\n",
" return self.binary_operator(other, Sum)\n",
" \n",
" def __mul__(self, other):\n",
" return self.binary_operator(other, Prod)\n",
"\n",
" def __truediv__(self, other):\n",
" return self.binary_operator(other, Div)\n",
"\n",
" def __sub__(self, other):\n",
" return self.binary_operator(other, Sub)\n",
"\n",
" def __radd__(self, other):\n",
" return self.reversed_binary_operator(other, Sum)\n",
" \n",
" def __rmul__(self, other):\n",
" return self.reversed_binary_operator(other, Prod)\n",
"\n",
" def __rtruediv__(self, other):\n",
" return self.reversed_binary_operator(other, Div)\n",
"\n",
" def __rsub__(self, other):\n",
" return self.reversed_binary_operator(other, Sub)\n",
"\n",
" def __neg__(self):\n",
" return Neg(self)\n",
" \n",
" def __repr__(self):\n",
" return self.display()\n",
"\n",
"\n",
" def diff(self, var):\n",
" out = self._diff(var)\n",
" return out.simplify()\n",
"\n",
"class Node(Expr):\n",
" pass\n",
"\n",
"class Leave(Expr):\n",
" def simplify(self):\n",
" return self\n",
"\n",
"class Symbol(Leave):\n",
" def __init__(self, symbole):\n",
" self.symb = symbole\n",
" \n",
" def display(self, *args):\n",
" return self.symb\n",
" \n",
" def evaluate(self, **kwd):\n",
" try:\n",
" return kwd[self.symb]\n",
" except KeyError:\n",
" raise Exception(\"La valeur de {} n'est pas définie\".format(self.symb))\n",
"\n",
" def __eq__(self, other):\n",
" if not type(self)==type(other):\n",
" return False\n",
" return self.symb==other.symb\n",
"\n",
" def _diff(self, var):\n",
" if self==var:\n",
" return Number(1)\n",
" return Number(0)\n",
"\n",
"class Number(Leave):\n",
" def __init__(self, nombre):\n",
" if not isinstance(nombre, numbers.Number):\n",
" raise ValueError\n",
" self.nbre = nombre\n",
" \n",
" def display(self, *args):\n",
" return str(self.nbre)\n",
" \n",
" def evaluate(self, **kwd):\n",
" return self.nbre\n",
"\n",
" def __eq__(self, other):\n",
" if isinstance(other, numbers.Number):\n",
" other = Number(other)\n",
" if isinstance(other, Number):\n",
" return other.nbre==self.nbre\n",
" return False\n",
"\n",
" def _diff(self, var):\n",
" return Number(0)\n",
"\n",
"class Function(Node):\n",
" \"\"\" Function with an arbitrary number of arguments \"\"\"\n",
" def __init__(self, *args):\n",
" self.args = args\n",
"\n",
" def evaluate(self, **kwd):\n",
" evaluated_args = [elm.evaluate(**kwd) for elm in self.args]\n",
" return self.math_function(*evaluated_args)\n",
"\n",
" def __eq__(self, other):\n",
" if not type(self)==type(other):\n",
" return False \n",
" return self.args==other.args\n",
"\n",
" def _diff(self, var):\n",
" partial_derivative = getattr(self, 'partial_derivative', None)\n",
" if partial_derivative is None:\n",
" raise NotImplementedError('Cannot derivate function {self.__class__}.'.format(self=self))\n",
" if len(self.args)==0:\n",
" return Number(0)\n",
" out = self.args[0].diff(var)*partial_derivative[0](*self.args)\n",
" for deriv, arg in zip(partial_derivative[1:], self.args[1:]):\n",
" out = out + arg.diff(var)*deriv(*self.args)\n",
" return out\n",
"\n",
" liste_simplification = []\n",
" def simplify(self):\n",
" out = type(self)(*[elm.simplify() for elm in self.args])\n",
" for elm in self.liste_simplification:\n",
" tmp = getattr(out, elm)()\n",
" if tmp is not None:\n",
" return tmp.simplify()\n",
" return out\n",
"\n",
"\n",
"class BinaryOperator(Function):\n",
" commutative=False\n",
" def display(self, parent_priority=0):\n",
" if parent_priority>self.priority:\n",
" fmt_str = '({} {} {})'\n",
" else:\n",
" fmt_str = '{} {} {}'\n",
" return fmt_str.format(self.args[0].display(self.priority), \n",
" self.operator_name, self.args[1].display(self.priority))\n",
"\n",
" def __eq__(self, other):\n",
" if not type(self)==type(other):\n",
" return False\n",
" if self.args==other.args:\n",
" return True\n",
" if self.commutative:\n",
" return self.args==other.args[::-1]\n",
" return False\n",
"\n",
"class Sum(BinaryOperator):\n",
" priority = 0\n",
" operator_name = '+'\n",
" math_function=operator.add\n",
" commutative=True \n",
" partial_derivative = (lambda x, y:1, lambda x, y:1)\n",
"\n",
"\n",
" def simplication_de_deux_nombres(self):\n",
" if isinstance(self.args[0], Number) and isinstance(self.args[1], Number):\n",
" return Number(self.args[0].nbre + self.args[1].nbre)\n",
"\n",
" def simplication_addition_avec_zero(self):\n",
" if self.args[0]==0:\n",
" return self.args[1]\n",
" if self.args[1]==0:\n",
" return self.args[0]\n",
"\n",
" def simplification_identique(self):\n",
" if self.args[1]==self.args[0]:\n",
" return 2*self.args[0]\n",
"\n",
" liste_simplification = ['simplication_de_deux_nombres',\n",
" 'simplication_addition_avec_zero', 'simplification_identique']\n",
"\n",
"class Prod(BinaryOperator):\n",
" priority = 1\n",
" operator_name = '*'\n",
" math_function=operator.mul\n",
" commutative=True \n",
" partial_derivative = (lambda x, y:y, lambda x, y:x)\n",
"\n",
" def simplication_de_deux_nombres(self):\n",
" if isinstance(self.args[0], Number) and isinstance(self.args[1], Number):\n",
" return Number(self.args[0].nbre * self.args[1].nbre)\n",
"\n",
"\n",
" def simplication_multiplication_par_un(self):\n",
" if self.args[0]==1:\n",
" return self.args[1]\n",
" if self.args[1]==1:\n",
" return self.args[0]\n",
"\n",
" def simplication_multiplication_par_zero(self):\n",
" if self.args[0]==0 or self.args[1]==0:\n",
" return Number(0)\n",
" \n",
" liste_simplification = ['simplication_de_deux_nombres',\n",
" 'simplication_multiplication_par_un', 'simplication_multiplication_par_zero']\n",
"\n",
"\n",
"class Div(BinaryOperator):\n",
" priority = 2\n",
" operator_name = '/'\n",
" math_function=operator.truediv\n",
" partial_derivative = (lambda x, y:1/y, lambda x, y:-x/(y*y))\n",
"\n",
"class Sub(BinaryOperator):\n",
" priority = 0.5\n",
" operator_name = '-'\n",
" math_function=operator.sub\n",
" partial_derivative = (lambda x, y:1, lambda x, y:-1)\n",
"\n",
"\n",
"class UnitaryOperator(Function): \n",
" def display(self, parent_priority=0):\n",
" if parent_priority>self.priority:\n",
" fmt_str = '({}{})'\n",
" else:\n",
" fmt_str = '{}{}'\n",
" return fmt_str.format(self.unitary_symbol, \n",
" self.args[0].display(self.priority))\n",
" \n",
"class Neg(UnitaryOperator):\n",
" priority = 0.5\n",
" unitary_symbol = \"-\"\n",
" math_function = operator.neg\n",
" partial_derivative = (lambda x:-1,)\n",
" \n",
"class MathFunction(Function):\n",
" def display(self, *args):\n",
" return '{}({})'.format(self.function_name, self.args[0].display())\n",
"\n",
"\n",
"class Sin(MathFunction): \n",
" function_name = 'sin'\n",
" math_function = math.sin\n",
" partial_derivative = (lambda x:cos(x),)\n",
"\n",
"\n",
"class Cos(MathFunction): \n",
" function_name = 'cos'\n",
" math_function = math.cos\n",
" partial_derivative = (lambda x:-sin(x),)\n",
"\n",
"\n",
"sin = Sin\n",
"cos = Cos \n"
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "a1bea39f",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"2 * cos(2 * x)"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"x = Symbol('x')\n",
"sin(2*x).diff(x).simplify()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d6339593",
"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.8.8"
},
"varInspector": {
"cols": {
"lenName": 16,
"lenType": 16,
"lenVar": 40
},
"kernels_config": {
"python": {
"delete_cmd_postfix": "",
"delete_cmd_prefix": "del ",
"library": "var_list.py",
"varRefreshCmd": "print(var_dic_list())"
},
"r": {
"delete_cmd_postfix": ") ",
"delete_cmd_prefix": "rm(",
"library": "var_list.r",
"varRefreshCmd": "cat(var_dic_list()) "
}
},
"types_to_exclude": [
"module",
"function",
"builtin_function_or_method",
"instance",
"_Feature"
],
"window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 5
}