diff --git a/MyIA.AI.Notebooks/Complexity/Complexity-04b-OnlineConjectures-Secretary-KServer.ipynb b/MyIA.AI.Notebooks/Complexity/Complexity-04b-OnlineConjectures-Secretary-KServer.ipynb index 21bcd84548..bdf32af227 100644 --- a/MyIA.AI.Notebooks/Complexity/Complexity-04b-OnlineConjectures-Secretary-KServer.ipynb +++ b/MyIA.AI.Notebooks/Complexity/Complexity-04b-OnlineConjectures-Secretary-KServer.ipynb @@ -15,7 +15,7 @@ }, "source": [ "# Complexity-04b — La semaine historique de l'algorithmique online : secrétaire matroïdal et k-server\n", - "\n**Navigation** : [Index](README.md) | [04 — Décider sans connaître la suite](Complexity-04-OnlineAlgorithms-Python.ipynb) | [05b — La permanente, frontière quantique →](Complexity-05b-AaronsonArkhipov-PermanenteBosonSampling.ipynb)\n", + "\n**Navigation** : [Index](README.md) | [04 — Décider sans connaître la suite](Complexity-04-OnlineAlgorithms-Python.ipynb) | [05 — Compter est plus dur que vérifier →](Complexity-05-CountingHarder-Permanent.ipynb)\n", "\n", "**Approfondissement de [04 — Décider sans connaître la suite](Complexity-04-OnlineAlgorithms-Python.ipynb)** | [Index de la série](README.md)\n", "\n", diff --git a/MyIA.AI.Notebooks/Complexity/Complexity-05-CountingHarder-Permanent.ipynb b/MyIA.AI.Notebooks/Complexity/Complexity-05-CountingHarder-Permanent.ipynb new file mode 100644 index 0000000000..062e295624 --- /dev/null +++ b/MyIA.AI.Notebooks/Complexity/Complexity-05-CountingHarder-Permanent.ipynb @@ -0,0 +1,1137 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "header-05", + "metadata": { + "papermill": { + "duration": 0.00299, + "end_time": "2026-10-02T06:34:13.506710", + "exception": false, + "start_time": "2026-10-02T06:34:13.503720", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "# Complexity-05 — Compter est plus dur que vérifier : #P et la permanente\n", + "\n", + "**Navigation** : [<< Complexity-04b — Secrétaire matroïdal et k-server](Complexity-04b-OnlineConjectures-Secretary-KServer.ipynb) | [Index de la série](README.md) | [Complexity-05b — La permanente, frontière quantique >>](Complexity-05b-AaronsonArkhipov-PermanenteBosonSampling.ipynb)\n", + "\n", + "> **Public : Licence.** La formule de Ryser, le boson sampling et la frontière quantique sont\n", + "> conservés pour l'approfondissement [05b](Complexity-05b-AaronsonArkhipov-PermanenteBosonSampling.ipynb) ;\n", + "> ce notebook s'arrête à la frontière #P.\n", + "\n", + "## Ce que ce notebook suppose\n", + "\n", + "- [02 — Vérifier ou trouver](Complexity-02-P-NP-Reduction.ipynb) : le **certificat** d'une\n", + " instance, ce que signifie **vérifier** en temps polynomial, les classes **P** et **NP** ;\n", + "- lire un programme Python simple (boucles, fonctions, tableaux).\n", + "\n", + "## Ce que vous saurez faire à la fin\n", + "\n", + "1. définir la classe **#P** — **compter** les témoins d'une instance, et non plus seulement\n", + " décider s'il en existe ;\n", + "2. écrire le **déterminant** et la **permanente** : la même somme de $n!$ produits, à un\n", + " signe près ;\n", + "3. **mesurer** sur un banc réel l'écart entre un calcul polynomial et un calcul factoriel ;\n", + "4. énoncer le théorème de Valiant (**cité**) : la permanente est #P-difficile, même en 0/1 ;\n", + "5. relier un comptage naturel — les **couplages parfaits** d'un graphe biparti — à une\n", + " permanente.\n", + "\n", + "**Durée estimée** : 45 minutes." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "imports", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-02T06:34:13.515797Z", + "iopub.status.busy": "2026-10-02T06:34:13.515485Z", + "iopub.status.idle": "2026-10-02T06:34:14.065562Z", + "shell.execute_reply": "2026-10-02T06:34:14.064741Z" + }, + "papermill": { + "duration": 0.553856, + "end_time": "2026-10-02T06:34:14.066173", + "exception": false, + "start_time": "2026-10-02T06:34:13.512317", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "numpy 2.4.4\n" + ] + } + ], + "source": [ + "import time\n", + "from itertools import permutations, product\n", + "from math import factorial, prod\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "print(\"numpy\", np.__version__)" + ] + }, + { + "cell_type": "markdown", + "id": "sec1-sharp-p", + "metadata": { + "papermill": { + "duration": 0.002106, + "end_time": "2026-10-02T06:34:14.070672", + "exception": false, + "start_time": "2026-10-02T06:34:14.068566", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "## 1. De vérifier à compter : la classe #P\n", + "\n", + "Le notebook 02 séparait deux gestes : **trouver** une solution (aucun algorithme polynomial\n", + "connu sur les problèmes NP-complets) et **vérifier** un certificat proposé (facile,\n", + "polynomial). Un certificat que le vérificateur accepte s'appelle ici un **témoin**. Décider\n", + "s'il existe un témoin, c'est un problème de **NP**. Mais le certificat soulève une question\n", + "que NP ne pose même pas : **combien** y en a-t-il ?\n", + "\n", + "Compter les témoins définit une nouvelle classe de fonctions :\n", + "\n", + "> **#P** (lire *sharp-P*) : les fonctions qui à une instance $x$ associent le **nombre de\n", + "> témoins** de $x$.\n", + "\n", + "Le comptage est au moins aussi difficile que la décision — un compte nul répond *non*, un\n", + "compte strictement positif répond *oui* — mais il est plus fin : il distingue des instances\n", + "que la décision confond. Déroulons un exemple complet, à la main, avant toute généralité :\n", + "l'expression à trois variables\n", + "\n", + "$$\\varphi(a, b, c) \\;=\\; (a \\vee b) \\wedge (\\neg a \\vee c)$$\n", + "\n", + "Un certificat est un triplet de booléens ; un témoin, un triplet accepté par les deux\n", + "clauses. Il n'y en a que huit : énumérons-les tous." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "sec1-banc-jouet", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-02T06:34:14.076179Z", + "iopub.status.busy": "2026-10-02T06:34:14.075854Z", + "iopub.status.idle": "2026-10-02T06:34:14.081594Z", + "shell.execute_reply": "2026-10-02T06:34:14.080822Z" + }, + "papermill": { + "duration": 0.009518, + "end_time": "2026-10-02T06:34:14.082266", + "exception": false, + "start_time": "2026-10-02T06:34:14.072748", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " a b c | (a ou b) | (non a ou c) | temoin\n", + "------------------------------------------------\n", + " 0 0 0 | 0 | 1 | 0\n", + " 0 0 1 | 0 | 1 | 0\n", + " 0 1 0 | 1 | 1 | 1\n", + " 0 1 1 | 1 | 1 | 1\n", + " 1 0 0 | 1 | 0 | 0\n", + " 1 0 1 | 1 | 1 | 1\n", + " 1 1 0 | 1 | 0 | 0\n", + " 1 1 1 | 1 | 1 | 1\n", + "\n", + "decision (NP) : existe-t-il un temoin ? -> OUI\n", + "comptage (#P) : combien de temoins ? -> 4 sur 8 certificats\n" + ] + } + ], + "source": [ + "# L'expression a deux clauses : verifier un certificat = lire chaque clause, c'est tout.\n", + "def phi(a, b, c):\n", + " \"\"\"(a ou b) et (non a ou c) -- le geste du notebook 02 : verifier est facile.\"\"\"\n", + " clause1 = 1 if (a or b) else 0\n", + " clause2 = 1 if (not a or c) else 0\n", + " return clause1, clause2\n", + "\n", + "\n", + "print(f\"{'a':>2} {'b':>2} {'c':>2} | {'(a ou b)':>8} | {'(non a ou c)':>11} | temoin\")\n", + "print(\"-\" * 48)\n", + "nb_temoins = 0\n", + "for a, b, c in product((0, 1), repeat=3):\n", + " c1, c2 = phi(a, b, c)\n", + " temoin = c1 * c2 # accepte par les deux clauses : un produit, comme un et logique\n", + " nb_temoins += temoin\n", + " print(f\"{a:>2} {b:>2} {c:>2} | {c1:>8} | {c2:>11} | {temoin:>6}\")\n", + "print()\n", + "print(f\"decision (NP) : existe-t-il un temoin ? -> {'OUI' if nb_temoins > 0 else 'NON'}\")\n", + "print(f\"comptage (#P) : combien de temoins ? -> {nb_temoins} sur 8 certificats\")\n", + "assert nb_temoins == 4" + ] + }, + { + "cell_type": "markdown", + "id": "sec2-det-intro", + "metadata": { + "papermill": { + "duration": 0.002635, + "end_time": "2026-10-02T06:34:14.087389", + "exception": false, + "start_time": "2026-10-02T06:34:14.084754", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "**Lecture du résultat.** Quatre témoins sur huit certificats — et le tableau qui les exhibe\n", + "répond aux deux questions d'un coup : l'instance est satisfiable (au moins un témoin), et son\n", + "**compte** de satisfaisabilité vaut quatre. Mais l'énumération a payé $2^n$ lectures : le mur\n", + "exponentiel du notebook 02. La vraie question de #P est là : peut-on obtenir le **nombre**\n", + "sans tout énumérer ? Cette question vit sur un objet inattendu — une simple somme de produits\n", + "de matrice.\n", + "\n", + "## 2. Le déterminant : des signes qui arrangent\n", + "\n", + "Le déterminant d'une matrice $n \\times n$ est, lui aussi, défini comme une somme sur les\n", + "permutations $\\sigma$ de $\\{0, \\dots, n-1\\}$ :\n", + "\n", + "$$\\det A \\;=\\; \\sum_{\\sigma} \\mathrm{signe}(\\sigma) \\prod_{i=0}^{n-1} A[i,\\sigma(i)]$$\n", + "\n", + "Chaque permutation apporte son produit des $n$ entrées, multiplié par son **signe** ($+1$ ou\n", + "$-1$ selon la parité des échanges qui la composent). En $2 \\times 2$, les deux permutations\n", + "redonnent le produit « en croix » connu :\n", + "\n", + "$$\\det\\begin{pmatrix} a & b \\\\ c & d \\end{pmatrix} = ad - bc$$\n", + "\n", + "La définition parcourt $n!$ termes — et pourtant le déterminant se calcule en\n", + "**polynomial** : l'élimination de Gauss factorise la matrice en triangles en environ\n", + "$\\tfrac{2}{3}n^3$ opérations, et c'est elle que numpy exécute. Vérifions que le polynomial\n", + "se **voit** sur la machine : les tailles doublent, un cube promet un facteur $2^3 = 8$ —\n", + "voyons ce que la machine rend. (Le banc chronomètre `slogdet`, le **log**-déterminant :\n", + "la forme stable du même calcul de Gauss — le déterminant lui-même déborderait des flottants\n", + "à ces tailles.)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "sec2-banc-det", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-02T06:34:14.093635Z", + "iopub.status.busy": "2026-10-02T06:34:14.093386Z", + "iopub.status.idle": "2026-10-02T06:34:16.642921Z", + "shell.execute_reply": "2026-10-02T06:34:16.641632Z" + }, + "papermill": { + "duration": 2.554379, + "end_time": "2026-10-02T06:34:16.643974", + "exception": false, + "start_time": "2026-10-02T06:34:14.089595", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "log-determinant par np.linalg.slogdet : la meme elimination de Gauss, chronometree\n", + "au minimum de 10 essais (machine partagee) ; le temps doit suivre un polynome en n\n", + " n | temps (s) | x au doublement | temps / n^3\n", + "----------------------------------------------------\n", + " 100 | 0.00004 | - | 4.23e-11\n", + " 200 | 0.00094 | 22.2 | 1.17e-10\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 400 | 0.01134 | 12.1 | 1.77e-10" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 800 | 0.15975 | 14.1 | 3.12e-10\n", + "\n", + "un facteur borne a chaque doublement : la signature d'un polynome en n,\n", + "a opposer au facteur n qui grandit a chaque taille de la permanente\n" + ] + } + ], + "source": [ + "rng = np.random.default_rng(5)\n", + "\n", + "print(\"log-determinant par np.linalg.slogdet : la meme elimination de Gauss, chronometree\")\n", + "print(\"au minimum de 10 essais (machine partagee) ; le temps doit suivre un polynome en n\")\n", + "print(f\"{'n':>6} | {'temps (s)':>9} | {'x au doublement':>15} | {'temps / n^3':>12}\")\n", + "print(\"-\" * 52)\n", + "\n", + "np.linalg.slogdet(rng.random((100, 100))) # echauffement : alloue les threads une fois pour toutes\n", + "\n", + "temps_prec = None\n", + "for n in (100, 200, 400, 800):\n", + " A = rng.random((n, n))\n", + " essais = []\n", + " for _ in range(10):\n", + " t0 = time.perf_counter()\n", + " np.linalg.slogdet(A)\n", + " essais.append(time.perf_counter() - t0)\n", + " duree = min(essais)\n", + " facteur = f\"{duree / temps_prec:.1f}\" if temps_prec is not None else \"-\"\n", + " print(f\"{n:>6} | {duree:>9.5f} | {facteur:>15} | {duree / n**3:>12.2e}\")\n", + " temps_prec = duree\n", + "\n", + "print()\n", + "print(\"un facteur borne a chaque doublement : la signature d'un polynome en n,\")\n", + "print(\"a opposer au facteur n qui grandit a chaque taille de la permanente\")" + ] + }, + { + "cell_type": "markdown", + "id": "sec3-perm-intro", + "metadata": { + "papermill": { + "duration": 0.003462, + "end_time": "2026-10-02T06:34:16.651381", + "exception": false, + "start_time": "2026-10-02T06:34:16.647919", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "**Lecture du résultat.** À chaque doublement de $n$, le temps du log-déterminant est\n", + "multiplié par un facteur **borné** — supérieur au $2^3$ promis par la seule arithmétique,\n", + "car les accès mémoire ajoutent leur pente, mais qui ne **grandit pas** avec $n$ : c'est la\n", + "signature d'un polynôme. La définition, elle, compterait $800!$ termes — un nombre à près de\n", + "deux mille chiffres. La leçon tient dans ce contraste : **ce sont les signes qui rendent\n", + "Gauss possible**, en provoquant les annulations dont l'élimination exploite la cascade.\n", + "\n", + "## 3. La permanente : la même somme, sans les signes\n", + "\n", + "Retirons les signes, gardons tout le reste. La **permanente** additionne les mêmes $n!$\n", + "produits, tous comptés positivement :\n", + "\n", + "$$\\mathrm{perm}(A) \\;=\\; \\sum_{\\sigma} \\prod_{i=0}^{n-1} A[i,\\sigma(i)]$$\n", + "\n", + "En $2 \\times 2$ : $\\mathrm{perm}\\begin{pmatrix} a & b \\\\ c & d \\end{pmatrix} = ad + bc$ —\n", + "contre $ad - bc$ pour le déterminant. Déroulons une $3 \\times 3$ à la main, permutation par\n", + "permutation :\n", + "\n", + "$$A = \\begin{pmatrix} 1 & 2 & 3 \\\\ 4 & 5 & 6 \\\\ 7 & 8 & 9 \\end{pmatrix}$$\n", + "\n", + "| $\\sigma$ | produit | valeur |\n", + "|---|---|---|\n", + "| $(0,1,2)$ | $1 \\cdot 5 \\cdot 9$ | $45$ |\n", + "| $(0,2,1)$ | $1 \\cdot 6 \\cdot 8$ | $48$ |\n", + "| $(1,0,2)$ | $2 \\cdot 4 \\cdot 9$ | $72$ |\n", + "| $(1,2,0)$ | $2 \\cdot 6 \\cdot 7$ | $84$ |\n", + "| $(2,0,1)$ | $3 \\cdot 4 \\cdot 8$ | $96$ |\n", + "| $(2,1,0)$ | $3 \\cdot 5 \\cdot 7$ | $105$ |\n", + "\n", + "La somme vaut $450$. La même matrice a un déterminant **nul** — les signes s'y annihilent\n", + "totalement, la permanente additionne tout. Le banc suivant exécute la définition **telle\n", + "quelle**, un terme par permutation : copie pédagogique déclarée, c'est précisément le point\n", + "du cours (l'approfondissement 05b réutilise cette définition, puis la contourne par la\n", + "formule de Ryser — hors périmètre ici)." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "sec3-banc-perm", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-02T06:34:16.659846Z", + "iopub.status.busy": "2026-10-02T06:34:16.659506Z", + "iopub.status.idle": "2026-10-02T06:34:16.667734Z", + "shell.execute_reply": "2026-10-02T06:34:16.666722Z" + }, + "papermill": { + "duration": 0.01357, + "end_time": "2026-10-02T06:34:16.668762", + "exception": false, + "start_time": "2026-10-02T06:34:16.655192", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "comptes canoniques :\n", + " identite 3x3 : 1 (formule attendue : 1)\n", + " [[1,2],[3,4]] : 10 (a la main : 1*4 + 2*3 = 10)\n", + " 3x3 1..9 : 450 (le tableau ci-dessus : 450)\n", + " det de la meme 3x3 : 0 (avec les signes, tout s'annule)\n", + "\n", + "matrices tout-un : la permanente vaut n! a chaque taille\n", + " n = 1 : perm = 1 = 1!\n", + " n = 2 : perm = 2 = 2!\n", + " n = 3 : perm = 6 = 3!\n", + " n = 4 : perm = 24 = 4!\n", + " n = 5 : perm = 120 = 5!\n" + ] + } + ], + "source": [ + "def permanente(A):\n", + " \"\"\"Permanent de A par la definition : la somme des n! produits, un par permutation.\n", + "\n", + " Copie pedagogique declaree : le point du cours est d'executer la definition\n", + " telle quelle -- ce banc ne cherche aucune astuce.\n", + " \"\"\"\n", + " n = len(A)\n", + " total = 0\n", + " for p in permutations(range(n)):\n", + " total += prod(A[i][p[i]] for i in range(n))\n", + " return total\n", + "\n", + "\n", + "# Comptes canoniques : la definition contre les formules fermees.\n", + "IDENTITE_3 = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]\n", + "M_22 = [[1, 2], [3, 4]]\n", + "M_33 = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]\n", + "\n", + "print(\"comptes canoniques :\")\n", + "print(f\" identite 3x3 : {permanente(IDENTITE_3)} (formule attendue : 1)\")\n", + "print(f\" [[1,2],[3,4]] : {permanente(M_22)} (a la main : 1*4 + 2*3 = 10)\")\n", + "print(f\" 3x3 1..9 : {permanente(M_33)} (le tableau ci-dessus : 450)\")\n", + "det_33 = int(round(float(np.linalg.det(np.array(M_33)))))\n", + "print(f\" det de la meme 3x3 : {det_33} (avec les signes, tout s'annule)\")\n", + "print()\n", + "print(\"matrices tout-un : la permanente vaut n! a chaque taille\")\n", + "for n in range(1, 6):\n", + " tout_un = [[1] * n for _ in range(n)]\n", + " valeur = permanente(tout_un)\n", + " assert valeur == factorial(n)\n", + " print(f\" n = {n} : perm = {valeur:>3} = {n}!\")" + ] + }, + { + "cell_type": "markdown", + "id": "sec4-ecart-intro", + "metadata": { + "papermill": { + "duration": 0.00349, + "end_time": "2026-10-02T06:34:16.676011", + "exception": false, + "start_time": "2026-10-02T06:34:16.672521", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "**Lecture du résultat.** Les comptes canoniques tombent les uns après les autres : l'identité\n", + "ne laisse qu'un seul terme non nul (celui de la permutation identité), les matrices tout-un\n", + "valent $n!$ (autant de produits que de permutations, tous égaux à 1), et le contraste final\n", + "est net — permanente $450$, déterminant $0$ sur la même matrice. La définition est correcte ;\n", + "reste à en payer le prix, et à le comparer à celui de Gauss.\n", + "\n", + "## 4. L'écart mesuré : polynomial contre factoriel\n", + "\n", + "Mêmes tailles, mêmes matrices, deux algorithmes : d'un côté `np.linalg.det` (élimination de\n", + "Gauss, environ $\\tfrac{2}{3}n^3$ opérations), de l'autre `permanente` (la définition, $n!$\n", + "termes). Les tailles vont de $n = 2$ à $n = 10$ — au-delà, la définition sortirait du budget\n", + "d'un notebook, et l'extrapolation prendra le relais. Chronomètre au `perf_counter`, puis\n", + "échelle logarithmique : c'est elle qui rend une factorielle lisible." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "sec4-banc-ecart", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-02T06:34:16.683667Z", + "iopub.status.busy": "2026-10-02T06:34:16.683369Z", + "iopub.status.idle": "2026-10-02T06:34:20.168928Z", + "shell.execute_reply": "2026-10-02T06:34:20.168369Z" + }, + "papermill": { + "duration": 3.489968, + "end_time": "2026-10-02T06:34:20.169532", + "exception": false, + "start_time": "2026-10-02T06:34:16.679564", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " n | n! | permanente (s) | x au pas prec. | determinant (s) | perm / det\n", + "--------------------------------------------------------------------------------\n", + " 2 | 2 | 0.000007 | - | 0.0000416 | 0\n", + " 3 | 6 | 0.000008 | 1.1 | 0.0000131 | 1\n", + " 4 | 24 | 0.000012 | 1.4 | 0.0000061 | 2\n", + " 5 | 120 | 0.000053 | 4.4 | 0.0000046 | 11\n", + " 6 | 720 | 0.000347 | 6.6 | 0.0000050 | 69\n", + " 7 | 5040 | 0.003190 | 9.2 | 0.0000291 | 110\n", + " 8 | 40320 | 0.034557 | 10.8 | 0.0000399 | 866\n", + " 9 | 362880 | 0.228124 | 6.6 | 0.0000547 | 4170\n", + " 10 | 3628800 | 2.720281 | 11.9 | 0.0000396 | 68694\n", + "\n", + "extrapole sur cette machine, definition a n = 11 (4.0e+07 termes) : 29.923 s\n", + "extrapole sur cette machine, definition a n = 12 (4.8e+08 termes) : 6.0 min\n", + "extrapole sur cette machine, definition a n = 15 (1.3e+12 termes) : 11.3 jours\n", + "extrapole sur cette machine, definition a n = 20 (2.4e+18 termes) : 57 792.5 annees\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAArIAAAGGCAYAAACHemKmAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAne1JREFUeJztnQd4FFX3xt/0BAid0HvvHVRQRBAUrFiwY//+ir2CHQvYK3Y/9dPPgv1TFBAVBKVX6b1DSAESkpC+/+e9y4TNZpPspm17f88zyc7s7MydM/feOXPuOeeG2Gw2G4QQQgghhPAzQr1dACGEEEIIIcqCFFkhhBBCCOGXSJEVQgghhBB+iRRZIYQQQgjhl0iRFUIIIYQQfokUWSGEEEII4ZdIkRVCCCGEEH6JFFkhhBBCCOGXSJEVQgghhBB+iRRZISqJuXPnIiQkxPwPBFq1aoVrr73W49/t3LnTyOHjjz9GoNyHgwcP4uKLL0a9evXMsV999VX4E0888YQpd1lgHWBdEFV/j5zbYFX1MYHWl4nAQoqs8AuoBLEjXbZsmbeLIrxMRkaGech786F69913Y9asWZg4cSI+/fRTnHXWWQF3jaLs7N+/39y/VatWBawYqVCffvrplXqO9evXGznyZViI4pAiK4Rwi02bNuH999/3urSo5E2aNMmrSt4ff/yB888/H/fddx+uuuoqdOrUya+u8ZFHHsGxY8cq5djCrsjy/pVHkdU9siuylKMUWVESUmSFEG4RFRWFiIgISQtAQkICateu7XeySE9PN//Dw8MRHR3t7eKIEtA98gybzaaXsyBFiqwIKPbt24frr78eDRs2NIpX165d8eGHH7r1W7ou3Hbbbfjss8/QsWNH86Dv27cv5s2bV2TflStX4uyzz0bNmjVRo0YNDBs2DIsWLSrx+I8//rhRBBMTE4t8d/PNNxvFKDMzs8AX7pxzzsFff/2FAQMGmLK0adMGn3zySZHfbt++HZdccgnq1q2LatWq4aSTTsLPP//s0sftq6++MhaOpk2bIjY21vh5pqSkICsrC3fddRfi4uLM9Vx33XVmW0n+eYcOHTIWye7du5vfUBaUyerVq1FWjhw5YsrRvHlzc//atWuH5557Dvn5+eZ7WmYaNGhgPvM6eE1cOPzoKYsXLzYuAbVq1TJyGzJkCP7++2+3XFz40HzzzTcLzu+pPHifWeYOHTqYe9u4cWOMGTMG27Ztc+saaRE+9dRTUb16dVNvaB3esGGDSx9LWrWuuOIK1KlTB4MHDy70nTP//e9/TZ2PiYkx9emyyy7Dnj17UFFY5928ebOxZFP2vNZHH33UyJTn4rVQdo0aNcJLL71U5Bisl2xLrBusI6wrDzzwQJH6arXnr7/+Gl26dDHXdPLJJ2PNmjXm+3fffdccg/LnELkrq19Z6gjbWv/+/c1ntiPr/lk+4vPnzzfttUWLFgXlp6uKs4W8PH7MZSm3xd69e3HBBReYusX+gGVzlm1xsJ3SX5z9LuXKfvhf//oXDh8+XGg/d/o3yotyIkOHDi2QozVKYR2DLj79+vUz95f31J1+xNPyCh/HJoQf8NFHH9lYXZcuXVrsPvHx8bZmzZrZmjdvbnvyySdtb7/9tu28884zv3vllVdKPQf369atm61+/frm988995ytZcuWtpiYGNuaNWsK9lu7dq2tevXqtsaNG9ueeuop27PPPmtr3bq1LSoqyrZo0aKC/ebMmWOOyf9ky5YtZv2NN94odN6srCxbnTp1bNdff33BNp63Y8eOtoYNG9oeeugh29SpU219+vSxhYSEmPM7XjP3iY2NtT388MO2l19+2dazZ09baGio7bvvvitSll69etlOPvlk2+uvv2674447zPEuu+wy2xVXXGE7++yzbW+++abt6quvNvtOmjSpUDlZpnHjxhWs8160bdvWNmHCBNu7775rZNa0aVNbrVq1bPv27SvYb8eOHeZ4vIclkZ6ebuvRo4etXr165prfeecd2zXXXGPKeOedd5p90tLSzH3l8S688ELbp59+apbVq1cXe1zn+0B+//13W2RkpJHFSy+9ZOoHz81tixcvLvZY27ZtM+fj8c4888yC83sij9zcXNuwYcPMMSh73tspU6bYzjjjDNsPP/xQ6jXOnj3bFh4ebuvQoYPt+eefN/eJdZZ1iLK2ePzxx80xunTpYjv//PNtb731lrm/jt858vTTTxtZjx071uxrHbdVq1a2w4cPF+zHOsC6UBas87IeXn755eY8o0ePNttYd1nnb7nlFrN90KBBZvuff/5Z8Pu8vDzbiBEjbNWqVbPdddddRs633XabkQev0RH+lveU/QHbKBfeixYtWhiZUy6894888oi570OHDi30+7LWEbZJ3nue/+abby64f6w7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IvoTq59KTk4OeousshYURTKRTIpzJ3jix7VFlFhibZv041r0rB9SKW4GnmQtkEW2GMaPH28WK2sBp2VzzlpAxZbCph8al4pCw3/BK5OPP/4Yd999t8mS4a5M4uPjcc0112DBggVmG39LR/nvvvsOF1xwQYWe19PjVjXBUk88hfetXr16iI6ORrArbTQSaBpwyUT1pGQWbU9GQlpOifscTMvBroxwnNSmHioaT/oqKbJu4ipSj+vsFK2lPG8+S3YcQkJqJupVD8dJbRsgPEyTrjkOjVaVlfr0009Hr1698Oqrr8IbuHOtzjJhWQ8cOIBVq1aZly5u4zrnqXZXZpdddhlGjx5dsP8TTzyBH374wRzTEU+P62v1xJ376+06UFky0dTXdiSLokgmkokziWnZcHe/yshioKwFfu1IDTSqFY0nfMiR2h04hGl1hqJq2bZtG/r27Yv27dsXbGvUyDO/pZiYGLOUhqfHDWaYY1qBoEIIfyQuNrpC96tMpHV4WYmlw7SjEksOHnek5veVAS1Ot912m1lowatfvz4effTRQgFmWVlZuO+++9C0aVNUr14dAwcOxNy5cwsNRdeuXRs//vgjunTpgqioKOzevdskXn/66afNUHeNGjXQsmVLsw99jM8//3yzrUePHli2bFnBsei7d/nll5tzMY1Z9+7d8cUXXxQq89ChQ3HHHXfggQceQN26dY1CRauhI0eOHMGNN95Y4AZyxhlnYPXq1QXfc39a2j799FNTTl47LZGWL861116LP//8E6+99lqBlZ2TXZC1a9fi7LPPNuVv2LAhrr76aiQlJZX7XlCOzO/J677wwguNLJz53//+hz59+pihljZt2mDSpEkFPtm8jm+//RaffPKJKS+vgfAzLaqE18B1ugRQjjxXz549sXDhwiL30/rMc1B2lhy4zfm4ZM2aNUbOVII5dH3zzTcjLS2t4HuWh24IL774Iho3bmz2ocsO/TZLUsxZVyhnyrt///747bffSpQj722/fv2KvbfuUp46YLWru+66y7SpkSNHmjbDY8yaNQu9e/c2cqK8EhISMGPGDHTu3NnU1SuuuMKk8nMcAp8yZQpat25tfsP79c033xR8TxeQK6+80tR1fs+XmI8++sijaxVCCFfk59uwbNchlATHepi9YEDruvA2UmQrECqCGdm5bi1HM3Pw+I/rSnSkfuLH9WY/d47naZaD//znP8avd8mSJeah/fLLL+ODDz4o+J4PZCo6X375Jf755x9ccsklOOuss7Bly5aCffjgfe6558zv1q1bZ/JVkldeeQWDBg3CypUrzXA1H/hUbK+66iqsWLECbdu2NetWmelrTIvizz//bJQFKkP8DcvmXGYq1YsXL8bzzz+PJ598ErNnzy74nmW0FITly5cb5W/YsGE4dOhQISWJitj06dPNQqXl2WefNd9RDieffDJuuukmM4TOpXnz5kZBpvJBRYQK+MyZM02C+UsvvbREGTsqgK7gddxwww1G1hzCp5LJlwBH5s+fb2R15513Yv369Xj33XeNHKjkkKVLl5r7wrKwvLyG4nj44YfNywnP1aFDB/Py4CpIcezYsbj33nvRtWvXAjlwmzPp6elGWaOrAcvx9ddfG4WT1+PInDlzjNz5n2WnTEqSCxXhUaNG4ffffzd1iNd37rnnmhelkti+fbtR+l3dW3cpbx3g9dEK+/fff+Odd94ppGhPnTrV+DHv2bPH/I6uC59//rmp97/++iveeOONgv15f/lywmOwbdF/me2H10T44sn6wLq+YcMGvP3220Z5FkKI8pCcloVrP16Kl37dXLDN2VnLWmcKLp/IJ2sTJZKSkkJty/x35tixY7b169eb/yQ9K8fW8sHpXll4bncZMmSIrXPnzrb8/PyCbQ8++KDZRnbt2mULCwuz7du3r9Dvhg0bZps4caL5/NFHHxm5rFq1qtA+LVu2tF111VUF6wcOHDD7PfroowXbFi5caLbxu+IYPXq07d577zVlzM7ONmUePHhwoX369+9vyk3mz59vq1mzpi0zM7PQPm3btrW9++675vPjjz9uq1atmi01NbXg+/vvv982cODAQrK58847Cx3jqaeeso0YMaLQtj179phr2LRpU7HX0LFjR9t3331X7PeXX365bdSoUYW2jR071larVq1CMp88eXKhfT755BNb48aNC+7f+eefbxs3blyhfVi277//3nzesWOHWf/ggw8Kvl+3bp3ZtmHDhoL76Xheyqpnz55Fyux43Pfee89Wp04dW1paWsH3P//8sy00NNQWHx9v1lku1onc3NyCfS655BJznZ7QtWtX2xtvvFHs94899pi5t47t1PneFnd/nSlrHeDvevfuXWifOXPmmH1+++23gm1Tpkwx27Zt21aw7V//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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "rng = np.random.default_rng(2026)\n", + "\n", + "tailles = list(range(2, 11))\n", + "chrono_perm, chrono_det = [], []\n", + "for n in tailles:\n", + " A = rng.integers(0, 10, size=(n, n)).tolist() # entiers : la definition tourne telle quelle\n", + " t0 = time.perf_counter()\n", + " permanente(A)\n", + " chrono_perm.append(time.perf_counter() - t0)\n", + "\n", + " A_numpy = np.array(A, dtype=float)\n", + " t0 = time.perf_counter()\n", + " np.linalg.det(A_numpy)\n", + " chrono_det.append(time.perf_counter() - t0)\n", + "\n", + "print(f\"{'n':>3} | {'n!':>8} | {'permanente (s)':>14} | {'x au pas prec.':>13} | \"\n", + " f\"{'determinant (s)':>15} | {'perm / det':>9}\")\n", + "print(\"-\" * 80)\n", + "for i, n in enumerate(tailles):\n", + " facteur = f\"{chrono_perm[i] / chrono_perm[i - 1]:.1f}\" if i > 0 else \"-\"\n", + " print(f\"{n:>3} | {factorial(n):>8} | {chrono_perm[i]:>14.6f} | {facteur:>13} | \"\n", + " f\"{chrono_det[i]:>15.7f} | {chrono_perm[i] / chrono_det[i]:>9.0f}\")\n", + "\n", + "\n", + "def lisible(secondes):\n", + " \"\"\"Formate une duree en unites humaines, comme dans le notebook 02.\"\"\"\n", + " for unite, taille in ((\"annees\", 31_557_600), (\"jours\", 86_400), (\"heures\", 3_600), (\"min\", 60)):\n", + " if secondes >= taille:\n", + " return f\"{secondes / taille:,.1f} {unite}\".replace(\",\", \" \")\n", + " return f\"{secondes:.3f} s\"\n", + "\n", + "\n", + "cout_terme = chrono_perm[-1] / factorial(10) # temps moyen par terme, mesure a n = 10\n", + "print()\n", + "for n in (11, 12, 15, 20):\n", + " print(f\"extrapole sur cette machine, definition a n = {n} ({factorial(n):.1e} termes) : \"\n", + " f\"{lisible(cout_terme * factorial(n))}\")\n", + "\n", + "plt.figure(figsize=(7, 4))\n", + "plt.semilogy(tailles, chrono_perm, \"o-\", label=\"permanente : definition a n! termes\")\n", + "plt.semilogy(tailles, chrono_det, \"s-\", label=\"determinant : Gauss, O(n^3)\")\n", + "plt.xlabel(\"taille n de la matrice\")\n", + "plt.ylabel(\"temps (s), echelle log\")\n", + "plt.title(\"Le polynomial et le factoriel, meme taille d'entree\")\n", + "plt.grid(True, which=\"both\", alpha=0.3)\n", + "plt.legend()\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "sec4-lecture", + "metadata": { + "papermill": { + "duration": 0.002784, + "end_time": "2026-10-02T06:34:20.175499", + "exception": false, + "start_time": "2026-10-02T06:34:20.172715", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "**Lecture du résultat.** La colonne « x au pas précédent » raconte tout : le facteur de la\n", + "permanente **grandit avec $n$** — il approche dix entre $n = 9$ et $n = 10$, exactement le\n", + "facteur que la taille ajoute au compte $n!$. Le déterminant, lui, reste sur un plancher : à\n", + "ces petites tailles, son coût est dominé par l'appel de la bibliothèque, et c'est bien le\n", + "message — le polynomial, c'est presque rien ; le factoriel, c'est le mur. L'extrapolation,\n", + "bâtie sur le temps par terme **mesuré** à $n = 10$, en donne les ordres de grandeur : encore\n", + "traitable à $n = 12$, hors de toute patience humaine vers $n = 15$. Sur l'échelle log, la\n", + "factorielle apparaît comme une droite qui grimpe — légèrement accélérée. Et comme dans 02, le\n", + "matériel ne sauve rien : une machine mille fois plus rapide ne recule la frontière que de\n", + "deux à trois tailles." + ] + }, + { + "cell_type": "markdown", + "id": "sec5-valiant", + "metadata": { + "papermill": { + "duration": 0.002665, + "end_time": "2026-10-02T06:34:20.180784", + "exception": false, + "start_time": "2026-10-02T06:34:20.178119", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "## 5. Valiant 1979 : compter est #P-difficile (cité)\n", + "\n", + "Un banc ne mesure qu'un programme — il ne dit rien des programmes qu'on n'a pas écrits. La\n", + "question sérieuse : un « Gauss de la permanente » existe-t-il ? Une astuce qui, exploitant la\n", + "structure de la somme, la ferait tomber à un polynomial ? Le théorème de Valiant ferme cette\n", + "porte de la façon la plus spectaculaire possible :\n", + "\n", + "> **Théorème (Valiant, 1979 — cité, non démontré ici).** Calculer la permanente d'une matrice\n", + "> à coefficients $0$ et $1$ est **#P-difficile** : tout problème de #P — tout comptage de\n", + "> témoins — se réduit à ce calcul en temps polynomial.\n", + "\n", + "La preuve (une réduction depuis le comptage des satisfaisations d'une formule 3-SAT) est un\n", + "chef-d'œuvre technique, hors de portée d'un notebook de licence ; l'énoncé seul suffit pour\n", + "la leçon :\n", + "\n", + "1. **un algorithme polynomial pour la permanente 0/1 résoudrait tout comptage** : chaque\n", + " problème de #P s'y réduirait, et la classe entière des comptages s'effondrerait sur les\n", + " calculs polynomiaux (P = #P) ;\n", + "2. **les coefficients ne sont pas le problème** : des $0$ et des $1$ suffisent à la\n", + " difficulté ;\n", + "3. le retournement est complet pour les graphes : décider s'il existe un couplage parfait\n", + " dans un graphe biparti est **facile** (polynomial) — mais les compter, c'est\n", + " carrément #P-difficile, et la section suivante montre pourquoi : ce comptage est une\n", + " permanente.\n", + "\n", + "La somme la plus simple du monde — des produits, tous comptés positivement — est donc\n", + "conjecturée hors de portée de tout algorithme polynomial." + ] + }, + { + "cell_type": "markdown", + "id": "sec6-couplages-intro", + "metadata": { + "papermill": { + "duration": 0.002674, + "end_time": "2026-10-02T06:34:20.186029", + "exception": false, + "start_time": "2026-10-02T06:34:20.183355", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "## 6. Compter des couplages parfaits : c'est une permanente\n", + "\n", + "Un graphe **biparti** : des sommets à gauche $u_0, \\dots, u_3$, des sommets à droite\n", + "$d_0, \\dots, d_3$, des arêtes entre les deux côtés seulement. Un **couplage parfait** apparie\n", + "chaque sommet de gauche à un voisin de droite, tous distincts — quatre arêtes, aucun sommet\n", + "utilisé deux fois. Les compter, c'est le comptage le plus naturel du monde : affecter des\n", + "tâches à des machines, des étudiants à des projets.\n", + "\n", + "Voici le nôtre, dessiné comme un tableau à deux dimensions — une ligne par $u_i$, une colonne\n", + "par $d_j$, un point par arête :\n", + "\n", + "```\n", + " d0 d1 d2 d3\n", + "u0 ● ● · ·\n", + "u1 ● ● ● ·\n", + "u2 · ● ● ●\n", + "u3 · · ● ●\n", + "```\n", + "\n", + "Ce tableau **est** la matrice d'adjacence $M$ du graphe (1 si l'arête existe, 0 sinon). Et\n", + "alors :\n", + "\n", + "$$\\#\\{\\text{couplages parfaits}\\} \\;=\\; \\mathrm{perm}(M)$$\n", + "\n", + "La raison tient en deux lignes : un couplage parfait choisit pour chaque $u_i$ une\n", + "destination, toutes distinctes — autrement dit une permutation $\\sigma$ ; et le produit des\n", + "$M[i,\\sigma(i)]$ vaut $1$ exactement quand toutes les arêtes choisies existent, $0$ sinon.\n", + "Chaque couplage parfait contribue $1$ à la somme, chaque autre permutation $0$. Le banc\n", + "suivant le vérifie avec **deux machines indépendantes** : la permanente d'un côté, et de\n", + "l'autre une énumération brute qui construit chaque candidat sommet par sommet — chaque $u_i$\n", + "essaie chacun de ses voisins — et ne garde que les choix tous distincts." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "sec6-banc-couplages", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-02T06:34:20.192672Z", + "iopub.status.busy": "2026-10-02T06:34:20.192411Z", + "iopub.status.idle": "2026-10-02T06:34:20.198237Z", + "shell.execute_reply": "2026-10-02T06:34:20.197653Z" + }, + "papermill": { + "duration": 0.010042, + "end_time": "2026-10-02T06:34:20.198775", + "exception": false, + "start_time": "2026-10-02T06:34:20.188733", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "matrice d'adjacence :\n", + " [1, 1, 0, 0]\n", + " [1, 1, 1, 0]\n", + " [0, 1, 1, 1]\n", + " [0, 0, 1, 1]\n", + "\n", + "permanente(M) = 5\n", + "\n", + "enumeration brute : 5 couplages parfaits\n", + " u0->d0 u1->d1 u2->d2 u3->d3\n", + " u0->d0 u1->d1 u2->d3 u3->d2\n", + " u0->d0 u1->d2 u2->d1 u3->d3\n", + " u0->d1 u1->d0 u2->d2 u3->d3\n", + " u0->d1 u1->d0 u2->d3 u3->d2\n", + "\n", + "les deux machines s'accordent : le nombre de couplages parfaits EST la permanente\n" + ] + } + ], + "source": [ + "# Le graphe ci-dessus, en machine : les aretes (i, j) = u_i vers d_j.\n", + "ARETES = {(0, 0), (0, 1),\n", + " (1, 0), (1, 1), (1, 2),\n", + " (2, 1), (2, 2), (2, 3),\n", + " (3, 2), (3, 3)}\n", + "\n", + "M = [[1 if (i, j) in ARETES else 0 for j in range(4)] for i in range(4)]\n", + "print(\"matrice d'adjacence :\")\n", + "for ligne in M:\n", + " print(\" \", ligne)\n", + "print()\n", + "\n", + "# Machine 1 : la permanente de la matrice d'adjacence.\n", + "print(\"permanente(M) =\", permanente(M))\n", + "print()\n", + "\n", + "# Machine 2 : enumeration brute, independante -- chaque u_i choisit un voisin,\n", + "# on ne garde que les choix ou toutes les destinations sont distinctes.\n", + "voisins = [[j for j in range(4) if (i, j) in ARETES] for i in range(4)]\n", + "couplages = [choix for choix in product(*voisins) if len(set(choix)) == 4]\n", + "\n", + "print(f\"enumeration brute : {len(couplages)} couplages parfaits\")\n", + "for choix in couplages:\n", + " print(\" \", \" \".join(f\"u{i}->d{choix[i]}\" for i in range(4)))\n", + "assert permanente(M) == len(couplages)\n", + "print()\n", + "print(\"les deux machines s'accordent : le nombre de couplages parfaits EST la permanente\")" + ] + }, + { + "cell_type": "markdown", + "id": "sec6-lecture-transition", + "metadata": { + "papermill": { + "duration": 0.00282, + "end_time": "2026-10-02T06:34:20.204518", + "exception": false, + "start_time": "2026-10-02T06:34:20.201698", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "**Lecture du résultat.** Les deux machines indépendantes rendent le même compte : la\n", + "permanente de la matrice d'adjacence vaut exactement le nombre de couplages listés un par un.\n", + "Le pont est franchi — un comptage naturel est littéralement une permanente de matrice 0/1,\n", + "et par le théorème de Valiant, compter les couplages parfaits d'un graphe biparti est\n", + "une tâche #P-difficile dans le cas général : décider s'il en existe un est polynomial,\n", + "les compter ne l'est probablement pas.\n", + "\n", + "Et l'histoire a continué : Aaronson et Arkhipov (2011) ont montré que la distribution de\n", + "sortie d'un réseau optique linéaire — des photons dans un interféromètre — dépend de\n", + "permanentes de matrices complexes, faisant de la fonction la plus dure du comptage classique\n", + "le candidat le plus sérieux d'un avantage quantique démontrable. Cette frontière est celle de\n", + "l'approfondissement [05b — La permanente, frontière quantique](Complexity-05b-AaronsonArkhipov-PermanenteBosonSampling.ipynb)." + ] + }, + { + "cell_type": "markdown", + "id": "exo1-enonce", + "metadata": { + "papermill": { + "duration": 0.002673, + "end_time": "2026-10-02T06:34:20.209993", + "exception": false, + "start_time": "2026-10-02T06:34:20.207320", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "## Exercices\n", + "\n", + "Chaque cellule s'exécute sans erreur telle qu'elle est livrée : remplacez les lignes\n", + "`# TODO etudiant` par votre solution.\n", + "\n", + "### Exercice 1 — Compter à la main\n", + "\n", + "Le banc de la section 3 l'a écrite en une boucle ; écrivez la vôtre, étape par étape, sans\n", + "la recopier — c'est en l'écrivant que la définition à $n!$ termes devient concrète.\n", + "\n", + "- **Etape 1 :** parcourir toutes les permutations `p` de `range(n)`, avec\n", + " `itertools.permutations` (déjà importé) ;\n", + "- **Etape 2 :** pour chaque `p`, calculer le produit des $A[i][p[i]]$ sur les lignes $i$ ;\n", + "- **Etape 3 :** accumuler la somme des produits et la renvoyer.\n", + "- **Indice :** `math.prod` sur un générateur fait l'étape 2 en une seule expression.\n", + "\n", + "La cellule valide trois comptes : l'identité $3 \\times 3$ (un seul terme non nul), la matrice\n", + "tout-un $3 \\times 3$ ($3!$ produits égaux à 1), et la matrice de l'énoncé, de permanente\n", + "connue." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "exo1-stub", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-02T06:34:20.216960Z", + "iopub.status.busy": "2026-10-02T06:34:20.216719Z", + "iopub.status.idle": "2026-10-02T06:34:20.221338Z", + "shell.execute_reply": "2026-10-02T06:34:20.220781Z" + }, + "papermill": { + "duration": 0.00898, + "end_time": "2026-10-02T06:34:20.221899", + "exception": false, + "start_time": "2026-10-02T06:34:20.212919", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Exercice a completer : perm_def renvoie encore None\n" + ] + } + ], + "source": [ + "# Exercice 1 : a completer\n", + "MATRICE_EXO = [[1, 1, 1],\n", + " [1, 1, 0],\n", + " [1, 0, 1]] # sa permanente vaut 3 (valeur fournie pour l'auto-verification)\n", + "\n", + "\n", + "def perm_def(A):\n", + " \"\"\"Permanent de A par la definition : la somme des n! produits, un par permutation.\"\"\"\n", + " n = len(A)\n", + " total = 0\n", + " # Etape 1 : parcourir toutes les permutations p de range(n)\n", + " # Etape 2 : pour chaque p, calculer le produit des A[i][p[i]] sur les i\n", + " # Etape 3 : accumuler dans total, puis renvoyer total\n", + " # Indice : une boucle for, math.prod sur un generateur, et c'est tout\n", + " # TODO etudiant\n", + " return None\n", + "\n", + "\n", + "tests_exo1 = [(\"identite 3x3\", [[1, 0, 0], [0, 1, 0], [0, 0, 1]], 1),\n", + " (\"tout-un 3x3\", [[1, 1, 1], [1, 1, 1], [1, 1, 1]], 6),\n", + " (\"matrice de l'enonce\", MATRICE_EXO, 3)]\n", + "\n", + "if perm_def(tests_exo1[0][1]) is None:\n", + " print(\"Exercice a completer : perm_def renvoie encore None\")\n", + "else:\n", + " for nom, A, attendu in tests_exo1:\n", + " valeur = perm_def(A)\n", + " verdict = \"OK\" if valeur == attendu else \"ECHEC\"\n", + " print(f\"{nom:>22} : perm_def = {valeur} (attendu {attendu}) {verdict}\")" + ] + }, + { + "cell_type": "markdown", + "id": "exo2-enonce", + "metadata": { + "papermill": { + "duration": 0.003227, + "end_time": "2026-10-02T06:34:20.228260", + "exception": false, + "start_time": "2026-10-02T06:34:20.225033", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Exercice 2 — L'échelle mesurée\n", + "\n", + "La section 4 a chronométré la permanente du banc jusqu'à $n = 10$. Faites la mesure\n", + "vous-même, avec votre `perm_def` de l'exercice 1 : pour chaque taille $n$ de 2 à 9, engendrer\n", + "une matrice d'entiers aléatoires, chronométrer `perm_def` au `time.perf_counter`, et remplir\n", + "la liste `mesures`.\n", + "\n", + "- **Etape 1 :** la boucle de mesure sur $n = 2, 3, \\dots, 9$ ;\n", + "- **Etape 2 :** reporter le temps de chaque taille dans `mesures`, dans l'ordre croissant\n", + " des $n$ ;\n", + "- **Etape 3 :** la cellule trace ensuite le temps en échelle log contre $n$ et affiche les\n", + " facteurs d'une taille à la suivante.\n", + "- **Indice :** sur l'échelle log, on attend une **droite** qui monte — chaque taille\n", + " supplémentaire multiplie le temps par environ $n$." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "exo2-stub", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-02T06:34:20.234907Z", + "iopub.status.busy": "2026-10-02T06:34:20.234730Z", + "iopub.status.idle": "2026-10-02T06:34:20.238905Z", + "shell.execute_reply": "2026-10-02T06:34:20.238377Z" + }, + "papermill": { + "duration": 0.008288, + "end_time": "2026-10-02T06:34:20.239403", + "exception": false, + "start_time": "2026-10-02T06:34:20.231115", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Exercice a completer : mesures de temps pour n = 2 a 9 (boucle + time.perf_counter)\n" + ] + } + ], + "source": [ + "# Exercice 2 : a completer\n", + "mesures = None # TODO etudiant : liste des durees de perm_def pour n = 2, 3, ..., 9\n", + "\n", + "if mesures is None:\n", + " print(\"Exercice a completer : mesures de temps pour n = 2 a 9 (boucle + time.perf_counter)\")\n", + "else:\n", + " ns = list(range(2, 10))\n", + " plt.figure(figsize=(7, 4))\n", + " plt.semilogy(ns, mesures, \"o-\", label=\"perm_def : temps mesure\")\n", + " plt.xlabel(\"taille n de la matrice\")\n", + " plt.ylabel(\"temps (s), echelle log\")\n", + " plt.title(\"Exercice 2 : la definition a n! termes, en echelle log\")\n", + " plt.grid(True, which=\"both\", alpha=0.3)\n", + " plt.legend()\n", + " plt.tight_layout()\n", + " plt.show()\n", + " facteurs = [mesures[i + 1] / mesures[i] for i in range(len(mesures) - 1)]\n", + " print(\"facteurs d'un n au suivant :\", \", \".join(f\"{f:.1f}\" for f in facteurs))\n", + " print(\"un facteur proche de n : chaque taille ajoutee multiplie le travail par n\")" + ] + }, + { + "cell_type": "markdown", + "id": "exo3-enonce", + "metadata": { + "papermill": { + "duration": 0.003115, + "end_time": "2026-10-02T06:34:20.245367", + "exception": false, + "start_time": "2026-10-02T06:34:20.242252", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Exercice 3 — Couplages parfaits\n", + "\n", + "Le graphe biparti ci-dessous a trois sommets de chaque côté ; les arêtes sont les traits du\n", + "dessin :\n", + "\n", + "```\n", + " d0\n", + " / \\\n", + " u0 u2\n", + " | |\n", + " d1 d2\n", + " \\ /\n", + " u1\n", + "```\n", + "\n", + "- **Etape 1 :** prédire, en lisant le dessin, combien de couplages parfaits possède ce\n", + " graphe ;\n", + "- **Etape 2 :** écrire sa matrice d'adjacence $3 \\times 3$ — une ligne par $u_i$, une colonne\n", + " par $d_j$, un 1 si l'arête existe, un 0 sinon ;\n", + "- **Etape 3 :** la cellule vérifie avec la permanente du banc ; votre `perm_def` doit rendre\n", + " la même valeur.\n", + "- **Indice :** chaque sommet a exactement deux voisins — dès que le partenaire de $u_0$ est\n", + " choisi, suivez les choix qui s'imposent." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "exo3-stub", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-02T06:34:20.251866Z", + "iopub.status.busy": "2026-10-02T06:34:20.251702Z", + "iopub.status.idle": "2026-10-02T06:34:20.255221Z", + "shell.execute_reply": "2026-10-02T06:34:20.254644Z" + }, + "papermill": { + "duration": 0.007487, + "end_time": "2026-10-02T06:34:20.255650", + "exception": false, + "start_time": "2026-10-02T06:34:20.248163", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Exercice a completer : ecrire la matrice d'adjacence du graphe de l'enonce\n" + ] + } + ], + "source": [ + "# Exercice 3 : a completer\n", + "matrice = None # TODO etudiant : la matrice d'adjacence 3x3 du graphe ci-dessus\n", + "prediction = None # TODO etudiant : votre pronostic, lu sur le dessin (un entier)\n", + "\n", + "if matrice is None:\n", + " print(\"Exercice a completer : ecrire la matrice d'adjacence du graphe de l'enonce\")\n", + "else:\n", + " compte = permanente(matrice)\n", + " print(f\"permanente de votre matrice : {compte} couplages parfaits\")\n", + " if prediction is None:\n", + " print(\"pronostic a remplir aussi : prediction = votre nombre, lu sur le dessin\")\n", + " elif prediction == compte:\n", + " print(\"pronostic exact : le dessin se lit, la permanente le confirme\")\n", + " else:\n", + " print(f\"pronostic {prediction} different du compte {compte} : relisez le dessin (ou la matrice)\")" + ] + }, + { + "cell_type": "markdown", + "id": "conclusion", + "metadata": { + "papermill": { + "duration": 0.003078, + "end_time": "2026-10-02T06:34:20.261610", + "exception": false, + "start_time": "2026-10-02T06:34:20.258532", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "## Conclusion\n", + "\n", + "| Question | Le geste de ce notebook | Statut |\n", + "|---|---|---|\n", + "| Vérifier ou compter ? | #P : les témoins d'une expression, énumérés à la main | **mesuré** |\n", + "| Le déterminant | la définition à $n!$ termes tombe à $O(n^3)$ par Gauss | **mesuré** (facteur borné au doublement) |\n", + "| La permanente | la même somme sans les signes, $n!$ termes exécutés | **mesuré** (facteur proche de $n$ à chaque taille) |\n", + "| Peut-on faire mieux ? | Valiant 1979 : #P-difficile, même en 0/1 | **cité** |\n", + "| Un comptage naturel | couplages parfaits = permanente, deux machines d'accord | **mesuré** |\n", + "\n", + "Ce que ce notebook a établi :\n", + "\n", + "1. **#P** compte les témoins là où **NP** demande seulement s'il en existe : une question\n", + " plus fine, qui n'hérite d'aucun raccourci de la vérification.\n", + "2. Le **déterminant** et la **permanente** partagent la même somme de $n!$ produits ; les\n", + " signes du premier autorisent les annulations de Gauss, leur absence maintient la seconde\n", + " à l'échelle factorielle — l'écart a été **chronométré**, pas seulement affirmé.\n", + "3. Cette difficulté n'est pas une impression de banc : c'est un **théorème** (Valiant, cité)\n", + " qui fait de la permanente le représentant le plus dur de toute la classe #P — et le pont\n", + " vers les graphes est franchi : compter leurs couplages parfaits, c'est déjà une permanente.\n", + "\n", + "La suite de l'histoire est quantique : la distribution de sortie d'un interféromètre linéaire\n", + "dépend de permanentes complexes, et c'est sur ce mur qu'Aaronson et Arkhipov ont fondé leur\n", + "proposition d'un avantage quantique démontrable — l'approfondissement 05b le fait tourner.\n", + "\n", + "**Suite** : [Complexity-05b — La permanente, frontière quantique >>](Complexity-05b-AaronsonArkhipov-PermanenteBosonSampling.ipynb)\n", + "\n", + "**Références**\n", + "\n", + "- Valiant, L. G. (1979), *The Complexity of Computing the Permanent*, Theoretical Computer Science 8(2), 189–201.\n", + "- Aaronson, S. et Arkhipov, A. (2011), *The Computational Complexity of Linear Optics*, Proceedings of the 43rd ACM Symposium on Theory of Computing (STOC), 333–342.\n", + "- Minc, H. (1978), *Permanents*, Encyclopedia of Mathematics and its Applications 6, Addison-Wesley." + ] + } + ], + "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.14" + }, + "papermill": { + "default_parameters": {}, + "duration": 10.732013, + "end_time": "2026-10-02T06:34:20.619501", + "environment_variables": {}, + "exception": null, + "input_path": "MyIA.AI.Notebooks/Complexity/Complexity-05-CountingHarder-Permanent.ipynb", + "output_path": "MyIA.AI.Notebooks/Complexity/Complexity-05-CountingHarder-Permanent.ipynb", + "parameters": {}, + "start_time": "2026-10-02T06:34:09.887488", + "version": "2.6.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/MyIA.AI.Notebooks/Complexity/Complexity-05b-AaronsonArkhipov-PermanenteBosonSampling.ipynb b/MyIA.AI.Notebooks/Complexity/Complexity-05b-AaronsonArkhipov-PermanenteBosonSampling.ipynb index 0b2562abf3..83fd6c9f41 100644 --- a/MyIA.AI.Notebooks/Complexity/Complexity-05b-AaronsonArkhipov-PermanenteBosonSampling.ipynb +++ b/MyIA.AI.Notebooks/Complexity/Complexity-05b-AaronsonArkhipov-PermanenteBosonSampling.ipynb @@ -42,8 +42,8 @@ "tags": [] }, "source": [ - "# Complexity-05 — La permanente, frontière quantique : BosonSampling (Aaronson–Arkhipov, 2011)\n", - "\n**Navigation** : [Index](README.md) | [04b — Conjectures online](Complexity-04b-OnlineConjectures-Secretary-KServer.ipynb) | [06 — Déquantifier les suprématies →](Complexity-06-Aaronson-Dequantification-Stabilizer.ipynb)\n", + "# Complexity-05b — La permanente, frontière quantique : BosonSampling (Aaronson–Arkhipov, 2011)\n", + "\n**Navigation** : [Index](README.md) | [05 — Compter est plus dur que vérifier](Complexity-05-CountingHarder-Permanent.ipynb) | [06 — Déquantifier les suprématies →](Complexity-06-Aaronson-Dequantification-Stabilizer.ipynb)\n", "\n", "> **Hommage.** Scott Aaronson et Alex Arkhipov ont montré en 2011 (*The Computational\n", "> Complexity of Linear Optics*) qu'un plateau optique passif — beam splitters et miroirs,\n", diff --git a/MyIA.AI.Notebooks/Complexity/README.md b/MyIA.AI.Notebooks/Complexity/README.md index 5d2c15ae36..dd7842b887 100644 --- a/MyIA.AI.Notebooks/Complexity/README.md +++ b/MyIA.AI.Notebooks/Complexity/README.md @@ -21,7 +21,8 @@ n'y existe pas encore, et pourquoi. | [`Complexity-03c-ComplexityZoo-Navigation-Python.ipynb`](Complexity-03c-ComplexityZoo-Navigation-Python.ipynb) | Recherche | approfondissement de **03 — Plus de temps, plus de problèmes** | Scott Aaronson (fondateur du Complexity Zoo, 2004 — plus de 500 classes répertoriées) ; Theodore Baker, John Gill, Robert Solovay (*Relativizations of the P=?NP Question*, 1975) | La chaîne $L \subseteq NL \subseteq P \subseteq NP$ **tournée sur instances** : atteignabilité décidée deux fois — vérificateur à 2 mots de mémoire (certificat lu une fois, accord sur 4 tailles, faux témoin amputé refusé) contre DFS dont la pile explore ~$n/3$ sommets ; 2-SAT (Kosaraju, linéaire, $n=800$) contre 3-SAT (DPLL, densité-seuil $4{,}267n$) : facteurs de croissance **mesurés** par variable (~1,10× en noeuds, ~1,16× en temps), 20 certificats sur 20 vérifiés en polynomial ; **Baker–Gill–Solovay exécuté** — monde A : oracle diagonal, 4 machines vaincues par basculement d'un seul témoin, le côté NP exhaustif confirme $L_O$ sur chaque stade ; monde B : l'oracle QBF donne $P^B = NP^B$ en une requête — la barrière de relativisation vue du jouet ; carte du Zoo à 8 arêtes classées Mesuré/Cité/Ouvert (+ carte mermaid) ; encart **gap Mathlib** : `Computability/` couvre la calculabilité, mais aucune classe à bornage — le Zoo est absent | Français | | [`Complexity-04-OnlineAlgorithms-Python.ipynb`](Complexity-04-OnlineAlgorithms-Python.ipynb) | Licence | — | — (socle) | Louer ou acheter des skis : balayage des seuils, pire rapport $2 - 1/B$ atteint exactement au seuil $T = B$, puis distribution randomisée dont le pire rapport **converge vers $e/(e-1) \approx 1{,}58198$** ; pagination : Belady hors ligne contre LRU et FIFO — rapport tendant vers $k$ sur le motif cyclique ($4{,}84$ à $k=5$), retombant à $1{,}47$ sur requêtes aléatoires ; secrétaire : fraction d'observation balayée, sommet **plat** autour de $1/e$ (indiscernable de $0{,}4$ à $0{,}003$ près) ; quatre énoncés séparés Mesuré/Cité ; trois exercices exécutables | Français | | [`Complexity-04b-OnlineConjectures-Secretary-KServer.ipynb`](Complexity-04b-OnlineConjectures-Secretary-KServer.ipynb) | Recherche | approfondissement de **04 — Décider sans connaître la suite** | Sahil Singla & Christian Coester, Elias Koutsoupias, Marek Zbysiński (les deux preprints de septembre 2026 : conjecture du secrétaire matroïdal 2007 et conjecture k-server ~1990, tombées à 24 h d'écart) | Baseline $1/e$ du secrétaire classique mesurée par Monte Carlo ($n$ = 20/100/1000) ; deux matroïdes à oracle d'indépendance (uniforme, graphique) avec optimum offline exact et compétitivité de deux stratégies online ; **work function algorithm implémenté exactement** (DP sur les $\binom{m}{2}$ configurations) : sur le cycle $C_5$, ratio WFA 1,67 sous la borne $k=2$ quand greedy explose (20 à 60 requêtes, 160 à 480 — non compétitif) ; tableau du double visage randomisation/déterminisme ; trois niveaux épistémiques Mesuré/Cité/Absent (prudence « preuve annoncée ») ; encart **gap Mathlib** (matroïdes présents, compétitivité/k-server/work functions absents) | Français | -| [`Complexity-05b-AaronsonArkhipov-PermanenteBosonSampling.ipynb`](Complexity-05b-AaronsonArkhipov-PermanenteBosonSampling.ipynb) | Recherche | deviendra **05b**, approfondissement du futur 05 *Compter est plus dur que vérifier* | Scott Aaronson & Alex Arkhipov (*The Computational Complexity of Linear Optics*, 2011 — la permanente comme frontière quantique) | La définition $n!$ **exécutée** avec comptes canoniques, vérifiée contre l'énumération brute des couplages parfaits (perm(0/1)) ; **Ryser vectorisé réemployé de Sudoku-15** (`batch_perm8`, généralisé à toute taille) contre Gauss instrumenté : comptes exacts par formules fermées validées par instrumentation — à $n=20$, définition $4{,}9 \times 10^{19}$ opérations contre 5 149 pour Gauss, Ryser/det ≈ $1{,}6 \times 10^4$ en temps à $n=16$ ; #P-difficulté (Valiant 1979) maintenue au niveau **Cité**, FPRAS non négatif (Jerrum–Sinclair–Vigoda) en exception qui désigne les signes ; **BosonSampling jouet** : unitaire Haar (QR, Mezzadri), loi $\lvert\mathrm{perm}\rvert^2$ exacte sur 56/924/12 870 sorties, TV(échantillon, exact) → 0, coût d'énumération extrapolé jusqu'à ~1 an à 1 TFLOP/s pour $(20, 40)$ ; encart **gap Mathlib** — premier verdict **positif** de la série : `Matrix.permanent` existe (forme $n!$) face à un dossier `Determinant/` entier, mais ni Ryser ni couche complexité | Français | +| [`Complexity-05-CountingHarder-Permanent.ipynb`](Complexity-05-CountingHarder-Permanent.ipynb) | Licence | — | — (socle) | De vérifier (02) à **compter** : la classe #P déroulée à la main sur une expression à trois variables (huit certificats énumérés, quatre témoins) ; le **déterminant** défini par la même somme de $n!$ produits mais tombé à $O(n^3)$ par Gauss — chronométré au doublement des tailles (facteur borné, la signature du polynôme) ; la **permanente** sans les signes : la définition $n!$ exécutée telle quelle (copie pédagogique déclarée), comptes canoniques validés (identité, tout-un $= n!$, la $3 imes3$ déroulée terme à terme) ; l'écart **mesuré** côte à côte jusqu'à $n = 10$ avec extrapolation du mur factoriel ; Valiant 1979 (#P-difficile même en 0/1) au niveau **Cité** ; le pont aux graphes : compter les couplages parfaits d'un biparti **est** une permanente — vérifié par deux machines indépendantes | Français | +| [`Complexity-05b-AaronsonArkhipov-PermanenteBosonSampling.ipynb`](Complexity-05b-AaronsonArkhipov-PermanenteBosonSampling.ipynb) | Recherche | approfondissement de **05 — Compter est plus dur que vérifier** | Scott Aaronson & Alex Arkhipov (*The Computational Complexity of Linear Optics*, 2011 — la permanente comme frontière quantique) | La définition $n!$ **exécutée** avec comptes canoniques, vérifiée contre l'énumération brute des couplages parfaits (perm(0/1)) ; **Ryser vectorisé réemployé de Sudoku-15** (`batch_perm8`, généralisé à toute taille) contre Gauss instrumenté : comptes exacts par formules fermées validées par instrumentation — à $n=20$, définition $4{,}9 \times 10^{19}$ opérations contre 5 149 pour Gauss, Ryser/det ≈ $1{,}6 \times 10^4$ en temps à $n=16$ ; #P-difficulté (Valiant 1979) maintenue au niveau **Cité**, FPRAS non négatif (Jerrum–Sinclair–Vigoda) en exception qui désigne les signes ; **BosonSampling jouet** : unitaire Haar (QR, Mezzadri), loi $\lvert\mathrm{perm}\rvert^2$ exacte sur 56/924/12 870 sorties, TV(échantillon, exact) → 0, coût d'énumération extrapolé jusqu'à ~1 an à 1 TFLOP/s pour $(20, 40)$ ; encart **gap Mathlib** — premier verdict **positif** de la série : `Matrix.permanent` existe (forme $n!$) face à un dossier `Determinant/` entier, mais ni Ryser ni couche complexité | Français | | [`Complexity-06-Aaronson-Dequantification-Stabilizer.ipynb`](Complexity-06-Aaronson-Dequantification-Stabilizer.ipynb) | Recherche | deviendra **06b**, approfondissement du futur 06 *Simuler un circuit quantique classiquement* | Scott Aaronson & Daniel Gottesman (*Improved Simulation of Stabilizer Circuits*, 2004 — déquantifier les suprématies par l'arbitre exécutable) | Le formalisme stabilisateur **exécuté** : table de phase et conjugaison CHP **dérivées des matrices 2×2**, jamais recopiées ; port CHP complet (rowsum, portes, mesures — déterministe par lecture GF(2), aléatoire par re-stabilisation) croisé contre l'état complet 2^n : **84 valeurs propres stabilisatrices exactes au 1e-9** (zéro bruit), distributions TV sous 6× le bruit de sondage, stim en troisième juge ; banc discriminant : bascule **mesurée** dès ~12 qubits, ratio ~1600× à n=20, échelons locaux au-dessus du plancher 2× (le mur réel est plus raide que 2^n : la mémoire aussi a une loi) ; une **seule** porte T ferme le port **et** stim (`no attribute 't'`) — la frontière est Clifford/le-reste, pas quantique/classique | Français | ## Parcours @@ -37,7 +38,7 @@ principal de niveau licence qui en pose d'abord les notions. | 02 | Vérifier ou trouver : P, NP, certificat, réduction | Découverte | — | | 03 | [Plus de temps, plus de problèmes](Complexity-03-TimeHierarchy.ipynb) | Licence | [03b — Hartmanis–Stearns](Complexity-03b-HartmanisStearns-TimeHierarchy.ipynb), [03c — Zoo et oracles](Complexity-03c-ComplexityZoo-Navigation-Python.ipynb) | | 04 | [Décider sans connaître la suite](Complexity-04-OnlineAlgorithms-Python.ipynb) | Licence | [04b — conjectures online](Complexity-04b-OnlineConjectures-Secretary-KServer.ipynb) | -| 05 | Compter est plus dur que vérifier *(à venir)* | Licence | 05b — Aaronson–Arkhipov (aujourd'hui 05) | +| 05 | [Compter est plus dur que vérifier](Complexity-05-CountingHarder-Permanent.ipynb) | Licence | [05b — La permanente, frontière quantique](Complexity-05b-AaronsonArkhipov-PermanenteBosonSampling.ipynb) | | 06 | Simuler un circuit quantique classiquement *(à venir)* | Licence | 06b — Aaronson–Gottesman (aujourd'hui 06) | | 07 | La chaîne des inclusions : L, NL, P, NP *(à venir)* | Licence | — | @@ -55,7 +56,11 @@ pagination, secrétaire) ; son approfondissement 04b y ajoute le secrétaire mat le k-server, en écho aux notebooks sœurs `RL/rl_17_k_server_wfa` et `RL/rl_18_matroid_secretary` qui distillent les mêmes preprints côté RL. -Le notebook 05 étend la mesure **côté algèbre** : pour la première fois la série rend un +Le notebook 05 amène la série **côté comptage** : après vérifier (02) et décider sans +connaître la suite (04), compter — la classe #P, le déterminant polynomial face à la +permanente factorielle, l'écart chronométré et le pont aux couplages parfaits des graphes +bipartis. Son approfondissement 05b étend la mesure **côté algèbre** : pour la première fois +la série rend un verdict Mathlib positif (`Matrix.permanent` existe — mais à l'état de définition $n!$, sans Ryser ni #P : encart §5), et relie la permanente au dépôt existant — couplages parfaits de [Sudoku-15](../Sudoku/Sudoku-15-Infer-Python.ipynb), dont le schéma Ryser diff --git a/_quarto.yml b/_quarto.yml index d7c9dd1275..2119f55f05 100644 --- a/_quarto.yml +++ b/_quarto.yml @@ -687,6 +687,7 @@ project: - "MyIA.AI.Notebooks/Complexity/Complexity-03c-ComplexityZoo-Navigation-Python.ipynb" - "MyIA.AI.Notebooks/Complexity/Complexity-04-OnlineAlgorithms-Python.ipynb" - "MyIA.AI.Notebooks/Complexity/Complexity-04b-OnlineConjectures-Secretary-KServer.ipynb" + - "MyIA.AI.Notebooks/Complexity/Complexity-05-CountingHarder-Permanent.ipynb" - "MyIA.AI.Notebooks/Complexity/Complexity-05b-AaronsonArkhipov-PermanenteBosonSampling.ipynb" - "MyIA.AI.Notebooks/Complexity/Complexity-06-Aaronson-Dequantification-Stabilizer.ipynb" - "MyIA.AI.Notebooks/cross-series/socle-metadata-driven/Socle-MetadataDriven-Csharp.ipynb"