diff --git a/MyIA.AI.Notebooks/SymbolicAI/Lean/Serre100/14-formes-quadratiques-composition-gauss.ipynb b/MyIA.AI.Notebooks/SymbolicAI/Lean/Serre100/14-formes-quadratiques-composition-gauss.ipynb new file mode 100644 index 0000000000..72c9450c30 --- /dev/null +++ b/MyIA.AI.Notebooks/SymbolicAI/Lean/Serre100/14-formes-quadratiques-composition-gauss.ipynb @@ -0,0 +1,724 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7d548e37", + "metadata": { + "papermill": { + "duration": 0.003101, + "end_time": "2026-10-02T17:11:05.205805+00:00", + "exception": false, + "start_time": "2026-10-02T17:11:05.202704+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "# 14 - Composition des formes quadratiques binaires et nombre de classes h(D)\n", + "*A Course in Arithmetic*, chapitre 4 (Serre, GTM 7) — *Disquisitiones Arithmeticae* §§ 222-256 (Gauss, 1801).\n", + "Le carnet 11 a établi la densité de Chebotarev 1/2 et la liste des 9 corps quadratiques imaginaires de Heegner pour lesquels h(D) = 1. Le carnet 13 a posé le symbole de Jacobi et l'extension Kronecker en O(log n). Ce carnet 14 franchit une étape : **comment calculer h(D)** ? La réponse canonique pour les **discriminants quadratiques imaginaires** D < 2 est l'algorithme de **réduction de Gauss** sur les formes quadratiques binaires — l'ancêtre de l'arithmetique des corps de nombres, antérieur de 100 ans aux idéaux de Dedekind.\n", + "Outils : Python stdlib pur (`math.isqrt`). Le carnet 11 a posé la notion de h(D) (citation en fin de parcours) ; ce 14 la calcule et la vérifie numériquement." + ] + }, + { + "cell_type": "markdown", + "id": "011de808", + "metadata": { + "papermill": { + "duration": 0.001973, + "end_time": "2026-10-02T17:11:05.210986+00:00", + "exception": false, + "start_time": "2026-10-02T17:11:05.209013+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "## Plan\n", + "1. Formes quadratiques binaires (a, b, c) de discriminant D = b² - 4ac\n", + "2. Réduction de Gauss : algorithme des pas `b → b ± 2a` et `swap(a,c)`\n", + "3. Nombre de classes h(D) pour D < 0 : énumération des formes réduites\n", + "4. Vérification sur les 9 corps de Heegner (h(D) = 1)\n", + "5. Exercices" + ] + }, + { + "cell_type": "markdown", + "id": "51d3d250", + "metadata": { + "papermill": { + "duration": 0.001513, + "end_time": "2026-10-02T17:11:05.214251+00:00", + "exception": false, + "start_time": "2026-10-02T17:11:05.212738+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "## 1. Formes quadratiques binaires\n", + "Une **forme quadratique binaire** de discriminant D est un triplet (a, b, c) avec a > 0 et\n", + "$$f(x, y) = ax^2 + bxy + cy^2, \\qquad D = b^2 - 4ac.$$\n", + "Pour un **corps quadratique** $K = \\mathbb{Q}(\\sqrt{D})$ de discriminant quadratique fondamental D (premier ou produit d'impar d ≥ 1), chaque idéal de $\\mathcal{O}_K$ correspond à une classe de formes équivalentes sous l'action de SL₂(Z). Le **nombre de classes** h(D) est le nombre de classes d'équivalence de formes **réduites** :\n", + "- $|b| \\leq a \\leq c$ ;\n", + "- si $|b| = a$ ou $a = c$, alors $b \\geq 0$.\n", + "Pour D < 0 (corps quadratique imaginaire), h(D) est **fini**. Pour D > 1 (corps quadratique réel), h(D) est infini (les classes sont parametrées par l'unité fondamentale)." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "c61541c1", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-02T17:11:05.218181Z", + "iopub.status.busy": "2026-10-02T17:11:05.217871Z", + "iopub.status.idle": "2026-10-02T17:11:05.225563Z", + "shell.execute_reply": "2026-10-02T17:11:05.224957Z" + }, + "papermill": { + "duration": 0.010629, + "end_time": "2026-10-02T17:11:05.226242+00:00", + "exception": false, + "start_time": "2026-10-02T17:11:05.215613+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Réduction de Gauss : invariance du discriminant et forme réduite\n", + "\n", + " Entrée | Réduite | D entrée | D sortie | invariant\n", + "------------------------------------------------------------------\n", + " (1, 1, 1) | (1, 1, 1) | -3 | -3 | oui\n", + " (2, 1, 3) | (2, 1, 3) | -23 | -23 | oui\n", + " (5, 13, 9) | (1, 1, 3) | -11 | -11 | oui\n", + " (2, 5, 9) | (2, 1, 6) | -47 | -47 | oui\n", + " (9, 25, 21) | (5, 3, 7) | -131 | -131 | oui\n", + " (3, 3, 5) | (3, 3, 5) | -51 | -51 | oui\n", + " (1, 0, 5) | (1, 0, 5) | -20 | -20 | oui\n", + " (2, 2, 3) | (2, 2, 3) | -20 | -20 | oui\n", + " (7, 13, 9) | (3, 1, 7) | -83 | -83 | oui\n", + "\n", + "Rejet de la précondition a > 0 :\n", + "gauss_reduce(-3, 1, 1) -> ValueError : a doit être strictement positif (forme positive définie)\n" + ] + } + ], + "source": [ + "from math import isqrt\n", + "\n", + "def gauss_reduce(a, b, c):\n", + " # Précondition (section 1) : a > 0, forme positive définie (D < 0).\n", + " # Pour a <= 0 la descente n'a pas de sens : on refuse l'entrée.\n", + " if a <= 0:\n", + " raise ValueError('a doit être strictement positif (forme positive définie)')\n", + " D = b * b - 4 * a * c\n", + " while True:\n", + " # Translation T^m de SL2(Z) : centrer b dans (-a, a] par pas de 2a.\n", + " # C'est une translation (pas une réflexion) : la classe propre est préservée.\n", + " b = b - ((b + a) // (2 * a)) * (2 * a)\n", + " # c RECALCULÉ à chaque étape : seule façon de garder D = b^2 - 4ac invariant.\n", + " c = (b * b - D) // (4 * a)\n", + " if a > c:\n", + " a, b, c = c, -b, a\n", + " continue\n", + " if (a == c or abs(b) == a) and b < 0:\n", + " b = -b\n", + " return (a, b, c)\n", + "\n", + "print('Réduction de Gauss : invariance du discriminant et forme réduite')\n", + "print()\n", + "tests = [(1, 1, 1), (2, 1, 3), (5, 13, 9), (2, 5, 9), (9, 25, 21), (3, 3, 5), (1, 0, 5), (2, 2, 3), (7, 13, 9)]\n", + "print(f\"{'Entrée':>13} | {'Réduite':>13} | {'D entrée':>9} | {'D sortie':>9} | invariant\")\n", + "print('-' * 66)\n", + "for (a, b, c) in tests:\n", + " D_in = b * b - 4 * a * c\n", + " red = gauss_reduce(a, b, c)\n", + " D_out = red[1] * red[1] - 4 * red[0] * red[2]\n", + " ok = 'oui' if D_in == D_out else 'NON'\n", + " print(f'{str((a, b, c)):>13} | {str(red):>13} | {D_in:>9} | {D_out:>9} | {ok}')\n", + "\n", + "print()\n", + "print('Rejet de la précondition a > 0 :')\n", + "try:\n", + " gauss_reduce(-3, 1, 1)\n", + "except ValueError as e:\n", + " print(f'gauss_reduce(-3, 1, 1) -> ValueError : {e}')" + ] + }, + { + "cell_type": "markdown", + "id": "c7711e0b", + "metadata": { + "papermill": { + "duration": 0.001385, + "end_time": "2026-10-02T17:11:05.229068+00:00", + "exception": false, + "start_time": "2026-10-02T17:11:05.227683+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Lecture du résultat\n", + "Sur les 9 formes de test (toutes à $a > 0$, conformément à la précondition de la section 1), la sortie vérifie ligne à ligne les deux garanties de l'algorithme :\n", + "- **Invariance du discriminant** : la colonne « D sortie » est identique à « D entrée » à chaque ligne — chaque étape est une transformation $SL_2(\\mathbb{Z})$ (translation $T^m$ pour centrer $b$ dans $(-a, a]$, puis swap $(a,b,c) \\leftrightarrow (c,-b,a)$ tant que $a > c$), avec $c$ recalculé à chaque étape pour maintenir $D = b^2 - 4ac$.\n", + "- **Bornage de la forme réduite** : $|b| \\leq a \\leq c$, avec $b \\geq 0$ dans les cas frontière $|b| = a$ ou $a = c$ — la sortie $(5, 3, 7)$ pour l'entrée $(9, 25, 21)$ en est l'illustration (trois itérations de swap).\n", + "La précondition $a > 0$ est enforced : une entrée à $a$ négatif est rejetée par un `ValueError` (dernière ligne de la sortie) — pour $a < 0$, la « réduction » $b \\to b \\pm 2a$ fait croître $|b|$ au lieu de le réduire, il n'y a pas de forme réduite dans cette direction." + ] + }, + { + "cell_type": "markdown", + "id": "b4fbc20d", + "metadata": { + "papermill": { + "duration": 0.001262, + "end_time": "2026-10-02T17:11:05.231663+00:00", + "exception": false, + "start_time": "2026-10-02T17:11:05.230401+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "## 2. Nombre de classes h(D) pour D < 0\n", + "Pour $D < 0$ discriminant quadratique fondamental, **h(D) = nombre de formes réduites** de discriminant D. C'est un algorithme en $O(\\sqrt{|D|})$ : on itère sur $b \\in [0, \\sqrt{|D|/3}]$ (borne due à la condition $a \\leq c \\Rightarrow b^2 \\leq -3D$), puis sur les diviseurs $a$ de $(b^2 - D)/4$, et on garde ceux qui satisfont $|b| \\leq a \\leq c$.\n", + "Pour les **9 corps quadratiques imaginaires de Heegner**, l'arithmétique exceptionnelle donne h(D) = 1. Cette liste est célèbre : c'est le théorème de Stark-Heegner (1952/1967), qui résout l'équation $x^2 + Dy^2 = $ premier en généralisant le théorème des deux carrés." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "021b44c7", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-02T17:11:05.235475Z", + "iopub.status.busy": "2026-10-02T17:11:05.235154Z", + "iopub.status.idle": "2026-10-02T17:11:05.240251Z", + "shell.execute_reply": "2026-10-02T17:11:05.239781Z" + }, + "papermill": { + "duration": 0.007779, + "end_time": "2026-10-02T17:11:05.240730+00:00", + "exception": false, + "start_time": "2026-10-02T17:11:05.232951+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Nombre de classes h(D) pour D < 0 (9 corps de Heegner + 4 controles a h>1)\n", + "\n", + " D | h(D) | Formes réduites\n", + "------------------------------------------------------------\n", + " -3 | 1 | [(1, 1, 1)]\n", + " -4 | 1 | [(1, 0, 1)]\n", + " -7 | 1 | [(1, 1, 2)]\n", + " -8 | 1 | [(1, 0, 2)]\n", + " -11 | 1 | [(1, 1, 3)]\n", + " -19 | 1 | [(1, 1, 5)]\n", + " -43 | 1 | [(1, 1, 11)]\n", + " -67 | 1 | [(1, 1, 17)]\n", + " -163 | 1 | [(1, 1, 41)]\n", + " -15 | 2 | [(1, 1, 4), (2, 1, 2)]\n", + " -20 | 2 | [(1, 0, 5), (2, 2, 3)]\n", + " -24 | 2 | [(1, 0, 6), (2, 0, 3)]\n", + " -35 | 2 | [(1, 1, 9), (3, 1, 3)]\n" + ] + } + ], + "source": [ + "def enumerate_reduced_forms(D):\n", + " forms = []\n", + " b_step = 2\n", + " b_parity = 0 if D % 4 == 0 else 1\n", + " # Borne de réduction : |b| <= a <= sqrt(|D|/3) (cf. section 2) - la même\n", + " # borne pour D = 0 mod 4 et D = 1 mod 4 (une borne -D/4 raterait des formes\n", + " # comme (6, 6, 7) de D = -132).\n", + " b_max = isqrt(-D // 3) if -D >= 3 else 0\n", + " for b in range(b_parity, b_max + 1, b_step):\n", + " if (b * b - D) % 4 != 0:\n", + " continue\n", + " a_c = (b * b - D) // 4\n", + " for a in range(1, isqrt(a_c) + 1):\n", + " if a_c % a != 0:\n", + " continue\n", + " c = a_c // a\n", + " if abs(b) <= a <= c:\n", + " if (a == c or abs(b) == a) and b < 0:\n", + " continue\n", + " forms.append((a, b, c))\n", + " # Miroir (a, -b, c) : classe PROPRE distincte quand\n", + " # 0 < b < a < c (forme non ambiguë). L'omettre sous-compte h(D)\n", + " # d'un facteur ~2 sur les discriminants génériques.\n", + " if b > 0 and b < a and a < c:\n", + " forms.append((a, -b, c))\n", + " return forms\n", + "\n", + "HEEGNER_D = [-3, -4, -7, -8, -11, -19, -43, -67, -163]\n", + "\n", + "print('Nombre de classes h(D) pour D < 0 (9 corps de Heegner + 4 controles a h>1)')\n", + "print()\n", + "print(f\"{'D':>6} | {'h(D)':>4} | {'Formes réduites'}\")\n", + "print('-' * 60)\n", + "test_D = HEEGNER_D + [-15, -20, -24, -35]\n", + "for D in test_D:\n", + " forms = enumerate_reduced_forms(D)\n", + " h = len(forms)\n", + " if h <= 3:\n", + " forms_str = str(forms)\n", + " else:\n", + " forms_str = f'({h} formes, premiere = {forms[0]})'\n", + " print(f'{D:>5} | {h:>4} | {forms_str}')" + ] + }, + { + "cell_type": "markdown", + "id": "fa81bc08", + "metadata": { + "papermill": { + "duration": 0.00134, + "end_time": "2026-10-02T17:11:05.243486+00:00", + "exception": false, + "start_time": "2026-10-02T17:11:05.242146+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Lecture du résultat\n", + "Les **9 corps quadratiques de Heegner** ont tous **h(D) = 1** :\n", + "- D = -3 : une seule forme $(1, 1, 1)$ (anneau des entiers de $\\mathbb{Q}(\\sqrt{-3})$ = $\\mathbb{Z}[(1+\\sqrt{-3})/2]$).\n", + "- D = -4 : forme $(1, 0, 1)$ (anneau des entiers de Gauss $\\mathbb{Z}[i]$).\n", + "- D = -7 : forme $(1, 1, 2)$.\n", + "- D = -8 : forme $(1, 0, 2)$.\n", + "- D = -11 : forme $(1, 1, 3)$.\n", + "- D = -19 : forme $(1, 1, 5)$.\n", + "- D = -43 : forme $(1, 1, 11)$.\n", + "- D = -67 : forme $(1, 1, 17)$.\n", + "- D = -163 : forme $(1, 1, 41)$.\n", + "En control, les discriminants $-15, -20, -24, -35$ ont h(D) = 2, 2, 2, 2 respectivement (les plus petits non-Heegner). La liste de Heegner est **close** : c'est l'invariant h(D) = 1 qui definit le théorème de Stark-Heegner." + ] + }, + { + "cell_type": "markdown", + "id": "af39d306", + "metadata": { + "papermill": { + "duration": 0.001245, + "end_time": "2026-10-02T17:11:05.246079+00:00", + "exception": false, + "start_time": "2026-10-02T17:11:05.244834+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "## 3. Vérification empirique du théorème de Stark-Heegner\n", + "Le **théorème de Stark-Heegner** (1952 pour h(D) ≤ 1, 1967 pour h(D) = 1 effectif) dit que la liste $\\{D \\in \\mathbb{Z} : D \\leq -3, h(D) = 1\\}$ est exactement les 9 discriminants $\\{-3, -4, -7, -8, -11, -19, -43, -67, -163\\}$. C'est l'une des listes les plus célèbres de l'arithmétique.\n", + "La démonstration originale de Stark (1967) utilise l'analyse complexe (formule de Dirichlet pour h(D) comme residu de la $L$-serie $L(1, \\chi_D)$) ; celle de Heegner (1952) utilise les fonctions modulaires et les valeurs de la fonction $\\eta$ de Dedekind." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "c5883cde", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-02T17:11:05.249233Z", + "iopub.status.busy": "2026-10-02T17:11:05.249118Z", + "iopub.status.idle": "2026-10-02T17:11:05.253750Z", + "shell.execute_reply": "2026-10-02T17:11:05.253247Z" + }, + "papermill": { + "duration": 0.006937, + "end_time": "2026-10-02T17:11:05.254297+00:00", + "exception": false, + "start_time": "2026-10-02T17:11:05.247360+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Verification : h(D) = 1 sur tous les D entre -3 et -200 ?\n", + "\n", + "Discriminants quadratiques fondamentaux D entre -3 et -200 avec h(D) = 1 :\n", + "[-3, -4, -7, -8, -11, -19, -43, -67, -163]\n", + "\n", + "Heegner connu : [-3, -4, -7, -8, -11, -19, -43, -67, -163]\n", + "\n", + "Concordance numerique avec la liste de Heegner : True\n" + ] + } + ], + "source": [ + "print('Verification : h(D) = 1 sur tous les D entre -3 et -200 ?')\n", + "print()\n", + "HEEGNER_D = [-3, -4, -7, -8, -11, -19, -43, -67, -163]\n", + "\n", + "def is_fundamental_disc(D):\n", + " # Discriminant quadratique fondamental (Python : D % 4 in (0, 1) garde\n", + " # les D négatifs congrus à 1 mod 4, que % == 1 exclusait à tort).\n", + " if D % 4 not in (0, 1):\n", + " return False\n", + " if D % 4 == 1:\n", + " m = D\n", + " else:\n", + " m = D // 4\n", + " if m % 4 not in (2, 3):\n", + " return False\n", + " n = abs(m)\n", + " for d in range(2, isqrt(n) + 1):\n", + " if n % (d * d) == 0:\n", + " return False\n", + " return True\n", + "\n", + "h_eq_1 = []\n", + "for D in range(-3, -200, -1):\n", + " if not is_fundamental_disc(D):\n", + " continue\n", + " forms = enumerate_reduced_forms(D)\n", + " if len(forms) == 1:\n", + " h_eq_1.append(D)\n", + "\n", + "print(f'Discriminants quadratiques fondamentaux D entre -3 et -200 avec h(D) = 1 :')\n", + "print(h_eq_1)\n", + "print()\n", + "print(f'Heegner connu : {HEEGNER_D[:9]}')\n", + "print()\n", + "matches = sorted(set(h_eq_1) & set(HEEGNER_D)) == sorted(h_eq_1)\n", + "print(f'Concordance numerique avec la liste de Heegner : {matches}')" + ] + }, + { + "cell_type": "markdown", + "id": "ef8eafb1", + "metadata": { + "papermill": { + "duration": 0.001311, + "end_time": "2026-10-02T17:11:05.256933+00:00", + "exception": false, + "start_time": "2026-10-02T17:11:05.255622+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Lecture du résultat\n", + "Avec le filtre de discriminant fondamental corrigé, l'algorithme de Gauss **retrouve** la liste de Heegner à partir de la seule condition h(D) = 1 sur l'intervalle D ∈ [-3, -200] :\n", + "- Pour chaque D discriminant quadratique fondamental (D ≡ 1 mod 4 avec |D| squarefree, ou D = 4m avec m ≡ 2, 3 mod 4 et |m| squarefree — le filtre exclut par exemple -12 = 4 × (-3) où -3 ≡ 1 mod 4), on énumère les formes réduites.\n", + "- La sortie donne les 9 discriminants h(D) = 1, exactement les Heegner : la concordance est **True**.\n", + "- Aucun autre D ∈ [-3, -200] fondamental n'a h(D) = 1.\n", + "C'est la **vérification numérique** du théorème de Stark-Heegner — pas la preuve (qui demande l'analyse complexe ou les fonctions modulaires), mais l'observation que la liste est close. Le carnet 07 (zéros de fonctions L) est lié : la formule analytique $h(D) = \\frac{w \\sqrt{|D|}}{2\\pi} L(1, \\chi_D)$ où $w$ est le nombre de racines de l'unité dans $\\mathcal{O}_K$ (exercice 2 ci-dessous)." + ] + }, + { + "cell_type": "markdown", + "id": "b3785fe2", + "metadata": { + "papermill": { + "duration": 0.001324, + "end_time": "2026-10-02T17:11:05.259582+00:00", + "exception": false, + "start_time": "2026-10-02T17:11:05.258258+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "## 4. Exercices\n", + "Trois prolongements qui poussent h(D) au-delà de la routine :\n", + "**Exercice 1** : Calculer h(D) pour tous les D entre -3 et -500, et lister les D avec h(D) = 2 ou h(D) = 3. Comparer avec la suite OEIS A014603 (Heegner) et la table des h(D) pour D < 0.\n", + "**Exercice 2** : Pour D = -163 (le plus grand Heegner), vérifier la formule analytique de Dirichlet $L(1, \\chi_{-163}) = \\frac{2\\pi \\, h(D)}{w \\sqrt{|D|}}$ où $w = 2$ (unités ±1), en la confrontant à un calcul **indépendant** de $L(1, \\chi_D)$ : le produit d'Euler tronqué $\\prod_p (1 - \\chi_D(p)/p)^{-1}$.\n", + "**Exercice 3** : Implémenter la **composition de Gauss** de deux formes réduites : pour (a1, b1, c1) et (a2, b2, c2) de discriminant D, calculer (a3, b3, c3) tel que la classe du composé soit le produit des classes. (Indication : pgcd(a1, a2, (b1+b2)/2), puis algorithme de Lagrange.)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "576ca3bb", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-02T17:11:05.262892Z", + "iopub.status.busy": "2026-10-02T17:11:05.262769Z", + "iopub.status.idle": "2026-10-02T17:11:05.267126Z", + "shell.execute_reply": "2026-10-02T17:11:05.266647Z" + }, + "papermill": { + "duration": 0.006631, + "end_time": "2026-10-02T17:11:05.267522+00:00", + "exception": false, + "start_time": "2026-10-02T17:11:05.260891+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Exo 1 : h(D) pour D entre -3 et -500, lister h=2 et h=3\n", + "\n", + "h(D) = 1 (9 discriminants) : [-163, -67, -43, -19, -11, -8, -7, -4, -3]\n", + "Concordance Heegner : True\n", + "\n", + "h(D) = 2 (18 discriminants) : [-427, -403, -267, -235, -232, -187, -148, -123, -115, -91, -88, -52, -51, -40, -35, -24, -20, -15]\n", + "\n", + "h(D) = 3 (12 discriminants) : [-499, -379, -331, -307, -283, -211, -139, -107, -83, -59, -31, -23]\n" + ] + } + ], + "source": [ + "print('Exo 1 : h(D) pour D entre -3 et -500, lister h=2 et h=3')\n", + "print()\n", + "h1_list = []\n", + "h2_list = []\n", + "h3_list = []\n", + "for D in range(-500, -2):\n", + " if not is_fundamental_disc(D):\n", + " continue\n", + " forms = enumerate_reduced_forms(D)\n", + " h = len(forms)\n", + " if h == 1:\n", + " h1_list.append(D)\n", + " elif h == 2:\n", + " h2_list.append(D)\n", + " elif h == 3:\n", + " h3_list.append(D)\n", + "\n", + "print(f'h(D) = 1 ({len(h1_list)} discriminants) : {sorted(h1_list)}')\n", + "print(f'Concordance Heegner : {sorted(h1_list) == sorted(HEEGNER_D)}')\n", + "print()\n", + "print(f'h(D) = 2 ({len(h2_list)} discriminants) : {sorted(h2_list)}')\n", + "print()\n", + "print(f'h(D) = 3 ({len(h3_list)} discriminants) : {sorted(h3_list)}')" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "b5f07fd9", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-02T17:11:05.270936Z", + "iopub.status.busy": "2026-10-02T17:11:05.270803Z", + "iopub.status.idle": "2026-10-02T17:11:05.288058Z", + "shell.execute_reply": "2026-10-02T17:11:05.287610Z" + }, + "papermill": { + "duration": 0.019687, + "end_time": "2026-10-02T17:11:05.288603+00:00", + "exception": false, + "start_time": "2026-10-02T17:11:05.268916+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Exo 2 : formule analytique de Dirichlet pour D = -163\n", + "\n", + "h(-163) = 1, w = 2 : L(1, chi_D) = 2*pi*h/(w*sqrt(|D|)) = 0.246069\n", + "\n", + "N = 10000 : L(1, chi_D) par produit d Euler = 0.245458, écart = 6.10e-04\n", + "N = 100000 : L(1, chi_D) par produit d Euler = 0.245861, écart = 2.07e-04\n", + "\n", + "Concordance (écart < 1e-3 à N = 10^5) : True\n" + ] + } + ], + "source": [ + "import math\n", + "\n", + "print('Exo 2 : formule analytique de Dirichlet pour D = -163')\n", + "print()\n", + "D = -163\n", + "h = len(enumerate_reduced_forms(D))\n", + "w = 2 # unités ±1 (D < -4)\n", + "sqrtD = math.sqrt(-D)\n", + "\n", + "# Côté 1 : formule de classes de Dirichlet, avec h compté par énumération des formes\n", + "L1_dirichlet = 2 * math.pi * h / (w * sqrtD)\n", + "print(f'h(-163) = {h}, w = {w} : L(1, chi_D) = 2*pi*h/(w*sqrt(|D|)) = {L1_dirichlet:.6f}')\n", + "print()\n", + "\n", + "# chi_D(p) = (D/p) : critère d'Euler pour p impair, cas p = 2 à part\n", + "# (chi_D(2) = +1 si D = ±1 mod 8, -1 si D = ±3 mod 8).\n", + "def euler_product_L1(D, N):\n", + " sieve = bytearray([1]) * (N + 1)\n", + " sieve[0:2] = b'\\x00\\x00'\n", + " prod = 1.0\n", + " if D % 2 != 0:\n", + " chi2 = 1 if D % 8 in (1, 7) else -1\n", + " prod /= (1 - chi2 / 2)\n", + " for p in range(3, N + 1, 2):\n", + " if not sieve[p]:\n", + " continue\n", + " for m in range(p * p, N + 1, p):\n", + " sieve[m] = 0\n", + " if D % p == 0:\n", + " continue # premier ramifié : facteur local 1\n", + " chi = pow(D, (p - 1) // 2, p)\n", + " prod /= (1 - (1 if chi == 1 else -1) / p)\n", + " return prod\n", + "\n", + "L1_euler = None\n", + "for N in (10**4, 10**5):\n", + " L1_euler = euler_product_L1(D, N)\n", + " écart = abs(L1_euler - L1_dirichlet)\n", + " print(f'N = {N:>6} : L(1, chi_D) par produit d Euler = {L1_euler:.6f}, écart = {écart:.2e}')\n", + "print()\n", + "print(f'Concordance (écart < 1e-3 à N = 10^5) : {abs(L1_euler - L1_dirichlet) < 1e-3}')" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "96786587", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-02T17:11:05.292368Z", + "iopub.status.busy": "2026-10-02T17:11:05.292239Z", + "iopub.status.idle": "2026-10-02T17:11:05.295452Z", + "shell.execute_reply": "2026-10-02T17:11:05.295060Z" + }, + "papermill": { + "duration": 0.005635, + "end_time": "2026-10-02T17:11:05.295914+00:00", + "exception": false, + "start_time": "2026-10-02T17:11:05.290279+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Exo 3 : pgcd pour composition de Gauss\n", + "\n", + "gcd pour compose((1, 1, 2), (1, 1, 2)) avec D=-7 : g = 1\n", + "gcd pour compose((1, 1, 3), (1, 1, 3)) avec D=-11 : g = 1\n", + "gcd pour compose((2, 1, 3), (2, 1, 3)) avec D=-23 : g = 1\n" + ] + } + ], + "source": [ + "from math import gcd\n", + "\n", + "def gauss_compose_step1(a1, b1, c1, a2, b2, c2):\n", + " if (b1 + b2) % 2 != 0:\n", + " raise ValueError('b1 + b2 doit etre pair')\n", + " D = b1*b1 - 4*a1*c1\n", + " if D != b2*b2 - 4*a2*c2:\n", + " raise ValueError('Discriminants incompatibles')\n", + " g = gcd(gcd(a1, a2), (b1 + b2) // 2)\n", + " return g\n", + "\n", + "print('Exo 3 : pgcd pour composition de Gauss')\n", + "print()\n", + "tests = [((1, 1, 2), (1, 1, 2), -7), ((1, 1, 3), (1, 1, 3), -11), ((2, 1, 3), (2, 1, 3), -23)]\n", + "for (a1, b1, c1), (a2, b2, c2), D in tests:\n", + " g = gauss_compose_step1(a1, b1, c1, a2, b2, c2)\n", + " print(f'gcd pour compose({(a1,b1,c1)}, {(a2,b2,c2)}) avec D={D} : g = {g}')" + ] + }, + { + "cell_type": "markdown", + "id": "5637bd5a", + "metadata": { + "papermill": { + "duration": 0.001363, + "end_time": "2026-10-02T17:11:05.298711+00:00", + "exception": false, + "start_time": "2026-10-02T17:11:05.297348+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "## Conclusion\n", + "Le **nombre de classes** h(D) est l'invariant arithmetique fondamental d'un corps quadratique. Pour D < 0, c'est un entier calculable par énumération des formes réduites (Gauss). Pour D > 0, h(D) est **infini** (les classes sont parametrées par l'unité fondamentale).\n", + "La liste des 9 corps de Heegner (h(D) = 1) est close et vérifiable : c'est le théorème de Stark-Heegner, à la croisée de l'arithmétique, de l'analyse et de la théorie des nombres.\n", + "Suite naturelle :\n", + "- Carnet 15.1 envisagé : composition de Gauss explicite (algorithme de Lagrange complet), vérification que le groupe de classes est bien abélien.\n", + "- Pont avec la série GenAI : la formule analytique $h(D) = \\frac{w \\sqrt{|D|}}{2\\pi} L(1, \\chi_D)$ est reliée à l'étude des zéros de fonctions L (carnet 07)." + ] + }, + { + "cell_type": "markdown", + "id": "89a47550", + "metadata": { + "papermill": { + "duration": 0.001396, + "end_time": "2026-10-02T17:11:05.301628+00:00", + "exception": false, + "start_time": "2026-10-02T17:11:05.300232+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "## Ressources\n", + "- **Serre**, *A Course in Arithmetic*, chapitre 4 (Springer GTM 7) — référence canonique.\n", + "- **Cox**, *Primes of the Form x² + ny²* (Wiley) — chapitre 7 sur la composition de Gauss.\n", + "- **Gauss**, *Disquisitiones Arithmeticae* §§ 222-256 (1801) — l'original.\n", + "- **Stark** (1967), *On the \"gap\" in a theorem of Heegner* — la preuve d'effectivité.\n", + "- **Heegner** (1952), *Diophantische Analysis und Modulfunktionen* — la construction.\n", + "- OEIS A014603 — discriminants quadratiques imaginaires fondamentaux avec h(D) = 1." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.15" + }, + "papermill": { + "default_parameters": {}, + "duration": 1.329538, + "end_time": "2026-10-02T17:11:05.437398+00:00", + "environment_variables": {}, + "exception": null, + "input_path": "14-formes-quadratiques-composition-gauss.ipynb", + "output_path": "14-formes-quadratiques-composition-gauss.ipynb", + "parameters": {}, + "start_time": "2026-10-02T17:11:04.107860+00:00", + "version": "2.7.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5, + "type": "notebook" +} \ No newline at end of file diff --git a/MyIA.AI.Notebooks/SymbolicAI/Lean/Serre100/README.md b/MyIA.AI.Notebooks/SymbolicAI/Lean/Serre100/README.md index e4d9202e1e..0ba671ca79 100644 --- a/MyIA.AI.Notebooks/SymbolicAI/Lean/Serre100/README.md +++ b/MyIA.AI.Notebooks/SymbolicAI/Lean/Serre100/README.md @@ -19,6 +19,7 @@ Le geste « distillation » : prendre un énoncé ou une construction centrale d | 09 | [09-congruences-tau-lacunarite-delta.ipynb](09-congruences-tau-lacunarite-delta.ipynb) | τ de Ramanujan — congruence mod 691, borne de Deligne, lacunarité des puissances de η (Euler, Jacobi, Serre) | Python stdlib + matplotlib | | 10 | [10-empilements-borne-lp-cohn-elkies.ipynb](10-empilements-borne-lp-cohn-elkies.ipynb) | Bornes d'empilements de sphères par LP (Cohn–Elkies, lignée Odlyzko–Serre) — Poisson mesuré, LP naïf autopsié, LP certifié par intervalles | numpy + scipy (HiGHS) + matplotlib | | 11 | [11-corps-quadratiques-reciprocite-quadratique.ipynb](11-corps-quadratiques-reciprocite-quadratique.ipynb) | Corps quadratiques imaginaires — réciprocité quadratique, Fermat des deux carrés / deux triangulaires, densités de Chebotarev via symbole de Kronecker, conjecture de Stark-Heegner (neuf nombres) | Python stdlib | +| 14 | [14-formes-quadratiques-composition-gauss.ipynb](14-formes-quadratiques-composition-gauss.ipynb) | Composition des formes quadratiques binaires (Gauss) — réduction, énumération des formes réduites, calcul de h(D), vérification du théorème de Stark–Heegner sur les 9 corps de Heegner | Python stdlib (math.isqrt) | ## Conventions