Lycée Thiers TP PYTHON - 05 Prise de contact avec un logiciel de calcul formel sympy est un module permettant d’effectuer du calcul symbolique. C’est un exemple de “CAS”, acronyme pour Computer Algebra System. sympy est entièrement écrit en Python et ne requiert aucun logicien additionnel pour fonctionner. Aucune procédure d’installation n’est nécessaire car sympy fait partie de la distribution de Pyzo. Toutes les commandes qui suivent doivent être saisies. Il est suggéré d’essayer aussi quelques variantes et d’observer les réponses du système, ce qui permettra d’en apprendre davantage. Pour commencer, on charge le module, comme on l’a déjà fait dans d’autres circonstances (pour le module math, par exemple) : >>> from sympy import * On peut jeter un coup d’oeil à la liste (imposante) de fonctionnalités mises à notre disposition : >>> dir() Ce qui suit n’est qu’une introduction sommaire. Pour en savoir plus, on peut consulter la documentation en ligne : http://docs.sympy.org/latest/index.html premières manipulations Afin de commencer à tester quelques unes de ces fonctionnalités, il faut créer des symboles (généralement, on les nomme avec des lettres minuscules, mais ce n’est pas une obligation). La fonction var accepte une chaîne de caractères et crée un ou plusieurs symboles : >>> >>> >>> >>> var(’x’) var(’y,z’) var(’a b’) var(’r:3’) # noter la virgule # noter le caractère ’espace’ # ceci crée des symboles numérotés Observer que x est une instance d’une classe particulière (et, bien sûr, même chose pour les autres symboles nouvellement créés) : >>> type(x) On peut maintenant former des expressions : >>> x + 1 >>> x + y + 2 - 2*(y-1) - 3 + y # à reproduire fidèlement ! Comme toujours, les espaces sont ignorés (on peut en insérer, afin d’augmenter la lisibilité). Par ailleurs, l’exemple ci-dessus montre que sympy tente, dans la mesure du possible, de réduire l’expression saisie par l’utilisateur à une forme standard, en utilisant des règles de simplification. Mais certaines transformations ne sont pas effectuées de manière automatique : TP PYTHON - 05 2 >>> (x+1)**3 On peut alors décider d’orienter le calcul dans telle ou telle direction, selon ce qu’on cherche à faire. Par exemple : >>> expand((x+1)**3) >>> ((x+1)**3).expand() # version fonction # version méthode On aurait pu écrire, de façon équivalente : expand(_) car le symbole _ fait référence au résultat de l’évaluation précédente. Autre exemple : >>> expand((x+y+z)**3 * (x-y+1)**2 * (x+y+1) * (y+2*z)) >>> factor(_) Impressionant, non ? Au cas où vous en douteriez encore, expand et factor permettent respectivement de développer et de factoriser une expression algébrique (comportant une ou plusieurs variables). L’affichage du résultat peut être rendu plus “joli”, mais cela n’a rien d’essentiel : >>> init_printing(pretty_print = True) >>> expand((x+y)**4) Voici deux autres exemples montrant que certaines simplifications ne se font pas automatiquement : >>> (x**3 - y**3) / (x - y) >>> cancel(_) >>> log(x*y) Dans ce dernier exemple, il faut préciser que la fonction log est celle prédéfinie par le module sympy (elle s’applique à des expressions comportant des symboles, et pas seulement à des expressions numériques, comme c’est le cas de la fonction log du module math). Si le module math avait été chargé avant le module sympy, la définition de log par math aurait été écrasée (ainsi que les définitions de quelques autres fonctions, pour lesquelles une fonction portant le même nom existe dans sympy. Ce comportement n’est pas génant ici, mais pourrait l’être dans d’autres circonstances : la solution consiste à charger un module via la commande import nomDuModule au lieu de from nomDuModule import * ; on doit allors accéder aux fonctions par leur nom complet, c’est-à-dire nomDuModule.nomDeLaFonction). Il est normal qu’aucune simplification de l’expression log(x*y) n’ait eu lieu ! En revanche, si l’on sait que les symboles x et y représentent des nombres réels strictement positifs, alors on est en droit d’attendre autre chose : >>> var(’x,y’, positive = True) >>> expand(log(x*y)) Concernant les fractions, les deux transformations de base sont, d’une part, la réduction au même dénominateur : >>> var(’r:3’) >>> 1/r0 + 1/r1 + 1/r2 >>> together(_) et, d’autre part, la décomposition en éléments simples (mais seulement pour des fractions en une seule variable) : >>> var(’x’) >>> f = (2*x + 1) / (x**3 - x) >>> apart(f) TP PYTHON - 05 3 Un peu de trigonométrie, à présent. Voici encore un exemple de simplification non automatique : >>> sin(x)**2 + cos(x)**2 >>> simplify(_) Le développement d’expressions trigonométriques usuelles se fait avec une fonction spécifique (expand_trig) : >>> expand(sin(3*x)) >>> expand_trig(sin(3*x)) Pour terminer cette première section, un mot sur la substitution d’expressions. On peut remplacer, au sein d’une expression, chaque occurrence d’une sous-expression par une autre. On dispose pour cela de la méthode subs. Exemple : >>> var(’x,y,z’) >>> e = x + y >>> e.subs(x, z**2) [Qu. 1] Pour sympy, le nombre complexe i est noté I. Utiliser la fonction expand pour obtenir la version simplifiée de l’expression : x + y + z x + wy + w2 z x + w2 y + wz √ −1 + i 3 . On définira w de deux façons possibles où w désigne 2 >>> w = (-1 + I * sqrt(3)) / 2 ou bien : >>> w = -1/2 + I * sqrt(3)/2 Quelle est la différence de comportement de sympy ? Comment peut-on l’expliquer ? [Qu. 2] Avant d’exécuter le petit script ci-dessous, essayer de prévoir le résultat. from sympy import * var(’x’) e = x for n in range(1,4): e = e.subs(x, (x,x)) print(e) [Qu. 3] On rappelle que le n−ème polynôme de Tchebychev Tn est caractérisé par : ∀x ∈ R, Tn (cos (x)) = cos (nx) 1 1 Conjecturer une formule simplifiée pour Tn x+ . 2 x [Qu. 4] Ont dit que deux polynômes P et Q (à coefficients réels) commutent lorsque : ∀x ∈ R, P (Q (x)) = Q (P (x)) Les polynômes suivants commutent-ils ? 5 P (x) = x2 + x + 1 , Q (x) = x2 TP PYTHON - 05 4 On utilisera la méthode subs et la fonction (ou la méthode) expand. Même question pour les polynômes de Tchebychev T7 et T8 . calcul numérique On a eu l’occasion de constater les imprécisions, inhérentes au type float, qui surviennent rapidement dans certains calculs. sympy apporte une amélioration très appréciable (avec une classe qui prolonge la classe float) : il s’agit du calcul numérique en précision arbitraire. Un exemple en dira plus que de longs discours : >>> pi >>> pi.evalf() >>> pi.evalf(100) La méthode evalf peut être appliquée à toute expression représentant un nombre réel (ou complexe). Par défaut, 15 chiffres significatifs sont utilisés, mais on peut demander la précision que l’on veut (les seules limitations sont le temps de calcul et la mémoire utilisée). On peut donc, sans se fatiguer trop, calculer la 10 000−ème décimale de π : >>> approxPi = pi.evalf(10000) >>> str(approxPi)[-1] # si s est une chaîne, s[-1] est son dernier caractère Le nombre π n’est pas la seule constante mathématique prédéfinie par sympy. Sans être exhaustif, signalons au moins les nombres e et i : >>> E.evalf() >>> expand((1+I)*(2-3*I)) La méthode evalf peut s’appliquer à des expressions arbitrairement compliquées : >>> ((pi + sqrt(2))**(1 + exp(-1))).evalf(30) sommes et produits Pour calculer 10 X k, on n’a pas besoin de sympy. On peut utiliser la fonction sum (prédéfinie à l’ouver- k=1 ture de la session Python) : >>> sum(k for k in range(1,11)) Résultat : 55 Pour calculer 10 X 1 k=1 k , on peut faire de même : >>> sum(1/k for k in range(1,11)) Mais cette fois, le résultat (2.8289682539682537) est approché (la faute au type float). Pour obtenir le résultat exact (un nombre rationnel, écrit sous la forme d’une fraction irréductible), une solution simple consiste à abandonner la fonction sum au profit de la fonction summation. Celle-ci permet de calculer : î des sommes finies, purement numériques : >>> var(’k,n’) >>> summation(1/k, (k,1,10)) # noter la syntaxe pour les bornes de la somme TP PYTHON - 05 5 î des sommes finies, non purement numériques, pour lesquelles une formule sommatoire est cherchée : >>> >>> >>> >>> summation(1/k, (k,1,n)) summation(k*factorial(k),(k,1,n)) summation(k**2, (k,1,n)) factor(_) î et des sommes de séries : >>> summation(1/k**2, (k,1,oo)) # le symbole ∞ est obtenu avec deux ’o’ Bien entendu, on ne sait pas toujours trouver de formule sommatoire et on ne sait pas non plus calculer explicitement la somme de n’importe quelle série convergente. En pareil cas, le résultat est laissé sous forme “non évaluée” : >>> summation(factorial(k), (k,1,n)) >>> summation(1/(k**2*log(k)),(k,2,oo)) Pour calculer des produits, l’analogue de summation est product : >>> product(sin(k), (k,1,10)) >>> _.evalf(30) >>> product(k, (k,1,n)) [Qu. 5] Vérifier avec sympy l’identité : ? ∀n ∈ N , n X k=1 n 2 X k k = 3 k=1 On pose maintenant : Sn,p = n X kp k=1 de sorte que la formule précédente s’écrit Sn,3 = Sn,1 2 . Calculer et simplifier avec sympy, pour diverses valeurs de p, l’expression : ! p X p+1 An,p = Sn, j j j=0 Les coefficients binomiaux se calculent avec la fonction binomial. Par exemple : >>> binomial(10,3) >>> (10*9*8) // factorial(3) Conjecturer puis démontrer un résultat général concernant An,p . [Qu. 6] On note, pour tout n ∈ N et pour tout x ∈ R : n Y k Pn (x) = 1 + x2 k=0 Ecrire une fonction produit(n) qui renvoie l’expression développée de ce produit. On procédera de deux façons : d’une part, avec la fonction product (cf. ci-dessus) et d’autre part en écrivant une boucle. Conjecturer puis démontrer une formule générale.