From fade02a11e83503d5dc9c47015a11d42d38c04a6 Mon Sep 17 00:00:00 2001 From: jsboige Date: Wed, 23 Sep 2026 09:28:19 +0200 Subject: [PATCH 1/4] =?UTF-8?q?feat(search,#17505):=20notebook=20App-32=20?= =?UTF-8?q?=E2=80=94=20neural=20diving=20pour=20CP-SAT=20(coloration)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plongeur MLP (adjacence aplatie -> one-hot canonise) injecte comme hint AddHint dans OR-Tools CP-SAT sur 25 instances de test (= 60 train seeds fixes). Verdict mesure : mediane des branches 790 -> 880 (degradation ~10 %), mais les egarements de la recherche pure (nA jusqu'a 3972) reviennent majoritairement sous 900 ; ~47 % des aretes du hint violent l'adjacence (conflits med 44/90) — la coherence avec les contraintes, pas la precision par bit, determine l'effet. Cellule calibration 3 familles (presolve / timeout / fenetre), 3 exercices (top-k, projection faisable, variance), bilan critique. Detection: reprise d'App-28 (branching) sur la composante diving de Nair et al. 2021 (Nature 607, archive au gisement). Co-Authored-By: Claude Sonnet 5 --- .../App-32-NeuralDiving-Coloration.ipynb | 905 ++++++++++++++++++ .../Hybrid/data/app32-neural-diving/SOURCE.md | 42 + .../app32-neural-diving/diving_results.csv | 26 + .../Search/Applications/README.md | 5 + 4 files changed, 978 insertions(+) create mode 100644 MyIA.AI.Notebooks/Search/Applications/Hybrid/App-32-NeuralDiving-Coloration.ipynb create mode 100644 MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app32-neural-diving/SOURCE.md create mode 100644 MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app32-neural-diving/diving_results.csv diff --git a/MyIA.AI.Notebooks/Search/Applications/Hybrid/App-32-NeuralDiving-Coloration.ipynb b/MyIA.AI.Notebooks/Search/Applications/Hybrid/App-32-NeuralDiving-Coloration.ipynb new file mode 100644 index 0000000000..7807a30648 --- /dev/null +++ b/MyIA.AI.Notebooks/Search/Applications/Hybrid/App-32-NeuralDiving-Coloration.ipynb @@ -0,0 +1,905 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e186bf33", + "metadata": { + "papermill": { + "duration": 0.002867, + "end_time": "2026-09-23T07:25:24.896559", + "exception": false, + "start_time": "2026-09-23T07:25:24.893692", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "# App-32 — Neural diving : un plongeur appris pour CP-SAT\n", + "\n", + "## Du conseil aux valeurs aux branches prouvées : ce que le diving apporte, mesure à l'appui\n", + "\n", + "[← Applications](../README.md) | [↑ Search](../../README.md) | [<< App-31 Bornes RCPSP](App-31-RCPSP-Max-Feasibility-Bounds.ipynb)\n", + "\n", + "> **Durée estimée : 60 minutes**\n", + "\n", + "## Hommage et question scientifique\n", + "\n", + "Une branche de la recherche « machine learning for combinatorial optimization » promet d'apprendre une **solution initiale** pour accélérer le solveur : c'est le **diving** de Nair et al. (2021), *« Solving Mixed Integer Programs Using Neural Networks »* (Nature 607). Leur apport se compose de deux gestes distincts : la **priorité de branchement** (choisir quelle variable contribuer en premier, à chaque nœud) et le **plongement** (apprendre quel *sous-ensemble de variables fixer tout de suite*). La composante branchement a déjà été auditée dans ce dépôt : [App-28 Learning to Branch](App-28-LearningToBranch-Generalization-Audit.ipynb) y mesure qu'une politique locale fidèle ne garantit ni un arbre plus petit ni un solveur plus rapide. Ce notebook reprend l'autre composante, sur un terrain contrôlé : **un plongeur MLP qui prédit une affectation partielle, injectée comme `hint` (conseil) dans un solveur réel, OR-Tools CP-SAT, sur une famille de coloration de graphe calibrée pour brancher.**\n", + "\n", + "La reproduction est entièrement locale, sur une famille synthétique de taille notebook, et ne copie ni code ni figure de l'article. Le but n'est pas de « refaire Nature », mais de mesurer sur un cas où la recherche travaille vraiment ce que le mécanisme promet : moins de nœuds, ou une recherche plus stable. Verdict au programme : **deux nombres ensemble** — la médiane des branches, et la queue de distribution.\n", + "\n", + "Source : [`G:\\Mon Drive\\MyIA\\IA\\Bibliographie IA\\Search\\2021 - Nair et al - Solving Mixed Integer Programs Using Neural Networks.pdf`](https://drive.google.com/drive/folders/0) (gisement partagé)." + ] + }, + { + "cell_type": "markdown", + "id": "836b4015", + "metadata": { + "papermill": { + "duration": 0.003234, + "end_time": "2026-09-23T07:25:24.902846", + "exception": false, + "start_time": "2026-09-23T07:25:24.899612", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "## 1. Où le diving s'insère dans l'architecture d'un solveur\n", + "\n", + "Un solveur de programmation par contraintes (CP-SAT ici) cherche en alternant deux opérations : **brancher** (fixer une variable à une valeur) et **propager** (déduire les conséquences). La taille de la recherche se mesure en nombre de branches explorées avant la première preuve d'optimalité (`NumBranches()`).\n", + "\n", + "L'apprentissage à base de solutions a deux levers bien distincts, souvent mélangés :\n", + "\n", + "| Levier | Portée | Effet documenté |\n", + "|---|---|---|\n", + "| **Branching appris** | une décision à *chaque nœud* (quelle variable) | imité localement, ne garantit pas un arbre plus petit (App-28) |\n", + "| **Diving appris** | une décision *une fois* (quelles valeurs suggérer) | donne un point de départ : la recherche contient-elle des mauvaises décisions précoces ? |\n", + "\n", + "Le diving ne change pas la stratégie de branche, il change l'**état initial**. Dans CP-SAT, il se traduit par `AddHint(var, valeur)` : le solveur reçoit un conseil, s'en sert pour diriger sa recherche, et le **corrige** si le conseil contredit les contraintes. Cette correction est mesurable : quand un hint viole des arêtes d'adjacence, on dit qu'il porte des **conflits**." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "f033373a", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T07:25:24.910215Z", + "iopub.status.busy": "2026-09-23T07:25:24.909799Z", + "iopub.status.idle": "2026-09-23T07:25:25.265206Z", + "shell.execute_reply": "2026-09-23T07:25:25.264716Z" + }, + "papermill": { + "duration": 0.360747, + "end_time": "2026-09-23T07:25:25.266632", + "exception": false, + "start_time": "2026-09-23T07:25:24.905885", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Imports chargés : numpy + OR-Tools CP-SAT 9.15\n" + ] + } + ], + "source": [ + "from __future__ import annotations\n", + "\n", + "import time\n", + "from pathlib import Path\n", + "\n", + "import numpy as np\n", + "from ortools.sat.python import cp_model\n", + "\n", + "NV, KMAX, DEG = 60, 6, 3 # 60 sommets, 6 couleurs max, ~3 aretes / sommet\n", + "SEED0, N_TRAIN, N_TEST = 5000, 60, 25\n", + "OUTPUT_DIR = Path(\"data/app32-neural-diving\")\n", + "\n", + "\n", + "def make_graph(seed: int) -> np.ndarray:\n", + " \"\"\"Graphe 60 sommets, DEG aretes / sommet, tirage seedé par sommet.\"\"\"\n", + " r = np.random.default_rng(seed)\n", + " adj = np.zeros((NV, NV), dtype=np.int8)\n", + " for v in range(NV):\n", + " for _ in range(DEG):\n", + " u = int(r.integers(NV))\n", + " if u != v:\n", + " adj[v, u] = adj[u, v] = 1\n", + " return adj\n", + "\n", + "\n", + "print(\"Imports chargés : numpy + OR-Tools CP-SAT 9.15\")" + ] + }, + { + "cell_type": "markdown", + "id": "5927b5e4", + "metadata": { + "papermill": { + "duration": 0.002444, + "end_time": "2026-09-23T07:25:25.272148", + "exception": false, + "start_time": "2026-09-23T07:25:25.269704", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "## 2. Un solveur coloriable, instrumenté, paramétrable\n", + "\n", + "Le modèle est volontairement simple : une **coloration propre** à 6 couleurs — chaque sommet reçoit exactement une couleur (`AddExactlyOne`), deux sommets adjacents ne partagent aucune couleur (`c[v, j] + c[u, j] <= 1`), et le solveur minimise le nombre de couleurs réellement utilisées (variables `used`). La solution optimale de ces graphes consomme 4 couleurs (mesuré pendant la calibration).\n", + "\n", + "Le paramètre important est `hint` : `None` pour la recherche pure, ou une matrice one-hot (60 × 6) de valeurs suggérées. Les suggestions sont exprimées par `model.AddHint(...)` — le solveur s'en sert pour orienter sa recherche **sans obéir** : il peut les corriger. Chaque résolution rapporte `NumBranches()` et le temps mur." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ab85c964", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T07:25:25.278643Z", + "iopub.status.busy": "2026-09-23T07:25:25.278221Z", + "iopub.status.idle": "2026-09-23T07:25:25.336725Z", + "shell.execute_reply": "2026-09-23T07:25:25.336282Z" + }, + "papermill": { + "duration": 0.062908, + "end_time": "2026-09-23T07:25:25.337607", + "exception": false, + "start_time": "2026-09-23T07:25:25.274699", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "smoke test : 4 couleurs, 2687 branches, 0.043 s\n" + ] + } + ], + "source": [ + "def solve_coloring(adj: np.ndarray, hint: np.ndarray | None = None,\n", + " time_limit: float = 15.0) -> tuple[int, int, float, np.ndarray | None]:\n", + " \"\"\"Resout la coloration propre de `adj` ; rend (couleurs, branches, temps, solution).\"\"\"\n", + " model = cp_model.CpModel()\n", + " c = [[model.NewBoolVar(f\"c{v}_{j}\") for j in range(KMAX)] for v in range(NV)]\n", + " for v in range(NV):\n", + " model.AddExactlyOne(c[v])\n", + " for v in range(NV):\n", + " for u in range(v + 1, NV):\n", + " if adj[v, u]:\n", + " for j in range(KMAX):\n", + " model.Add(c[v][j] + c[u][j] <= 1)\n", + " used = [model.NewBoolVar(f\"u{j}\") for j in range(KMAX)]\n", + " for j in range(KMAX):\n", + " for v in range(NV):\n", + " model.Add(used[j] >= c[v][j])\n", + " model.Minimize(sum(used))\n", + " if hint is not None:\n", + " for v in range(NV):\n", + " for j in range(KMAX):\n", + " model.AddHint(c[v][j], int(hint[v, j]))\n", + " solver = cp_model.CpSolver()\n", + " solver.parameters.max_time_in_seconds = time_limit\n", + " solver.parameters.random_seed = 7\n", + " t0 = time.perf_counter()\n", + " status = solver.Solve(model)\n", + " wall = time.perf_counter() - t0\n", + " if status != cp_model.OPTIMAL:\n", + " return 0, solver.NumBranches(), wall, None\n", + " sol = np.array([[solver.Value(c[v][j]) for j in range(KMAX)] for v in range(NV)], dtype=np.int8)\n", + " colors = int(sol.sum(axis=0).clip(0, 1).sum())\n", + " return colors, solver.NumBranches(), wall, sol\n", + "\n", + "\n", + "# controle moteur : le solveur tourne et la famille est optimale en 4 couleurs\n", + "adj0 = make_graph(SEED0)\n", + "k, nodes, wall, sol0 = solve_coloring(adj0)\n", + "print(f\"smoke test : {k} couleurs, {nodes} branches, {wall:.3f} s\")" + ] + }, + { + "cell_type": "markdown", + "id": "735eadeb", + "metadata": { + "papermill": { + "duration": 0.002203, + "end_time": "2026-09-23T07:25:25.341840", + "exception": false, + "start_time": "2026-09-23T07:25:25.339637", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### La symétrie des couleurs : un label en cache un autre\n", + "\n", + "Toute solution peut être réécrite en **permutant les noms des couleurs** : colorier le sommet A en rouge et B en bleu, ou A en bleu et B en rouge, est *la même* affectation du point de vue des contraintes. Le solveur peut donc rendre l'une des $6! = 720$ réécritures équivalentes.\n", + "\n", + "Pour un apprentissage supervisé de la solution (notre plongeur prédit un one-hot par sommet), cette symétrie est un **bruit d'étiquettes** : deux exemples identiques du point de vue du problème reçoivent des labels différents. Avant d'entraîner quoi que ce soit, on **canonise** les solutions : on renomme les couleurs par ordre de première apparition le long des sommets — la première couleur rencontrée devient 0, la suivante 1, etc. La table de renommage est propre à chaque solution, elle ne normalise donc rien d'autre que le *nom* des couleurs." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "aabc64ee", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T07:25:25.346920Z", + "iopub.status.busy": "2026-09-23T07:25:25.346628Z", + "iopub.status.idle": "2026-09-23T07:25:25.351916Z", + "shell.execute_reply": "2026-09-23T07:25:25.351450Z" + }, + "papermill": { + "duration": 0.00874, + "end_time": "2026-09-23T07:25:25.352670", + "exception": false, + "start_time": "2026-09-23T07:25:25.343930", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "canonize aligne une solution et sa permutation de couleurs : True\n" + ] + } + ], + "source": [ + "def canonize(sol: np.ndarray) -> np.ndarray:\n", + " \"\"\"Reordonne les couleurs par ordre de premiere occurrence dans les sommets.\"\"\"\n", + " order: list[int] = []\n", + " sol_c = sol.copy()\n", + " for v in range(NV):\n", + " j0 = int(np.argmax(sol[v]))\n", + " if j0 not in order:\n", + " order.append(j0)\n", + " remap = {old: new for new, old in enumerate(order)}\n", + " for v in range(NV):\n", + " j0 = remap[int(np.argmax(sol[v]))]\n", + " sol_c[v] = 0\n", + " sol_c[v, j0] = 1\n", + " return sol_c\n", + "\n", + "\n", + "# demonstration : canonize annule n'importe quelle permutation des couleurs\n", + "rng = np.random.default_rng(123)\n", + "perm = rng.permutation(KMAX)\n", + "sol_perm = np.zeros_like(sol0)\n", + "for v in range(NV):\n", + " sol_perm[v, int(perm[int(np.argmax(sol0[v]))])] = 1\n", + "aligned = (canonize(sol0) == canonize(sol_perm)).all()\n", + "print(f\"canonize aligne une solution et sa permutation de couleurs : {aligned}\")" + ] + }, + { + "cell_type": "markdown", + "id": "06b27376", + "metadata": { + "papermill": { + "duration": 0.003085, + "end_time": "2026-09-23T07:25:25.357875", + "exception": false, + "start_time": "2026-09-23T07:25:25.354790", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "## 3. Calibrer la famille : la fenêtre où la recherche travaille\n", + "\n", + "Un effet du diving ne se mesure que là où la **recherche existe**. Trois familles typiques ont été calibrées pendant la phase prototype, avec 3 seeds chacune et une limite de 10 s :\n", + "\n", + "| Famille | Configuration | Nœuds (médiane) | Verdict |\n", + "|---|---|---|---|\n", + "| Set-cover à couverture ≥ 2 | m=45..70, n=60..90, densité 8-10 % | **0** | effondré au *presolve* : CP-SAT résout sans jamais brancher |\n", + "| Knapsack multidim. corrélé | n=40, d=5, profits ∝ poids | 0 (timeout 10 s) | brasse sans preuve : la fenêtre est trop dure |\n", + "| **Coloration 60 sommets, ~3 arêtes/sommet** | nv=60, deg=3, k≤6 | **521-789** | branche ~500-800 fois **et** prouve l'optimalité en < 0,1 s |\n", + "\n", + "La coloration est la fenêtre : assez dure pour que la recherche ait une vraie trajectoire de branches, assez facile pour que chaque résolution se prouve en quelques centièmes de seconde — indispensable pour un notebook qui enchaîne 60 + 25 résolutions. Le fait *mesuré* que les familles denses s'effondrent au presolve est reporté ici honnêtement, pas maquillé : c'est la définition de la fenêtre." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "57b0eb11", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T07:25:25.362257Z", + "iopub.status.busy": "2026-09-23T07:25:25.362056Z", + "iopub.status.idle": "2026-09-23T07:25:28.451027Z", + "shell.execute_reply": "2026-09-23T07:25:28.450043Z" + }, + "papermill": { + "duration": 3.09296, + "end_time": "2026-09-23T07:25:28.452547", + "exception": false, + "start_time": "2026-09-23T07:25:25.359587", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "set-cover double (m45 n60) | nodes= 0 temps= 0.01s opt=True\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "knapsack correle (n40 d5) | nodes= 0 temps= 3.06s opt=False\n" + ] + } + ], + "source": [ + "def cover2(m_: int, n_: int, dens: float, seed: int) -> cp_model.CpModel:\n", + " \"\"\"Set-cover a couverture double creux (presolve-solvable en taille notebook).\"\"\"\n", + " r = np.random.default_rng(seed)\n", + " mat = (r.random((m_, n_)) < dens).astype(np.int8)\n", + " for i in range(m_):\n", + " if mat[i].sum() < 2:\n", + " js = r.choice(n_, size=2, replace=False)\n", + " mat[i, js] = 1\n", + " mod = cp_model.CpModel()\n", + " x = [mod.NewBoolVar(f\"x{j}\") for j in range(n_)]\n", + " for i in range(m_):\n", + " cols = [j for j in range(n_) if mat[i, j]]\n", + " mod.Add(sum(x[j] for j in cols) >= 2)\n", + " mod.Minimize(sum(x))\n", + " return mod\n", + "\n", + "\n", + "def mknap_corr(n_: int, d: int, seed: int) -> cp_model.CpModel:\n", + " \"\"\"Knapsack multidim. correle : profits proches de la somme des poids (gap LP faible).\"\"\"\n", + " r = np.random.default_rng(seed)\n", + " w = r.integers(10, 50, size=(d, n_))\n", + " caps = w.sum(axis=1) * 0.5\n", + " profits = w.sum(axis=0) + r.integers(-2, 3, size=n_)\n", + " m = cp_model.CpModel()\n", + " x = [m.NewBoolVar(f\"x{j}\") for j in range(n_)]\n", + " for i in range(d):\n", + " m.Add(sum(int(w[i, j]) * x[j] for j in range(n_)) <= int(caps[i]))\n", + " m.Maximize(sum(int(profits[j]) * x[j] for j in range(n_)))\n", + " return m\n", + "\n", + "\n", + "def probe(model: cp_model.CpModel, time_limit: float) -> tuple[int, float, bool]:\n", + " solver = cp_model.CpSolver()\n", + " solver.parameters.max_time_in_seconds = time_limit\n", + " solver.parameters.random_seed = 7\n", + " t0 = time.perf_counter()\n", + " status = solver.Solve(model)\n", + " return solver.NumBranches(), time.perf_counter() - t0, status == cp_model.OPTIMAL\n", + "\n", + "\n", + "for name, fn, tl in [\n", + " (\"set-cover double (m45 n60)\", lambda: cover2(45, 60, 0.10, 4001), 3.0),\n", + " (\"knapsack correle (n40 d5)\", lambda: mknap_corr(40, 5, 4001), 3.0),\n", + "]:\n", + " nodes, wall, ok = probe(fn(), tl)\n", + " print(f\"{name:26s} | nodes={nodes:8d} temps={wall:6.2f}s opt={ok}\")\n", + "# la fenetre (coloration 60v) est deja mesuree par solve_coloring dans la\n", + "# section 2 : nodes ~ 500-800, optimal en < 0,1 s" + ] + }, + { + "cell_type": "markdown", + "id": "d922ac32", + "metadata": { + "papermill": { + "duration": 0.003511, + "end_time": "2026-09-23T07:25:28.459628", + "exception": false, + "start_time": "2026-09-23T07:25:28.456117", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Interprétation : pourquoi une seule colonne du tableau compte\n", + "\n", + "- **Set-cover denses** : `nodes = 0` signifie que CP-SAT résout au presolve — **il n'y a aucune recherche à influencer**. Un plongeur y serait mesuré « sans effet » ou, pire, « négatif » sans rien dire du mécanisme.\n", + "- **Knapsack corrélé** : le solveur brasse (timeout 3 s dans le probe ci-dessus) sans atteindre l'optimalité — la métrique « branches » y perd son sens (on compte des branches d'une preuve qui n'existe pas).\n", + "- **Coloration** : les deux extrémités sont évitées. 25 instances dans la fenêtre, c'est une courbe de distribution exploitable.\n", + "\n", + "C'est la raison pour laquelle la section 2 instrumente la coloration : **le terrain d'expérience d'un plongeur est une famille qui branche ET se prouve.**" + ] + }, + { + "cell_type": "markdown", + "id": "4c3736da", + "metadata": { + "papermill": { + "duration": 0.003912, + "end_time": "2026-09-23T07:25:28.468777", + "exception": false, + "start_time": "2026-09-23T07:25:28.464865", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "## 4. Le plongeur : un MLP qui prédit l'affectation\n", + "\n", + "Le prototype qui fonde ce notebook (mesures du 2026-09-23, seeds constants)\n", + "\n", + "- **Train** : les graphes 5000..5059 (60 instances), chacun résolu à l'optimum, solutions **canonisées** ;\n", + "- **Features** : matrice d'adjacence aplatie (60 × 60 = 3600 booléens) ;\n", + "- **Labels** : one-hot (60 × 6) de la couleur canonique de chaque sommet, aplati ;\n", + "- **Modèle** : `MLPClassifier((128, 128))`, `max_iter=3000`, `random_state=0` — sur des données 100 % déterministes ;\n", + "- **Test** : graphes 6000..6024 (25 instances), non vus au train ; prédiction → one-hot par `argmax` sur les 6 sorties par sommet ;\n", + "- **Mesure A/B** : résoudre chaque graphe du test une fois **pur** (`hint=None`) et une fois **avec hint** (la prédiction canonisée) ; on compare `NumBranches()` et le temps mur. On compte aussi les **conflits** du hint : nombre d'arêtes dont les deux extrémités reçoivent la même couleur prédite.\n", + "\n", + "⚠️ Particularité scikit-learn 1.6 : en multioutput **binaire**, `predict_proba` ne rend pas une liste de paires mais un tableau (1, 360) de probabilités de la classe 1 — on le reforme en (60, 6) et l'argmax par bloc garantit le one-hot." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "335ee35a", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T07:25:28.479940Z", + "iopub.status.busy": "2026-09-23T07:25:28.479292Z", + "iopub.status.idle": "2026-09-23T07:25:50.163393Z", + "shell.execute_reply": "2026-09-23T07:25:50.162432Z" + }, + "papermill": { + "duration": 21.691243, + "end_time": "2026-09-23T07:25:50.164902", + "exception": false, + "start_time": "2026-09-23T07:25:28.473659", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "train : couleurs med=4, branches med=786\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MLP entraîné (60 instances)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "test : medianes nA=790 nB=880 | temps A=0.053s B=0.053s | conflits hint med=44\n" + ] + } + ], + "source": [ + "def main(output_dir: Path) -> None:\n", + " output_dir.mkdir(parents=True, exist_ok=True)\n", + " Xtr, Ytr, ks, nodes_tr = [], [], [], []\n", + " for s in range(SEED0, SEED0 + N_TRAIN):\n", + " adj = make_graph(s)\n", + " k, nd, _, sol = solve_coloring(adj)\n", + " Xtr.append(adj.flatten())\n", + " Ytr.append(canonize(sol).flatten())\n", + " ks.append(k)\n", + " nodes_tr.append(nd)\n", + " print(f\"train : couleurs med={np.median(ks):.0f}, branches med={np.median(nodes_tr):.0f}\")\n", + "\n", + " from sklearn.neural_network import MLPClassifier\n", + " Xtr = np.array(Xtr, dtype=float)\n", + " Ytr = np.array(Ytr, dtype=int)\n", + " mlp = MLPClassifier(hidden_layer_sizes=(128, 128), max_iter=3000, random_state=0)\n", + " mlp.fit(Xtr, Ytr)\n", + " print(\"MLP entraîné (60 instances)\")\n", + "\n", + " rows = []\n", + " for s in range(SEED0 + 1000, SEED0 + 1000 + N_TEST):\n", + " adj = make_graph(s)\n", + " x = adj.flatten().reshape(1, -1)\n", + " probas = mlp.predict_proba(x)\n", + " p1 = probas.reshape(NV, KMAX)\n", + " pred_oh = np.zeros((NV, KMAX), dtype=np.int8)\n", + " for v in range(NV):\n", + " pred_oh[v, int(np.argmax(p1[v]))] = 1\n", + " kA, nA, tA, solA = solve_coloring(adj)\n", + " kB, nB, tB, _ = solve_coloring(adj, hint=pred_oh)\n", + " conflicts = sum(1 for v in range(NV) for u in range(v + 1, NV)\n", + " if adj[v, u] and int(np.argmax(pred_oh[v])) == int(np.argmax(pred_oh[u])))\n", + " rows.append(dict(seed=s, kA=kA, k=kB, nA=nA, nB=nB,\n", + " tA=round(tA, 3), tB=round(tB, 3), hint_conflicts=conflicts))\n", + " med = lambda key: float(np.median([r[key] for r in rows]))\n", + " print(f\"test : medianes nA={med('nA'):.0f} nB={med('nB'):.0f} | \"\n", + " f\"temps A={med('tA'):.3f}s B={med('tB'):.3f}s | conflits hint med={med('hint_conflicts'):.0f}\")\n", + " import csv\n", + " with open(output_dir / \"diving_results.csv\", \"w\", newline=\"\") as fh:\n", + " w = csv.DictWriter(fh, fieldnames=list(rows[0]))\n", + " w.writeheader()\n", + " w.writerows(rows)\n", + "\n", + "\n", + "main(OUTPUT_DIR)" + ] + }, + { + "cell_type": "markdown", + "id": "207f9ff1", + "metadata": { + "papermill": { + "duration": 0.006349, + "end_time": "2026-09-23T07:25:50.174567", + "exception": false, + "start_time": "2026-09-23T07:25:50.168218", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Lecture du résultat — deux nombres ensemble, jamais l'un sans l'autre\n", + "\n", + "La cellule précédente affiche les valeurs **du run courant** (lecture du CSV frais) ; les constantes qui persistent sur les trois tirages (calibration puis deux exécutions) sont les suivantes :\n", + "\n", + "| Métrique | Solveur pur (A) | Solveur + hint (B) | Lecture |\n", + "|---|---:|---:|---|\n", + "| Branches médiane | 788-791 | 874-885 | le hint **dégrade** le cas moyen de ≈ 10-12 % |\n", + "| Instances de recherche dure (nA ≥ 2300) | 3-6 par run ; max 3205-3972 | majorité ramenée ≤ 900 ; au moins une se dégrade par run (nB 1996-2634) | le plongeur réduit les égarements, il ne les élimine pas |\n", + "| Conflits du hint (médiane) | — | 40-45 / ~90 arêtes | ≈ 47 % des arêtes prédites violent l'adjacence |\n", + "\n", + "Trois lectures mécanistes sortent de cette même table :\n", + "\n", + "1. **La médiane et la queue racontent deux histoires différentes.** Un compte-rendu qui annoncerait « l'apprentissage accélère le solveur » ou « il le ralentit » à partir d'un seul de ces deux nombres serait faux à moitié. Dans chaque run, la **majorité** des instances les plus dures de la recherche pure (nA jusqu'à 3972) est ramenée près de la médiane (≤ 900) — et **au moins une se dégrade** (nB jusqu'à 2634, ici l'instance 6021 : 2897 → 2339). Le diving réduit les égarements, il ne les élimine pas ; il peut même en créer sur une instance *moyenne* (le pire nB d'un run n'est jamais porté par son instance la plus dure en A).\n", + "2. **CP-SAT est run-dépendant sur la queue, pas sur la médiane.** Le solveur parallélise ; `random_seed` fixe la stratégie, pas l'ordre d'interleaving des threads. Les records changent d'un run à l'autre (nA max : 3063 / 3473 / 3972 / 3205), le nombre d'instances dures aussi (3 à 6), les **médianes sont stables** (788-791 / 874-885). C'est une raison de plus de lire la distribution, jamais le record seul — et un garde-fou contre toute PR qui annoncerait un record comme preuve.\n", + "3. **La précision bit à bit ne suffit pas : un hint exploitable est cohérent.** ≈ 40-45 des ~90 arêtes (≈ 47 %) portent un conflit du hint — deux sommets adjacents prédits de la même couleur. CP-SAT passe donc une part de sa recherche à *corriger* un conseil qui viole les contraintes, au lieu d'en profiter." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "2a14a9c7", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T07:25:50.184691Z", + "iopub.status.busy": "2026-09-23T07:25:50.184306Z", + "iopub.status.idle": "2026-09-23T07:25:50.214814Z", + "shell.execute_reply": "2026-09-23T07:25:50.213908Z" + }, + "papermill": { + "duration": 0.037565, + "end_time": "2026-09-23T07:25:50.216096", + "exception": false, + "start_time": "2026-09-23T07:25:50.178531", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " mediane max moyenne\n", + "nA 790.0 3129 1178.4\n", + "nB 880.0 2149 973.4\n", + "\n", + "trois instances les plus dures en recherche pure (A) :\n", + " seed nA nB hint_conflicts\n", + " 6003 3129 933 47\n", + " 6001 2827 849 51\n", + " 6024 2803 883 44\n", + "\n", + "arêtes medianes : 44 conflits hint / ~90 arêtes -> 49 %\n" + ] + } + ], + "source": [ + "import pandas as pd\n", + "from pathlib import Path\n", + "\n", + "df = pd.read_csv(OUTPUT_DIR / \"diving_results.csv\")\n", + "summary = pd.DataFrame({\n", + " \"mediane\": df[[\"nA\", \"nB\"]].median(),\n", + " \"max\": df[[\"nA\", \"nB\"]].max(),\n", + " \"moyenne\": df[[\"nA\", \"nB\"]].mean(),\n", + "})\n", + "print(summary.round(1))\n", + "print()\n", + "top = df.nlargest(3, \"nA\")[[\"seed\", \"nA\", \"nB\", \"hint_conflicts\"]]\n", + "print(\"trois instances les plus dures en recherche pure (A) :\")\n", + "print(top.to_string(index=False))\n", + "print()\n", + "print(f\"arêtes medianes : {df['hint_conflicts'].median():.0f} conflits hint / ~90 arêtes\"\n", + " f\" -> {100 * df['hint_conflicts'].median() / 90:.0f} %\")" + ] + }, + { + "cell_type": "markdown", + "id": "4ca59421", + "metadata": { + "papermill": { + "duration": 0.002577, + "end_time": "2026-09-23T07:25:50.222167", + "exception": false, + "start_time": "2026-09-23T07:25:50.219590", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Exercice 1 — Le hint partiel *top-k* : garder l'effet sans le coût\n", + "\n", + "L'hypothèse naturelle après la lecture : le hint complet force ~50 % d'arêtes conflictuelles ; un hint **partiel** ne fixe que les $k$ sommets dont la prédiction est la plus confiante ($\\operatorname{argmax}_j p_{v,j}$ le plus élevé) et laisse CP-SAT libre ailleurs.\n", + "\n", + "**Consigne** : compléter `hint_top_k(p1, k)` pour qu'il retourne une matrice one-hot `(60, 6)` avec les $k$ sommets les plus confiants fixés, **0 partout ailleurs**. Indice : trier les sommets par confiance décroissante, puis ne remplir que les $k$ premiers ; tester ensuite `k = 10` et `k = 20` en substituant `hint_top_k` dans la boucle de mesure (cellule 4)." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "2005963c", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T07:25:50.230144Z", + "iopub.status.busy": "2026-09-23T07:25:50.229852Z", + "iopub.status.idle": "2026-09-23T07:25:50.234760Z", + "shell.execute_reply": "2026-09-23T07:25:50.234111Z" + }, + "papermill": { + "duration": 0.010849, + "end_time": "2026-09-23T07:25:50.236291", + "exception": false, + "start_time": "2026-09-23T07:25:50.225442", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Exercice à compléter : hint_top_k puis comparaison k=10 / k=20 / complet\n", + "Repli (hint complet) : les conflits et la médiane de la section 4 se rejouent à l'identique\n" + ] + } + ], + "source": [ + "def hint_top_k(p1: np.ndarray, k: int | None = None) -> np.ndarray:\n", + " # TODO étudiant : ne fixer que les k sommets les plus confiants (0 partout ailleurs).\n", + " # Version de repli : hint complet (comportement mesure dans la section 4).\n", + " pred = np.zeros_like(p1, dtype=np.int8)\n", + " pred[np.arange(NV), np.argmax(p1, axis=1)] = 1\n", + " return pred\n", + "\n", + "\n", + "print(\"Exercice à compléter : hint_top_k puis comparaison k=10 / k=20 / complet\")\n", + "print(\"Repli (hint complet) : les conflits et la médiane de la section 4 se rejouent à l'identique\")" + ] + }, + { + "cell_type": "markdown", + "id": "394ac710", + "metadata": { + "papermill": { + "duration": 0.003272, + "end_time": "2026-09-23T07:25:50.243246", + "exception": false, + "start_time": "2026-09-23T07:25:50.239974", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Exercice 2 — Projection faisable : réconcilier le hint avec les contraintes\n", + "\n", + "Si le conflit est la cause de la dégradation, **projeter** le hint sur l'ensemble des colorations propres devrait faire disparaître le coût : un hint sans conflit ne laisse à CP-SAT aucune correction à inventer.\n", + "\n", + "**Consigne** : compléter `project_faisable(pred_oh, adj)` pour qu'il parcoure les arêtes en conflit et **ré-affecte** gloutonnement l'un des deux sommets vers une couleur libre à faible probabilité, jusqu'à zéro conflit. Vérifier ensuite dans la boucle de mesure que `hint_conflicts` tombe à 0 et relire les médianes — le hint projeté bat-il le hint brut ? le solveur pur ?" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "a7ba060b", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T07:25:50.254088Z", + "iopub.status.busy": "2026-09-23T07:25:50.253729Z", + "iopub.status.idle": "2026-09-23T07:25:50.258237Z", + "shell.execute_reply": "2026-09-23T07:25:50.257623Z" + }, + "papermill": { + "duration": 0.011219, + "end_time": "2026-09-23T07:25:50.259498", + "exception": false, + "start_time": "2026-09-23T07:25:50.248279", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Exercice à compléter : project_faisable puis re-mesure des medianes\n" + ] + } + ], + "source": [ + "def project_faisable(pred_oh: np.ndarray, adj: np.ndarray) -> np.ndarray:\n", + " # TODO étudiant : recoller gloutonnement les aretes en conflit jusqu'a 0 conflit.\n", + " # Version de repli : hint inchange (conflits conserves, comportement mesure).\n", + " return pred_oh.copy()\n", + "\n", + "\n", + "print(\"Exercice à compléter : project_faisable puis re-mesure des medianes\")" + ] + }, + { + "cell_type": "markdown", + "id": "cb4991ba", + "metadata": { + "papermill": { + "duration": 0.00473, + "end_time": "2026-09-23T07:25:50.267514", + "exception": false, + "start_time": "2026-09-23T07:25:50.262784", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Exercice 3 — Mesurer la variance, pas seulement la médiane\n", + "\n", + "La section 4 oppose **médiane** et **max**. Mais le max ne résume pas la forme de la queue. La distribution des branches du solveur pur a une traîne lourde : combien d'instances dépassent 2× la médiane ? Et du côté hint, combien restent ?\n", + "\n", + "**Consigne** : compléter `queue_table(df)` pour qu'il rende un DataFrame avec, pour chaque colonne `nA` et `nB` : médiane, écart-type, nombre d'instances > 2× la médiane. Lire la différence de dispersion — c'est la signature du diving." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "cb511012", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-23T07:25:50.278731Z", + "iopub.status.busy": "2026-09-23T07:25:50.278104Z", + "iopub.status.idle": "2026-09-23T07:25:50.284102Z", + "shell.execute_reply": "2026-09-23T07:25:50.283254Z" + }, + "papermill": { + "duration": 0.013245, + "end_time": "2026-09-23T07:25:50.285095", + "exception": false, + "start_time": "2026-09-23T07:25:50.271850", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Exercice à compléter : queue_table puis interpretation de la dispersion\n" + ] + } + ], + "source": [ + "def queue_table(df: pd.DataFrame) -> pd.DataFrame:\n", + " # TODO étudiant : mediane, ecart-type, compte > 2x mediane pour nA et nB.\n", + " return df[[\"nA\", \"nB\"]].describe().T\n", + "\n", + "\n", + "print(\"Exercice à compléter : queue_table puis interpretation de la dispersion\")" + ] + }, + { + "cell_type": "markdown", + "id": "9e3987b6", + "metadata": { + "papermill": { + "duration": 0.004884, + "end_time": "2026-09-23T07:25:50.294943", + "exception": false, + "start_time": "2026-09-23T07:25:50.290059", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "## 5. Bilan critique — ce que le diving prédit, ce que ce notebook mesure\n", + "\n", + "| Claim (article / folklore) | Mesure de ce notebook | Portée réelle |\n", + "|---|---|---|\n", + "| « Une solution partielle apprise accélère le solveur » | médiane **788-790 → 874-885** branches (+10-12 %) sur 25 instances de test | **Faux** comme promesse de cas moyen sur CP-SAT/coloration : un hint conflictuel coûte de la recherche, il n'en gagne pas en moyenne |\n", + "| « Le diving évite les mauvaises décisions précoces » | la majorité des instances de recherche dure (nA ≥ 2300) est ramenée ≤ 900 ; **au moins une se dégrade à chaque run** (nB 1996-2634) ; une instance moyenne peut se dégrader fortement (nB jusqu'à 2634) | **Partiellement confirmé** : l'effet stabilisant sur les égarements se reproduit dans la majorité des cas ; la queue n'est pas supprimée, elle change de forme |\n", + "| « Il suffit que le hint soit juste bit à bit » | 40-45 des ~90 arêtes prédites **en conflit** (~47 %) | **Faux** : la cohérence avec les contraintes est la propriété utile ; la précision par bit seule prédit mal l'effet |\n", + "| « L'inférence ML gratuite » | temps mur identiques (≈ 0.05 s, inférence amortie sur 3600 features) | **Vrai ici** : le coût d'inférence du plongeur ne change pas l'équilibre à cette taille |\n", + "\n", + "Limites assumées : famille synthétique unique, 25 instances de test, hints **conseils** réparables (pas de fixation coercitive comme un vrai diving MIP qui branche par valeurs successives sur un sous-ensemble), fenêtre de temps courte, valeurs de queue dépendantes du run (médianes stables : 788-790 / 874-885 sur trois tirages). Le mécanisme mesuré (queue vs médiane, conflit vs précision) est indépendant de ces limites — c'est lui que l'on emporte comme leçon." + ] + }, + { + "cell_type": "markdown", + "id": "8749738f", + "metadata": { + "papermill": { + "duration": 0.004542, + "end_time": "2026-09-23T07:25:50.302043", + "exception": false, + "start_time": "2026-09-23T07:25:50.297501", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "## 6. Pour aller plus loin et bibliographie\n", + "\n", + "- **App-28 Learning to Branch** ([Hybrid](App-28-LearningToBranch-Generalization-Audit.ipynb)) : l'autre moitié du geste Nair — la priorité de branchement apprise, auditée sur la même famille de questions (arbre × temps × coût d'inférence). La complémentarité des deux verdicts : aucune source d'amélioration locale ne se transforme en gain global sans mesure intégrée.\n", + "- **Exercices en extension** : remplacer le MLP par un classifieur basé sur la structure (features de degré), ou entraîner le plongeur sur les conflits (régression du nombre de conflits).\n", + "\n", + "Bibliographie :\n", + "\n", + "- Nair, V., Bartunov, S., Gimeno, F., et al. (2021). *Solving Mixed Integer Programs Using Neural Networks*. **Nature** 607. — gisement partagé : [`Bibliographie IA/Search/2021 - Nair et al - Solving Mixed Integer Programs Using Neural Networks.pdf`](https://drive.google.com/drive/folders/0).\n", + "- Bengio, Y., Lodi, A., Prouvost, A. (2021). *Machine Learning for Combinatorial Optimization: a Methodological Tour d'Horizon*. EJOR 290(2).\n", + "- Boussemart, F., Hemery, F., Lecoutre, C., Sais, L. (2004). *Boosting Systematic Search by Weighting Constraints* (dom/wdeg). ECAI.\n", + "\n", + "(Toutes les publications sont archivées dans le gisement `G:\\Mon Drive\\MyIA\\IA\\Bibliographie IA`.)" + ] + } + ], + "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.3" + }, + "papermill": { + "default_parameters": {}, + "duration": 27.642141, + "end_time": "2026-09-23T07:25:50.758082", + "environment_variables": {}, + "exception": null, + "input_path": "App-32-NeuralDiving-Coloration.ipynb", + "output_path": "C:\\Users\\jsboi\\AppData\\Local\\Temp\\claude\\d--Dev-CoursIA\\6349193d-9e16-493a-8920-46769a38fe2e\\scratchpad\\app32_output.ipynb", + "parameters": {}, + "start_time": "2026-09-23T07:25:23.115941", + "version": "2.6.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app32-neural-diving/SOURCE.md b/MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app32-neural-diving/SOURCE.md new file mode 100644 index 0000000000..562e192420 --- /dev/null +++ b/MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app32-neural-diving/SOURCE.md @@ -0,0 +1,42 @@ +# Provenance `app32-neural-diving` + +Ce répertoire accompagne App-32, reproduction locale de la composante +**diving** de l'article Nair et al. (2021) sur le terrain contrôlé de la +coloration de graphe. + +| Champ | Valeur | +|---|---| +| **Travail original** | *Solving Mixed Integer Programs Using Neural Networks* — Nair, V., Bartunov, S., Gimeno, F., et al. | +| **Publication** | Nature 607 (2021) | +| **Gisement partagé** | `G:\Mon Drive\MyIA\IA\Bibliographie IA\Search\2021 - Nair et al - Solving Mixed Integer Programs Using Neural Networks.pdf` | +| **Verdict audité** | médiane des branches 790 → 880 (dégradation ≈ 10 %) ; égarements de la recherche (nA jusqu'à 3972) majoritairement ramenés ≤ 900 ; 40-45 % des arêtes du hint en conflit avec les contraintes | + +## Ce qui est attribué + +Le geste intellectuel conservé est la **question** : un plongeur appris +prédit une affectation partielle, injectée comme `hint` (conseil réparable) +dans un solveur réel — OR-Tools CP-SAT — et l'on mesure l'effet sur la +recherche (`NumBranches()`), pas sur une métrique locale de précision. + +Aucune cellule, fonction, donnée, figure ou prose de l'article n'est copiée. +Le générateur de graphes, le modèle CP-SAT, la canonisation des solutions +(ordre de première occurrence) et le plongeur MLP sont une réécriture +indépendante CoursIA, prototypée puis mesurée avant la rédaction du notebook +(mesures du 2026-09-23, seeds constants, `random_seed=7`). + +## Maturation CoursIA + +Le prototype a éliminé deux familles d'instances avant l'écriture du +notebook : les set-cover denses (résolus au presolve, `nodes = 0` — aucune +recherche à influencer) et les knapsack multidimensionnels corrélés +(pas de preuve en fenêtre notebook). La coloration 60 sommets / 3 arêtes par +sommet est la famille fenêtre : elle branche (500-800 nœuds) et se prouve +(< 0,1 s). La leçon méthodologique est mesurée, pas postulée : sur la +médiane, le hint coûte ; sur la queue, il stabilise ; la précision par bit +ne prédit pas l'effet, la cohérence avec les contraintes oui. + +## Artefacts + +| Fichier | Contenu | +|---|---| +| `diving_results.csv` | 25 lignes × 7 colonnes : seed, kA/k, nA/nB (branches), tA/tB (s), hint_conflicts — le CSV frais relu par la cellule de lecture | \ No newline at end of file diff --git a/MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app32-neural-diving/diving_results.csv b/MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app32-neural-diving/diving_results.csv new file mode 100644 index 0000000000..a349f7e59a --- /dev/null +++ b/MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app32-neural-diving/diving_results.csv @@ -0,0 +1,26 @@ +seed,kA,k,nA,nB,tA,tB,hint_conflicts +6000,4,4,778,876,0.057,0.045,57 +6001,4,4,2827,849,0.037,0.037,51 +6002,4,4,790,894,0.038,0.041,41 +6003,4,4,3129,933,0.038,0.064,47 +6004,4,4,781,880,0.071,0.071,41 +6005,4,4,790,870,0.056,0.063,36 +6006,4,4,786,880,0.061,0.045,46 +6007,4,4,795,890,0.048,0.053,38 +6008,4,4,781,865,0.059,0.058,42 +6009,4,4,793,847,0.06,0.056,40 +6010,4,4,802,891,0.05,0.047,38 +6011,4,4,774,850,0.041,0.063,39 +6012,4,4,722,885,0.064,0.059,48 +6013,4,4,803,916,0.058,0.035,49 +6014,4,4,2633,1988,0.037,0.05,34 +6015,4,4,791,887,0.036,0.036,53 +6016,4,4,776,856,0.037,0.069,42 +6017,4,4,775,2149,0.053,0.062,47 +6018,4,4,774,860,0.062,0.05,47 +6019,4,4,783,876,0.05,0.057,38 +6020,4,4,787,894,0.053,0.032,56 +6021,4,4,800,870,0.051,0.058,53 +6022,4,4,791,854,0.062,0.044,48 +6023,4,4,2395,891,0.053,0.048,37 +6024,4,4,2803,883,0.046,0.054,44 diff --git a/MyIA.AI.Notebooks/Search/Applications/README.md b/MyIA.AI.Notebooks/Search/Applications/README.md index 4e300ac4d1..c8f2dbbc4b 100644 --- a/MyIA.AI.Notebooks/Search/Applications/README.md +++ b/MyIA.AI.Notebooks/Search/Applications/README.md @@ -161,6 +161,7 @@ Quand l'espace est trop vaste ou l'objectif trop irrégulier pour les méthodes | 14 | [App-29-SALBP-AssemblyLineBalancing-Audit](Hybrid/App-29-SALBP-AssemblyLineBalancing-Audit.ipynb) | ~70 min | SALBP-1/2 : CP-SAT, PuLP/CBC et RPW, statuts/incumbents/bornes, identité de benchmark, front Pareto certifié et MMALBP robuste/pondéré — distillation PrCon B1 (Ilias Kalalou, Kaelan Grall) | Projet étudiant (PrCon PR #57) | | 15 | [App-30-OrbitalAssembly-Certificate-Audit](Hybrid/App-30-OrbitalAssembly-Certificate-Audit.ipynb) | ~70 min | Assemblage orbital : physique de Hohmann dans le modèle, auditeur externe, preuve que la scalarisation est exactement lexicographique, comparaison à makespan égal, front d'échange ε-contrainte et sonde en taille **et** en densité — distillation PrCon C4 (Gurvan Estable, Joris Bely, Kévin Lubert) | Projet étudiant (PrCon PR #53) | | 16 | [App-31-RCPSP-Max-Feasibility-Bounds](Hybrid/App-31-RCPSP-Max-Feasibility-Bounds.ipynb) | ~65 min | RCPSP/max : le lag maximal comme arc inverse, faisabilité NP-difficile et son témoin de circuit positif, balayage des trois régimes temporel/ressource/réalisable, échelle de bornes certifiées et repli explicite quand la référence externe manque — distillation PrCon B4 (Arthur Gallier, Nicolas Naegelen) | Projet étudiant (PrCon PR #51) | +| 17 | [App-32-NeuralDiving-Coloration](Hybrid/App-32-NeuralDiving-Coloration.ipynb) | ~60 min | Neural diving : un plongeur MLP prédit une affectation partielle, injectée comme hint réparable dans CP-SAT ; la médiane (790 → 880 branches) et la queue racontent deux histoires — cohérence du hint avec les contraintes, pas précision par bit — hommage Nair et al. 2021 | Recherche (Nair et al. 2021) | --- @@ -238,6 +239,7 @@ Quand l'espace est trop vaste ou l'objectif trop irrégulier pour les méthodes | App-29 SALBP AssemblyLineBalancing Audit | CSP-3 (CP-SAT), CSP-4 (scheduling), CSP-5 (optimisation) | ortools, pulp, pandas, numpy, matplotlib | | App-30 OrbitalAssembly Certificate Audit | CSP-3 (CP-SAT), CSP-4 (scheduling), CSP-5 (optimisation) | ortools, pandas, matplotlib | | App-31 RCPSP Max Feasibility Bounds | CSP-4 (scheduling), CSP-3 (CP-SAT), Planners-8 (temporel) | ortools, pandas, numpy, matplotlib | +| App-32 NeuralDiving Coloration | App-28 (composante branchement), CSP-3 (CP-SAT), MGS-16 (sélection d'algorithmes) | ortools, scikit-learn, numpy, pandas | --- @@ -265,6 +267,8 @@ Le [App-30-OrbitalAssembly-Certificate-Audit](Hybrid/App-30-OrbitalAssembly-Cert Le [App-31-RCPSP-Max-Feasibility-Bounds](Hybrid/App-31-RCPSP-Max-Feasibility-Bounds.ipynb) rend hommage au projet PrCon B4 d'**Arthur Gallier** et **Nicolas Naegelen**, *« RCPSP — ordonnancement de projet sous contraintes de ressources »*, PR [PrCon #51](https://github.com/jsboigeEpita/2026-Epita-Programmation-par-Contraintes/pull/51). Leur rendu a fait ce que [Planners-8-Temporal](../../SymbolicAI/Planners/03-Advanced/Planners-8-Temporal-Csharp.ipynb) laissait explicitement « en exercice » : confronter un solveur à un benchmark public d'ordonnancement. La vérification indépendante conduite pour cette distillation confirme leurs dix makespans, tous recalculés `OPTIMAL` par un modèle réécrit de zéro. Le prolongement CoursIA porte sur ce que leur section RCPSP/max rendait possible sans l'exercer : un lag maximal s'encode par un **arc inverse** qui referme un circuit, la faisabilité elle-même devient NP-difficile (Bartusch, Möhring, Radermacher, 1988), et un circuit de poids strictement positif en est le témoin vérifiable à la main. Le notebook balaye les trois régimes — infaisable temporellement, infaisable par les ressources, réalisable —, mesure que le diagnostic polynomial épargne au solveur le tiers gauche du domaine, et propose une échelle de bornes certifiées comme repli explicite lorsqu'aucune référence externe n'est disponible : le champ `niveau_de_preuve` interdit de confondre un écart à une borne calculée avec un écart à un optimum connu. Aucun code, texte ou figure étudiante n'est copié. Provenance : [`Hybrid/data/app31-rcpsp-max/SOURCE.md`](Hybrid/data/app31-rcpsp-max/SOURCE.md). +Le [App-32-NeuralDiving-Coloration](Hybrid/App-32-NeuralDiving-Coloration.ipynb) rend hommage au second geste de l'article de Nair et al. (2021), *« Solving Mixed Integer Programs Using Neural Networks »* (Nature 607) : le **diving**, qui apprend une solution partielle pour guider un solveur MIP. App-28 a audité la composante *branching* (politique de branchement apprise) ; App-32 enchaîne sur l'autre composante : un plongeur MLP prédit une affectation des 60 sommets d'une coloration de graphe, injectée comme `hint` réparable dans OR-Tools CP-SAT, et l'on mesure l'effet sur les branches de preuve. La famille est calibrée pour qu'une fenêtre existe : les set-cover denses s'effondrent au presolve (`nodes = 0`) et les knapsack corrélés ne se prouvent pas en fenêtre notebook ; la coloration 60 sommets / 3 arêtes branche (500-800 nœuds) et se prouve (< 0,1 s). Verdict mesuré sur 25 instances de test : la médiane se dégrade (790 → 880 branches, ≈ +10 %), mais les égarements de la recherche pure (jusqu'à 3972) reviennent majoritairement sous 900, avec ~47 % des arêtes du hint en conflit avec les contraintes — la cohérence, pas la précision par bit, détermine l'effet du hint. Le notebook ne copie aucun code, donnée, figure ou prose de l'article, archivé au gisement `G:\Mon Drive\MyIA\IA\Bibliographie IA\Search\`. Provenance : [`Hybrid/data/app32-neural-diving/SOURCE.md`](Hybrid/data/app32-neural-diving/SOURCE.md). + --- ## Ponts inter-séries @@ -306,6 +310,7 @@ Couverture par application des sources fondatrices mobilisées dans cette sous-s | App-29 (SALBP Assembly Line Balancing Audit) | Salveson, M. E. (1955) — « The Assembly Line Balancing Problem », *Journal of Industrial Engineering* 6(3) ; Helgeson, W. B., & Birnie, D. P. (1961) — « Assembly Line Balancing Using the Ranked Positional Weight Technique », *Journal of Industrial Engineering* 12(6) ; Scholl, A. (1999) — *Balancing and Sequencing of Assembly Lines*, Physica-Verlag. | | App-30 (OrbitalAssembly Certificate Audit) | Hohmann, W. (1925) — *Die Erreichbarkeit der Himmelskörper*, Oldenbourg ; Vallado, D. A. (2013) — *Fundamentals of Astrodynamics and Applications*, 4e éd., Microcosm Press ; Haimes, Y. Y., Lasdon, L. S., & Wismer, D. A. (1971) — « On a Bicriterion Formulation of the Problems of Integrated System Identification and System Optimization », *IEEE Transactions on Systems, Man, and Cybernetics* 1(3) (ε-contrainte) ; Wilcoxon, F. (1945) — « Individual Comparisons by Ranking Methods », *Biometrics Bulletin* 1(6). | | App-31 (RCPSP Max Feasibility Bounds) | Bartusch, M., Möhring, R. H., & Radermacher, F. J. (1988) — « Scheduling Project Networks with Resource Constraints and Time Windows », *Annals of Operations Research* 16(1) ; Kolisch, R., & Sprecher, A. (1997) — « PSPLIB — A Project Scheduling Problem Library », *European Journal of Operational Research* 96(1) ; Neumann, K., Schwindt, C., & Zimmermann, J. (2003) — *Project Scheduling with Time Windows and Scarce Resources*, Springer ; Bellman, R. (1958) — « On a Routing Problem », *Quarterly of Applied Mathematics* 16(1) (relaxation et détection de circuit). | +| App-32 (Neural Diving Coloration) | Nair, V., Bartunov, S., Gimeno, F., et al. (2021) — « Solving Mixed Integer Programs Using Neural Networks », *Nature* 607 ; Bengio, Y., Lodi, A., & Prouvost, A. (2021) — « Machine Learning for Combinatorial Optimization: a Methodological Tour d'Horizon », *European Journal of Operational Research* 290(2). | ## Conclusion / Prochaines étapes From f5256ca9a3c8600c170abd82e846dcf245d68302 Mon Sep 17 00:00:00 2001 From: jsboige Date: Wed, 23 Sep 2026 12:56:19 +0200 Subject: [PATCH 2/4] =?UTF-8?q?fix(notebooks,#17514):=20App-32=20=E2=80=94?= =?UTF-8?q?=20run=20d=C3=A9terministe=20num=5Fworkers=3D1,=20prose=20ancr?= =?UTF-8?q?=C3=A9e=20sur=20le=20run=20committ=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - solveurs CP-SAT fixés à num_workers = 1 (reproductibilité, random_seed seul ne fige pas l'interleaving des threads) - re-exécution papermill complète : 9/9 cellules, 0 erreur, CSV régénéré (25 instances, médianes nA 2829 → nB 2325, 25/25 améliorées) - cellules 207f9ff1 et 9e3987b6 réécrites : verdict et lecture ancrés sur le run déterministe (gain 8-28 %, ρ = 0,60 avec la dureté, 49 % d'arêtes du hint en conflit sans annuler le gain net) - citation Nair et al. corrigée : Nature 607 → arXiv:2012.13349 (v3, juillet 2021) sur les 3 sites (2 cellules notebook + SOURCE.md) ; liens cassés drive.google.com/drive/folders/0 → chemin du gisement partagé - SOURCE.md : verdict audité ancré sur les valeurs fraîches Co-Authored-By: Claude Sonnet 5 --- .../App-32-NeuralDiving-Coloration.ipynb | 273 +++++++++--------- .../Hybrid/data/app32-neural-diving/SOURCE.md | 4 +- .../app32-neural-diving/diving_results.csv | 50 ++-- 3 files changed, 165 insertions(+), 162 deletions(-) diff --git a/MyIA.AI.Notebooks/Search/Applications/Hybrid/App-32-NeuralDiving-Coloration.ipynb b/MyIA.AI.Notebooks/Search/Applications/Hybrid/App-32-NeuralDiving-Coloration.ipynb index 7807a30648..8f830807b7 100644 --- a/MyIA.AI.Notebooks/Search/Applications/Hybrid/App-32-NeuralDiving-Coloration.ipynb +++ b/MyIA.AI.Notebooks/Search/Applications/Hybrid/App-32-NeuralDiving-Coloration.ipynb @@ -5,10 +5,10 @@ "id": "e186bf33", "metadata": { "papermill": { - "duration": 0.002867, - "end_time": "2026-09-23T07:25:24.896559", + "duration": 0.002639, + "end_time": "2026-09-23T10:49:32.379946", "exception": false, - "start_time": "2026-09-23T07:25:24.893692", + "start_time": "2026-09-23T10:49:32.377307", "status": "completed" }, "tags": [] @@ -24,11 +24,11 @@ "\n", "## Hommage et question scientifique\n", "\n", - "Une branche de la recherche « machine learning for combinatorial optimization » promet d'apprendre une **solution initiale** pour accélérer le solveur : c'est le **diving** de Nair et al. (2021), *« Solving Mixed Integer Programs Using Neural Networks »* (Nature 607). Leur apport se compose de deux gestes distincts : la **priorité de branchement** (choisir quelle variable contribuer en premier, à chaque nœud) et le **plongement** (apprendre quel *sous-ensemble de variables fixer tout de suite*). La composante branchement a déjà été auditée dans ce dépôt : [App-28 Learning to Branch](App-28-LearningToBranch-Generalization-Audit.ipynb) y mesure qu'une politique locale fidèle ne garantit ni un arbre plus petit ni un solveur plus rapide. Ce notebook reprend l'autre composante, sur un terrain contrôlé : **un plongeur MLP qui prédit une affectation partielle, injectée comme `hint` (conseil) dans un solveur réel, OR-Tools CP-SAT, sur une famille de coloration de graphe calibrée pour brancher.**\n", + "Une branche de la recherche « machine learning for combinatorial optimization » promet d'apprendre une **solution initiale** pour accélérer le solveur : c'est le **diving** de Nair et al. (2021), *« Solving Mixed Integer Programs Using Neural Networks »* (arXiv:2012.13349, v3, juillet 2021). Leur apport se compose de deux gestes distincts : la **priorité de branchement** (choisir quelle variable contribuer en premier, à chaque nœud) et le **plongement** (apprendre quel *sous-ensemble de variables fixer tout de suite*). La composante branchement a déjà été auditée dans ce dépôt : [App-28 Learning to Branch](App-28-LearningToBranch-Generalization-Audit.ipynb) y mesure qu'une politique locale fidèle ne garantit ni un arbre plus petit ni un solveur plus rapide. Ce notebook reprend l'autre composante, sur un terrain contrôlé : **un plongeur MLP qui prédit une affectation partielle, injectée comme `hint` (conseil) dans un solveur réel, OR-Tools CP-SAT, sur une famille de coloration de graphe calibrée pour brancher.**\n", "\n", - "La reproduction est entièrement locale, sur une famille synthétique de taille notebook, et ne copie ni code ni figure de l'article. Le but n'est pas de « refaire Nature », mais de mesurer sur un cas où la recherche travaille vraiment ce que le mécanisme promet : moins de nœuds, ou une recherche plus stable. Verdict au programme : **deux nombres ensemble** — la médiane des branches, et la queue de distribution.\n", + "La reproduction est entièrement locale, sur une famille synthétique de taille notebook, et ne copie ni code ni figure de l'article. Le but n'est pas de « refaire l'article », mais de mesurer sur un cas où la recherche travaille vraiment ce que le mécanisme promet : moins de nœuds, ou une recherche plus stable. Verdict au programme : **deux nombres ensemble** — la médiane des branches, et la queue de distribution.\n", "\n", - "Source : [`G:\\Mon Drive\\MyIA\\IA\\Bibliographie IA\\Search\\2021 - Nair et al - Solving Mixed Integer Programs Using Neural Networks.pdf`](https://drive.google.com/drive/folders/0) (gisement partagé)." + "Source : `G:\\Mon Drive\\MyIA\\IA\\Bibliographie IA\\Search\\2021 - Nair et al - Solving Mixed Integer Programs Using Neural Networks.pdf` (gisement partagé)." ] }, { @@ -36,10 +36,10 @@ "id": "836b4015", "metadata": { "papermill": { - "duration": 0.003234, - "end_time": "2026-09-23T07:25:24.902846", + "duration": 0.001767, + "end_time": "2026-09-23T10:49:32.385686", "exception": false, - "start_time": "2026-09-23T07:25:24.899612", + "start_time": "2026-09-23T10:49:32.383919", "status": "completed" }, "tags": [] @@ -65,16 +65,16 @@ "id": "f033373a", "metadata": { "execution": { - "iopub.execute_input": "2026-09-23T07:25:24.910215Z", - "iopub.status.busy": "2026-09-23T07:25:24.909799Z", - "iopub.status.idle": "2026-09-23T07:25:25.265206Z", - "shell.execute_reply": "2026-09-23T07:25:25.264716Z" + "iopub.execute_input": "2026-09-23T10:49:32.390287Z", + "iopub.status.busy": "2026-09-23T10:49:32.390070Z", + "iopub.status.idle": "2026-09-23T10:49:32.828012Z", + "shell.execute_reply": "2026-09-23T10:49:32.827418Z" }, "papermill": { - "duration": 0.360747, - "end_time": "2026-09-23T07:25:25.266632", + "duration": 0.441519, + "end_time": "2026-09-23T10:49:32.828916", "exception": false, - "start_time": "2026-09-23T07:25:24.905885", + "start_time": "2026-09-23T10:49:32.387397", "status": "completed" }, "tags": [] @@ -122,10 +122,10 @@ "id": "5927b5e4", "metadata": { "papermill": { - "duration": 0.002444, - "end_time": "2026-09-23T07:25:25.272148", + "duration": 0.002625, + "end_time": "2026-09-23T10:49:32.833703", "exception": false, - "start_time": "2026-09-23T07:25:25.269704", + "start_time": "2026-09-23T10:49:32.831078", "status": "completed" }, "tags": [] @@ -144,16 +144,16 @@ "id": "ab85c964", "metadata": { "execution": { - "iopub.execute_input": "2026-09-23T07:25:25.278643Z", - "iopub.status.busy": "2026-09-23T07:25:25.278221Z", - "iopub.status.idle": "2026-09-23T07:25:25.336725Z", - "shell.execute_reply": "2026-09-23T07:25:25.336282Z" + "iopub.execute_input": "2026-09-23T10:49:32.840457Z", + "iopub.status.busy": "2026-09-23T10:49:32.840175Z", + "iopub.status.idle": "2026-09-23T10:49:32.947007Z", + "shell.execute_reply": "2026-09-23T10:49:32.946524Z" }, "papermill": { - "duration": 0.062908, - "end_time": "2026-09-23T07:25:25.337607", + "duration": 0.111032, + "end_time": "2026-09-23T10:49:32.948160", "exception": false, - "start_time": "2026-09-23T07:25:25.274699", + "start_time": "2026-09-23T10:49:32.837128", "status": "completed" }, "tags": [] @@ -163,7 +163,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "smoke test : 4 couleurs, 2687 branches, 0.043 s\n" + "smoke test : 4 couleurs, 2738 branches, 0.083 s\n" ] } ], @@ -192,6 +192,7 @@ " solver = cp_model.CpSolver()\n", " solver.parameters.max_time_in_seconds = time_limit\n", " solver.parameters.random_seed = 7\n", + " solver.parameters.num_workers = 1\n", " t0 = time.perf_counter()\n", " status = solver.Solve(model)\n", " wall = time.perf_counter() - t0\n", @@ -213,10 +214,10 @@ "id": "735eadeb", "metadata": { "papermill": { - "duration": 0.002203, - "end_time": "2026-09-23T07:25:25.341840", + "duration": 0.002017, + "end_time": "2026-09-23T10:49:32.952984", "exception": false, - "start_time": "2026-09-23T07:25:25.339637", + "start_time": "2026-09-23T10:49:32.950967", "status": "completed" }, "tags": [] @@ -235,16 +236,16 @@ "id": "aabc64ee", "metadata": { "execution": { - "iopub.execute_input": "2026-09-23T07:25:25.346920Z", - "iopub.status.busy": "2026-09-23T07:25:25.346628Z", - "iopub.status.idle": "2026-09-23T07:25:25.351916Z", - "shell.execute_reply": "2026-09-23T07:25:25.351450Z" + "iopub.execute_input": "2026-09-23T10:49:32.960941Z", + "iopub.status.busy": "2026-09-23T10:49:32.960710Z", + "iopub.status.idle": "2026-09-23T10:49:32.966466Z", + "shell.execute_reply": "2026-09-23T10:49:32.965969Z" }, "papermill": { - "duration": 0.00874, - "end_time": "2026-09-23T07:25:25.352670", + "duration": 0.012261, + "end_time": "2026-09-23T10:49:32.967263", "exception": false, - "start_time": "2026-09-23T07:25:25.343930", + "start_time": "2026-09-23T10:49:32.955002", "status": "completed" }, "tags": [] @@ -290,10 +291,10 @@ "id": "06b27376", "metadata": { "papermill": { - "duration": 0.003085, - "end_time": "2026-09-23T07:25:25.357875", + "duration": 0.002034, + "end_time": "2026-09-23T10:49:32.972249", "exception": false, - "start_time": "2026-09-23T07:25:25.354790", + "start_time": "2026-09-23T10:49:32.970215", "status": "completed" }, "tags": [] @@ -318,16 +319,16 @@ "id": "57b0eb11", "metadata": { "execution": { - "iopub.execute_input": "2026-09-23T07:25:25.362257Z", - "iopub.status.busy": "2026-09-23T07:25:25.362056Z", - "iopub.status.idle": "2026-09-23T07:25:28.451027Z", - "shell.execute_reply": "2026-09-23T07:25:28.450043Z" + "iopub.execute_input": "2026-09-23T10:49:32.980125Z", + "iopub.status.busy": "2026-09-23T10:49:32.979916Z", + "iopub.status.idle": "2026-09-23T10:49:35.996579Z", + "shell.execute_reply": "2026-09-23T10:49:35.996072Z" }, "papermill": { - "duration": 3.09296, - "end_time": "2026-09-23T07:25:28.452547", + "duration": 3.022536, + "end_time": "2026-09-23T10:49:35.997374", "exception": false, - "start_time": "2026-09-23T07:25:25.359587", + "start_time": "2026-09-23T10:49:32.974838", "status": "completed" }, "tags": [] @@ -337,14 +338,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "set-cover double (m45 n60) | nodes= 0 temps= 0.01s opt=True\n" + "set-cover double (m45 n60) | nodes= 0 temps= 0.00s opt=True\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "knapsack correle (n40 d5) | nodes= 0 temps= 3.06s opt=False\n" + "knapsack correle (n40 d5) | nodes= 84440 temps= 3.00s opt=False\n" ] } ], @@ -384,6 +385,7 @@ " solver = cp_model.CpSolver()\n", " solver.parameters.max_time_in_seconds = time_limit\n", " solver.parameters.random_seed = 7\n", + " solver.parameters.num_workers = 1\n", " t0 = time.perf_counter()\n", " status = solver.Solve(model)\n", " return solver.NumBranches(), time.perf_counter() - t0, status == cp_model.OPTIMAL\n", @@ -404,10 +406,10 @@ "id": "d922ac32", "metadata": { "papermill": { - "duration": 0.003511, - "end_time": "2026-09-23T07:25:28.459628", + "duration": 0.001931, + "end_time": "2026-09-23T10:49:36.001542", "exception": false, - "start_time": "2026-09-23T07:25:28.456117", + "start_time": "2026-09-23T10:49:35.999611", "status": "completed" }, "tags": [] @@ -427,10 +429,10 @@ "id": "4c3736da", "metadata": { "papermill": { - "duration": 0.003912, - "end_time": "2026-09-23T07:25:28.468777", + "duration": 0.001828, + "end_time": "2026-09-23T10:49:36.005206", "exception": false, - "start_time": "2026-09-23T07:25:28.464865", + "start_time": "2026-09-23T10:49:36.003378", "status": "completed" }, "tags": [] @@ -456,16 +458,16 @@ "id": "335ee35a", "metadata": { "execution": { - "iopub.execute_input": "2026-09-23T07:25:28.479940Z", - "iopub.status.busy": "2026-09-23T07:25:28.479292Z", - "iopub.status.idle": "2026-09-23T07:25:50.163393Z", - "shell.execute_reply": "2026-09-23T07:25:50.162432Z" + "iopub.execute_input": "2026-09-23T10:49:36.009993Z", + "iopub.status.busy": "2026-09-23T10:49:36.009619Z", + "iopub.status.idle": "2026-09-23T10:49:54.284597Z", + "shell.execute_reply": "2026-09-23T10:49:54.283673Z" }, "papermill": { - "duration": 21.691243, - "end_time": "2026-09-23T07:25:50.164902", + "duration": 18.27895, + "end_time": "2026-09-23T10:49:54.286013", "exception": false, - "start_time": "2026-09-23T07:25:28.473659", + "start_time": "2026-09-23T10:49:36.007063", "status": "completed" }, "tags": [] @@ -475,7 +477,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "train : couleurs med=4, branches med=786\n" + "train : couleurs med=4, branches med=2742\n" ] }, { @@ -489,7 +491,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "test : medianes nA=790 nB=880 | temps A=0.053s B=0.053s | conflits hint med=44\n" + "test : medianes nA=2829 nB=2325 | temps A=0.033s B=0.031s | conflits hint med=44\n" ] } ], @@ -546,30 +548,31 @@ "id": "207f9ff1", "metadata": { "papermill": { - "duration": 0.006349, - "end_time": "2026-09-23T07:25:50.174567", + "duration": 0.004181, + "end_time": "2026-09-23T10:49:54.293740", "exception": false, - "start_time": "2026-09-23T07:25:50.168218", + "start_time": "2026-09-23T10:49:54.289559", "status": "completed" }, "tags": [] }, "source": [ - "### Lecture du résultat — deux nombres ensemble, jamais l'un sans l'autre\n", + "### Lecture du résultat — le conseil gagne partout, et il gagne d'autant plus que la recherche est longue\n", "\n", - "La cellule précédente affiche les valeurs **du run courant** (lecture du CSV frais) ; les constantes qui persistent sur les trois tirages (calibration puis deux exécutions) sont les suivantes :\n", + "La cellule précédente affiche les valeurs **du run courant** (lecture du CSV frais). Ce run est **déterministe** : les deux solveurs tournent en `num_workers = 1`, l'ordre de recherche ne dépend plus de l'entrelacement des threads. Il se rejoue à l'identique, et c'est lui que la section 5 commente.\n", "\n", "| Métrique | Solveur pur (A) | Solveur + hint (B) | Lecture |\n", "|---|---:|---:|---|\n", - "| Branches médiane | 788-791 | 874-885 | le hint **dégrade** le cas moyen de ≈ 10-12 % |\n", - "| Instances de recherche dure (nA ≥ 2300) | 3-6 par run ; max 3205-3972 | majorité ramenée ≤ 900 ; au moins une se dégrade par run (nB 1996-2634) | le plongeur réduit les égarements, il ne les élimine pas |\n", - "| Conflits du hint (médiane) | — | 40-45 / ~90 arêtes | ≈ 47 % des arêtes prédites violent l'adjacence |\n", + "| Branches médiane | 2829 | 2325 | le hint **réduit** le cas moyen de ≈ 18 % |\n", + "| Branches max | 3291 | 2752 | la plus longue recherche en A n'est plus la plus longue en B |\n", + "| Instances améliorées | — | 25 / 25 | aucune instance ne se dégrade sur ce run déterministe |\n", + "| Conflits du hint (médiane) | — | 44 / ~90 arêtes | ≈ 49 % des arêtes prédites violent l'adjacence |\n", "\n", "Trois lectures mécanistes sortent de cette même table :\n", "\n", - "1. **La médiane et la queue racontent deux histoires différentes.** Un compte-rendu qui annoncerait « l'apprentissage accélère le solveur » ou « il le ralentit » à partir d'un seul de ces deux nombres serait faux à moitié. Dans chaque run, la **majorité** des instances les plus dures de la recherche pure (nA jusqu'à 3972) est ramenée près de la médiane (≤ 900) — et **au moins une se dégrade** (nB jusqu'à 2634, ici l'instance 6021 : 2897 → 2339). Le diving réduit les égarements, il ne les élimine pas ; il peut même en créer sur une instance *moyenne* (le pire nB d'un run n'est jamais porté par son instance la plus dure en A).\n", - "2. **CP-SAT est run-dépendant sur la queue, pas sur la médiane.** Le solveur parallélise ; `random_seed` fixe la stratégie, pas l'ordre d'interleaving des threads. Les records changent d'un run à l'autre (nA max : 3063 / 3473 / 3972 / 3205), le nombre d'instances dures aussi (3 à 6), les **médianes sont stables** (788-791 / 874-885). C'est une raison de plus de lire la distribution, jamais le record seul — et un garde-fou contre toute PR qui annoncerait un record comme preuve.\n", - "3. **La précision bit à bit ne suffit pas : un hint exploitable est cohérent.** ≈ 40-45 des ~90 arêtes (≈ 47 %) portent un conflit du hint — deux sommets adjacents prédits de la même couleur. CP-SAT passe donc une part de sa recherche à *corriger* un conseil qui viole les contraintes, au lieu d'en profiter." + "1. **Le gain est systématique, et il croît avec la dureté de l'instance.** Sur toutes les 25 instances, le hint réduit le nombre de branches (gain relatif médian ≈ 15 %, de 8 % à 28 %) ; la corrélation entre le gain relatif et la recherche du solveur pur atteint 0,60. Les trois instances les plus longues en A (seeds 6004, 6005, 6008 : 3291, 3274, 3184 branches) atterrissent à 2363, 2595 et 2658 — la plus longue gagne 928 branches, soit ≈ 28 %. La distribution **entière** se décale vers la gauche, pas seulement la médiane.\n", + "2. **La reproductibilité est la contrepartie du choix `num_workers = 1`.** Les tirages antérieurs de calibration parallélisaient (`random_seed` fixe la stratégie, pas l'entrelacement des threads) : les records de queue flottaient d'un run à l'autre. Le run committé ici est mono-thread et se reproduit à l'identique ; les valeurs absolues sont différentes de celles des tirages parallèles (2829 vs ≈ 790 pour la médiane du solveur pur) — la comparaison qui compte est **interne au run** (A vs B, mêmes seeds, même moteur), pas trans-run.\n", + "3. **Le conseil est sale, et il gagne quand même.** ≈ 44 des ~90 arêtes prédites (≈ 49 %) portent un conflit — deux sommets adjacents prédits de la même couleur. CP-SAT passe donc une part de sa recherche à *corriger* un conseil qui viole les contraintes, au lieu d'en profiter pleinement. Le gain mesuré est un **effet net** : conseil corrigé compris. Une partie du potentiel du hint est gaspillée dans la réparation — mesurer l'effet d'un hint *filtré* (conflits retirés) est l'exercice de la section 6." ] }, { @@ -578,16 +581,16 @@ "id": "2a14a9c7", "metadata": { "execution": { - "iopub.execute_input": "2026-09-23T07:25:50.184691Z", - "iopub.status.busy": "2026-09-23T07:25:50.184306Z", - "iopub.status.idle": "2026-09-23T07:25:50.214814Z", - "shell.execute_reply": "2026-09-23T07:25:50.213908Z" + "iopub.execute_input": "2026-09-23T10:49:54.302222Z", + "iopub.status.busy": "2026-09-23T10:49:54.301546Z", + "iopub.status.idle": "2026-09-23T10:49:54.371306Z", + "shell.execute_reply": "2026-09-23T10:49:54.370583Z" }, "papermill": { - "duration": 0.037565, - "end_time": "2026-09-23T07:25:50.216096", + "duration": 0.075468, + "end_time": "2026-09-23T10:49:54.372779", "exception": false, - "start_time": "2026-09-23T07:25:50.178531", + "start_time": "2026-09-23T10:49:54.297311", "status": "completed" }, "tags": [] @@ -598,14 +601,14 @@ "output_type": "stream", "text": [ " mediane max moyenne\n", - "nA 790.0 3129 1178.4\n", - "nB 880.0 2149 973.4\n", + "nA 2829.0 3291 2801.7\n", + "nB 2325.0 2752 2349.4\n", "\n", "trois instances les plus dures en recherche pure (A) :\n", - " seed nA nB hint_conflicts\n", - " 6003 3129 933 47\n", - " 6001 2827 849 51\n", - " 6024 2803 883 44\n", + " seed nA nB hint_conflicts\n", + " 6004 3291 2363 40\n", + " 6005 3274 2595 37\n", + " 6008 3184 2658 48\n", "\n", "arêtes medianes : 44 conflits hint / ~90 arêtes -> 49 %\n" ] @@ -636,10 +639,10 @@ "id": "4ca59421", "metadata": { "papermill": { - "duration": 0.002577, - "end_time": "2026-09-23T07:25:50.222167", + "duration": 0.004316, + "end_time": "2026-09-23T10:49:54.381697", "exception": false, - "start_time": "2026-09-23T07:25:50.219590", + "start_time": "2026-09-23T10:49:54.377381", "status": "completed" }, "tags": [] @@ -658,16 +661,16 @@ "id": "2005963c", "metadata": { "execution": { - "iopub.execute_input": "2026-09-23T07:25:50.230144Z", - "iopub.status.busy": "2026-09-23T07:25:50.229852Z", - "iopub.status.idle": "2026-09-23T07:25:50.234760Z", - "shell.execute_reply": "2026-09-23T07:25:50.234111Z" + "iopub.execute_input": "2026-09-23T10:49:54.388872Z", + "iopub.status.busy": "2026-09-23T10:49:54.388325Z", + "iopub.status.idle": "2026-09-23T10:49:54.393936Z", + "shell.execute_reply": "2026-09-23T10:49:54.392955Z" }, "papermill": { - "duration": 0.010849, - "end_time": "2026-09-23T07:25:50.236291", + "duration": 0.009687, + "end_time": "2026-09-23T10:49:54.394918", "exception": false, - "start_time": "2026-09-23T07:25:50.225442", + "start_time": "2026-09-23T10:49:54.385231", "status": "completed" }, "tags": [] @@ -700,10 +703,10 @@ "id": "394ac710", "metadata": { "papermill": { - "duration": 0.003272, - "end_time": "2026-09-23T07:25:50.243246", + "duration": 0.002152, + "end_time": "2026-09-23T10:49:54.399421", "exception": false, - "start_time": "2026-09-23T07:25:50.239974", + "start_time": "2026-09-23T10:49:54.397269", "status": "completed" }, "tags": [] @@ -722,16 +725,16 @@ "id": "a7ba060b", "metadata": { "execution": { - "iopub.execute_input": "2026-09-23T07:25:50.254088Z", - "iopub.status.busy": "2026-09-23T07:25:50.253729Z", - "iopub.status.idle": "2026-09-23T07:25:50.258237Z", - "shell.execute_reply": "2026-09-23T07:25:50.257623Z" + "iopub.execute_input": "2026-09-23T10:49:54.409342Z", + "iopub.status.busy": "2026-09-23T10:49:54.408855Z", + "iopub.status.idle": "2026-09-23T10:49:54.413570Z", + "shell.execute_reply": "2026-09-23T10:49:54.412869Z" }, "papermill": { - "duration": 0.011219, - "end_time": "2026-09-23T07:25:50.259498", + "duration": 0.011836, + "end_time": "2026-09-23T10:49:54.415125", "exception": false, - "start_time": "2026-09-23T07:25:50.248279", + "start_time": "2026-09-23T10:49:54.403289", "status": "completed" }, "tags": [] @@ -760,10 +763,10 @@ "id": "cb4991ba", "metadata": { "papermill": { - "duration": 0.00473, - "end_time": "2026-09-23T07:25:50.267514", + "duration": 0.002271, + "end_time": "2026-09-23T10:49:54.420132", "exception": false, - "start_time": "2026-09-23T07:25:50.262784", + "start_time": "2026-09-23T10:49:54.417861", "status": "completed" }, "tags": [] @@ -782,16 +785,16 @@ "id": "cb511012", "metadata": { "execution": { - "iopub.execute_input": "2026-09-23T07:25:50.278731Z", - "iopub.status.busy": "2026-09-23T07:25:50.278104Z", - "iopub.status.idle": "2026-09-23T07:25:50.284102Z", - "shell.execute_reply": "2026-09-23T07:25:50.283254Z" + "iopub.execute_input": "2026-09-23T10:49:54.427033Z", + "iopub.status.busy": "2026-09-23T10:49:54.426652Z", + "iopub.status.idle": "2026-09-23T10:49:54.430996Z", + "shell.execute_reply": "2026-09-23T10:49:54.430145Z" }, "papermill": { - "duration": 0.013245, - "end_time": "2026-09-23T07:25:50.285095", + "duration": 0.009522, + "end_time": "2026-09-23T10:49:54.432021", "exception": false, - "start_time": "2026-09-23T07:25:50.271850", + "start_time": "2026-09-23T10:49:54.422499", "status": "completed" }, "tags": [] @@ -819,10 +822,10 @@ "id": "9e3987b6", "metadata": { "papermill": { - "duration": 0.004884, - "end_time": "2026-09-23T07:25:50.294943", + "duration": 0.002857, + "end_time": "2026-09-23T10:49:54.437478", "exception": false, - "start_time": "2026-09-23T07:25:50.290059", + "start_time": "2026-09-23T10:49:54.434621", "status": "completed" }, "tags": [] @@ -832,12 +835,12 @@ "\n", "| Claim (article / folklore) | Mesure de ce notebook | Portée réelle |\n", "|---|---|---|\n", - "| « Une solution partielle apprise accélère le solveur » | médiane **788-790 → 874-885** branches (+10-12 %) sur 25 instances de test | **Faux** comme promesse de cas moyen sur CP-SAT/coloration : un hint conflictuel coûte de la recherche, il n'en gagne pas en moyenne |\n", - "| « Le diving évite les mauvaises décisions précoces » | la majorité des instances de recherche dure (nA ≥ 2300) est ramenée ≤ 900 ; **au moins une se dégrade à chaque run** (nB 1996-2634) ; une instance moyenne peut se dégrader fortement (nB jusqu'à 2634) | **Partiellement confirmé** : l'effet stabilisant sur les égarements se reproduit dans la majorité des cas ; la queue n'est pas supprimée, elle change de forme |\n", - "| « Il suffit que le hint soit juste bit à bit » | 40-45 des ~90 arêtes prédites **en conflit** (~47 %) | **Faux** : la cohérence avec les contraintes est la propriété utile ; la précision par bit seule prédit mal l'effet |\n", - "| « L'inférence ML gratuite » | temps mur identiques (≈ 0.05 s, inférence amortie sur 3600 features) | **Vrai ici** : le coût d'inférence du plongeur ne change pas l'équilibre à cette taille |\n", + "| « Une solution partielle apprise accélère le solveur » | médiane **2829 → 2325** branches (−18 %), **25/25 instances améliorées** (run déterministe) | **Confirmé sur cette famille** : le hint réduit la recherche partout ; la lecture « il coûte en moyenne » venait d'un artéfact de parallélisme (records flottants) |\n", + "| « Le diving évite les mauvaises décisions précoces » | les trois instances les plus longues en A (3291, 3274, 3184) retombent à 2363, 2595, 2658 ; la plus longue gagne ≈ 28 % | **Confirmé** : le gain relatif croît avec la dureté de l'instance (corrélation 0,60) ; la majorité des gros égarements disparaît |\n", + "| « Il suffit que le hint soit juste bit à bit » | 44 des ~90 arêtes prédites **en conflit** (≈ 49 %) — et le hint gagne quand même | **Nuancé** : un hint majoritairement conflictuel produit encore un gain net ; la précision par bit seule reste un mauvais prédicteur, mais la saleté du conseil ne suffit pas à le rendre inutile |\n", + "| « L'inférence ML gratuite » | temps mur identiques (médianes ≈ 0,033 s / 0,031 s, inference amortie sur 3600 features) | **Vrai ici** : le coût d'inférence du plongeur ne change pas l'équilibre à cette taille |\n", "\n", - "Limites assumées : famille synthétique unique, 25 instances de test, hints **conseils** réparables (pas de fixation coercitive comme un vrai diving MIP qui branche par valeurs successives sur un sous-ensemble), fenêtre de temps courte, valeurs de queue dépendantes du run (médianes stables : 788-790 / 874-885 sur trois tirages). Le mécanisme mesuré (queue vs médiane, conflit vs précision) est indépendant de ces limites — c'est lui que l'on emporte comme leçon." + "Limites assumées : famille synthétique unique, 25 instances de test, hints **conseils** réparables (pas de fixation coercitive comme un vrai diving MIP qui branche par valeurs successives sur un sous-ensemble), solveur séquentiel (`num_workers = 1` : reproductible, mais les valeurs absolues ne se comparent pas aux tirages parallèles antérieurs), fenêtre de temps courte. Le mécanisme mesuré (effet net d'un conseil sale, gain croissant avec la dureté) est indépendant de ces limites — c'est lui que l'on emporte comme leçon." ] }, { @@ -845,10 +848,10 @@ "id": "8749738f", "metadata": { "papermill": { - "duration": 0.004542, - "end_time": "2026-09-23T07:25:50.302043", + "duration": 0.002266, + "end_time": "2026-09-23T10:49:54.443924", "exception": false, - "start_time": "2026-09-23T07:25:50.297501", + "start_time": "2026-09-23T10:49:54.441658", "status": "completed" }, "tags": [] @@ -861,7 +864,7 @@ "\n", "Bibliographie :\n", "\n", - "- Nair, V., Bartunov, S., Gimeno, F., et al. (2021). *Solving Mixed Integer Programs Using Neural Networks*. **Nature** 607. — gisement partagé : [`Bibliographie IA/Search/2021 - Nair et al - Solving Mixed Integer Programs Using Neural Networks.pdf`](https://drive.google.com/drive/folders/0).\n", + "- Nair, V., Bartunov, S., Gimeno, F., et al. (2021). *Solving Mixed Integer Programs Using Neural Networks*. arXiv:2012.13349 (v3, juillet 2021). — gisement partagé : `G:\\Mon Drive\\MyIA\\IA\\Bibliographie IA\\Search\\2021 - Nair et al - Solving Mixed Integer Programs Using Neural Networks.pdf`.\n", "- Bengio, Y., Lodi, A., Prouvost, A. (2021). *Machine Learning for Combinatorial Optimization: a Methodological Tour d'Horizon*. EJOR 290(2).\n", "- Boussemart, F., Hemery, F., Lecoutre, C., Sais, L. (2004). *Boosting Systematic Search by Weighting Constraints* (dom/wdeg). ECAI.\n", "\n", @@ -889,14 +892,14 @@ }, "papermill": { "default_parameters": {}, - "duration": 27.642141, - "end_time": "2026-09-23T07:25:50.758082", + "duration": 24.291134, + "end_time": "2026-09-23T10:49:54.893239", "environment_variables": {}, "exception": null, - "input_path": "App-32-NeuralDiving-Coloration.ipynb", - "output_path": "C:\\Users\\jsboi\\AppData\\Local\\Temp\\claude\\d--Dev-CoursIA\\6349193d-9e16-493a-8920-46769a38fe2e\\scratchpad\\app32_output.ipynb", + "input_path": "MyIA.AI.Notebooks/Search/Applications/Hybrid/App-32-NeuralDiving-Coloration.ipynb", + "output_path": "C:/Users/jsboi/AppData/Local/Temp/app32_rerun.ipynb", "parameters": {}, - "start_time": "2026-09-23T07:25:23.115941", + "start_time": "2026-09-23T10:49:30.602105", "version": "2.6.0" } }, diff --git a/MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app32-neural-diving/SOURCE.md b/MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app32-neural-diving/SOURCE.md index 562e192420..48518a0ea2 100644 --- a/MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app32-neural-diving/SOURCE.md +++ b/MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app32-neural-diving/SOURCE.md @@ -7,9 +7,9 @@ coloration de graphe. | Champ | Valeur | |---|---| | **Travail original** | *Solving Mixed Integer Programs Using Neural Networks* — Nair, V., Bartunov, S., Gimeno, F., et al. | -| **Publication** | Nature 607 (2021) | +| **Publication** | arXiv:2012.13349 (v3, juillet 2021) | | **Gisement partagé** | `G:\Mon Drive\MyIA\IA\Bibliographie IA\Search\2021 - Nair et al - Solving Mixed Integer Programs Using Neural Networks.pdf` | -| **Verdict audité** | médiane des branches 790 → 880 (dégradation ≈ 10 %) ; égarements de la recherche (nA jusqu'à 3972) majoritairement ramenés ≤ 900 ; 40-45 % des arêtes du hint en conflit avec les contraintes | +| **Verdict audité** | run déterministe (`num_workers = 1`) : médiane des branches 2829 → 2325 (−18 %) ; **25/25 instances améliorées**, gain relatif 8-28 % croissant avec la dureté (ρ = 0,60) ; ≈ 49 % des arêtes du hint en conflit avec l'adjacence sans annuler le gain net | ## Ce qui est attribué diff --git a/MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app32-neural-diving/diving_results.csv b/MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app32-neural-diving/diving_results.csv index a349f7e59a..837a5792e3 100644 --- a/MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app32-neural-diving/diving_results.csv +++ b/MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app32-neural-diving/diving_results.csv @@ -1,26 +1,26 @@ seed,kA,k,nA,nB,tA,tB,hint_conflicts -6000,4,4,778,876,0.057,0.045,57 -6001,4,4,2827,849,0.037,0.037,51 -6002,4,4,790,894,0.038,0.041,41 -6003,4,4,3129,933,0.038,0.064,47 -6004,4,4,781,880,0.071,0.071,41 -6005,4,4,790,870,0.056,0.063,36 -6006,4,4,786,880,0.061,0.045,46 -6007,4,4,795,890,0.048,0.053,38 -6008,4,4,781,865,0.059,0.058,42 -6009,4,4,793,847,0.06,0.056,40 -6010,4,4,802,891,0.05,0.047,38 -6011,4,4,774,850,0.041,0.063,39 -6012,4,4,722,885,0.064,0.059,48 -6013,4,4,803,916,0.058,0.035,49 -6014,4,4,2633,1988,0.037,0.05,34 -6015,4,4,791,887,0.036,0.036,53 -6016,4,4,776,856,0.037,0.069,42 -6017,4,4,775,2149,0.053,0.062,47 -6018,4,4,774,860,0.062,0.05,47 -6019,4,4,783,876,0.05,0.057,38 -6020,4,4,787,894,0.053,0.032,56 -6021,4,4,800,870,0.051,0.058,53 -6022,4,4,791,854,0.062,0.044,48 -6023,4,4,2395,891,0.053,0.048,37 -6024,4,4,2803,883,0.046,0.054,44 +6000,4,4,2991,2752,0.026,0.045,39 +6001,4,4,2997,2462,0.029,0.026,43 +6002,4,4,2831,2278,0.03,0.028,44 +6003,4,4,2885,2430,0.033,0.037,44 +6004,4,4,3291,2363,0.035,0.035,40 +6005,4,4,3274,2595,0.036,0.038,37 +6006,4,4,2441,2096,0.033,0.031,54 +6007,4,4,2829,2255,0.033,0.029,39 +6008,4,4,3184,2658,0.039,0.033,48 +6009,4,4,2463,2269,0.031,0.03,41 +6010,4,4,2419,2105,0.027,0.029,37 +6011,4,4,3012,2644,0.037,0.04,51 +6012,4,4,3039,2418,0.039,0.029,42 +6013,4,4,2328,2087,0.028,0.03,45 +6014,4,4,3094,2638,0.032,0.032,45 +6015,4,4,2561,2325,0.035,0.036,46 +6016,4,4,3013,2315,0.047,0.04,59 +6017,4,4,2623,2227,0.044,0.047,46 +6018,4,4,2664,2262,0.041,0.028,46 +6019,4,4,2798,2349,0.027,0.029,48 +6020,4,4,2464,2108,0.037,0.045,40 +6021,4,4,2595,2140,0.03,0.025,34 +6022,4,4,2743,2371,0.024,0.024,49 +6023,4,4,2600,2241,0.028,0.027,44 +6024,4,4,2904,2348,0.039,0.043,39 From 00c79e7e6f5f99afaf58f2e32e371b660963ecfd Mon Sep 17 00:00:00 2001 From: jsboige Date: Wed, 23 Sep 2026 15:43:57 +0200 Subject: [PATCH 3/4] =?UTF-8?q?fix(notebooks,#17514):=20App-33=20=E2=80=94?= =?UTF-8?q?=20index=20de=20s=C3=A9rie=20(App-32=20pris)=20et=20chiffres=20?= =?UTF-8?q?ancr=C3=A9s=20sur=20le=20run=20d=C3=A9terministe?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Deux réparations nommées par le dossier adjoint, plus un chiffre faux qu'elles ont mis au jour. 1. Renommage App-32 -> App-33 (fichier, titre, `OUTPUT_DIR`, `data/app32-neural-diving` -> `data/app33-neural-diving`) : l'index `App-32` est déjà pris sur main par `Search/App-32-Szpiro-Pasten-2026.ipynb` (#16690), la série numérote globalement entre `CSP/`, `Hybrid/` et `Search/`. 2. README et `SOURCE.md` alignés sur le run committé : arXiv:2012.13349 (v3, juillet 2021), médiane 2829 -> 2325 (-18 %), 25/25 instances améliorées, ~49 % de conflits. 3. Chiffre de branches faux (521-789 / « 500-800 nœuds ») porté par quatre supports : la table de la cellule 7, un commentaire de la cellule 8, le README et `SOURCE.md`. Il décrivait les tirages **parallèles**, pas le run déterministe committé (`num_workers = 1`). Mesuré : médiane 2742 (2303-3799) sur les 60 instances d'entraînement, 2738 au smoke test, aucune instance sous 2303 sur 85. Les tirages parallèles, eux, rendent bien ~790 (`num_workers = 8` : 782 et 787 sur deux passes) — la phrase de la cellule 12 qui les cite comme tels est donc vérifiée et conservée. Ré-exécution papermill après l'édition de la cellule 8 : 22/22 cellules, 0 erreur, 0 `execution_count` nul. `diving_results.csv` recommité avec le run frais (seules les colonnes de temps `tA`/`tB` bougent ; `nA`, `nB`, `k`, `kA`, `hint_conflicts` identiques). Co-Authored-By: Claude Sonnet 5 --- ...b => App-33-NeuralDiving-Coloration.ipynb} | 240 +++++++++--------- .../app32-neural-diving/diving_results.csv | 26 -- .../SOURCE.md | 8 +- .../app33-neural-diving/diving_results.csv | 26 ++ .../Search/Applications/README.md | 8 +- 5 files changed, 154 insertions(+), 154 deletions(-) rename MyIA.AI.Notebooks/Search/Applications/Hybrid/{App-32-NeuralDiving-Coloration.ipynb => App-33-NeuralDiving-Coloration.ipynb} (84%) delete mode 100644 MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app32-neural-diving/diving_results.csv rename MyIA.AI.Notebooks/Search/Applications/Hybrid/data/{app32-neural-diving => app33-neural-diving}/SOURCE.md (87%) create mode 100644 MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app33-neural-diving/diving_results.csv diff --git a/MyIA.AI.Notebooks/Search/Applications/Hybrid/App-32-NeuralDiving-Coloration.ipynb b/MyIA.AI.Notebooks/Search/Applications/Hybrid/App-33-NeuralDiving-Coloration.ipynb similarity index 84% rename from MyIA.AI.Notebooks/Search/Applications/Hybrid/App-32-NeuralDiving-Coloration.ipynb rename to MyIA.AI.Notebooks/Search/Applications/Hybrid/App-33-NeuralDiving-Coloration.ipynb index 8f830807b7..d64a9c15fa 100644 --- a/MyIA.AI.Notebooks/Search/Applications/Hybrid/App-32-NeuralDiving-Coloration.ipynb +++ b/MyIA.AI.Notebooks/Search/Applications/Hybrid/App-33-NeuralDiving-Coloration.ipynb @@ -5,16 +5,16 @@ "id": "e186bf33", "metadata": { "papermill": { - "duration": 0.002639, - "end_time": "2026-09-23T10:49:32.379946", + "duration": 0.002678, + "end_time": "2026-09-23T13:42:09.416516", "exception": false, - "start_time": "2026-09-23T10:49:32.377307", + "start_time": "2026-09-23T13:42:09.413838", "status": "completed" }, "tags": [] }, "source": [ - "# App-32 — Neural diving : un plongeur appris pour CP-SAT\n", + "# App-33 — Neural diving : un plongeur appris pour CP-SAT\n", "\n", "## Du conseil aux valeurs aux branches prouvées : ce que le diving apporte, mesure à l'appui\n", "\n", @@ -36,10 +36,10 @@ "id": "836b4015", "metadata": { "papermill": { - "duration": 0.001767, - "end_time": "2026-09-23T10:49:32.385686", + "duration": 0.001765, + "end_time": "2026-09-23T13:42:09.420360", "exception": false, - "start_time": "2026-09-23T10:49:32.383919", + "start_time": "2026-09-23T13:42:09.418595", "status": "completed" }, "tags": [] @@ -65,16 +65,16 @@ "id": "f033373a", "metadata": { "execution": { - "iopub.execute_input": "2026-09-23T10:49:32.390287Z", - "iopub.status.busy": "2026-09-23T10:49:32.390070Z", - "iopub.status.idle": "2026-09-23T10:49:32.828012Z", - "shell.execute_reply": "2026-09-23T10:49:32.827418Z" + "iopub.execute_input": "2026-09-23T13:42:09.425083Z", + "iopub.status.busy": "2026-09-23T13:42:09.424839Z", + "iopub.status.idle": "2026-09-23T13:42:09.796622Z", + "shell.execute_reply": "2026-09-23T13:42:09.796060Z" }, "papermill": { - "duration": 0.441519, - "end_time": "2026-09-23T10:49:32.828916", + "duration": 0.375273, + "end_time": "2026-09-23T13:42:09.797465", "exception": false, - "start_time": "2026-09-23T10:49:32.387397", + "start_time": "2026-09-23T13:42:09.422192", "status": "completed" }, "tags": [] @@ -99,7 +99,7 @@ "\n", "NV, KMAX, DEG = 60, 6, 3 # 60 sommets, 6 couleurs max, ~3 aretes / sommet\n", "SEED0, N_TRAIN, N_TEST = 5000, 60, 25\n", - "OUTPUT_DIR = Path(\"data/app32-neural-diving\")\n", + "OUTPUT_DIR = Path(\"data/app33-neural-diving\")\n", "\n", "\n", "def make_graph(seed: int) -> np.ndarray:\n", @@ -122,10 +122,10 @@ "id": "5927b5e4", "metadata": { "papermill": { - "duration": 0.002625, - "end_time": "2026-09-23T10:49:32.833703", + "duration": 0.003242, + "end_time": "2026-09-23T13:42:09.802904", "exception": false, - "start_time": "2026-09-23T10:49:32.831078", + "start_time": "2026-09-23T13:42:09.799662", "status": "completed" }, "tags": [] @@ -144,16 +144,16 @@ "id": "ab85c964", "metadata": { "execution": { - "iopub.execute_input": "2026-09-23T10:49:32.840457Z", - "iopub.status.busy": "2026-09-23T10:49:32.840175Z", - "iopub.status.idle": "2026-09-23T10:49:32.947007Z", - "shell.execute_reply": "2026-09-23T10:49:32.946524Z" + "iopub.execute_input": "2026-09-23T13:42:09.807605Z", + "iopub.status.busy": "2026-09-23T13:42:09.807338Z", + "iopub.status.idle": "2026-09-23T13:42:09.864202Z", + "shell.execute_reply": "2026-09-23T13:42:09.863676Z" }, "papermill": { - "duration": 0.111032, - "end_time": "2026-09-23T10:49:32.948160", + "duration": 0.060251, + "end_time": "2026-09-23T13:42:09.864935", "exception": false, - "start_time": "2026-09-23T10:49:32.837128", + "start_time": "2026-09-23T13:42:09.804684", "status": "completed" }, "tags": [] @@ -163,7 +163,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "smoke test : 4 couleurs, 2738 branches, 0.083 s\n" + "smoke test : 4 couleurs, 2738 branches, 0.041 s\n" ] } ], @@ -214,10 +214,10 @@ "id": "735eadeb", "metadata": { "papermill": { - "duration": 0.002017, - "end_time": "2026-09-23T10:49:32.952984", + "duration": 0.002944, + "end_time": "2026-09-23T13:42:09.870013", "exception": false, - "start_time": "2026-09-23T10:49:32.950967", + "start_time": "2026-09-23T13:42:09.867069", "status": "completed" }, "tags": [] @@ -236,16 +236,16 @@ "id": "aabc64ee", "metadata": { "execution": { - "iopub.execute_input": "2026-09-23T10:49:32.960941Z", - "iopub.status.busy": "2026-09-23T10:49:32.960710Z", - "iopub.status.idle": "2026-09-23T10:49:32.966466Z", - "shell.execute_reply": "2026-09-23T10:49:32.965969Z" + "iopub.execute_input": "2026-09-23T13:42:09.881943Z", + "iopub.status.busy": "2026-09-23T13:42:09.881425Z", + "iopub.status.idle": "2026-09-23T13:42:09.889419Z", + "shell.execute_reply": "2026-09-23T13:42:09.888878Z" }, "papermill": { - "duration": 0.012261, - "end_time": "2026-09-23T10:49:32.967263", + "duration": 0.017203, + "end_time": "2026-09-23T13:42:09.890720", "exception": false, - "start_time": "2026-09-23T10:49:32.955002", + "start_time": "2026-09-23T13:42:09.873517", "status": "completed" }, "tags": [] @@ -291,10 +291,10 @@ "id": "06b27376", "metadata": { "papermill": { - "duration": 0.002034, - "end_time": "2026-09-23T10:49:32.972249", + "duration": 0.003862, + "end_time": "2026-09-23T13:42:09.898691", "exception": false, - "start_time": "2026-09-23T10:49:32.970215", + "start_time": "2026-09-23T13:42:09.894829", "status": "completed" }, "tags": [] @@ -302,15 +302,15 @@ "source": [ "## 3. Calibrer la famille : la fenêtre où la recherche travaille\n", "\n", - "Un effet du diving ne se mesure que là où la **recherche existe**. Trois familles typiques ont été calibrées pendant la phase prototype, avec 3 seeds chacune et une limite de 10 s :\n", + "Un effet du diving ne se mesure que là où la **recherche existe**. Trois familles typiques ont été calibrées avec `num_workers = 1` et `random_seed = 7`, comme tout le reste du notebook :\n", "\n", "| Famille | Configuration | Nœuds (médiane) | Verdict |\n", "|---|---|---|---|\n", - "| Set-cover à couverture ≥ 2 | m=45..70, n=60..90, densité 8-10 % | **0** | effondré au *presolve* : CP-SAT résout sans jamais brancher |\n", - "| Knapsack multidim. corrélé | n=40, d=5, profits ∝ poids | 0 (timeout 10 s) | brasse sans preuve : la fenêtre est trop dure |\n", - "| **Coloration 60 sommets, ~3 arêtes/sommet** | nv=60, deg=3, k≤6 | **521-789** | branche ~500-800 fois **et** prouve l'optimalité en < 0,1 s |\n", + "| Set-cover à couverture ≥ 2 | m=45, n=60, densité 10 % | **0** | effondré au *presolve* : CP-SAT résout sans jamais brancher |\n", + "| Knapsack multidim. corrélé | n=40, d=5, profits ∝ poids | 102 284 (limite 3 s, non prouvé) | brasse sans preuve : la fenêtre est trop dure |\n", + "| **Coloration 60 sommets, ~3 arêtes/sommet** | nv=60, deg=3, k≤6 | **2742** (2303-3799) | branche quelques milliers de fois **et** prouve l'optimalité en < 0,1 s |\n", "\n", - "La coloration est la fenêtre : assez dure pour que la recherche ait une vraie trajectoire de branches, assez facile pour que chaque résolution se prouve en quelques centièmes de seconde — indispensable pour un notebook qui enchaîne 60 + 25 résolutions. Le fait *mesuré* que les familles denses s'effondrent au presolve est reporté ici honnêtement, pas maquillé : c'est la définition de la fenêtre." + "La coloration est la fenêtre : assez dure pour que la recherche ait une vraie trajectoire de branches, assez facile pour que chaque résolution se prouve en quelques centièmes de seconde — indispensable pour un notebook qui enchaîne 60 + 25 résolutions. Les deux premières lignes viennent de la sonde `probe` ci-dessous (un tirage chacune) ; la médiane de la coloration est celle des 60 instances d'entraînement, imprimée par la cellule d'entraînement. Le fait *mesuré* que les familles denses s'effondrent au presolve est reporté ici honnêtement, pas maquillé : c'est la définition de la fenêtre." ] }, { @@ -319,16 +319,16 @@ "id": "57b0eb11", "metadata": { "execution": { - "iopub.execute_input": "2026-09-23T10:49:32.980125Z", - "iopub.status.busy": "2026-09-23T10:49:32.979916Z", - "iopub.status.idle": "2026-09-23T10:49:35.996579Z", - "shell.execute_reply": "2026-09-23T10:49:35.996072Z" + "iopub.execute_input": "2026-09-23T13:42:09.907363Z", + "iopub.status.busy": "2026-09-23T13:42:09.907153Z", + "iopub.status.idle": "2026-09-23T13:42:12.924472Z", + "shell.execute_reply": "2026-09-23T13:42:12.923966Z" }, "papermill": { - "duration": 3.022536, - "end_time": "2026-09-23T10:49:35.997374", + "duration": 3.022745, + "end_time": "2026-09-23T13:42:12.925337", "exception": false, - "start_time": "2026-09-23T10:49:32.974838", + "start_time": "2026-09-23T13:42:09.902592", "status": "completed" }, "tags": [] @@ -345,7 +345,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "knapsack correle (n40 d5) | nodes= 84440 temps= 3.00s opt=False\n" + "knapsack correle (n40 d5) | nodes= 102384 temps= 3.00s opt=False\n" ] } ], @@ -397,8 +397,8 @@ "]:\n", " nodes, wall, ok = probe(fn(), tl)\n", " print(f\"{name:26s} | nodes={nodes:8d} temps={wall:6.2f}s opt={ok}\")\n", - "# la fenetre (coloration 60v) est deja mesuree par solve_coloring dans la\n", - "# section 2 : nodes ~ 500-800, optimal en < 0,1 s" + "# la fenetre (coloration 60v) est mesuree par solve_coloring en section 2\n", + "# (smoke test : 2738 branches) et par la boucle d'entrainement (mediane 2742)" ] }, { @@ -406,10 +406,10 @@ "id": "d922ac32", "metadata": { "papermill": { - "duration": 0.001931, - "end_time": "2026-09-23T10:49:36.001542", + "duration": 0.002062, + "end_time": "2026-09-23T13:42:12.929531", "exception": false, - "start_time": "2026-09-23T10:49:35.999611", + "start_time": "2026-09-23T13:42:12.927469", "status": "completed" }, "tags": [] @@ -429,10 +429,10 @@ "id": "4c3736da", "metadata": { "papermill": { - "duration": 0.001828, - "end_time": "2026-09-23T10:49:36.005206", + "duration": 0.00192, + "end_time": "2026-09-23T13:42:12.933406", "exception": false, - "start_time": "2026-09-23T10:49:36.003378", + "start_time": "2026-09-23T13:42:12.931486", "status": "completed" }, "tags": [] @@ -458,16 +458,16 @@ "id": "335ee35a", "metadata": { "execution": { - "iopub.execute_input": "2026-09-23T10:49:36.009993Z", - "iopub.status.busy": "2026-09-23T10:49:36.009619Z", - "iopub.status.idle": "2026-09-23T10:49:54.284597Z", - "shell.execute_reply": "2026-09-23T10:49:54.283673Z" + "iopub.execute_input": "2026-09-23T13:42:12.938854Z", + "iopub.status.busy": "2026-09-23T13:42:12.938646Z", + "iopub.status.idle": "2026-09-23T13:42:30.361783Z", + "shell.execute_reply": "2026-09-23T13:42:30.361242Z" }, "papermill": { - "duration": 18.27895, - "end_time": "2026-09-23T10:49:54.286013", + "duration": 17.426771, + "end_time": "2026-09-23T13:42:30.362666", "exception": false, - "start_time": "2026-09-23T10:49:36.007063", + "start_time": "2026-09-23T13:42:12.935895", "status": "completed" }, "tags": [] @@ -491,7 +491,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "test : medianes nA=2829 nB=2325 | temps A=0.033s B=0.031s | conflits hint med=44\n" + "test : medianes nA=2829 nB=2325 | temps A=0.025s B=0.025s | conflits hint med=44\n" ] } ], @@ -548,10 +548,10 @@ "id": "207f9ff1", "metadata": { "papermill": { - "duration": 0.004181, - "end_time": "2026-09-23T10:49:54.293740", + "duration": 0.002938, + "end_time": "2026-09-23T13:42:30.367864", "exception": false, - "start_time": "2026-09-23T10:49:54.289559", + "start_time": "2026-09-23T13:42:30.364926", "status": "completed" }, "tags": [] @@ -581,16 +581,16 @@ "id": "2a14a9c7", "metadata": { "execution": { - "iopub.execute_input": "2026-09-23T10:49:54.302222Z", - "iopub.status.busy": "2026-09-23T10:49:54.301546Z", - "iopub.status.idle": "2026-09-23T10:49:54.371306Z", - "shell.execute_reply": "2026-09-23T10:49:54.370583Z" + "iopub.execute_input": "2026-09-23T13:42:30.372766Z", + "iopub.status.busy": "2026-09-23T13:42:30.372436Z", + "iopub.status.idle": "2026-09-23T13:42:30.392818Z", + "shell.execute_reply": "2026-09-23T13:42:30.392331Z" }, "papermill": { - "duration": 0.075468, - "end_time": "2026-09-23T10:49:54.372779", + "duration": 0.023753, + "end_time": "2026-09-23T13:42:30.393602", "exception": false, - "start_time": "2026-09-23T10:49:54.297311", + "start_time": "2026-09-23T13:42:30.369849", "status": "completed" }, "tags": [] @@ -639,10 +639,10 @@ "id": "4ca59421", "metadata": { "papermill": { - "duration": 0.004316, - "end_time": "2026-09-23T10:49:54.381697", + "duration": 0.001849, + "end_time": "2026-09-23T13:42:30.397660", "exception": false, - "start_time": "2026-09-23T10:49:54.377381", + "start_time": "2026-09-23T13:42:30.395811", "status": "completed" }, "tags": [] @@ -661,16 +661,16 @@ "id": "2005963c", "metadata": { "execution": { - "iopub.execute_input": "2026-09-23T10:49:54.388872Z", - "iopub.status.busy": "2026-09-23T10:49:54.388325Z", - "iopub.status.idle": "2026-09-23T10:49:54.393936Z", - "shell.execute_reply": "2026-09-23T10:49:54.392955Z" + "iopub.execute_input": "2026-09-23T13:42:30.402270Z", + "iopub.status.busy": "2026-09-23T13:42:30.402080Z", + "iopub.status.idle": "2026-09-23T13:42:30.405773Z", + "shell.execute_reply": "2026-09-23T13:42:30.405397Z" }, "papermill": { - "duration": 0.009687, - "end_time": "2026-09-23T10:49:54.394918", + "duration": 0.007116, + "end_time": "2026-09-23T13:42:30.406593", "exception": false, - "start_time": "2026-09-23T10:49:54.385231", + "start_time": "2026-09-23T13:42:30.399477", "status": "completed" }, "tags": [] @@ -703,10 +703,10 @@ "id": "394ac710", "metadata": { "papermill": { - "duration": 0.002152, - "end_time": "2026-09-23T10:49:54.399421", + "duration": 0.001954, + "end_time": "2026-09-23T13:42:30.410744", "exception": false, - "start_time": "2026-09-23T10:49:54.397269", + "start_time": "2026-09-23T13:42:30.408790", "status": "completed" }, "tags": [] @@ -725,16 +725,16 @@ "id": "a7ba060b", "metadata": { "execution": { - "iopub.execute_input": "2026-09-23T10:49:54.409342Z", - "iopub.status.busy": "2026-09-23T10:49:54.408855Z", - "iopub.status.idle": "2026-09-23T10:49:54.413570Z", - "shell.execute_reply": "2026-09-23T10:49:54.412869Z" + "iopub.execute_input": "2026-09-23T13:42:30.415735Z", + "iopub.status.busy": "2026-09-23T13:42:30.415553Z", + "iopub.status.idle": "2026-09-23T13:42:30.419334Z", + "shell.execute_reply": "2026-09-23T13:42:30.418416Z" }, "papermill": { - "duration": 0.011836, - "end_time": "2026-09-23T10:49:54.415125", + "duration": 0.007422, + "end_time": "2026-09-23T13:42:30.420115", "exception": false, - "start_time": "2026-09-23T10:49:54.403289", + "start_time": "2026-09-23T13:42:30.412693", "status": "completed" }, "tags": [] @@ -763,10 +763,10 @@ "id": "cb4991ba", "metadata": { "papermill": { - "duration": 0.002271, - "end_time": "2026-09-23T10:49:54.420132", + "duration": 0.001982, + "end_time": "2026-09-23T13:42:30.424269", "exception": false, - "start_time": "2026-09-23T10:49:54.417861", + "start_time": "2026-09-23T13:42:30.422287", "status": "completed" }, "tags": [] @@ -785,16 +785,16 @@ "id": "cb511012", "metadata": { "execution": { - "iopub.execute_input": "2026-09-23T10:49:54.427033Z", - "iopub.status.busy": "2026-09-23T10:49:54.426652Z", - "iopub.status.idle": "2026-09-23T10:49:54.430996Z", - "shell.execute_reply": "2026-09-23T10:49:54.430145Z" + "iopub.execute_input": "2026-09-23T13:42:30.429068Z", + "iopub.status.busy": "2026-09-23T13:42:30.428859Z", + "iopub.status.idle": "2026-09-23T13:42:30.432537Z", + "shell.execute_reply": "2026-09-23T13:42:30.432161Z" }, "papermill": { - "duration": 0.009522, - "end_time": "2026-09-23T10:49:54.432021", + "duration": 0.007023, + "end_time": "2026-09-23T13:42:30.433253", "exception": false, - "start_time": "2026-09-23T10:49:54.422499", + "start_time": "2026-09-23T13:42:30.426230", "status": "completed" }, "tags": [] @@ -822,10 +822,10 @@ "id": "9e3987b6", "metadata": { "papermill": { - "duration": 0.002857, - "end_time": "2026-09-23T10:49:54.437478", + "duration": 0.002114, + "end_time": "2026-09-23T13:42:30.437385", "exception": false, - "start_time": "2026-09-23T10:49:54.434621", + "start_time": "2026-09-23T13:42:30.435271", "status": "completed" }, "tags": [] @@ -848,10 +848,10 @@ "id": "8749738f", "metadata": { "papermill": { - "duration": 0.002266, - "end_time": "2026-09-23T10:49:54.443924", + "duration": 0.001964, + "end_time": "2026-09-23T13:42:30.441244", "exception": false, - "start_time": "2026-09-23T10:49:54.441658", + "start_time": "2026-09-23T13:42:30.439280", "status": "completed" }, "tags": [] @@ -892,17 +892,17 @@ }, "papermill": { "default_parameters": {}, - "duration": 24.291134, - "end_time": "2026-09-23T10:49:54.893239", + "duration": 23.136589, + "end_time": "2026-09-23T13:42:30.790789", "environment_variables": {}, "exception": null, - "input_path": "MyIA.AI.Notebooks/Search/Applications/Hybrid/App-32-NeuralDiving-Coloration.ipynb", - "output_path": "C:/Users/jsboi/AppData/Local/Temp/app32_rerun.ipynb", + "input_path": "App-33-NeuralDiving-Coloration.ipynb", + "output_path": "App-33-NeuralDiving-Coloration.ipynb", "parameters": {}, - "start_time": "2026-09-23T10:49:30.602105", + "start_time": "2026-09-23T13:42:07.654200", "version": "2.6.0" } }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app32-neural-diving/diving_results.csv b/MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app32-neural-diving/diving_results.csv deleted file mode 100644 index 837a5792e3..0000000000 --- a/MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app32-neural-diving/diving_results.csv +++ /dev/null @@ -1,26 +0,0 @@ -seed,kA,k,nA,nB,tA,tB,hint_conflicts -6000,4,4,2991,2752,0.026,0.045,39 -6001,4,4,2997,2462,0.029,0.026,43 -6002,4,4,2831,2278,0.03,0.028,44 -6003,4,4,2885,2430,0.033,0.037,44 -6004,4,4,3291,2363,0.035,0.035,40 -6005,4,4,3274,2595,0.036,0.038,37 -6006,4,4,2441,2096,0.033,0.031,54 -6007,4,4,2829,2255,0.033,0.029,39 -6008,4,4,3184,2658,0.039,0.033,48 -6009,4,4,2463,2269,0.031,0.03,41 -6010,4,4,2419,2105,0.027,0.029,37 -6011,4,4,3012,2644,0.037,0.04,51 -6012,4,4,3039,2418,0.039,0.029,42 -6013,4,4,2328,2087,0.028,0.03,45 -6014,4,4,3094,2638,0.032,0.032,45 -6015,4,4,2561,2325,0.035,0.036,46 -6016,4,4,3013,2315,0.047,0.04,59 -6017,4,4,2623,2227,0.044,0.047,46 -6018,4,4,2664,2262,0.041,0.028,46 -6019,4,4,2798,2349,0.027,0.029,48 -6020,4,4,2464,2108,0.037,0.045,40 -6021,4,4,2595,2140,0.03,0.025,34 -6022,4,4,2743,2371,0.024,0.024,49 -6023,4,4,2600,2241,0.028,0.027,44 -6024,4,4,2904,2348,0.039,0.043,39 diff --git a/MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app32-neural-diving/SOURCE.md b/MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app33-neural-diving/SOURCE.md similarity index 87% rename from MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app32-neural-diving/SOURCE.md rename to MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app33-neural-diving/SOURCE.md index 48518a0ea2..15b7945e19 100644 --- a/MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app32-neural-diving/SOURCE.md +++ b/MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app33-neural-diving/SOURCE.md @@ -1,6 +1,6 @@ -# Provenance `app32-neural-diving` +# Provenance `app33-neural-diving` -Ce répertoire accompagne App-32, reproduction locale de la composante +Ce répertoire accompagne App-33, reproduction locale de la composante **diving** de l'article Nair et al. (2021) sur le terrain contrôlé de la coloration de graphe. @@ -30,8 +30,8 @@ Le prototype a éliminé deux familles d'instances avant l'écriture du notebook : les set-cover denses (résolus au presolve, `nodes = 0` — aucune recherche à influencer) et les knapsack multidimensionnels corrélés (pas de preuve en fenêtre notebook). La coloration 60 sommets / 3 arêtes par -sommet est la famille fenêtre : elle branche (500-800 nœuds) et se prouve -(< 0,1 s). La leçon méthodologique est mesurée, pas postulée : sur la +sommet est la famille fenêtre : elle branche (médiane 2742 sur les 60 +instances d'entraînement, 2303-3799) et se prouve (< 0,1 s). La leçon méthodologique est mesurée, pas postulée : sur la médiane, le hint coûte ; sur la queue, il stabilise ; la précision par bit ne prédit pas l'effet, la cohérence avec les contraintes oui. diff --git a/MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app33-neural-diving/diving_results.csv b/MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app33-neural-diving/diving_results.csv new file mode 100644 index 0000000000..4332c8630b --- /dev/null +++ b/MyIA.AI.Notebooks/Search/Applications/Hybrid/data/app33-neural-diving/diving_results.csv @@ -0,0 +1,26 @@ +seed,kA,k,nA,nB,tA,tB,hint_conflicts +6000,4,4,2991,2752,0.025,0.025,39 +6001,4,4,2997,2462,0.026,0.025,43 +6002,4,4,2831,2278,0.025,0.029,44 +6003,4,4,2885,2430,0.035,0.031,44 +6004,4,4,3291,2363,0.025,0.026,40 +6005,4,4,3274,2595,0.025,0.024,37 +6006,4,4,2441,2096,0.025,0.024,54 +6007,4,4,2829,2255,0.024,0.025,39 +6008,4,4,3184,2658,0.024,0.024,48 +6009,4,4,2463,2269,0.024,0.036,41 +6010,4,4,2419,2105,0.036,0.025,37 +6011,4,4,3012,2644,0.025,0.025,51 +6012,4,4,3039,2418,0.025,0.028,42 +6013,4,4,2328,2087,0.028,0.026,45 +6014,4,4,3094,2638,0.025,0.024,45 +6015,4,4,2561,2325,0.025,0.023,46 +6016,4,4,3013,2315,0.024,0.025,59 +6017,4,4,2623,2227,0.024,0.025,46 +6018,4,4,2664,2262,0.025,0.025,46 +6019,4,4,2798,2349,0.036,0.039,48 +6020,4,4,2464,2108,0.034,0.042,40 +6021,4,4,2595,2140,0.029,0.025,34 +6022,4,4,2743,2371,0.025,0.026,49 +6023,4,4,2600,2241,0.025,0.026,44 +6024,4,4,2904,2348,0.026,0.026,39 diff --git a/MyIA.AI.Notebooks/Search/Applications/README.md b/MyIA.AI.Notebooks/Search/Applications/README.md index c8f2dbbc4b..5c0a81fe83 100644 --- a/MyIA.AI.Notebooks/Search/Applications/README.md +++ b/MyIA.AI.Notebooks/Search/Applications/README.md @@ -161,7 +161,7 @@ Quand l'espace est trop vaste ou l'objectif trop irrégulier pour les méthodes | 14 | [App-29-SALBP-AssemblyLineBalancing-Audit](Hybrid/App-29-SALBP-AssemblyLineBalancing-Audit.ipynb) | ~70 min | SALBP-1/2 : CP-SAT, PuLP/CBC et RPW, statuts/incumbents/bornes, identité de benchmark, front Pareto certifié et MMALBP robuste/pondéré — distillation PrCon B1 (Ilias Kalalou, Kaelan Grall) | Projet étudiant (PrCon PR #57) | | 15 | [App-30-OrbitalAssembly-Certificate-Audit](Hybrid/App-30-OrbitalAssembly-Certificate-Audit.ipynb) | ~70 min | Assemblage orbital : physique de Hohmann dans le modèle, auditeur externe, preuve que la scalarisation est exactement lexicographique, comparaison à makespan égal, front d'échange ε-contrainte et sonde en taille **et** en densité — distillation PrCon C4 (Gurvan Estable, Joris Bely, Kévin Lubert) | Projet étudiant (PrCon PR #53) | | 16 | [App-31-RCPSP-Max-Feasibility-Bounds](Hybrid/App-31-RCPSP-Max-Feasibility-Bounds.ipynb) | ~65 min | RCPSP/max : le lag maximal comme arc inverse, faisabilité NP-difficile et son témoin de circuit positif, balayage des trois régimes temporel/ressource/réalisable, échelle de bornes certifiées et repli explicite quand la référence externe manque — distillation PrCon B4 (Arthur Gallier, Nicolas Naegelen) | Projet étudiant (PrCon PR #51) | -| 17 | [App-32-NeuralDiving-Coloration](Hybrid/App-32-NeuralDiving-Coloration.ipynb) | ~60 min | Neural diving : un plongeur MLP prédit une affectation partielle, injectée comme hint réparable dans CP-SAT ; la médiane (790 → 880 branches) et la queue racontent deux histoires — cohérence du hint avec les contraintes, pas précision par bit — hommage Nair et al. 2021 | Recherche (Nair et al. 2021) | +| 17 | [App-33-NeuralDiving-Coloration](Hybrid/App-33-NeuralDiving-Coloration.ipynb) | ~60 min | Neural diving : un plongeur MLP prédit une affectation partielle, injectée comme hint réparable dans CP-SAT ; la médiane des branches recule (2829 → 2325, 25/25 instances améliorées) alors que ≈ 49 % des arêtes du hint violent l'adjacence — cohérence du hint avec les contraintes, pas précision par bit — hommage Nair et al. 2021 | Recherche (Nair et al. 2021) | --- @@ -239,7 +239,7 @@ Quand l'espace est trop vaste ou l'objectif trop irrégulier pour les méthodes | App-29 SALBP AssemblyLineBalancing Audit | CSP-3 (CP-SAT), CSP-4 (scheduling), CSP-5 (optimisation) | ortools, pulp, pandas, numpy, matplotlib | | App-30 OrbitalAssembly Certificate Audit | CSP-3 (CP-SAT), CSP-4 (scheduling), CSP-5 (optimisation) | ortools, pandas, matplotlib | | App-31 RCPSP Max Feasibility Bounds | CSP-4 (scheduling), CSP-3 (CP-SAT), Planners-8 (temporel) | ortools, pandas, numpy, matplotlib | -| App-32 NeuralDiving Coloration | App-28 (composante branchement), CSP-3 (CP-SAT), MGS-16 (sélection d'algorithmes) | ortools, scikit-learn, numpy, pandas | +| App-33 NeuralDiving Coloration | App-28 (composante branchement), CSP-3 (CP-SAT), MGS-16 (sélection d'algorithmes) | ortools, scikit-learn, numpy, pandas | --- @@ -267,7 +267,7 @@ Le [App-30-OrbitalAssembly-Certificate-Audit](Hybrid/App-30-OrbitalAssembly-Cert Le [App-31-RCPSP-Max-Feasibility-Bounds](Hybrid/App-31-RCPSP-Max-Feasibility-Bounds.ipynb) rend hommage au projet PrCon B4 d'**Arthur Gallier** et **Nicolas Naegelen**, *« RCPSP — ordonnancement de projet sous contraintes de ressources »*, PR [PrCon #51](https://github.com/jsboigeEpita/2026-Epita-Programmation-par-Contraintes/pull/51). Leur rendu a fait ce que [Planners-8-Temporal](../../SymbolicAI/Planners/03-Advanced/Planners-8-Temporal-Csharp.ipynb) laissait explicitement « en exercice » : confronter un solveur à un benchmark public d'ordonnancement. La vérification indépendante conduite pour cette distillation confirme leurs dix makespans, tous recalculés `OPTIMAL` par un modèle réécrit de zéro. Le prolongement CoursIA porte sur ce que leur section RCPSP/max rendait possible sans l'exercer : un lag maximal s'encode par un **arc inverse** qui referme un circuit, la faisabilité elle-même devient NP-difficile (Bartusch, Möhring, Radermacher, 1988), et un circuit de poids strictement positif en est le témoin vérifiable à la main. Le notebook balaye les trois régimes — infaisable temporellement, infaisable par les ressources, réalisable —, mesure que le diagnostic polynomial épargne au solveur le tiers gauche du domaine, et propose une échelle de bornes certifiées comme repli explicite lorsqu'aucune référence externe n'est disponible : le champ `niveau_de_preuve` interdit de confondre un écart à une borne calculée avec un écart à un optimum connu. Aucun code, texte ou figure étudiante n'est copié. Provenance : [`Hybrid/data/app31-rcpsp-max/SOURCE.md`](Hybrid/data/app31-rcpsp-max/SOURCE.md). -Le [App-32-NeuralDiving-Coloration](Hybrid/App-32-NeuralDiving-Coloration.ipynb) rend hommage au second geste de l'article de Nair et al. (2021), *« Solving Mixed Integer Programs Using Neural Networks »* (Nature 607) : le **diving**, qui apprend une solution partielle pour guider un solveur MIP. App-28 a audité la composante *branching* (politique de branchement apprise) ; App-32 enchaîne sur l'autre composante : un plongeur MLP prédit une affectation des 60 sommets d'une coloration de graphe, injectée comme `hint` réparable dans OR-Tools CP-SAT, et l'on mesure l'effet sur les branches de preuve. La famille est calibrée pour qu'une fenêtre existe : les set-cover denses s'effondrent au presolve (`nodes = 0`) et les knapsack corrélés ne se prouvent pas en fenêtre notebook ; la coloration 60 sommets / 3 arêtes branche (500-800 nœuds) et se prouve (< 0,1 s). Verdict mesuré sur 25 instances de test : la médiane se dégrade (790 → 880 branches, ≈ +10 %), mais les égarements de la recherche pure (jusqu'à 3972) reviennent majoritairement sous 900, avec ~47 % des arêtes du hint en conflit avec les contraintes — la cohérence, pas la précision par bit, détermine l'effet du hint. Le notebook ne copie aucun code, donnée, figure ou prose de l'article, archivé au gisement `G:\Mon Drive\MyIA\IA\Bibliographie IA\Search\`. Provenance : [`Hybrid/data/app32-neural-diving/SOURCE.md`](Hybrid/data/app32-neural-diving/SOURCE.md). +Le [App-33-NeuralDiving-Coloration](Hybrid/App-33-NeuralDiving-Coloration.ipynb) rend hommage au second geste de l'article de Nair et al. (2021), *« Solving Mixed Integer Programs Using Neural Networks »* (arXiv:2012.13349) : le **diving**, qui apprend une solution partielle pour guider un solveur MIP. App-28 a audité la composante *branching* (politique de branchement apprise) ; App-33 enchaîne sur l'autre composante : un plongeur MLP prédit une affectation des 60 sommets d'une coloration de graphe, injectée comme `hint` réparable dans OR-Tools CP-SAT, et l'on mesure l'effet sur les branches de preuve. La famille est calibrée pour qu'une fenêtre existe : les set-cover denses s'effondrent au presolve (`nodes = 0`) et les knapsack corrélés ne se prouvent pas en fenêtre notebook ; la coloration 60 sommets / 3 arêtes branche (médiane 2742, 2303-3799 sur les 60 instances d'entraînement) et se prouve (< 0,1 s). Verdict mesuré sur 25 instances de test, sur un run déterministe (`num_workers = 1`) : la médiane des branches recule (2829 → 2325, ≈ −18 %), les 25 instances s'améliorent (gain relatif médian ≈ 15 %, de 8 % à 28 %), et ≈ 49 % des arêtes du hint (44 sur ~90) sont en conflit avec l'adjacence — la cohérence, pas la précision par bit, détermine l'effet du hint. Le notebook ne copie aucun code, donnée, figure ou prose de l'article, archivé au gisement `G:\Mon Drive\MyIA\IA\Bibliographie IA\Search\`. Provenance : [`Hybrid/data/app33-neural-diving/SOURCE.md`](Hybrid/data/app33-neural-diving/SOURCE.md). --- @@ -310,7 +310,7 @@ Couverture par application des sources fondatrices mobilisées dans cette sous-s | App-29 (SALBP Assembly Line Balancing Audit) | Salveson, M. E. (1955) — « The Assembly Line Balancing Problem », *Journal of Industrial Engineering* 6(3) ; Helgeson, W. B., & Birnie, D. P. (1961) — « Assembly Line Balancing Using the Ranked Positional Weight Technique », *Journal of Industrial Engineering* 12(6) ; Scholl, A. (1999) — *Balancing and Sequencing of Assembly Lines*, Physica-Verlag. | | App-30 (OrbitalAssembly Certificate Audit) | Hohmann, W. (1925) — *Die Erreichbarkeit der Himmelskörper*, Oldenbourg ; Vallado, D. A. (2013) — *Fundamentals of Astrodynamics and Applications*, 4e éd., Microcosm Press ; Haimes, Y. Y., Lasdon, L. S., & Wismer, D. A. (1971) — « On a Bicriterion Formulation of the Problems of Integrated System Identification and System Optimization », *IEEE Transactions on Systems, Man, and Cybernetics* 1(3) (ε-contrainte) ; Wilcoxon, F. (1945) — « Individual Comparisons by Ranking Methods », *Biometrics Bulletin* 1(6). | | App-31 (RCPSP Max Feasibility Bounds) | Bartusch, M., Möhring, R. H., & Radermacher, F. J. (1988) — « Scheduling Project Networks with Resource Constraints and Time Windows », *Annals of Operations Research* 16(1) ; Kolisch, R., & Sprecher, A. (1997) — « PSPLIB — A Project Scheduling Problem Library », *European Journal of Operational Research* 96(1) ; Neumann, K., Schwindt, C., & Zimmermann, J. (2003) — *Project Scheduling with Time Windows and Scarce Resources*, Springer ; Bellman, R. (1958) — « On a Routing Problem », *Quarterly of Applied Mathematics* 16(1) (relaxation et détection de circuit). | -| App-32 (Neural Diving Coloration) | Nair, V., Bartunov, S., Gimeno, F., et al. (2021) — « Solving Mixed Integer Programs Using Neural Networks », *Nature* 607 ; Bengio, Y., Lodi, A., & Prouvost, A. (2021) — « Machine Learning for Combinatorial Optimization: a Methodological Tour d'Horizon », *European Journal of Operational Research* 290(2). | +| App-33 (Neural Diving Coloration) | Nair, V., Bartunov, S., Gimeno, F., et al. (2021) — « Solving Mixed Integer Programs Using Neural Networks », arXiv:2012.13349 (v3, juillet 2021) ; Bengio, Y., Lodi, A., & Prouvost, A. (2021) — « Machine Learning for Combinatorial Optimization: a Methodological Tour d'Horizon », *European Journal of Operational Research* 290(2). | ## Conclusion / Prochaines étapes From 01fcb768d6ac6d4af6a7c3c9d4fa7e03e78d79c3 Mon Sep 17 00:00:00 2001 From: jsboige Date: Wed, 23 Sep 2026 16:57:53 +0200 Subject: [PATCH 4/4] =?UTF-8?q?docs(search,#17514):=20decomptes=20README?= =?UTF-8?q?=20Applications=20alignes=20sur=20l'arbre=20=E2=80=94=2059=20no?= =?UTF-8?q?tebooks,=20Hybrid=2023=20(18=20Python=20+=205=20twins),=20~46h3?= =?UTF-8?q?5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Verification firsthand git ls-tree a la tete : CSP 31 (18 Python + 13 twins), Hybrid 23 (18 Python + 5 twins), Search 5 (3 Python + 2 twins) = 59 total. Duree : App-33 ~60 min (ligne 164) porte le total de ~45h35 a ~46h35. Co-Authored-By: Claude Sonnet 5 --- MyIA.AI.Notebooks/Search/Applications/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MyIA.AI.Notebooks/Search/Applications/README.md b/MyIA.AI.Notebooks/Search/Applications/README.md index 5c0a81fe83..8a3c02504e 100644 --- a/MyIA.AI.Notebooks/Search/Applications/README.md +++ b/MyIA.AI.Notebooks/Search/Applications/README.md @@ -1,8 +1,8 @@ # Search - Applications -C'est ici que la série Search se confronte au réel. Les 58 notebooks d'application, pour la plupart adaptés de projets étudiants, prennent les algorithmes des Parties 1 et 2 et les mettent face à des problèmes qui ne se laissent pas faire : planifier les gardes d'un service hospitalier, ordonnancer un atelier, construire un calendrier sportif équitable, router une flotte de véhicules. Trois catégories les organisent — **Search pur** (jeux combinatoires), **CSP** (satisfaction de contraintes) et **Hybride** (combinaisons de solveurs, modèles exacts et métaheuristiques) — et la plupart sont autonomes, avec des pointeurs vers les prérequis pertinents. À cela s'ajoutent les **jumeaux C#** (App-1b, App-2b, App-3b, App-4b, App-5b, App-6-CSharp, App-7b, App-8-CSharp, App-9b, App-10b, App-11b, App-13b, App-14-CSharp, App-14c, App-15b, App-16-CSharp, App-17b, App-18b, App-19-CSharp, App-20b) qui déroulent les mêmes algorithmes *from-scratch* en .NET, en complément des versions Python qui invoquent des solveurs industriels. +C'est ici que la série Search se confronte au réel. Les 59 notebooks d'application, pour la plupart adaptés de projets étudiants, prennent les algorithmes des Parties 1 et 2 et les mettent face à des problèmes qui ne se laissent pas faire : planifier les gardes d'un service hospitalier, ordonnancer un atelier, construire un calendrier sportif équitable, router une flotte de véhicules. Trois catégories les organisent — **Search pur** (jeux combinatoires), **CSP** (satisfaction de contraintes) et **Hybride** (combinaisons de solveurs, modèles exacts et métaheuristiques) — et la plupart sont autonomes, avec des pointeurs vers les prérequis pertinents. À cela s'ajoutent les **jumeaux C#** (App-1b, App-2b, App-3b, App-4b, App-5b, App-6-CSharp, App-7b, App-8-CSharp, App-9b, App-10b, App-11b, App-13b, App-14-CSharp, App-14c, App-15b, App-16-CSharp, App-17b, App-18b, App-19-CSharp, App-20b) qui déroulent les mêmes algorithmes *from-scratch* en .NET, en complément des versions Python qui invoquent des solveurs industriels. -Sous-série de **58 notebooks** | **~45h35** | Python 3.10+ (`ortools`, `python-sat`, `deap`, `mealpy`, `minizinc`, `optuna`, `rustuna`) ; .NET 9 (`dotnet-interactive`) pour les jumeaux C# +Sous-série de **59 notebooks** | **~46h35** | Python 3.10+ (`ortools`, `python-sat`, `deap`, `mealpy`, `minizinc`, `optuna`, `rustuna`) ; .NET 9 (`dotnet-interactive`) pour les jumeaux C# ## Pourquoi cette sous-série @@ -33,7 +33,7 @@ Un algorithme compris sur un exemple jouet n'est pas encore un algorithme maîtr Applications/ ├── Search/ # Applications purement Search (5 notebooks : 3 Python + 2 twins C#) ├── CSP/ # Applications CSP (31 notebooks : 18 Python + 13 twins C#) -└── Hybrid/ # Méthodes hybrides / métaheuristiques (22 notebooks : 17 Python + 5 twins C#) +└── Hybrid/ # Méthodes hybrides / métaheuristiques (23 notebooks : 18 Python + 5 twins C#) ``` ```mermaid