Quelques mathématiques plus avancées

Géométrie algébrique

Il est possible de définir des variétés algébriques arbitraires avec Sage, mais les fonctionnalités non triviales sont parfois limitées aux anneaux sur \(\QQ\) ou sur les corps finis. Calculons par exemple la réunion de deux courbes planes affines, puis récupérons chaque courbe en tant que composante irréductible de la réunion.

sage: x, y = AffineSpace(2, QQ, 'xy').gens()
sage: C2 = Curve(x^2 + y^2 - 1)
sage: C3 = Curve(x^3 + y^3 - 1)
sage: D = C2 + C3
sage: D
Affine Plane Curve over Rational Field defined by
   x^5 + x^3*y^2 + x^2*y^3 + y^5 - x^3 - y^3 - x^2 - y^2 + 1
sage: D.irreducible_components()
[
Closed subscheme of Affine Space of dimension 2 over Rational Field defined by:
  x^2 + y^2 - 1,
Closed subscheme of Affine Space of dimension 2 over Rational Field defined by:
  x^3 + y^3 - 1
]
>>> from sage.all import *
>>> x, y = AffineSpace(Integer(2), QQ, 'xy').gens()
>>> C2 = Curve(x**Integer(2) + y**Integer(2) - Integer(1))
>>> C3 = Curve(x**Integer(3) + y**Integer(3) - Integer(1))
>>> D = C2 + C3
>>> D
Affine Plane Curve over Rational Field defined by
   x^5 + x^3*y^2 + x^2*y^3 + y^5 - x^3 - y^3 - x^2 - y^2 + 1
>>> D.irreducible_components()
[
Closed subscheme of Affine Space of dimension 2 over Rational Field defined by:
  x^2 + y^2 - 1,
Closed subscheme of Affine Space of dimension 2 over Rational Field defined by:
  x^3 + y^3 - 1
]
x, y = AffineSpace(2, QQ, 'xy').gens()
C2 = Curve(x^2 + y^2 - 1)
C3 = Curve(x^3 + y^3 - 1)
D = C2 + C3
D
D.irreducible_components()

Nous pouvons également trouver tous les points d’intersection des deux courbes en les intersectant et en calculant les composantes irréductibles.

sage: V = C2.intersection(C3)
sage: V.irreducible_components()
[
Closed subscheme of Affine Space of dimension 2 over Rational Field defined by:
  y,
  x - 1,
Closed subscheme of Affine Space of dimension 2 over Rational Field defined by:
  y - 1,
  x,
Closed subscheme of Affine Space of dimension 2 over Rational Field defined by:
  x + y + 2,
  2*y^2 + 4*y + 3
]
>>> from sage.all import *
>>> V = C2.intersection(C3)
>>> V.irreducible_components()
[
Closed subscheme of Affine Space of dimension 2 over Rational Field defined by:
  y,
  x - 1,
Closed subscheme of Affine Space of dimension 2 over Rational Field defined by:
  y - 1,
  x,
Closed subscheme of Affine Space of dimension 2 over Rational Field defined by:
  x + y + 2,
  2*y^2 + 4*y + 3
]
V = C2.intersection(C3)
V.irreducible_components()

Ainsi, par exemple, \((1,0)\) et \((0,1)\) appartiennent aux deux courbes (ce dont on pouvait directement s’apercevoir) ; il en va de même des points (quadratiques), dont la coordonnée en \(y\) satisfait à l’équation \(2y^2 + 4y + 3=0\).

Sage peut calculer l’idéal torique de la cubique gauche dans l’espace projectif de dimension 3.

sage: R.<a,b,c,d> = PolynomialRing(QQ, 4)
sage: I = ideal(b^2-a*c, c^2-b*d, a*d-b*c)
sage: F = I.groebner_fan(); F
Groebner fan of the ideal:
Ideal (b^2 - a*c, c^2 - b*d, -b*c + a*d) of Multivariate Polynomial Ring
in a, b, c, d over Rational Field
sage: F.reduced_groebner_bases ()
[[-c^2 + b*d, -b*c + a*d, -b^2 + a*c],
 [-b*c + a*d, -c^2 + b*d, b^2 - a*c],
 [-c^3 + a*d^2, -c^2 + b*d, b*c - a*d, b^2 - a*c],
 [-c^2 + b*d, b^2 - a*c, b*c - a*d, c^3 - a*d^2],
 [-b*c + a*d, -b^2 + a*c, c^2 - b*d],
 [-b^3 + a^2*d, -b^2 + a*c, c^2 - b*d, b*c - a*d],
 [-b^2 + a*c, c^2 - b*d, b*c - a*d, b^3 - a^2*d],
 [c^2 - b*d, b*c - a*d, b^2 - a*c]]
sage: F.polyhedralfan()
Polyhedral fan in 4 dimensions of dimension 4
>>> from sage.all import *
>>> R = PolynomialRing(QQ, Integer(4), names=('a', 'b', 'c', 'd',)); (a, b, c, d,) = R._first_ngens(4)
>>> I = ideal(b**Integer(2)-a*c, c**Integer(2)-b*d, a*d-b*c)
>>> F = I.groebner_fan(); F
Groebner fan of the ideal:
Ideal (b^2 - a*c, c^2 - b*d, -b*c + a*d) of Multivariate Polynomial Ring
in a, b, c, d over Rational Field
>>> F.reduced_groebner_bases ()
[[-c^2 + b*d, -b*c + a*d, -b^2 + a*c],
 [-b*c + a*d, -c^2 + b*d, b^2 - a*c],
 [-c^3 + a*d^2, -c^2 + b*d, b*c - a*d, b^2 - a*c],
 [-c^2 + b*d, b^2 - a*c, b*c - a*d, c^3 - a*d^2],
 [-b*c + a*d, -b^2 + a*c, c^2 - b*d],
 [-b^3 + a^2*d, -b^2 + a*c, c^2 - b*d, b*c - a*d],
 [-b^2 + a*c, c^2 - b*d, b*c - a*d, b^3 - a^2*d],
 [c^2 - b*d, b*c - a*d, b^2 - a*c]]
>>> F.polyhedralfan()
Polyhedral fan in 4 dimensions of dimension 4
R.<a,b,c,d> = PolynomialRing(QQ, 4)
I = ideal(b^2-a*c, c^2-b*d, a*d-b*c)
F = I.groebner_fan(); F
F.reduced_groebner_bases ()
F.polyhedralfan()

Courbes elliptiques

Les fonctionnalités relatives aux courbes elliptiques comprennent la plupart des fonctionnalités de PARI, l’accès aux données des tables en ligne de Cremona (ceci requiert le chargement d’une base de donnée optionnelle), les fonctionnalités de mwrank, c’est-à-dire la 2-descente avec calcul du groupe de Mordell-Weil complet, l’algorithme SEA, le calcul de toutes les isogénies, beaucoup de nouveau code pour les courbes sur \(\QQ\) et une partie du code de descente algébrique de Denis Simon.

La commande EllipticCurve permet de créer une courbe elliptique avec beaucoup de souplesse :

  • EllipticCurve([\(a_1\), \(a_2\), \(a_3\), \(a_4\), \(a_6\)]) : renvoie la courbe elliptique

    \[y^2+a_1xy+a_3y=x^3+a_2x^2+a_4x+a_6,\]

    où les \(a_i\)’s sont convertis par coercition dans le parent de \(a_1\). Si tous les \(a_i\) ont pour parent \(\ZZ\), ils sont convertis par coercition dans \(\QQ\).

  • EllipticCurve([\(a_4\), \(a_6\)]) : idem avec \(a_1=a_2=a_3=0\).

  • EllipticCurve(label) : Renvoie la courbe elliptique sur \(\QQ\) de la base de données de Cremona selon son nom dans la (nouvelle !) nomenclature de Cremona. Les courbes sont étiquetées par une chaîne de caractère telle que "11a" ou "37b2". La lettre doit être en minuscule (pour faire la différence avec l’ancienne nomenclature).

  • EllipticCurve(j) : renvoie une courbe elliptique de \(j\)-invariant \(j\).

  • EllipticCurve(R, [\(a_1\), \(a_2\), \(a_3\), \(a_4\), \(a_6\)]) : Crée la courbe elliptique sur l’anneau \(R\) donnée par les coefficients \(a_i\) comme ci-dessus.

Illustrons chacune de ces constructions :

sage: EllipticCurve([0,0,1,-1,0])
Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field

sage: EllipticCurve([GF(5)(0),0,1,-1,0])
Elliptic Curve defined by y^2 + y = x^3 + 4*x over Finite Field of size 5

sage: EllipticCurve([1,2])
Elliptic Curve defined by y^2  = x^3 + x + 2 over Rational Field

sage: EllipticCurve('37a')
Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field

sage: EllipticCurve_from_j(1)
Elliptic Curve defined by y^2 + x*y = x^3 + 36*x + 3455 over Rational Field

sage: EllipticCurve(GF(5), [0,0,1,-1,0])
Elliptic Curve defined by y^2 + y = x^3 + 4*x over Finite Field of size 5
>>> from sage.all import *
>>> EllipticCurve([Integer(0),Integer(0),Integer(1),-Integer(1),Integer(0)])
Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field

>>> EllipticCurve([GF(Integer(5))(Integer(0)),Integer(0),Integer(1),-Integer(1),Integer(0)])
Elliptic Curve defined by y^2 + y = x^3 + 4*x over Finite Field of size 5

>>> EllipticCurve([Integer(1),Integer(2)])
Elliptic Curve defined by y^2  = x^3 + x + 2 over Rational Field

>>> EllipticCurve('37a')
Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field

>>> EllipticCurve_from_j(Integer(1))
Elliptic Curve defined by y^2 + x*y = x^3 + 36*x + 3455 over Rational Field

>>> EllipticCurve(GF(Integer(5)), [Integer(0),Integer(0),Integer(1),-Integer(1),Integer(0)])
Elliptic Curve defined by y^2 + y = x^3 + 4*x over Finite Field of size 5
EllipticCurve([0,0,1,-1,0])
EllipticCurve([GF(5)(0),0,1,-1,0])
EllipticCurve([1,2])
EllipticCurve('37a')
EllipticCurve_from_j(1)
EllipticCurve(GF(5), [0,0,1,-1,0])

Le couple \((0,0)\) est un point de la courbe elliptique \(E\) définie par \(y^2 + y = x^3 - x\). Pour créer ce point avec Sage, il convient de taper E([0,0]). Sage peut additionner des points sur une telle courbe elliptique (rappelons qu’une courbe elliptique possède une structure de groupe additif où le point à l’infini représente l’élément neutre et où trois points alignés de la courbe sont de somme nulle) :

sage: E = EllipticCurve([0,0,1,-1,0])
sage: E
Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field
sage: P = E([0,0])
sage: P + P
(1 : 0 : 1)
sage: 10*P
(161/16 : -2065/64 : 1)
sage: 20*P
(683916417/264517696 : -18784454671297/4302115807744 : 1)
sage: E.conductor()
37
>>> from sage.all import *
>>> E = EllipticCurve([Integer(0),Integer(0),Integer(1),-Integer(1),Integer(0)])
>>> E
Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field
>>> P = E([Integer(0),Integer(0)])
>>> P + P
(1 : 0 : 1)
>>> Integer(10)*P
(161/16 : -2065/64 : 1)
>>> Integer(20)*P
(683916417/264517696 : -18784454671297/4302115807744 : 1)
>>> E.conductor()
37
E = EllipticCurve([0,0,1,-1,0])
E
P = E([0,0])
P + P
10*P
20*P
E.conductor()

Les courbes elliptiques sur les nombres complexes sont paramétrées par leur \(j\)-invariant. Sage calcule le \(j\)-invariant comme suit :

sage: E = EllipticCurve([0,0,0,-4,2]); E
Elliptic Curve defined by y^2 = x^3 - 4*x + 2 over Rational Field
sage: E.conductor()
2368
sage: E.j_invariant()
110592/37
>>> from sage.all import *
>>> E = EllipticCurve([Integer(0),Integer(0),Integer(0),-Integer(4),Integer(2)]); E
Elliptic Curve defined by y^2 = x^3 - 4*x + 2 over Rational Field
>>> E.conductor()
2368
>>> E.j_invariant()
110592/37
E = EllipticCurve([0,0,0,-4,2]); E
E.conductor()
E.j_invariant()

Si l’on fabrique une courbe avec le même \(j\)-invariant que celui de \(E\), elle n’est pas nécessairement isomorphe à \(E\). Dans l’exemple suivant, les courbes ne sont pas isomorphes parce que leur conducteur est différent.

sage: F = EllipticCurve_from_j(110592/37)
sage: F.conductor()
37
>>> from sage.all import *
>>> F = EllipticCurve_from_j(Integer(110592)/Integer(37))
>>> F.conductor()
37
F = EllipticCurve_from_j(110592/37)
F.conductor()

Toutefois, le twist de \(F\) par 2 donne une courbe isomorphe.

sage: G = F.quadratic_twist(2); G
Elliptic Curve defined by y^2 = x^3 - 4*x + 2 over Rational Field
sage: G.conductor()
2368
sage: G.j_invariant()
110592/37
>>> from sage.all import *
>>> G = F.quadratic_twist(Integer(2)); G
Elliptic Curve defined by y^2 = x^3 - 4*x + 2 over Rational Field
>>> G.conductor()
2368
>>> G.j_invariant()
110592/37
G = F.quadratic_twist(2); G
G.conductor()
G.j_invariant()

On peut calculer les coefficients \(a_n\) de la série-\(L\) ou forme modulaire \(\sum_{n=0}^\infty a_nq^n\) attachée à une courbe elliptique. Le calcul s’effectue en utilisant la bibliothèque PARI écrite en C :

sage: E = EllipticCurve([0,0,1,-1,0])
sage: E.anlist(30)
[0, 1, -2, -3, 2, -2, 6, -1, 0, 6, 4, -5, -6, -2, 2, 6, -4, 0, -12, 0, -4,
 3, 10, 2, 0, -1, 4, -9, -2, 6, -12]
sage: v = E.anlist(10000)
>>> from sage.all import *
>>> E = EllipticCurve([Integer(0),Integer(0),Integer(1),-Integer(1),Integer(0)])
>>> E.anlist(Integer(30))
[0, 1, -2, -3, 2, -2, 6, -1, 0, 6, 4, -5, -6, -2, 2, 6, -4, 0, -12, 0, -4,
 3, 10, 2, 0, -1, 4, -9, -2, 6, -12]
>>> v = E.anlist(Integer(10000))
E = EllipticCurve([0,0,1,-1,0])
E.anlist(30)
v = E.anlist(10000)

Il faut à peine quelques secondes pour calculer tous les coefficients \(a_n\) pour \(n\leq 10^5\):

sage: %time v = E.anlist(100000)
CPU times: user 0.98 s, sys: 0.06 s, total: 1.04 s
Wall time: 1.06
>>> from sage.all import *
>>> %time v = E.anlist(Integer(100000))
CPU times: user 0.98 s, sys: 0.06 s, total: 1.04 s
Wall time: 1.06
%time v = E.anlist(100000)

Les courbes elliptiques peuvent être construites en utilisant leur nom dans la nomenclature de Cremona. Ceci charge par avance la courbe elliptique avec les informations la concernant, telles que son rang, son nombre de Tamagawa, son régulateur, etc.

sage: E = EllipticCurve("37b2")
sage: E
Elliptic Curve defined by y^2 + y = x^3 + x^2 - 1873*x - 31833 over Rational
Field
sage: E = EllipticCurve("389a")
sage: E
Elliptic Curve defined by y^2 + y = x^3 + x^2 - 2*x  over Rational Field
sage: E.rank()
2
sage: E = EllipticCurve("5077a")
sage: E.rank()
3
>>> from sage.all import *
>>> E = EllipticCurve("37b2")
>>> E
Elliptic Curve defined by y^2 + y = x^3 + x^2 - 1873*x - 31833 over Rational
Field
>>> E = EllipticCurve("389a")
>>> E
Elliptic Curve defined by y^2 + y = x^3 + x^2 - 2*x  over Rational Field
>>> E.rank()
2
>>> E = EllipticCurve("5077a")
>>> E.rank()
3
E = EllipticCurve("37b2")
E
E = EllipticCurve("389a")
E
E.rank()
E = EllipticCurve("5077a")
E.rank()

On peut aussi accéder à la base de données de Cremona directement.

sage: db = sage.databases.cremona.CremonaDatabase()
sage: db.curves(37)
{'a1': [[0, 0, 1, -1, 0], 1, 1], 'b1': [[0, 1, 1, -23, -50], 0, 3]}
sage: db.allcurves(37)
{'a1': [[0, 0, 1, -1, 0], 1, 1],
 'b1': [[0, 1, 1, -23, -50], 0, 3],
 'b2': [[0, 1, 1, -1873, -31833], 0, 1],
 'b3': [[0, 1, 1, -3, 1], 0, 3]}
>>> from sage.all import *
>>> db = sage.databases.cremona.CremonaDatabase()
>>> db.curves(Integer(37))
{'a1': [[0, 0, 1, -1, 0], 1, 1], 'b1': [[0, 1, 1, -23, -50], 0, 3]}
>>> db.allcurves(Integer(37))
{'a1': [[0, 0, 1, -1, 0], 1, 1],
 'b1': [[0, 1, 1, -23, -50], 0, 3],
 'b2': [[0, 1, 1, -1873, -31833], 0, 1],
 'b3': [[0, 1, 1, -3, 1], 0, 3]}
db = sage.databases.cremona.CremonaDatabase()
db.curves(37)
db.allcurves(37)

Les objets extraits de la base de données ne sont pas de type EllipticCurve, mais de simples entrées de base de données formées de quelques champs. Par défaut, Sage est distribué avec une version réduite de la base de données de Cremona qui ne contient que des informations limitées sur les courbes elliptiques de conducteur \(\leq 10000\). Il existe également en option une version plus complète qui contient des données étendues portant sur toute les courbes de conducteur jusqu’à \(120000\) (à la date d’octobre 2005). Une autre - énorme (2GB) - base de données optionnelle, fournie dans un package séparé, contient des centaines de millions de courbes elliptiques de la bases de donnée de Stein-Watkins.

Caractères de Dirichlet

Un caractère de Dirichlet est une extension d’un homomorphisme \((\ZZ/N\ZZ)^* \to R^*\), pour un certain anneau \(R\), à l’application \(\ZZ \to R\) obtenue en envoyant les entiers \(x\) tels que \(\gcd(N,x)>1\) vers 0.

sage: G = DirichletGroup(12)
sage: G.list()
[Dirichlet character modulo 12 of conductor 1 mapping 7 |--> 1, 5 |--> 1,
Dirichlet character modulo 12 of conductor 4 mapping 7 |--> -1, 5 |--> 1,
Dirichlet character modulo 12 of conductor 3 mapping 7 |--> 1, 5 |--> -1,
Dirichlet character modulo 12 of conductor 12 mapping 7 |--> -1, 5 |--> -1]
sage: G.gens()
(Dirichlet character modulo 12 of conductor 4 mapping 7 |--> -1, 5 |--> 1,
Dirichlet character modulo 12 of conductor 3 mapping 7 |--> 1, 5 |--> -1)
sage: len(G)
4
>>> from sage.all import *
>>> G = DirichletGroup(Integer(12))
>>> G.list()
[Dirichlet character modulo 12 of conductor 1 mapping 7 |--> 1, 5 |--> 1,
Dirichlet character modulo 12 of conductor 4 mapping 7 |--> -1, 5 |--> 1,
Dirichlet character modulo 12 of conductor 3 mapping 7 |--> 1, 5 |--> -1,
Dirichlet character modulo 12 of conductor 12 mapping 7 |--> -1, 5 |--> -1]
>>> G.gens()
(Dirichlet character modulo 12 of conductor 4 mapping 7 |--> -1, 5 |--> 1,
Dirichlet character modulo 12 of conductor 3 mapping 7 |--> 1, 5 |--> -1)
>>> len(G)
4
G = DirichletGroup(12)
G.list()
G.gens()
len(G)

Une fois le groupe créé, on crée aussitôt un élément et on calcule avec lui.

sage: G = DirichletGroup(21)
sage: chi = G.1; chi
Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> zeta6
sage: chi.values()
[0, 1, zeta6 - 1, 0, -zeta6, -zeta6 + 1, 0, 0, 1, 0, zeta6, -zeta6, 0, -1,
 0, 0, zeta6 - 1, zeta6, 0, -zeta6 + 1, -1]
sage: chi.conductor()
7
sage: chi.modulus()
21
sage: chi.order()
6
sage: chi(19)
-zeta6 + 1
sage: chi(40)
-zeta6 + 1
>>> from sage.all import *
>>> G = DirichletGroup(Integer(21))
>>> chi = G.gen(1); chi
Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> zeta6
>>> chi.values()
[0, 1, zeta6 - 1, 0, -zeta6, -zeta6 + 1, 0, 0, 1, 0, zeta6, -zeta6, 0, -1,
 0, 0, zeta6 - 1, zeta6, 0, -zeta6 + 1, -1]
>>> chi.conductor()
7
>>> chi.modulus()
21
>>> chi.order()
6
>>> chi(Integer(19))
-zeta6 + 1
>>> chi(Integer(40))
-zeta6 + 1
G = DirichletGroup(21)
chi = G.1; chi
chi.values()
chi.conductor()
chi.modulus()
chi.order()
chi(19)
chi(40)

Il est possible aussi de calculer l’action d’un groupe de Galois \(\text{Gal}(\QQ(\zeta_N)/\QQ)\) sur l’un de ces caractères, de même qu’une décomposition en produit direct correspondant à la factorisation du module.

sage: chi.galois_orbit()
[Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> -zeta6 + 1,
 Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> zeta6]

sage: go = G.galois_orbits()
sage: [len(orbit) for orbit in go]
[1, 2, 2, 1, 1, 2, 2, 1]

sage: G.decomposition()
[
Group of Dirichlet characters modulo 3 with values in Cyclotomic Field of order 6 and degree 2,
Group of Dirichlet characters modulo 7 with values in Cyclotomic Field of order 6 and degree 2
]
>>> from sage.all import *
>>> chi.galois_orbit()
[Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> -zeta6 + 1,
 Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> zeta6]

>>> go = G.galois_orbits()
>>> [len(orbit) for orbit in go]
[1, 2, 2, 1, 1, 2, 2, 1]

>>> G.decomposition()
[
Group of Dirichlet characters modulo 3 with values in Cyclotomic Field of order 6 and degree 2,
Group of Dirichlet characters modulo 7 with values in Cyclotomic Field of order 6 and degree 2
]
chi.galois_orbit()
go = G.galois_orbits()
[len(orbit) for orbit in go]
G.decomposition()

Construisons à present le groupe de caractères de Dirichlet modulo 20, mais à valeur dans \(\QQ(i)\):

sage: K.<i> = NumberField(x^2+1)
sage: G = DirichletGroup(20,K)
sage: G
Group of Dirichlet characters modulo 20 with values in Number Field in i with defining polynomial x^2 + 1
>>> from sage.all import *
>>> K = NumberField(x**Integer(2)+Integer(1), names=('i',)); (i,) = K._first_ngens(1)
>>> G = DirichletGroup(Integer(20),K)
>>> G
Group of Dirichlet characters modulo 20 with values in Number Field in i with defining polynomial x^2 + 1
K.<i> = NumberField(x^2+1)
G = DirichletGroup(20,K)
G

Nous calculons ensuite différents invariants de G:

sage: G.gens()
(Dirichlet character modulo 20 of conductor 4 mapping 11 |--> -1, 17 |--> 1,
Dirichlet character modulo 20 of conductor 5 mapping 11 |--> 1, 17 |--> i)

sage: G.unit_gens()
(11, 17)
sage: G.zeta()
i
sage: G.zeta_order()
4
>>> from sage.all import *
>>> G.gens()
(Dirichlet character modulo 20 of conductor 4 mapping 11 |--> -1, 17 |--> 1,
Dirichlet character modulo 20 of conductor 5 mapping 11 |--> 1, 17 |--> i)

>>> G.unit_gens()
(11, 17)
>>> G.zeta()
i
>>> G.zeta_order()
4
G.gens()
G.unit_gens()
G.zeta()
G.zeta_order()

Dans cet exemple, nous créons un caractère de Dirichlet à valeurs dans un corps de nombres. Nous spécifions ci-dessous explicitement le choix de la racine de l’unité par le troisième argument de la fonction DirichletGroup.

sage: x = polygen(QQ, 'x')
sage: K = NumberField(x^4 + 1, 'a'); a = K.0
sage: b = K.gen(); a == b
True
sage: K
Number Field in a with defining polynomial x^4 + 1
sage: G = DirichletGroup(5, K, a); G
Group of Dirichlet characters modulo 5 with values in the group of order 8 generated by a in Number Field in a with defining polynomial x^4 + 1
sage: chi = G.0; chi
Dirichlet character modulo 5 of conductor 5 mapping 2 |--> a^2
sage: [(chi^i)(2) for i in range(4)]
[1, a^2, -1, -a^2]
>>> from sage.all import *
>>> x = polygen(QQ, 'x')
>>> K = NumberField(x**Integer(4) + Integer(1), 'a'); a = K.gen(0)
>>> b = K.gen(); a == b
True
>>> K
Number Field in a with defining polynomial x^4 + 1
>>> G = DirichletGroup(Integer(5), K, a); G
Group of Dirichlet characters modulo 5 with values in the group of order 8 generated by a in Number Field in a with defining polynomial x^4 + 1
>>> chi = G.gen(0); chi
Dirichlet character modulo 5 of conductor 5 mapping 2 |--> a^2
>>> [(chi**i)(Integer(2)) for i in range(Integer(4))]
[1, a^2, -1, -a^2]
x = polygen(QQ, 'x')
K = NumberField(x^4 + 1, 'a'); a = K.0
b = K.gen(); a == b
K
G = DirichletGroup(5, K, a); G
chi = G.0; chi
[(chi^i)(2) for i in range(4)]

Ici, NumberField(x^4 + 1, 'a') indique à Sage d’utiliser le symbole « a » dans l’affichage de ce qu’est K (un corps de nombre en « a » défini par le polynôme \(x^4 + 1\)). Le nom « a » n’est pas déclaré à ce point. Une fois que a = K.0 (ou de manière équivalente a = K.gen()) est évalué, le symbole « a » représente une racine du polynôme générateur \(x^4+1\).

Formes modulaires

Sage peut accomplir des calculs relatifs aux formes modulaires, notamment des calculs de dimension, d’espace de symboles modulaires, d’opérateurs de Hecke et de décomposition.

Il y a plusieurs fonctions disponibles pour calculer la dimension d’espaces de formes modulaires. Par exemple,

sage: from sage.modular.dims import dimension_cusp_forms
sage: dimension_cusp_forms(Gamma0(11),2)
1
sage: dimension_cusp_forms(Gamma0(1),12)
1
sage: dimension_cusp_forms(Gamma1(389),2)
6112
>>> from sage.all import *
>>> from sage.modular.dims import dimension_cusp_forms
>>> dimension_cusp_forms(Gamma0(Integer(11)),Integer(2))
1
>>> dimension_cusp_forms(Gamma0(Integer(1)),Integer(12))
1
>>> dimension_cusp_forms(Gamma1(Integer(389)),Integer(2))
6112
from sage.modular.dims import dimension_cusp_forms
dimension_cusp_forms(Gamma0(11),2)
dimension_cusp_forms(Gamma0(1),12)
dimension_cusp_forms(Gamma1(389),2)

Nous illustrons ci-dessous le calcul des opérateurs de Hecke sur un espace de symboles modulaires de niveau \(1\) et de poids \(12\).

sage: M = ModularSymbols(1,12)
sage: M.basis()
([X^8*Y^2,(0,0)], [X^9*Y,(0,0)], [X^10,(0,0)])
sage: t2 = M.T(2)
sage: t2
Hecke operator T_2 on Modular Symbols space of dimension 3 for Gamma_0(1)
of weight 12 with sign 0 over Rational Field
sage: t2.matrix()
[ -24    0    0]
[   0  -24    0]
[4860    0 2049]
sage: f = t2.charpoly('x'); f
x^3 - 2001*x^2 - 97776*x - 1180224
sage: factor(f)
(x - 2049) * (x + 24)^2
sage: M.T(11).charpoly('x').factor()
(x - 285311670612) * (x - 534612)^2
>>> from sage.all import *
>>> M = ModularSymbols(Integer(1),Integer(12))
>>> M.basis()
([X^8*Y^2,(0,0)], [X^9*Y,(0,0)], [X^10,(0,0)])
>>> t2 = M.T(Integer(2))
>>> t2
Hecke operator T_2 on Modular Symbols space of dimension 3 for Gamma_0(1)
of weight 12 with sign 0 over Rational Field
>>> t2.matrix()
[ -24    0    0]
[   0  -24    0]
[4860    0 2049]
>>> f = t2.charpoly('x'); f
x^3 - 2001*x^2 - 97776*x - 1180224
>>> factor(f)
(x - 2049) * (x + 24)^2
>>> M.T(Integer(11)).charpoly('x').factor()
(x - 285311670612) * (x - 534612)^2
M = ModularSymbols(1,12)
M.basis()
t2 = M.T(2)
t2
t2.matrix()
f = t2.charpoly('x'); f
factor(f)
M.T(11).charpoly('x').factor()

Nous pouvons aussi créer des espaces pour \(\Gamma_0(N)\) et \(\Gamma_1(N)\).

sage: ModularSymbols(11,2)
Modular Symbols space of dimension 3 for Gamma_0(11) of weight 2 with sign
 0 over Rational Field
sage: ModularSymbols(Gamma1(11),2)
Modular Symbols space of dimension 11 for Gamma_1(11) of weight 2 with
sign 0 over Rational Field
>>> from sage.all import *
>>> ModularSymbols(Integer(11),Integer(2))
Modular Symbols space of dimension 3 for Gamma_0(11) of weight 2 with sign
 0 over Rational Field
>>> ModularSymbols(Gamma1(Integer(11)),Integer(2))
Modular Symbols space of dimension 11 for Gamma_1(11) of weight 2 with
sign 0 over Rational Field
ModularSymbols(11,2)
ModularSymbols(Gamma1(11),2)

Calculons quelques polynômes caractéristiques et développements en série de Fourier.

sage: M = ModularSymbols(Gamma1(11),2)
sage: M.T(2).charpoly('x')
x^11 - 8*x^10 + 20*x^9 + 10*x^8 - 145*x^7 + 229*x^6 + 58*x^5 - 360*x^4
     + 70*x^3 - 515*x^2 + 1804*x - 1452
sage: M.T(2).charpoly('x').factor()
(x - 3) * (x + 2)^2 * (x^4 - 7*x^3 + 19*x^2 - 23*x + 11)
        * (x^4 - 2*x^3 + 4*x^2 + 2*x + 11)
sage: S = M.cuspidal_submodule()
sage: S.T(2).matrix()
[-2  0]
[ 0 -2]
sage: S.q_expansion_basis(10)
[
    q - 2*q^2 - q^3 + 2*q^4 + q^5 + 2*q^6 - 2*q^7 - 2*q^9 + O(q^10)
]
>>> from sage.all import *
>>> M = ModularSymbols(Gamma1(Integer(11)),Integer(2))
>>> M.T(Integer(2)).charpoly('x')
x^11 - 8*x^10 + 20*x^9 + 10*x^8 - 145*x^7 + 229*x^6 + 58*x^5 - 360*x^4
     + 70*x^3 - 515*x^2 + 1804*x - 1452
>>> M.T(Integer(2)).charpoly('x').factor()
(x - 3) * (x + 2)^2 * (x^4 - 7*x^3 + 19*x^2 - 23*x + 11)
        * (x^4 - 2*x^3 + 4*x^2 + 2*x + 11)
>>> S = M.cuspidal_submodule()
>>> S.T(Integer(2)).matrix()
[-2  0]
[ 0 -2]
>>> S.q_expansion_basis(Integer(10))
[
    q - 2*q^2 - q^3 + 2*q^4 + q^5 + 2*q^6 - 2*q^7 - 2*q^9 + O(q^10)
]
M = ModularSymbols(Gamma1(11),2)
M.T(2).charpoly('x')
M.T(2).charpoly('x').factor()
S = M.cuspidal_submodule()
S.T(2).matrix()
S.q_expansion_basis(10)

On peut même calculer des espaces de formes modulaires avec caractères.

sage: G = DirichletGroup(13)
sage: e = G.0^2
sage: M = ModularSymbols(e,2); M
Modular Symbols space of dimension 4 and level 13, weight 2, character
[zeta6], sign 0, over Cyclotomic Field of order 6 and degree 2
sage: M.T(2).charpoly('x').factor()
(x - zeta6 - 2) * (x - 2*zeta6 - 1) * (x + zeta6 + 1)^2
sage: S = M.cuspidal_submodule(); S
Modular Symbols subspace of dimension 2 of Modular Symbols space of
dimension 4 and level 13, weight 2, character [zeta6], sign 0, over
Cyclotomic Field of order 6 and degree 2
sage: S.T(2).charpoly('x').factor()
(x + zeta6 + 1)^2
sage: S.q_expansion_basis(10)
[
q + (-zeta6 - 1)*q^2 + (2*zeta6 - 2)*q^3 + zeta6*q^4 + (-2*zeta6 + 1)*q^5
  + (-2*zeta6 + 4)*q^6 + (2*zeta6 - 1)*q^8 - zeta6*q^9 + O(q^10)
]
>>> from sage.all import *
>>> G = DirichletGroup(Integer(13))
>>> e = G.gen(0)**Integer(2)
>>> M = ModularSymbols(e,Integer(2)); M
Modular Symbols space of dimension 4 and level 13, weight 2, character
[zeta6], sign 0, over Cyclotomic Field of order 6 and degree 2
>>> M.T(Integer(2)).charpoly('x').factor()
(x - zeta6 - 2) * (x - 2*zeta6 - 1) * (x + zeta6 + 1)^2
>>> S = M.cuspidal_submodule(); S
Modular Symbols subspace of dimension 2 of Modular Symbols space of
dimension 4 and level 13, weight 2, character [zeta6], sign 0, over
Cyclotomic Field of order 6 and degree 2
>>> S.T(Integer(2)).charpoly('x').factor()
(x + zeta6 + 1)^2
>>> S.q_expansion_basis(Integer(10))
[
q + (-zeta6 - 1)*q^2 + (2*zeta6 - 2)*q^3 + zeta6*q^4 + (-2*zeta6 + 1)*q^5
  + (-2*zeta6 + 4)*q^6 + (2*zeta6 - 1)*q^8 - zeta6*q^9 + O(q^10)
]
G = DirichletGroup(13)
e = G.0^2
M = ModularSymbols(e,2); M
M.T(2).charpoly('x').factor()
S = M.cuspidal_submodule(); S
S.T(2).charpoly('x').factor()
S.q_expansion_basis(10)

Voici un autre exemple montrant comment Sage peut calculer l’action d’un opérateur de Hecke sur un espace de formes modulaires.

sage: T = ModularForms(Gamma0(11),2)
sage: T
Modular Forms space of dimension 2 for Congruence Subgroup Gamma0(11) of
weight 2 over Rational Field
sage: T.degree()
2
sage: T.level()
11
sage: T.group()
Congruence Subgroup Gamma0(11)
sage: T.dimension()
2
sage: T.cuspidal_subspace()
Cuspidal subspace of dimension 1 of Modular Forms space of dimension 2 for
Congruence Subgroup Gamma0(11) of weight 2 over Rational Field
sage: T.eisenstein_subspace()
Eisenstein subspace of dimension 1 of Modular Forms space of dimension 2
for Congruence Subgroup Gamma0(11) of weight 2 over Rational Field
sage: M = ModularSymbols(11); M
Modular Symbols space of dimension 3 for Gamma_0(11) of weight 2 with sign
0 over Rational Field
sage: M.weight()
2
sage: M.basis()
((1,0), (1,8), (1,9))
sage: M.sign()
0
>>> from sage.all import *
>>> T = ModularForms(Gamma0(Integer(11)),Integer(2))
>>> T
Modular Forms space of dimension 2 for Congruence Subgroup Gamma0(11) of
weight 2 over Rational Field
>>> T.degree()
2
>>> T.level()
11
>>> T.group()
Congruence Subgroup Gamma0(11)
>>> T.dimension()
2
>>> T.cuspidal_subspace()
Cuspidal subspace of dimension 1 of Modular Forms space of dimension 2 for
Congruence Subgroup Gamma0(11) of weight 2 over Rational Field
>>> T.eisenstein_subspace()
Eisenstein subspace of dimension 1 of Modular Forms space of dimension 2
for Congruence Subgroup Gamma0(11) of weight 2 over Rational Field
>>> M = ModularSymbols(Integer(11)); M
Modular Symbols space of dimension 3 for Gamma_0(11) of weight 2 with sign
0 over Rational Field
>>> M.weight()
2
>>> M.basis()
((1,0), (1,8), (1,9))
>>> M.sign()
0
T = ModularForms(Gamma0(11),2)
T
T.degree()
T.level()
T.group()
T.dimension()
T.cuspidal_subspace()
T.eisenstein_subspace()
M = ModularSymbols(11); M
M.weight()
M.basis()
M.sign()

Notons \(T_p\) les opérateurs de Hecke usuels (\(p\) premier). Comment agissent les opérateurs de Hecke \(T_2\), \(T_3\), \(T_5\) sur l’espace des symboles modulaires ?

sage: M.T(2).matrix()
[ 3  0 -1]
[ 0 -2  0]
[ 0  0 -2]
sage: M.T(3).matrix()
[ 4  0 -1]
[ 0 -1  0]
[ 0  0 -1]
sage: M.T(5).matrix()
[ 6  0 -1]
[ 0  1  0]
[ 0  0  1]
>>> from sage.all import *
>>> M.T(Integer(2)).matrix()
[ 3  0 -1]
[ 0 -2  0]
[ 0  0 -2]
>>> M.T(Integer(3)).matrix()
[ 4  0 -1]
[ 0 -1  0]
[ 0  0 -1]
>>> M.T(Integer(5)).matrix()
[ 6  0 -1]
[ 0  1  0]
[ 0  0  1]
M.T(2).matrix()
M.T(3).matrix()
M.T(5).matrix()