Séance 5#

Objectifs

  • Introduction à Sagemath

  • Calcul exact : entiers, rationnels, complexes

  • Notions sur le calcul symbolique

  • Notion de méthode et de classe

Sagemath#

Sagemath est un logiciel de calcul formel dont la syntaxe est celle du langage python. Les notions vues dans 4 premières séances de cours vous seront donc essentielles pour utiliser ce logiciel.

Sagemath peut être utilisé en ligne de commande (comme python), mais également dans un notebook Jupyter. C’est ce dernier choix qui a été effectué pour ce cours. Vous utiliserez donc le même environnement de travail que lors des 4 premières séances.

Note

Lorsque vous ouvrez un notebook Jupyter, pour vérifier que c’est bien le « noyau » Sagemath (et non python) qui est lancé, observez ce qui est indiqué en haut à droite de votre fichier.

Sagemath regroupe de nombreuses bibliothèques et de nombreux logiciels sous un même envrionnement. En particulier, les bibliothèques math, matplotlib et random de python sont déjà incluses dans Sagemath, et importées à son ouverture. Vous n’aurez donc pas besoin de les importer, ni de les mentionner.

print(randint(0, 1000))
883

Avertissement

Certaines de ces fonctions (par exemple, plot ou sqrt) ont été modifiées pour les simplifier, ou pour permettre d’accéder à d’autres fonctionnalités.

Par exemple, l’affichage graphique d’une liste de points ne se fera plus sous la forme suivante (qui provoque une erreur) :

X = [ i for i in range(12) ]
Y = [ 2*x-5 for x in X ]
plot(X, Y)

Hide code cell output

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
/tmp/ipykernel_116844/2299422708.py in <module>
      1 X = [ i for i in range(Integer(12)) ]
      2 Y = [ Integer(2)*x-Integer(5) for x in X ]
----> 3 plot(X, Y)

/usr/lib/python3/dist-packages/sage/misc/decorators.py in wrapper(*args, **kwds)
    489                 options['__original_opts'] = kwds
    490             options.update(kwds)
--> 491             return func(*args, **options)
    492 
    493         #Add the options specified by @options to the signature of the wrapped

/usr/lib/python3/dist-packages/sage/plot/plot.py in plot(funcs, *args, **kwds)
   1974         # if there is one extra arg, then it had better be a tuple
   1975         elif n == 1:
-> 1976             G = _plot(funcs, *args, **kwds)
   1977         elif n == 2:
   1978         # if there are two extra args, then pull them out and pass them as a tuple

/usr/lib/python3/dist-packages/sage/plot/plot.py in _plot(funcs, xrange, parametric, polar, fill, label, randomize, **options)
   2092     orig_funcs = funcs # keep the original functions (for use in legend labels)
   2093     excluded_points = []
-> 2094     funcs, ranges = setup_for_eval_on_grid(funcs, [xrange], options['plot_points'])
   2095     xmin, xmax, delta = ranges[0]
   2096     xrange=ranges[0][:2]

/usr/lib/python3/dist-packages/sage/plot/misc.py in setup_for_eval_on_grid(funcs, ranges, plot_points, return_vars)
    102     """
    103     if max(map(len, ranges)) > 3:
--> 104         raise ValueError("At least one variable range has more than 3 entries: each should either have 2 or 3 entries, with one of the forms (xmin, xmax) or (x, xmin, xmax)")
    105     if max(map(len, ranges)) != min(map(len, ranges)):
    106         raise ValueError("Some variable ranges specify variables while others do not")

ValueError: At least one variable range has more than 3 entries: each should either have 2 or 3 entries, with one of the forms (xmin, xmax) or (x, xmin, xmax)

mais peut se faire sous la forme suivante :

points = [ (X[i],Y[i]) for i in range(len(X))] 
list_plot(points)
../_images/e0b14c3102a64f50c6e64ad58cdbf88ca250f2dddebb193232b5edff40c9356d.png

Nous verrons ces subtilités dans une prochaine séance (ce n’est pas nécessaire de retenir la syntaxe ci-dessus pour l’instant).

Entiers#

Déclaration et manipulation élémentaire#

Avec Sagemath, on peut déclarer et utiliser les entiers comme en python.

a = 1
print(a)
1
x = 17
print(((x % 5)**123 + 3*x - 2)//8)
1329227995784915872903807060280344582

On peut également appliquer des fonctions externes sur ces entiers, par exemple la fonction gcd qui calcule un pgcd, ou la fonction xgcd qui calcule également les coefficients de Bezout. En revanche, contrairement à python, ces fonctions ne nécessitent pas d’import, elles sont directement incluses dans Sagemath.

gcd(12, 27)
3
xgcd(12, 27)
(3, -2, 1)

Remarque importante : Pour savoir comment utiliser une fonction, on peut s’aider de l’aide interactive de Sagemath. Pour cela, il faut taper le nom de la fonction suivi de ?. Une mini-fenêtre s’ouvre alors, avec l’aide (en anglais) associée à la fonction. Exemple (ne s’affiche pas sur la page web) :

xgcd?

Fonctions spécifiques à Sagemath#

Un avantage de Sagemath est qu’il contient une quantité importante de fonctions qui sont absentes de la bibliothèque math de python. Prenons l’exemple de la fonction factor, qui permet d’obtenir la factorisation d’un entier :

print(factor(12))
2^2 * 3
factor(1234567890)
2 * 3^2 * 5 * 3607 * 3803

On peut également penser à la fonction next_prime(a) qui retourne le plus petit nombre premier strictement supérieur à a :

next_prime(100)
101

Il existe beaucoup d’autres fonctions à appliquer directement sur des entiers… à vous de les chercher/tester !

Retour aux entiers : la classe Integer#

Un changement majeur entre python et sagemath provient de la manière dont les entiers sont créés et stockés. Leur type a changé : ce sont maintenant des objets provenant d’une classe appelée Integer.

type(a)
<class 'sage.rings.integer.Integer'>

Note

En informatique, la notion de classe permet de représenter une catégorie d’objets ayant des attributs, des propriétés ou des méthodes (au sens de fonction applicable à un objet) en commun. La classe est une sorte de « moule » dans lequel on peut créer des objets similares, qu’on appelle instances de classe.

La classe Integer est donc le « moule » des entiers, et elle attache à ses objets (les entiers) de nombreuses propriétés et fonctions. On peut s’en apercevoir en tapant a.<Tab> après avoir créé un entier a. Sagemath va alors lister tous les attributs de a et toutes les méthodes que l’on peut lui appliquer. Exemple :

a = 12
# a.<Tab>

Note

Cette technique vous sera très utile, pour retrouver le nom d’une méthode, ou pour compléter automatiquement (et correctement !) un nom de méthode déjà entamé.

Pour des raisons de commodité, Sagemath a créé des fonctions du même nom que certaines méthodes de la classe Integer. L’intérêt est de pouvoir les appliquer directement sur l’écriture entière, sans passer par une variable. Par exemple, l’instruction factorial(4) existe et a le même comportement que a.factorial() où la variable a a été assignée à la valeur \(4\).

Autre exemple : on obtient le coefficient binomial \(\binom{n}{k}\) par l’appel à la fonction binomial(n, k), mais également par l’utilisation de la méthode de classe n.binomial(k) :

n = 5
k = 2
print(n.binomial(k))
print(binomial(n, k))
10
10

Néanmoins, si ces « raccourcis » sont assez fréquents pour les entiers, ils le seront beaucoup moins pour les objets que nous verrons par la suite. Retenez-donc plutôt l’approche par méthode de classe.

Tableau récapitulatif des méthodes ou fonctions sur les entiers :

Méthode (où n est de type Integer)

Description

Syntaxe équivalente

n.factorial()

Calcule \(n!\), la factorielle de l’entier \(n\).

factorial(n)

n.binomial(k)

Calcule le coefficient binomial \(\binom{n}{k}\).

binomial(n, k)

n.gcd(k)

Retourne le pgcd de la paire \((n, k)\).

gcd(n, k)

n.xgcd(k)

Retourne le triplet formé du pgcd et des coefficients de Bezout de la paire \((n, k)\).

xgcd(n, k)

n.quo_rem(k)

Calcule la paire (quotient, reste) de la division euclidienne de \(n\) par \(k\).

divmod(n, k)

n.inverse_mod(k)

Calcule l’inverse de \(n\) modulo \(k\).

inverse_mod(n, k)

n.divides(k)

Teste si l’entier \(n\) est divisible par \(k\).

n % k == 0

n.is_prime()

Teste si \(n\) est premier.

is_prime(n)

n.is_square()

Teste si \(n\) est un carré parfait.

is_square(n)

n.factor()

Retourne la factorisation de \(n\).

factor(n)

n.divisors()

Retourne la liste des diviseurs de \(n\).

divisors(n)

n.bits()

Retourne la liste des coefficients de la décomposition binaire de \(n\) (en commençant par le bit de poids faible).

pas d’équivalent direct

Quelques exemples :

n = 9
print(n.factorial())
print(n.divides(36))
print(n.is_prime())
print(n.is_square())
print(n.inverse_mod(17))
print(n.quo_rem(4))
362880
True
False
True
2
(2, 1)

Observons ici que Sagemath n’est pas gêné pour le calcul d’entiers de taille importante.

m = 100
print(m.factorial())
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

Notion de classe parente#

Pour finir, Sagemath permet de manipuler non seulement les entiers, mais également l’ensemble des entiers, vus comme une structure algébrique (on sait que cet ensemble a une structure d’anneau).

La variable ZZ a été prédéfinie dans Sagemath pour stocker cet anneau. L’instruction IntegerRing() permet aussi d’y accéder.

ZZ
Integer Ring

Sagemath a donc lié les entiers à leur anneau par une relation de parenté : l’anneau ZZ est un parent de tout objet de la classe Integer. C’est une manière de représenter la relation d’appartenance à une structure.

Pour observer cela, on peut demander à Sagemath le parent d’un entier a quelconque, ou simplement tester l’appartenance de a à l’objet ZZ :

a = -2
print(a.parent())
print(a in ZZ)
Integer Ring
True

Nombres rationnels#

Création et classes associées#

Contrairement à python, Sagemath permet de stocker des nombres rationnels sous forme exacte et réduite. Si a et b sont deux entiers, alors le rationnel \(\frac{a}{b}\) peut être construit avec a/b.

x = 4/6
x
2/3

Avertissement

L’opérateur / a donc un comportement différent sous Sagemath et sous python !

Un rationnel est un élément de la classe Rational.

print(type(x))
<class 'sage.rings.rational.Rational'>

Sa classe parente est le corps des rationnels, affiché Rational Field, à laquelle on accède par la variable prédéfinie QQ ou par le constructeur RationalField().

print(parent(x))
print(x in QQ)
Rational Field
True

Opérations élémentaires#

On peut effectuer des opérations élémentaires sur les rationnels, et ce, avec les mêmes opérateurs que sur les entiers :

x = 2/3
print(x + 1/2)
print(3*x)
print(x**2)
print(-3/x)
7/6
2
4/9
-9/2

Comme pour les entiers, il existe des méthodes à appliquer aux rationnels. Par exemple, on peut accéder au numérateur et au dénominateur de la fraction réduite :

a = 4/-6
p = a.numerator()
q = a.denominator()
print(p)
print(q)
-2
3

On observe que le numérateur et le dénominateur sont des objets de la classe Integer :

print(type(p))
print(isinstance(p, Integer))      # teste si p est une instance de la classe Integer 
<class 'sage.rings.integer.Integer'>
True

Conversion de type#

Enfin, il peut être utile de convertir un objet de la classe Rational en un objet de la class Integer. On parle de coercion.

Attention, ce n’est pas un arrondi : on transfère les informations d’une classe à une autre, autrement dit, on transforme simplement le type de l’objet.

Dans l’exemple suivant, la variable b contient la valeur \(1\), vue comme un rationnel :

a = 1/3
b = a + 2/3
print(b)
print(isinstance(b, Rational))
print(isinstance(b, Integer))
1
True
False

On peut ensuite la convertir en un entier :

c = Integer(b)
print(c)
print(isinstance(c, Integer))
print(b == c)
1
True
True

Bien entendu, pour être effective, cette conversion doit être possible, sinon on obtient une erreur :

x = 1/3
y = Integer(x)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/tmp/ipykernel_116844/2720979961.py in <module>
      1 x = Integer(1)/Integer(3)
----> 2 y = Integer(x)

/usr/lib/python3/dist-packages/sage/rings/integer.pyx in sage.rings.integer.Integer.__init__ (build/cythonized/sage/rings/integer.c:5816)()
    656                 otmp = getattr(x, "_integer_", None)
    657                 if otmp is not None:
--> 658                     set_from_Integer(self, otmp(the_integer_ring))
    659                     return
    660 

/usr/lib/python3/dist-packages/sage/rings/rational.pyx in sage.rings.rational.Rational._integer_ (build/cythonized/sage/rings/rational.cpp:24315)()
   2969         """
   2970         if not mpz_cmp_si(mpq_denref(self.value), 1) == 0:
-> 2971             raise TypeError("no conversion of this rational to integer")
   2972         cdef Integer n = Integer.__new__(Integer)
   2973         n.set_from_mpz(mpq_numref(self.value))

TypeError: no conversion of this rational to integer

Tableau récapitulatif des méthodes ou fonctions sur les rationnels :

Méthode (où r est de type Rational)

Description

r.denominator()

Retourne de dénominateur de \(r \in \mathbb{Q}\)

r.numerator()

Retourne de numérateur de \(r \in \mathbb{Q}\)

Nombres complexes#

Dans Sagemath, on peut également manipuler des nombres complexes sous une forme exacte. Pour cela, il suffit d’avoir accès à une variable qui stocke \(i\), le nombre imaginaire pur. Dans Sagemath, c’est la variable prédéfinie I.

print(I)
print(I**2)
I
-1

La classe parente de la variable prédéfinie I a varié suivant les versions de Sagemath, et peut vous paraître obscure pour certaines (« corps de nombres défini par le polynôme \(x^2+1\) » pour la version 10.1), mais pourra peut-être s’expliquer dans vos prochaines années d’études en mathématiques.

print(parent(I))
Number Field in I with defining polynomial x^2 + 1 with I = 1*I

Avertissement

Il faut donc faire attention à ne jamais écraser la variable I !

Opérations élémentaires#

On peut effectuer des opérations élémnentaires sur les complexes d’une manière similaire à celles faites sur les entiers ou les rationnels.

x = 3 - I/2
print(I * (x - 1))
print(x**2)
print(x / (2 + I))
2*I + 1/2
-3*I + 35/4
-4/5*I + 11/10

Certaines méthodes permettent d’accéder à la partie réelle, la partie imaginaire, le conjugué et le module (au carré ou non) d’un nombre complexe.

z = 1 - 2*I
print(z.real_part())
print(z.imag_part())
print(z.conjugate())
print(z.norm())
print(z.abs())
1
-2
2*I + 1
5
2.23606797749979

Enfin, on peut accéder à l’argument principal d’un nombre complexe \(z\) grâce à la fonction arg. Il n’existe pas de méthode équivalente, car la classe parente I manipule aussi des éléments qui n’ont pas d’argument.

print(arg(z))
-arctan(2)

Tableau récapitulatif des méthodes ou fonctions sur les rationnels :

Méthode ou fonction

Description

z.real_part()

Retourne la partie réelle de \(z\).

z.imag_part()

Retourne la partie imaginaire de \(z\).

z.conjugate()

Retourne la conjugué complexe de \(z\).

z.norm()

Retourne le module au carré de \(z\).

z.abs()

Retourne le module de \(z\).

arg(z)

Retourne l’argument de \(z\).

Fonctions et constantes classiques#

Sagemath permet la manipulation exacte de fonctions et de constantes classiques en mathématiques. Pour cela, certains de ces objets doivent être vus de manière symbolique (et non numérique).

Prenons l’exemple de la fonction sinus et de la constante \(\pi\). Lorsqu’on demande la valeur de \(\sin(\pi)\) à Sagemath, aucun calcul numérique n’est effectué ; simplement, on applique les propriétés symboliques de ces objets, parmi lesquelles on retrouve le fait que \(\sin(\pi)\) vaut exactement \(0\).

sin(pi)
0

De la même manière, \(\sqrt{2}\) n’est pas stockée comme sa valeur approchée \(1.414...\) mais comme le nombre positif qui, lorsqu’il est élevé au carré, donne \(2\).

a = sqrt(2)
print(a)
print(a**2)
sqrt(2)
2

On peut d’ailleurs remarquer que la classe parente de tous ces nombres (\(\pi\), \(e\), \(\zeta(3)\), \(\sqrt{2}\), etc.) est Symbolic Ring. Cela signifie qu’on manipule bien des expressions symboliques, qui ne sont donc pas des valeurs numériques.

print(a.parent())
Symbolic Ring

Nous verrons dans une séance future les subtilités de cette distinction. Gardons néanmoins en mémoire la particularité suivante. Grâce à ces expessions symboliques (et donc, contrairement à la bibliothèque standard de python), Sagemath sait que certaines fonctions ne sont pas définies en des points singuliers. Par exemple :

tan(pi/2)
Infinity

Pour finir, voici une liste de fonctions courantes en mathématiques (il en manque certainement, n’hésitez pas à chercher ou demander !).

Tableau récapitulatif de fonctions ou objets symboliques dans Sagemath.

Fonction ou objet

Description

exp(x, b) et log(x, b)

Exponentielle et logarithme de \(x\) en base \(b\).

exp(x) et log(x)

Exponentielle et logarithme de \(x\) en base \(e\).

sin(t), cos(t), tan(t)

Sinus, cosinus et tangente de \(t\).

asin(y), acos(y), atan(y)

Arcsinus, arccosinus et arctangente de \(y\).

sqrt(x), x**(p/q)

Racine carrée et puissance rationnelle \((p/q)\)-ème de \(x\).

pi, e, I

Nombres \(\pi\), \(e\) et \(i\).