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
Le langage des q-expansions — séries formelles à coefficients entiers
Séries d’Eisenstein \(E_4\) et \(E_6\)
Le discriminant \(\Delta\) et la fonction \(\tau\) de Ramanujan
L’opérateur de Hecke \(T_p\) — la formule des coefficients, calculée
\(\Gamma_0(11)\) : une forme propre de poids 2
Le pont — compter des points sur une courbe elliptique
Ce que le lake hecke_lean formalise, et ce qui reste
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 matplotlibimport matplotlib.pyplot as pltimport numpy as npfig, 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.0arc = 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 vertfor 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 mathM =40# profondeur commune des q-expansionsdef 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 inenumerate(a):for j, bj inenumerate(b):if i + j <len(c): c[i + j] += ai * bjreturn cdef 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 >>=1return rdef one_minus_qn(n):"""Coefficients de (1 - q^n).""" out = [1] + [0] * Mif n <= M: out[n] =-1return outdemo = mul([0, 1], [0, 1]) # q * q = q^2print("q * q = coeff q^0..q^3 :", demo[:4])assert demo[2] ==1andsum(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."""returnsum(d ** k for d inrange(1, n +1) if n % d ==0)E4 = [1] + [240* sigma(3, n) for n inrange(1, M +1)]E6 = [1] + [-504* sigma(5, n) for n inrange(1, M +1)]print("E4 = 1 +", E4[1:6], "+ ...")print("E6 = 1 +", E6[1:4], "+ ...")assert E4[1] ==240and E4[2] ==2160assert E6[1] ==-504and E6[2] ==-16632print("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] * Mfor n inrange(1, M +1): prod = mul(prod, one_minus_qn(n))Delta = [0] + powser(prod, 24)[:M]tau = Deltaprint("tau(1..10) =", tau[1:11])E4c = mul(mul(E4, E4), E4)E6c = mul(E6, E6)lhs = [x - y for x, y inzip(E4c, E6c)]assertall(lhs[n] ==1728* Delta[n] for n inrange(1, M +1)), \ [(n, lhs[n], 1728* Delta[n]) for n inrange(1, M +1) if lhs[n] !=1728* Delta[n]][:4]print("Identite E4^3 - E6^2 = 1728*Delta verifiee jusqu'a l'ordre", M)
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 inrange(2, M +1) ifall(p % d for d inrange(2, int(p **0.5) +1))]abs_tau = [abs(tau[p]) for p in ps]borne12 = [2* p **5.5for p in ps]borne2 = [2* p **0.5for 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(1for p, t inzip(ps, abs_tau) if t >2* p **5.5)sous2 =sum(1for p, t inzip(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 inrange(1, depth +1): v = a[p * n] if p * n <len(a) else0if n % p ==0: v += p ** (k -1) * a[n // p] b.append(v)return bfor p, attendu in ((2, -24), (3, 252)): Tp = hecke_Tp_coeffs(tau, p, 12, 10)assertall(Tp[n -1] == attendu * tau[n] for n inrange(1, 11)), (p, Tp[:5])print(f"T_{p} Delta = {attendu} * Delta -- verifie sur 10 coefficients (valeur propre tau({p}) = {attendu})")
\(\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] * Mfor n inrange(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 euxfor m, n in ((2, 3), (2, 5), (3, 5), (2, 7)):assert f11[m * n] == f11[m] * f11[n] if math.gcd(m, n) ==1elseTrueprint("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."""returnsum(1for x inrange(p) for y inrange(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 =Truefor 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 okprint("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 Pathdef 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 praiseFileNotFoundError("hecke_lean introuvable")lake = trouve_lake()for rel in ("Hecke/HeckeOperator.lean", "Hecke/FltRoute.lean"): chemin = lake / relprint(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 ")) andany( w in s for w in ("hecke", "frey", "Hecke", "Frey", "Gamma0", "coset")):print(" ", s.split("(")[0].split(":")[0].strip())
La symétrie des deux mondes : dans ce carnet, hecke_Tp_coeffscalcule\((T_p f)_n = a(np) + p^{k-1} a(n/p)\) sur des exemples ; dans le lake, coeffHeckeTdé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 etudiantreturn resultprint("Exercice a completer : sigma3_par_multiplicativite(5, 7) devrait rendre", 1+5**3, "*", 1+7**3, "=", (1+5**3) * (1+7**3))
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 etudiantreturn resultprint("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 etudiantreturn resultprint("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).