Langlands 01 : formes modulaires — de SL₂(ℤ) aux opérateurs de Hecke

Premier carnet de la série Langlands (Epic #17969) : donner aux formes modulaires un parcours calculable, en amont du socle formel hecke_lean/. Le fil directeur est un geste de Serre — rendre l’objet calculable avant de le croire — appliqué à la direction qu’il décrivait comme orthogonale à la marée montante de Grothendieck : les formes modulaires, les formules explicites, les voies surprenantes (l’entretien Serre–Connes est distillé dans Lean-15, section « Les limites de la marée »).

Ce carnet calcule tout ce qu’il énonce : séries d’Eisenstein, discriminant, valeurs propres de Hecke, et le pont courbe elliptique ↔︎ forme modulaire vérifié premier par premier sur Γ₀(11). Chaque identité est un assert — ce que le carnet affirme, le carnet le vérifie.

« I think it was Martin Eichler who once said that there are five fundamental operations in arithmetic: addition, subtraction, multiplication, division, and modular forms. » – Martin Eichler, rapportée par R. Borcherds en ouverture de l’entretien The most magical subject in math (The Mansions of Science, 2026, YouTube lhE4DZWFQ1U, [00:00]) – la phrase qui donne à la série son titre spirituel : la forme modulaire comme opération arithmétique à part entière.

Plan

  1. Le langage des q-expansions — séries formelles à coefficients entiers
  2. Séries d’Eisenstein \(E_4\) et \(E_6\)
  3. Le discriminant \(\Delta\) et la fonction \(\tau\) de Ramanujan
  4. L’opérateur de Hecke \(T_p\) — la formule des coefficients, calculée
  5. \(\Gamma_0(11)\) : une forme propre de poids 2
  6. Le pont — compter des points sur une courbe elliptique
  7. Ce que le lake hecke_lean formalise, et ce qui reste
  8. Exercices

1. Le langage des q-expansions

Une forme modulaire de poids \(k\) pour \(SL_2(\mathbb{Z})\) est une fonction holomorphe \(f\) sur le demi-plan supérieur avec \(f\left(\frac{a\tau+b}{c\tau+d}\right) = (c\tau+d)^k f(\tau)\), et sa donnée entière est la série de Fourier \(f(\tau) = \sum_{n \geq 0} a_n q^n\), \(q = e^{2\pi i \tau}\). Tout ce carnet vit dans l’algèbre de ces séries à coefficients entiers, tronquées à un ordre borné \(M\) : multiplier, élever à une puissance, comparer — c’est du calcul exact.

« Whittaker and Watson’s book has several chapters on them, and I don’t think either of them ever studied algebraic geometry much. » – R. Borcherds [06:44] : les formes modulaires s’étudiaient bien avant la géométrie algébrique moderne, avec l’outillage analytique du classique de Whittaker & Watson – perspective utile au §1, qui ne demande que des q-séries.

Un mot de géométrie avant le calcul : la condition \(f\left(\frac{a\tau+b}{c\tau+d}\right) = (c\tau+d)^k f(\tau)\) dit que \(f\) est déterminée par ses valeurs sur un seul pavé du demi-plan supérieur — le domaine fondamental \(\mathcal{D} = \{\tau : |\tau| \geq 1,\ |\mathrm{Re}\,\tau| \leq 1/2\}\). Les deux générateurs \(T : \tau \mapsto \tau + 1\) (translation) et \(S : \tau \mapsto -1/\tau\) (inversion dans le cercle unité) envoient \(\mathcal{D}\) sur les pavés voisins. Le schéma ci-dessous, même grossier, fixe l’intuition : tout ce carnet lit \(f\) sur \(\mathcal{D}\) seul.

import matplotlib
import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots(figsize=(7.0, 4.6))
theta = np.linspace(0, np.pi, 200)
cercle = np.exp(1j * theta)

# Domaine fondamental D : |z|>=1, |Re z|<=1/2, Im z > 0 (dessine jusqu'a Im=3)
TOP = 3.0
arc = cercle[(cercle.real >= -0.5) & (cercle.real <= 0.5)]
poly = np.concatenate([[0.5 + 0j], [0.5 + TOP * 1j], [-0.5 + TOP * 1j], [-0.5 + 0j], arc[::-1]])
ax.fill(poly.real, poly.imag, color="#4C72B0", alpha=0.30)
ax.plot(poly.real, poly.imag, color="#4C72B0", lw=1.6, label=r"$\mathcal{D}$")

# Pavés voisins : T(D) et T^-1(D) en translation, images par S en vert
for shift in (1.0, -1.0):
    p2 = poly + shift
    ax.plot(p2.real, p2.imag, color="#DD8452", lw=1.1, ls="--")
for x0, x1 in ((-2.0, -0.5), (0.5, 2.0)):
    xs = np.linspace(x0, x1, 60) + 1e-9
    ys = (-1.0 / (xs + 1j * 1e-9)).imag
    m = ys > 0.05
    ax.plot(xs[m], ys[m], color="#55A868", lw=1.1, ls="-.")

ax.annotate(r"$T:\ \tau \mapsto \tau + 1$", xy=(1.15, 2.6), fontsize=10, color="#DD8452")
ax.annotate(r"$S:\ \tau \mapsto -1/\tau$", xy=(-2.25, 0.72), fontsize=10, color="#55A868")
ax.annotate(r"$\mathcal{D}$", xy=(-0.12, 1.3), fontsize=13, color="#2E4C7E")
ax.set_xlim(-2.4, 2.4)
ax.set_ylim(0, TOP + 0.15)
ax.set_xlabel(r"$\mathrm{Re}\ \tau$")
ax.set_ylabel(r"$\mathrm{Im}\ \tau$")
ax.set_aspect("equal")
ax.set_title(r"Domaine fondamental de $SL_2(\mathbb{Z})$ et pavés voisins sous $T$, $S$")
ax.axhline(0, color="k", lw=0.6)
ax.legend(fontsize=9, loc="upper right")
plt.tight_layout()
plt.show()
print("Le carnet ne travaille que sur le pave central : les q-expansions qui suivent lisent f sur D seul.")

Le carnet ne travaille que sur le pave central : les q-expansions qui suivent lisent f sur D seul.
import math

M = 40  # profondeur commune des q-expansions

def mul(a, b):
    """Produit de deux series formelles, tronque a l'ordre M."""
    c = [0] * min(M + 1, len(a) + len(b) - 1)
    for i, ai in enumerate(a):
        for j, bj in enumerate(b):
            if i + j < len(c):
                c[i + j] += ai * bj
    return c

def powser(base, e):
    """Puissance e-ieme d'une serie formelle (exponentiation rapide)."""
    r = [1] + [0] * M
    b = base[:]
    while e:
        if e & 1:
            r = mul(r, b)
        b = mul(b, b)
        e >>= 1
    return r

def one_minus_qn(n):
    """Coefficients de (1 - q^n)."""
    out = [1] + [0] * M
    if n <= M:
        out[n] = -1
    return out

demo = mul([0, 1], [0, 1])  # q * q = q^2
print("q * q = coeff q^0..q^3 :", demo[:4])
assert demo[2] == 1 and sum(demo[:2]) == 0
q * q = coeff q^0..q^3 : [0, 0, 1]

Le produit mul et l’exponentiation powser sont exacts : aucun flottant, aucune approximation — chaque coefficient est un entier calculé par sommation finie. C’est la brique unique dont tout le carnet est bâti.

2. Séries d’Eisenstein \(E_4\) et \(E_6\)

Les formes modulaires les plus simples sont les séries d’Eisenstein : pour \(k \geq 4\) pair, \[E_k = 1 - \frac{2k}{B_k} \sum_{n \geq 1} \sigma_{k-1}(n) q^n, \qquad \sigma_{k-1}(n) = \sum_{d \mid n} d^{k-1}.\] Le coefficient de \(q^n\) est une somme de diviseurs — encore un calcul élémentaire exact.

def sigma(k, n):
    """Somme des d^k pour d parcourant les diviseurs de n."""
    return sum(d ** k for d in range(1, n + 1) if n % d == 0)

E4 = [1] + [240 * sigma(3, n) for n in range(1, M + 1)]
E6 = [1] + [-504 * sigma(5, n) for n in range(1, M + 1)]
print("E4 = 1 +", E4[1:6], "+ ...")
print("E6 = 1 +", E6[1:4], "+ ...")
assert E4[1] == 240 and E4[2] == 2160
assert E6[1] == -504 and E6[2] == -16632
print("Coefficients q et q^2 conformes aux valeurs de reference.")
E4 = 1 + [240, 2160, 6720, 17520, 30240] + ...
E6 = 1 + [-504, -16632, -122976] + ...
Coefficients q et q^2 conformes aux valeurs de reference.

\(E_4\) est de poids 4, \(E_6\) de poids 6 — le poids est additif au produit : \(E_4^3\) et \(E_6^2\) sont toutes deux de poids 12, donc leur différence est de poids 12. C’est cette coincidence de poids qui va produire le discriminant.

3. Le discriminant \(\Delta\) et la fonction \(\tau\) de Ramanujan

La forme \(\Delta = q \prod_{n \geq 1} (1 - q^n)^{24} = \sum_{n \geq 1} \tau(n) q^n\) est la première forme parabolique (cusp form) de poids 12 : elle s’annule en \(q = 0\), et l’espace \(S_{12}\) est de dimension 1 — tout ce qui est de poids 12, parabolique et non nul est multiple de \(\Delta\).

prod = [1] + [0] * M
for n in range(1, M + 1):
    prod = mul(prod, one_minus_qn(n))
Delta = [0] + powser(prod, 24)[:M]
tau = Delta
print("tau(1..10) =", tau[1:11])

E4c = mul(mul(E4, E4), E4)
E6c = mul(E6, E6)
lhs = [x - y for x, y in zip(E4c, E6c)]
assert all(lhs[n] == 1728 * Delta[n] for n in range(1, M + 1)), \
    [(n, lhs[n], 1728 * Delta[n]) for n in range(1, M + 1) if lhs[n] != 1728 * Delta[n]][:4]
print("Identite E4^3 - E6^2 = 1728*Delta verifiee jusqu'a l'ordre", M)
tau(1..10) = [1, -24, 252, -1472, 4830, -6048, -16744, 84480, -113643, -115920]
Identite E4^3 - E6^2 = 1728*Delta verifiee jusqu'a l'ordre 40

L’identité \(E_4^3 - E_6^2 = 1728\,\Delta\) relie trois objets construits indépendamment : deux séries d’Eisenstein et un produit infini. Les coefficients \(\tau(n)\) de Ramanujan (ici \(1, -24, 252, -1472, \dots\)) sont une fonction arithmétique profonde — la conjecture de Ramanujan \(|\tau(p)| \leq 2\,p^{11/2}\) — cas \(k = 12\) de la borne générale \(|a_p| \leq 2\,p^{(k-1)/2}\), prouvée par Deligne en 1974, est un théorème de Weil II, l’autre versant du programme de Langlands.

La borne familière \(|a_p| \leq 2\sqrt{p}\) est le cas poids 2 de la même famille (borne de Hasse) — elle vaut pour les \(a_p\) de la courbe elliptique de la section 6, pas pour \(\tau\), dont \(|\tau(5)| = 4\,830\) dépasse déjà \(2\sqrt{5} \approx 4{,}47\).

La borne se voit : \(|\tau(p)|\) croît bien plus vite que \(2\sqrt{p}\) — qui est la borne de poids 2 et ne concerne pas \(\Delta\) — et reste sous \(2\,p^{11/2}\), sa borne de poids 12. C’est l’écart entre les deux courbes qui mesure le prix de l’exposant \((k-1)/2\).

ps = [p for p in range(2, M + 1) if all(p % d for d in range(2, int(p ** 0.5) + 1))]
abs_tau = [abs(tau[p]) for p in ps]
borne12 = [2 * p ** 5.5 for p in ps]
borne2 = [2 * p ** 0.5 for p in ps]

fig, ax = plt.subplots(figsize=(7.0, 4.2))
ax.plot(ps, borne12, "-", color="#55A868", lw=1.8, label=r"$2\,p^{11/2}$ (Ramanujan-Deligne, poids 12)")
ax.plot(ps, abs_tau, "o-", color="#4C72B0", lw=1.4, ms=5, label=r"$|\tau(p)|$ (calcule ci-dessus)")
ax.plot(ps, borne2, "--", color="#C44E52", lw=1.5, label=r"$2\sqrt{p}$ (Hasse, poids 2 — hors sujet pour $\Delta$)")
ax.set_yscale("log")
ax.set_xlabel("premier $p$")
ax.set_ylabel("valeur")
ax.set_title(r"$|\tau(p)|$ contre ses bornes, premiers $p \leq %d$" % M)
ax.legend(fontsize=9, loc="upper left")
plt.tight_layout()
plt.show()
viol12 = sum(1 for p, t in zip(ps, abs_tau) if t > 2 * p ** 5.5)
sous2 = sum(1 for p, t in zip(ps, abs_tau) if t <= 2 * p ** 0.5)
print(f"premiers traces : {len(ps)} ; depassements de 2*p^(11/2) : {viol12} ; sous 2*sqrt(p) : {sous2}")
print("tau vit entre les deux bornes, colle a celle du poids 12 — l'exposant (k-1)/2 n'est pas decoratif.")

premiers traces : 12 ; depassements de 2*p^(11/2) : 0 ; sous 2*sqrt(p) : 0
tau vit entre les deux bornes, colle a celle du poids 12 — l'exposant (k-1)/2 n'est pas decoratif.

4. L’opérateur de Hecke \(T_p\) — la formule des coefficients, calculée

Pour une forme \(f = \sum a_n q^n\) de poids \(k\), l’opérateur de Hecke \(T_p\) agit sur les coefficients par \[(T_p f)_n = a(np) + \begin{cases} p^{k-1}\, a(n/p) & \text{si } p \mid n, \\ 0 & \text{sinon.} \end{cases}\] C’est exactement la formule que le lake hecke_lean formalise (coeffHeckeT et ses deux lemmes de lecture, README.md du lake). Ici, nous la calculons.

def hecke_Tp_coeffs(a, p, k, depth):
    """Coefficients (T_p f)_n pour n = 1..depth, f de poids k, a[n] = a_n."""
    b = []
    for n in range(1, depth + 1):
        v = a[p * n] if p * n < len(a) else 0
        if n % p == 0:
            v += p ** (k - 1) * a[n // p]
        b.append(v)
    return b

for p, attendu in ((2, -24), (3, 252)):
    Tp = hecke_Tp_coeffs(tau, p, 12, 10)
    assert all(Tp[n - 1] == attendu * tau[n] for n in range(1, 11)), (p, Tp[:5])
    print(f"T_{p} Delta = {attendu} * Delta  -- verifie sur 10 coefficients (valeur propre tau({p}) = {attendu})")
T_2 Delta = -24 * Delta  -- verifie sur 10 coefficients (valeur propre tau(2) = -24)
T_3 Delta = 252 * Delta  -- verifie sur 10 coefficients (valeur propre tau(3) = 252)

\(\Delta\) est forme propre pour tous les \(T_p\) : \(T_p \Delta = \tau(p)\, \Delta\). Le calcul ci-dessus le vérifie pour \(p = 2\) et \(p = 3\) — les deux exemples que le lake déclare dans son README (« exemples calculables, poids 12, \(p \in \{2, 3\}\) ») : ce carnet calcule ce que le lake démontre. Les valeurs propres \(\tau(p)\) portent l’arithmétique : c’est en étudiant leur répartition que naissent les liens avec les représentations galoisiennes.

5. \(\Gamma_0(11)\) : une forme propre de poids 2

Le groupe de congruence \(\Gamma_0(N)\) — matrices \(\begin{psmallmatrix} a & b \\ c & d \end{psmallmatrix} \in SL_2(\mathbb{Z})\) avec \(N \mid c\) — porte des formes de poids 2 dès que \(N\) assez grand. Pour \(N = 11\), l’espace \(S_2(\Gamma_0(11))\) est de dimension 1, engendré par le produit eta \[f = \eta(z)^2 \eta(11z)^2 = q \prod_{n \geq 1} (1-q^n)^2 (1 - q^{11n})^2.\]

prod11 = [1] + [0] * M
for n in range(1, M // 11 + 1):
    prod11 = mul(prod11, one_minus_qn(11 * n))
f11 = [0] + mul(powser(prod, 2), powser(prod11, 2))[:M]
print("f = q +", f11[2:12], "+ ...")
# multiplicativite des coefficients pour (m,n) premiers entre eux
for m, n in ((2, 3), (2, 5), (3, 5), (2, 7)):
    assert f11[m * n] == f11[m] * f11[n] if math.gcd(m, n) == 1 else True
print("a_mn = a_m * a_n verifiee pour les paires (2,3), (2,5), (3,5), (2,7) : f est forme propre normalisee.")
f = q + [-2, -1, 2, 1, 2, -2, 0, -2, -2, 1] + ...
a_mn = a_m * a_n verifiee pour les paires (2,3), (2,5), (3,5), (2,7) : f est forme propre normalisee.

6. Le pont — compter des points sur une courbe elliptique

La courbe elliptique \(E : y^2 + y = x^3 - x^2 - 10x - 20\) est la courbe 11a1, de conducteur 11 — le même 11 que \(\Gamma_0(11)\). Le théorème de modularité (Taniyama–Shimura–Weil, prouvé par Breuil–Conrad–Diamond–Taylor en suivant Wiles) dit que c’est la même chose : \(a_p(E) = p + 1 - \#E(\mathbb{F}_p)\) est le \(p\)-ième coefficient de \(f\). Vérifions-le premier par premier.

def points_affines_E11(p):
    """Nombre de solutions affines (x, y) dans F_p x F_p de y^2 + y = x^3 - x^2 - 10x - 20."""
    return sum(1 for x in range(p) for y in range(p)
               if (y * y + y - (x ** 3 - x ** 2 - 10 * x - 20)) % p == 0)

def a_p_geom(p):
    """a_p = p + 1 - #E(F_p), point a l'infini inclus."""
    return p + 1 - (points_affines_E11(p) + 1)

premiers = (2, 3, 5, 7, 13, 17, 19, 23, 29, 31)
print(f"{'p':>3} | {'a_p geometrie':>13} | {'a_p forme modulaire':>19}")
ok = True
for p in premiers:
    g, an = a_p_geom(p), f11[p]
    ok = ok and (g == an)
    print(f"{p:>3} | {g:>13} | {an:>19}", "" if g == an else "  <-- ECART")
assert ok
print("Les DEUX calculs coincident sur", len(premiers), "premiers : compter des points, c'est lire un coefficient.")
  p | a_p geometrie | a_p forme modulaire
  2 |            -2 |                  -2 
  3 |            -1 |                  -1 
  5 |             1 |                   1 
  7 |            -2 |                  -2 
 13 |             4 |                   4 
 17 |            -2 |                  -2 
 19 |             0 |                   0 
 23 |            -1 |                  -1 
 29 |             0 |                   0 
 31 |             7 |                   7 
Les DEUX calculs coincident sur 10 premiers : compter des points, c'est lire un coefficient.

Deux calculs indépendants — géométrie (énumérer \(\mathbb{F}_p \times \mathbb{F}_p\)) et analyse (produit infini tronqué) — rendent la même table. C’est le pont de modularité, vu de près. C’est aussi le premier maillon de la chaîne qui mène à Fermat : d’une équation \(a^p + b^p = c^p\) on tire la courbe de Frey (construite formellement dans le lake, FltRoute.lean, freyCurve), dont l’impossibilité d’être modulaire — le défaut du pont que nous venons de vérifier chez 11a1 — conduit à l’absence de solution. Le lake ne démontre pas la chaîne entière (personne ne le peut en un lake de taille lisible) : il en formalise les premiers gestes.

7. Ce que le lake hecke_lean formalise, et ce qui reste

Le lake voisin (SymbolicAI/Lean/hecke_lean/) est le socle formel de cette série. Énumérons ce qu’il expose, pour donner à chaque calcul du carnet son pendant démontré.

from pathlib import Path

def trouve_lake():
    import os
    starts = [Path.cwd()] + ([Path(os.environ["LANGLANDS_LAKE_ROOT"])] if os.environ.get("LANGLANDS_LAKE_ROOT") else [])
    for start in starts:
        for cand in (start, *start.parents):
            p = cand / "MyIA.AI.Notebooks/SymbolicAI/Lean/hecke_lean"
            if p.is_dir():
                return p
    raise FileNotFoundError("hecke_lean introuvable")

lake = trouve_lake()
for rel in ("Hecke/HeckeOperator.lean", "Hecke/FltRoute.lean"):
    chemin = lake / rel
    print(f"--- {rel} ---")
    for ligne in chemin.read_text(encoding="utf-8").splitlines():
        s = ligne.strip()
        if s.startswith(("def ", "theorem ", "lemma ")) and "coeffHeckeT" in s or \
           s.startswith(("def ", "theorem ", "lemma ")) and any(
               w in s for w in ("hecke", "frey", "Hecke", "Frey", "Gamma0", "coset")):
            print("  ", s.split("(")[0].split(":")[0].strip())
--- Hecke/HeckeOperator.lean ---
   def heckeMatrix
   def heckeDiagMatrix
   theorem det_heckeMatrix {p
   theorem det_heckeDiagMatrix {p
   theorem det_heckeMatrix_pos
   theorem det_heckeDiagMatrix_pos
   theorem denom_heckeMatrix {p
   theorem denom_heckeDiagMatrix {p
   theorem coe_heckeMatrix_smul {p
   theorem coe_heckeDiagMatrix_smul {p
   theorem σ_heckeMatrix
   theorem σ_heckeDiagMatrix
   theorem slash_heckeMatrix_apply
   theorem slash_heckeDiagMatrix_apply
   def heckeU
   def heckeT
   theorem heckeU_def
   theorem heckeT_eq_heckeU_add
   theorem heckeT_def
   theorem heckeU_apply
   theorem heckeT_apply
   theorem heckeU_add
   theorem heckeT_add
   theorem heckeU_smul
   theorem heckeT_smul
   theorem heckeU_neg
   theorem heckeT_neg
   theorem heckeU_sub
   theorem heckeT_sub
   def coeffHeckeT
   def coeffHeckeU
   theorem coeffHeckeT_apply
   theorem coeffHeckeU_apply
   theorem coeffHeckeT_of_dvd
   theorem coeffHeckeT_of_not_dvd
   theorem coeffHeckeT_eq_coeffHeckeU_add
   theorem coeffHeckeT_add
   theorem coeffHeckeT_smul
   theorem coeffHeckeU_add
   theorem coeffHeckeU_smul
--- Hecke/FltRoute.lean ---
   theorem frey_congr_mod_eight {a b c p
   def freyCurve
   def cosetToProj
   lemma cosetToProj_injective
   lemma cosetToProj_surjective
   theorem gamma0_two_index_eq_three
   theorem s2_gamma0_2_eq_zero

La symétrie des deux mondes : dans ce carnet, hecke_Tp_coeffs calcule \((T_p f)_n = a(np) + p^{k-1} a(n/p)\) sur des exemples ; dans le lake, coeffHeckeT démontre la même formule pour toute forme, avec heckeMatrix/heckeDiagMatrix comme représentants explicites de l’action. Ce qui reste hors du lake : la modularité elle-même (le pont de la section 6), la chaîne Ribet–Wiles vers Fermat — la preuve formalisée publiée en 2026 l’a traversée en entier ; le dépôt en porte les morceaux choisis.

8. Exercices

Les trois exercices suivent le fil du carnet ; chaque stub s’exécute sans erreur (rendre None tant qu’il n’est pas complété).

Exercice 1 — multiplicativité de \(\sigma_k\)

Pour \(k \geq 1\) et \(m, n\) premiers entre eux, \(\sigma_k(mn) = \sigma_k(m)\,\sigma_k(n)\). Le vérifier, puis l’utiliser pour prédire \(\sigma_3(35)\) sans passer par sigma.

def sigma3_par_multiplicativite(m, n):
    """Renvoie sigma_3(m*n) calcule comme sigma_3(m)*sigma_3(n) (m, n premiers entre eux).

    # TODO etudiant :
    # 1. verifier avec sigma(3, .) que l'identite tient sur quelques paires copremieres
    # 2. renvoyer le produit -- sans appeler sigma(3, m*n)
    """
    # Etape 1 : verifier l'identite sur les paires (2, 3), (4, 5), (3, 10)
    # Etape 2 : renvoyer le produit
    result = None  # TODO etudiant
    return result

print("Exercice a completer : sigma3_par_multiplicativite(5, 7) devrait rendre", 1 + 5**3, "*", 1 + 7**3, "=", (1 + 5**3) * (1 + 7**3))
Exercice a completer : sigma3_par_multiplicativite(5, 7) devrait rendre 126 * 344 = 43344

Exercice 2 — la valeur propre \(\tau(5)\)

Calculer \(T_5 \Delta\) avec hecke_Tp_coeffs et vérifier que \(\Delta\) est forme propre avec pour valeur propre \(\tau(5)\) — puis comparer à la borne de Ramanujan \(|\tau(5)| \leq 2\cdot 5^{11/2} \approx 13\,975\).

def tau5_par_hecke():
    """Renvoie la valeur propre tau(5) telle que T_5 Delta = tau(5) * Delta.

    # TODO etudiant :
    # 1. calculer T5 = hecke_Tp_coeffs(tau, 5, 12, 8)
    # 2. en extraire le rapport T5[0] / tau[1] et verifier qu'il est constant
    #    (depth 8 : au-dela, T_5 interpelle a(5n) avec 5n > 40 = M, hors liste)
    """
    # Etape 1 : T5 = hecke_Tp_coeffs(tau, 5, 12, 10)
    # Etape 2 : verifier T5[n-1] == lambda * tau[n] pour n = 1..8, renvoyer lambda
    result = None  # TODO etudiant
    return result

print("Exercice a completer : tau(5) attendu =", tau[5], "(valeur affichee par le carnet, a retrouver par Hecke)")
Exercice a completer : tau(5) attendu = 4830 (valeur affichee par le carnet, a retrouver par Hecke)

Exercice 3 — \(a_{37}\) par comptage

Étendre le pont de la section 6 au premier 37 : compter les points de \(E\) sur \(\mathbb{F}_{37}\) et retrouver le coefficient \(f_{37}\) de la forme modulaire.

def a37_par_comptage():
    """Renvoie a_37 = 37 + 1 - #E(F_37) pour la courbe 11a1.

    # TODO etudiant :
    # 1. reutiliser points_affines_E11(37)
    # 2. appliquer la formule a_p = p + 1 - (points affines + 1)
    """
    # Etape 1 : points = points_affines_E11(37)
    # Etape 2 : renvoyer 37 + 1 - (points + 1)
    result = None  # TODO etudiant
    return result

print("Exercice a completer : a_37 attendu =", f11[37], "(coefficient de la forme modulaire, a retrouver par geometrie)")
Exercice a completer : a_37 attendu = 3 (coefficient de la forme modulaire, a retrouver par geometrie)

Conclusion

Ce que ce carnet a distillé :

  • le langage des q-expansions — séries formelles exactes, une seule brique (mul) ;
  • les séries d’Eisenstein \(E_4\), \(E_6\) et l’identité \(E_4^3 - E_6^2 = 1728\,\Delta\) ;
  • la fonction \(\tau\) et les valeurs propres de Hecke \(T_p \Delta = \tau(p) \Delta\), calculées par la même formule que coeffHeckeT démontre ;
  • le pont de modularité sur \(\Gamma_0(11)\) : compter des points sur une courbe elliptique, c’est lire un coefficient d’un produit infini — vérifié sur dix premiers ;
  • la chaîne vers Fermat esquissée (courbe de Frey), et la frontière exacte de ce que le lake formalise.

La suite de la série (Epic #17969) : le Monstrous Moonshine — où l’invariant \(j\) et le groupe Monstre rejouent le même pont entre deux mondes qui ne se connaissaient pas.

« You can construct the monster Lie algebra from the monster vertex algebra. » – R. Borcherds [02:04], du prouveur lui-même : la suite naturelle de ce carnet est 02-monstrous-moonshine-invariant-j.ipynb, où l’invariant \(j\) rencontre le groupe Monstre.

Ressources

  • Jean-Pierre Serre, A Course in Arithmetic, Springer, ch. VII (formes modulaires).
  • Fred Diamond & Jerry Shurman, A First Course in Modular Forms, Springer.
  • Lake du dépôt : hecke_lean/ — opérateurs de Hecke classiques, coeffHeckeT, courbe de Frey.
  • Lean-29-Hecke-Operators-Native.ipynb — le carnet d’entrée du lake.
  • Epic #17969 — Langlands ; premier jalon de la distillation : #17889, #17970.
  • Talk du centenaire Serre (IHP 2026) et entretien Serre–Connes (2019), transcriptions : G:\Mon Drive\MyIA\IA\Bibliographie IA\NumberTheory\ (hors dépôt).
Retour au sommet