From c673835d64503b58ad641075912d98d144a54f0f Mon Sep 17 00:00:00 2001 From: jsboige Date: Thu, 8 Oct 2026 01:23:22 +0200 Subject: [PATCH 1/6] =?UTF-8?q?Fix(probas,#18556):=20tranche=2018/19=20?= =?UTF-8?q?=E2=80=94=20intervalle=20credible=20mesure,=20censure=20decrite?= =?UTF-8?q?=20par=20son=20chemin=20execute?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Infer-18 (re-execute 9/9, 0 erreur) : - cell 10 : ajout du calcul d'intervalle credible a 90 % (plus petit intervalle contenant >= 90 % de la masse du posterior de cp) - cell 11 : la prose annoncait "1890-1891" sans mesure — alignee sur la sortie fraiche (mode 1891, IC 90 % [1886..1892], masse 90,9 %) - cell 12 : le resultat de l'entropie etait annonce avant le calcul — reframe en question, la Lecture suit la cellule executee - cell 13 : renvoi Bayes factor "cf. Infer-8" -> "cf. Infer-10" (les Bayes factors vivent en Infer-10, coheremment avec cell 0 et README:801) Infer-19 (markdown-only, pas de re-exec due) : - cell 24 (BLOQUANT) : la lecture affirmait une censure "ici par Variable.ConstrainPositive" — contredit par la section 7 elle-meme (non compilable) ; decrit des lors le chemin reellement execute : statistiques suffisantes Gamma(nObs, lambda) au temps total a risque - cell 18 : "score minimal en k = 1,8" -> "maximal" (le balayage maximise la log-vraisemblance ; -466,7 est le pic) - cell 19 : "environ 45 % de censures" -> theorie 44,9 % / realise 51,7 % - nouvelle cellule de Lecture entre modele naif et modele censure : le run de 4 cellules code consecutives passe a 2+2 - cellule 0 : bloc Navigation canonique (forme #19788), Infer-18 | - README : - ligne 70 : la methode de censure nommee des lors les statistiques suffisantes, pas ConstrainPositive - ligne 818 : (cf. Infer-8) -> (cf. Infer-10) - ligne 820 : "1890-1891" -> mode 1891, IC 90 % 1886-1892 (mesure) - nouvelle section "### Infer-19" (absente — le notebook n'avait pas de section dediee entre Infer-18 et Concepts Cles) Organes : nav-chain 0 NEW (rc 0) ; output-collapse 0 flagged ; source-collapse 0 flagged ; output-failure 0 regressed ; consecutive-code-cells max run 2 (etait 4). See #18556 Co-Authored-By: Claude Sonnet 5.5 --- .../Probas/Infer/Infer-18-Change-Point.ipynb | 383 +++++------------- .../Infer/Infer-19-Survival-Analysis.ipynb | 29 +- MyIA.AI.Notebooks/Probas/Infer/README.md | 32 +- 3 files changed, 146 insertions(+), 298 deletions(-) diff --git a/MyIA.AI.Notebooks/Probas/Infer/Infer-18-Change-Point.ipynb b/MyIA.AI.Notebooks/Probas/Infer/Infer-18-Change-Point.ipynb index 0e0ced3183..56c6b6d08c 100644 --- a/MyIA.AI.Notebooks/Probas/Infer/Infer-18-Change-Point.ipynb +++ b/MyIA.AI.Notebooks/Probas/Infer/Infer-18-Change-Point.ipynb @@ -27,7 +27,8 @@ "sur l'indice de rupture (`Variable.DiscreteUniform`), une sélection de vraisemblance conditionnée\n", "par cet indice (`Variable.If` / `Variable.IfNot` sur une plage), et le moteur **EP** qui retourne\n", "un postérieur **discret** sur la localisation de la rupture — l’idiome message-passing-sur-graphe\n", - "de-facteurs que Infer.NET est conçu pour résoudre." + "de-facteurs que Infer.NET est conçu pour résoudre.", + "\n***\n## Navigation\n| Précédent | Suivant |\n|-----------|--------|\n| [Infer-17-Kalman-Filter](Infer-17-Kalman-Filter.ipynb) | [Infer-19-Survival-Analysis](Infer-19-Survival-Analysis.ipynb) |\n***\n" ] }, { @@ -68,129 +69,21 @@ "cell_type": "code", "execution_count": 1, "id": "ac22d2ac", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-14T23:03:29.168395Z", - "iopub.status.busy": "2026-07-14T23:03:29.156251Z", - "iopub.status.idle": "2026-07-14T23:03:35.967261Z", - "shell.execute_reply": "2026-07-14T23:03:35.959468Z" - } - }, + "metadata": {}, "outputs": [ { + "output_type": "display_data", "data": { - "text/html": [ - "\r\n", - "
\r\n", - " \r\n", - " \r\n", - "
" - ] + "text/html": "" }, - "metadata": {}, - "output_type": "display_data" + "metadata": {} }, { + "output_type": "display_data", "data": { - "text/html": [ - "
Installed Packages
  • Microsoft.ML.Probabilistic, 0.4.2504.701
  • Microsoft.ML.Probabilistic.Compiler, 0.4.2504.701
" - ] + "text/html": "
Installing Packages
  • Microsoft.ML.Probabilistic
  • Microsoft.ML.Probabilistic.Compiler
" }, - "metadata": {}, - "output_type": "display_data" + "metadata": {} } ], "source": [ @@ -214,21 +107,12 @@ "cell_type": "code", "execution_count": 2, "id": "26d51878", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-14T23:03:35.970935Z", - "iopub.status.busy": "2026-07-14T23:03:35.970460Z", - "iopub.status.idle": "2026-07-14T23:03:37.949989Z", - "shell.execute_reply": "2026-07-14T23:03:37.949646Z" - } - }, + "metadata": {}, "outputs": [ { - "name": "stdout", "output_type": "stream", - "text": [ - "Serie generee : N=100 points, vrai cp cache a t=50 (mu 2 -> 7, sigma=1,5)\r\n" - ] + "name": "stdout", + "text": "Serie generee : N=100 points, vrai cp cache a t=50 (mu 2 -> 7, sigma=1,5)\r\n" } ], "source": [ @@ -286,56 +170,37 @@ "cell_type": "code", "execution_count": 3, "id": "d80b88b4", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-14T23:03:37.952252Z", - "iopub.status.busy": "2026-07-14T23:03:37.951847Z", - "iopub.status.idle": "2026-07-14T23:03:44.017903Z", - "shell.execute_reply": "2026-07-14T23:03:44.017550Z" - } - }, + "metadata": {}, "outputs": [ { - "name": "stdout", "output_type": "stream", - "text": [ - "=== Posterieur sur le point de rupture cp ===\r\n" - ] + "name": "stdout", + "text": "=== Posterieur sur le point de rupture cp ===\r\n" }, { - "name": "stdout", "output_type": "stream", - "text": [ - " mode (argmax) = 50 (vrai cp cache = 50)\r\n" - ] + "name": "stdout", + "text": " mode (argmax) = 50 (vrai cp cache = 50)\r\n" }, { - "name": "stdout", "output_type": "stream", - "text": [ - " moyenne = 50,00\r\n" - ] + "name": "stdout", + "text": " moyenne = 50,00\r\n" }, { - "name": "stdout", "output_type": "stream", - "text": [ - "Moyenne avant (mu1) : 1,892 (vrai 2)\r\n" - ] + "name": "stdout", + "text": "Moyenne avant (mu1) : 1,892 (vrai 2)\r\n" }, { - "name": "stdout", "output_type": "stream", - "text": [ - "Moyenne apres (mu2) : 7,197 (vrai 7)\r\n" - ] + "name": "stdout", + "text": "Moyenne apres (mu2) : 7,197 (vrai 7)\r\n" }, { - "name": "stdout", "output_type": "stream", - "text": [ - "Precision commune : 0,550 (vrai 0,444)\r\n" - ] + "name": "stdout", + "text": "Precision commune : 0,550 (vrai 0,444)\r\n" } ], "source": [ @@ -387,51 +252,35 @@ "cell_type": "code", "execution_count": 4, "id": "e72abe1e", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-14T23:03:44.020017Z", - "iopub.status.busy": "2026-07-14T23:03:44.019726Z", - "iopub.status.idle": "2026-07-14T23:03:44.563888Z", - "shell.execute_reply": "2026-07-14T23:03:44.563482Z" - } - }, + "metadata": {}, "outputs": [ { - "name": "stdout", "output_type": "stream", - "text": [ - "Posterieur sur cp (masses > 0.1%, normalisees au pic) :\r\n" - ] + "name": "stdout", + "text": "Posterieur sur cp (masses > 0.1%, normalisees au pic) :\r\n" }, { - "name": "stdout", "output_type": "stream", - "text": [ - " t= 50 1,00 (masse 0,9978)\r\n" - ] + "name": "stdout", + "text": " t= 50 1,00 (masse 0,9978)\r\n" }, { - "name": "stdout", "output_type": "stream", - "text": [ - " t= 51 0,00 (masse 0,0022)\r\n" - ] + "name": "stdout", + "text": " t= 51 0,00 (masse 0,0022)\r\n" }, { - "name": "stdout", "output_type": "stream", - "text": [ - "\n", - "Mode (MAP, vrai point de rupture estime) : t=50 (masse 0,9978)\r\n" - ] + "name": "stdout", + "text": "\nMode (MAP, vrai point de rupture estime) : t=50 (masse 0,9978)\r\n" }, { + "output_type": "execute_result", "data": { "text/html": "Posterieur sur la localisation de la rupture cp (pic = mode/MAP)00.2690.5390.8081.0780123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899" }, - "execution_count": 4, "metadata": {}, - "output_type": "execute_result" + "execution_count": 4 } ], "source": [ @@ -481,49 +330,37 @@ "cell_type": "code", "execution_count": 5, "id": "7ba74aef", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-14T23:03:44.565990Z", - "iopub.status.busy": "2026-07-14T23:03:44.565647Z", - "iopub.status.idle": "2026-07-14T23:03:46.623040Z", - "shell.execute_reply": "2026-07-14T23:03:46.622477Z" - } - }, + "metadata": {}, "outputs": [ { - "name": "stdout", "output_type": "stream", - "text": [ - "Serie cataclysmes miniers : 112 annees (1851..1962)\r\n" - ] + "name": "stdout", + "text": "Serie cataclysmes miniers : 112 annees (1851..1962)\r\n" }, { - "name": "stdout", "output_type": "stream", - "text": [ - "Annee de rupture detectee : 1891 (indice 40)\r\n" - ] + "name": "stdout", + "text": "Annee de rupture detectee : 1891 (indice 40)\r\n" }, { - "name": "stdout", "output_type": "stream", - "text": [ - "Taux AVANT : 3,12 catastrophes/an\r\n" - ] + "name": "stdout", + "text": "Taux AVANT : 3,12 catastrophes/an\r\n" }, { - "name": "stdout", "output_type": "stream", - "text": [ - "Taux APRES : 0,94 catastrophes/an\r\n" - ] + "name": "stdout", + "text": "Taux APRES : 0,94 catastrophes/an\r\n" }, { + "output_type": "stream", "name": "stdout", + "text": "Rapport de taux : 3,3x\r\n" + }, + { "output_type": "stream", - "text": [ - "Rapport de taux : 3,3x\r\n" - ] + "name": "stdout", + "text": "Intervalle credible a 90 % : [1886..1892] (masse couverte 90,9 %)\r\n" } ], "source": [ @@ -574,7 +411,18 @@ "Console.WriteLine($\"Annee de rupture detectee : {1851 + modeCp2} (indice {modeCp2})\");\n", "Console.WriteLine($\"Taux AVANT : {tauxAvantPost.GetMean(),5:F2} catastrophes/an\");\n", "Console.WriteLine($\"Taux APRES : {tauxApresPost.GetMean(),5:F2} catastrophes/an\");\n", - "Console.WriteLine($\"Rapport de taux : {tauxAvantPost.GetMean() / Math.Max(1e-6, tauxApresPost.GetMean()):F1}x\");" + "Console.WriteLine($\"Rapport de taux : {tauxAvantPost.GetMean() / Math.Max(1e-6, tauxApresPost.GetMean()):F1}x\");", + "\n", + "// Intervalle credible a 90 % : plus petit intervalle contenant >= 90 % de la masse du posterior\n", + "int cpLo = modeCp2, cpHi = modeCp2;\n", + "double masse = cp2Post[cpLo];\n", + "while (masse < 0.90 && (cpLo > 0 || cpHi < N2 - 1)) {\n", + " double gauche = cpLo > 0 ? cp2Post[cpLo - 1] : -1.0;\n", + " double droite = cpHi < N2 - 1 ? cp2Post[cpHi + 1] : -1.0;\n", + " if (droite >= gauche) { cpHi++; masse += Math.Max(0.0, droite); }\n", + " else { cpLo--; masse += Math.Max(0.0, gauche); }\n", + "}\n", + "Console.WriteLine($\"Intervalle credible a 90 % : [{1851 + cpLo}..{1851 + cpHi}] (masse couverte {masse:P1})\");" ] }, { @@ -582,7 +430,7 @@ "id": "e5a20101", "metadata": {}, "source": [ - "**Lecture.** Le postérieur concentre la rupture autour de **1890–1891** — soit exactement\n", + "**Lecture.** Le mode du postérieur tombe en **1891**, intervalle crédible à 90 % **[1886..1892]** —\n", "la période des grandes réformes de sécurité dans les mines britanniques. Le taux passe de ~3,1 à\n", "~0,9 catastrophe par an, un rapport de ~3,3×. C'est le résultat historique de la littérature\n", "sur les modèles de point de rupture (Carlin, Gelfand & Smith 1992 ; PyMC en fait son exemple\n", @@ -606,67 +454,49 @@ "par son **entropie** $H$ (en bits) : $H_{\\max} = \\log_2 N$ pour un postérieur plat (aucune idée sur\n", "la localisation), $H \\to 0$ pour un postérieur piqué (localisation quasi certaine).\n", "\n", - "Le résultat est instructif : une **vraie** rupture fait chuter l'entropie vers 0, mais même du\n", - "pur bruit laisse l'entropie **nettement sous** le maximum — le modèle surajuste et « trouve » une\n", - "coupure spurieuse. Distinguer les deux exige donc plus que la seule concentration : un **Bayes\n", - "factor** (rapport de vraisemblance marginale) qui pénalise la flexibilité supplémentaire du modèle\n", - "à rupture (rasoir d'Occam) — c'est l'objet d'[Infer-10](Infer-10-Model-Selection.ipynb)." + "Question posée à la cellule suivante : la concentration du postérieur **suffit-elle** à conclure ?\n", + "Une **vraie** rupture doit faire chuter l'entropie vers 0 ; reste à mesurer ce que donne du pur\n", + "bruit. Si le bruit concentre lui aussi le postérieur, la seule concentration ne discrimine pas\n", + "les deux cas : il faut un **Bayes factor** (rapport de vraisemblance marginale), qui pénalise la\n", + "flexibilité supplémentaire du modèle à rupture (rasoir d'Occam) — c'est l'objet\n", + "d'[Infer-10](Infer-10-Model-Selection.ipynb)." ] }, { "cell_type": "code", "execution_count": 6, "id": "388b1f4b", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-14T23:03:46.626312Z", - "iopub.status.busy": "2026-07-14T23:03:46.625741Z", - "iopub.status.idle": "2026-07-14T23:03:49.208893Z", - "shell.execute_reply": "2026-07-14T23:03:49.208426Z" - } - }, + "metadata": {}, "outputs": [ { - "name": "stdout", "output_type": "stream", - "text": [ - "Serie AVEC rupture : H(cp) = 0,023 bits / 6,644 => information = 6,621 bits\r\n" - ] + "name": "stdout", + "text": "Serie AVEC rupture : H(cp) = 0,023 bits / 6,644 => information = 6,621 bits\r\n" }, { - "name": "stdout", "output_type": "stream", - "text": [ - "Controle SANS rupture : H(cp) = 0,686 bits / 6,644 => information = 5,957 bits\r\n" - ] + "name": "stdout", + "text": "Controle SANS rupture : H(cp) = 0,686 bits / 6,644 => information = 5,957 bits\r\n" }, { - "name": "stdout", "output_type": "stream", - "text": [ - "\r\n" - ] + "name": "stdout", + "text": "\r\n" }, { - "name": "stdout", "output_type": "stream", - "text": [ - "=> Vraie rupture : H -> 0 (localisation quasi certaine).\r\n" - ] + "name": "stdout", + "text": "=> Vraie rupture : H -> 0 (localisation quasi certaine).\r\n" }, { - "name": "stdout", "output_type": "stream", - "text": [ - "=> Pur bruit : H reste BAS mais non nul -- le modele SURAJUSTE une coupure spurieuse.\r\n" - ] + "name": "stdout", + "text": "=> Pur bruit : H reste BAS mais non nul -- le modele SURAJUSTE une coupure spurieuse.\r\n" }, { - "name": "stdout", "output_type": "stream", - "text": [ - "=> Test decisif (vraie rupture vs bruit) : Bayes factor (cf. Infer-8), pas la seule entropie.\r\n" - ] + "name": "stdout", + "text": "=> Test decisif (vraie rupture vs bruit) : Bayes factor (cf. Infer-10), pas la seule entropie.\r\n" } ], "source": [ @@ -704,7 +534,7 @@ "Console.WriteLine();\n", "Console.WriteLine(\"=> Vraie rupture : H -> 0 (localisation quasi certaine).\");\n", "Console.WriteLine(\"=> Pur bruit : H reste BAS mais non nul -- le modele SURAJUSTE une coupure spurieuse.\");\n", - "Console.WriteLine(\"=> Test decisif (vraie rupture vs bruit) : Bayes factor (cf. Infer-8), pas la seule entropie.\");" + "Console.WriteLine(\"=> Test decisif (vraie rupture vs bruit) : Bayes factor (cf. Infer-10), pas la seule entropie.\");" ] }, { @@ -761,21 +591,12 @@ "cell_type": "code", "execution_count": 7, "id": "8bdae4b6", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-14T23:03:49.211877Z", - "iopub.status.busy": "2026-07-14T23:03:49.211501Z", - "iopub.status.idle": "2026-07-14T23:03:49.318789Z", - "shell.execute_reply": "2026-07-14T23:03:49.318411Z" - } - }, + "metadata": {}, "outputs": [ { - "name": "stdout", "output_type": "stream", - "text": [ - "Exercice 1 a completer : deux points de rupture (trois regimes).\r\n" - ] + "name": "stdout", + "text": "Exercice 1 a completer : deux points de rupture (trois regimes).\r\n" } ], "source": [ @@ -807,21 +628,12 @@ "cell_type": "code", "execution_count": 8, "id": "99527d63", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-14T23:03:49.321244Z", - "iopub.status.busy": "2026-07-14T23:03:49.320861Z", - "iopub.status.idle": "2026-07-14T23:03:49.378785Z", - "shell.execute_reply": "2026-07-14T23:03:49.378248Z" - } - }, + "metadata": {}, "outputs": [ { - "name": "stdout", "output_type": "stream", - "text": [ - "Exercice 2 a completer : detection d'un changement de variance.\r\n" - ] + "name": "stdout", + "text": "Exercice 2 a completer : detection d'un changement de variance.\r\n" } ], "source": [ @@ -853,21 +665,12 @@ "cell_type": "code", "execution_count": 9, "id": "8fdbb0ca", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-14T23:03:49.381334Z", - "iopub.status.busy": "2026-07-14T23:03:49.380853Z", - "iopub.status.idle": "2026-07-14T23:03:49.432093Z", - "shell.execute_reply": "2026-07-14T23:03:49.431746Z" - } - }, + "metadata": {}, "outputs": [ { - "name": "stdout", "output_type": "stream", - "text": [ - "Exercice 3 a completer : robustesse du posterieur au signal et a la taille.\r\n" - ] + "name": "stdout", + "text": "Exercice 3 a completer : robustesse du posterieur au signal et a la taille.\r\n" } ], "source": [ diff --git a/MyIA.AI.Notebooks/Probas/Infer/Infer-19-Survival-Analysis.ipynb b/MyIA.AI.Notebooks/Probas/Infer/Infer-19-Survival-Analysis.ipynb index f2631f4b1e..cf218cdca3 100644 --- a/MyIA.AI.Notebooks/Probas/Infer/Infer-19-Survival-Analysis.ipynb +++ b/MyIA.AI.Notebooks/Probas/Infer/Infer-19-Survival-Analysis.ipynb @@ -18,7 +18,8 @@ "**Plan.** (1) Pourquoi un modèle dedie aux durees. (2) Le modèle exponentiel (cas conjugue).\n", "(3) Fonction de survie predictive en forme fermee. (4) Le modèle de Weibull via une astuce de\n", "transformee. (5) Sélection de la forme `k`. (6) Trois exercices (censure, regression AFT,\n", - "comparaison de modèles)." + "comparaison de modèles).", + "\n***\n## Navigation\n| Précédent | Suivant |\n|-----------|--------|\n| [Infer-18-Change-Point](Infer-18-Change-Point.ipynb) | - |\n***\n" ] }, { @@ -1060,7 +1061,7 @@ "\n", "Le test de coherence reussit sur les deux jeux :\n", "\n", - "- **Jeu Weibull** (`k = 1,8`) : le score est **minimal en `k = 1,8`** (`-466,7`), c'est-a-dire\n", + "- **Jeu Weibull** (`k = 1,8`) : le score est **maximal en `k = 1,8`** (`-466,7`), c'est-a-dire\n", " exactement la valeur vraie. Le pic est net (`k = 1,5` et `k = 2,0` sont déjà moins bons).\n", " La méthode retrouve la signature d'usure.\n", "- **Jeu exponentiel** (`k = 1`) : le meilleur est `k = 1,2` (`-486,8`), mais `k = 1,0` (`-488,5`)\n", @@ -1087,7 +1088,8 @@ "`f(c_i)` mais la survie `S(c_i) = exp(-lambda * c_i)`.\n", "\n", "Nous executons la comparaison complete sur le jeu exponentiel de la section 3, censuré a\n", - "`c* = 800` h — environ 45 % de censures :\n", + "`c* = 800` h : 44,9 % de censures attendues en théorie (`exp(-lambda·c*)`), 51,7 % sur cet\n", + " échantillon — l'écart est la variabilité d'échantillonnage :\n", "\n", "- **Modele naif** : les durees enregistrees (censures figees a `c*`) traitees comme des evenements\n", " exacts. Chaque censure est comptee comme une mort precoce : le taux estime **monte**, la survie\n", @@ -1230,6 +1232,20 @@ "Console.WriteLine($\" S_naif(1500) = {Math.Exp(-lambdaNaif * 1500):F3} (vraie survie = {Math.Exp(-lambdaTrue * 1500):F3})\");" ] }, + { + "cell_type": "markdown", + "id": "f3a19c72", + "metadata": {}, + "source": [ + "### Lecture : le prix du comptage naïf\n", + "\n", + "Le modèle naïf pousse `lambda` vers le haut (`0,001617` contre un vrai `0,001000`, un biais\n", + "de **1,62×**) et écrase la survie (`S_naif(1500) = 0,088` contre `0,223` pour la vérité) :\n", + "chaque censure enregistrée à `c*` est comptée comme une mort précoce. La correction n'exige\n", + "pas un nouveau moteur, elle exige la **bonne vraisemblance** — `S(c_i)` pour une censure,\n", + "`f(t_i)` pour un événement. C'est la cellule suivante qui la construit." + ] + }, { "cell_type": "code", "execution_count": 10, @@ -1490,8 +1506,11 @@ " modele parametrique, lui, extrapole (au prix de son hypothese de loi) — la complementarite\n", " non-parametrique/parametrique.\n", "- **Pont moteur** : PyMC-19 mene la meme comparaison cote NUTS — la censure y entre par un\n", - " `pm.Potential(-lambda * c_i)` ecrit a la main, ici par `Variable.ConstrainPositive`. Meme\n", - " protocole, meme forme fermee, deux machines." + " `pm.Potential(-lambda * c_i)` ecrit a la main. Cote Infer.NET, la meme vraisemblance passe par\n", + " la **forme exacte en statistiques suffisantes** (`Gamma(nObs, lambda)` observe au temps total a\n", + " risque, section 7) : la contrainte latente `T_i > c*` n'y est pas compilable, et la sufficience\n", + " porte la meme vraisemblance en restant conjuguee. Meme protocole, meme forme fermee, deux\n", + " chemins d'encodage de la censure." ] }, { diff --git a/MyIA.AI.Notebooks/Probas/Infer/README.md b/MyIA.AI.Notebooks/Probas/Infer/README.md index 10c9ddd879..bf625f25e1 100644 --- a/MyIA.AI.Notebooks/Probas/Infer/README.md +++ b/MyIA.AI.Notebooks/Probas/Infer/README.md @@ -67,7 +67,7 @@ Le trait distinctif d'Infer.NET : le modèle déclaratif est **compilé** (via R | 16 | [Infer-16-Sparse-Gaussian-Process](Infer-16-Sparse-Gaussian-Process.ipynb) | 55 min | Processus gaussiens, noyau RBF, classification non-linéaire, sparse GP | | 17 | [Infer-17-Kalman-Filter](Infer-17-Kalman-Filter.ipynb) | 55 min | Filtre de Kalman, système dynamique linéaire gaussien, conjugaison, EP exacte | | 18 | [Infer-18-Change-Point](Infer-18-Change-Point.ipynb) | 50 min | Détection de rupture, DiscreteUniform, ForEach + If/IfNot sur plage, EP, Poisson | -| 19 | [Infer-19-Survival-Analysis](Infer-19-Survival-Analysis.ipynb) | 50 min | Analyse de survie, Exponentielle conjugée (Gamma), Weibull par transformée, S(t) forme fermée, censure à droite exécutée (`ConstrainPositive` vs Kaplan–Meier) | +| 19 | [Infer-19-Survival-Analysis](Infer-19-Survival-Analysis.ipynb) | 50 min | Analyse de survie, Exponentielle conjuguée (Gamma), Weibull par transformée, S(t) forme fermée, censure à droite exécutée (statistiques suffisantes `Gamma(nObs, lambda)` vs Kaplan–Meier) | > **Théorie de la décision** : les notebooks de décision (utilité, EVPI, MDPs, Thompson Sampling, plus les companions Lean) forment désormais un arc autonome dans [`../DecisionTheory/DecInfer/`](../DecisionTheory/DecInfer/README.md), adossé au lake [`decision_theory_lean`](../DecisionTheory/decision_theory_lean/). @@ -815,12 +815,38 @@ Le **point de rupture** (*change-point*) modélise une série qui suit un régim | Sélection de régime | `Variable.If(block.Index <= cp)` / `IfNot` | Branche la vraisemblance avant/après la rupture | | Postérieur discret | `ie.Infer(cp)` | Vecteur de probabilités sur les $N$ positions | | Initialisation EP | `taux.InitialiseTo(...)` | EP déterministe : amorcer les taux évite les optima locaux | -| Concentration | entropie $H(\text{cp})$ | $H \to 0$ = rupture certaine ; le Bayes factor teste sa réalite (cf. Infer-8) | +| Concentration | entropie $H(\text{cp})$ | $H \to 0$ = rupture certaine ; le Bayes factor teste sa réalite (cf. Infer-10) | -**Positionnement** : [Infer-14](Infer-14-Sequences.ipynb) et [Infer-17](Infer-17-Kalman-Filter.ipynb) infèrent un état **récurrent** (un par pas) ; Infer-18 infère un **indice structurel unique** couplé à toute la plage — un usage du `If` sur une plage qu'aucun autre notebook n'exploite. Sur le cas gaussien, EP récupère le vrai point caché **exactement** (mode = 50, masse 0,998) ; sur les catastrophes minières (1851–1962), la rupture est datée à **1890–1891** (taux 3,1 → 0,9, rapport 3,3×), soit le résultat canonique de la littérature. Le notebook pointe aussi vers [Infer-5 (Causal)](Infer-5-Causal-Inference.ipynb) : un point de rupture est un changement de mécanisme générateur. +**Positionnement** : [Infer-14](Infer-14-Sequences.ipynb) et [Infer-17](Infer-17-Kalman-Filter.ipynb) infèrent un état **récurrent** (un par pas) ; Infer-18 infère un **indice structurel unique** couplé à toute la plage — un usage du `If` sur une plage qu'aucun autre notebook n'exploite. Sur le cas gaussien, EP récupère le vrai point caché **exactement** (mode = 50, masse 0,998) ; sur les catastrophes minières (1851–1962), le mode du postérieur tombe en **1891** (intervalle crédible à 90 % : **1886–1892** ; taux 3,1 → 0,9, rapport 3,3×), soit le résultat canonique de la littérature. Le notebook pointe aussi vers [Infer-5 (Causal)](Infer-5-Causal-Inference.ipynb) : un point de rupture est un changement de mécanisme générateur. **Applications** : contrôle qualité (dérive de production), finance (changement de régime de marché), épidémiologie, surveillance de capteurs, datation d'événements structurels en sciences sociales et climatiques. +### Infer-19 : Analyse de survie (time-to-event bayésien) + +L'**analyse de survie** modélise le **délai** qui sépare un instant initial d'un événement — panne, décès, désabonnement. Ce délai `T` est positif et asymétrique : une gaussienne est inadaptée, et la grandeur centrale n'est plus une moyenne mais la **fonction de survie** `S(t) = P(T > t)`. Le notebook construit la famille exponentielle/Weibull en restant **conjuguée** : l'exponentielle par conjugaison Gamma directe, la Weibull par **transformée de puissance** (`U = T^k ~ Exp(r)`), la forme `k` étant sélectionnée par balayage de log-vraisemblance prédictive. La **censure à droite** y est traitée sur un exemple **exécuté** : modèle naïf (biaissé), modèle corrigé par la **forme exacte en statistiques suffisantes** (`Gamma(nObs, lambda)` observée au temps total à risque — la contrainte latente `T_i > c*` n'étant pas compilable dans Infer.NET), confrontés à l'estimateur non paramétrique de Kaplan–Meier. + +**Durée** : 50 min + +**Objectifs** : + +- Modéliser un délai positif asymétrique et comprendre pourquoi la gaussienne échoue (temps négatifs, asymétrie, queue) +- Maîtriser le modèle exponentiel conjugué (postérieur Gamma fermé) et la survie prédictive en forme fermée +- Étendre à la Weibull par transformée de puissance et sélectionner la forme `k` par balayage de log-vraisemblance prédictive +- Traiter la **censure à droite** : le biais du modèle naïf, la correction par statistiques suffisantes, la confrontation à Kaplan–Meier + +**Concepts clés** : + +| Concept | API | Description | +| --------- | --------------- | ------------- | +| Conjugaison exponentielle | `Variable.GammaFromShapeAndRate` | Postérieur Gamma fermé sur le taux `lambda` | +| Transformée Weibull | `U = T^k ~ Exp(r)` | Ramène la Weibull au cas conjugué | +| Censure à droite | `Gamma(nObs, lambda)` observée au temps total à risque | Vraisemblance exacte en statistiques suffisantes (la contrainte `T_i > c*` n'est pas compilable) | +| Estimation non paramétrique | Kaplan–Meier (1958) | Escalier empirique adapté à la censure ; plat au-delà de `c*` | + +**Positionnement** : [Infer-17](Infer-17-Kalman-Filter.ipynb) et [Infer-18](Infer-18-Change-Point.ipynb) infèrent des paramètres d'un processus **observé partout** ; Infer-19 infère un **délai unique** par individu, **partiellement observé** (censure) — l'information manquante entre dans la vraisemblance au lieu d'être ignorée. PyMC-19 mène la même comparaison côté NUTS (censure par `pm.Potential`) : même protocole, même forme fermée, deux encodages du moteur. + +**Applications** : fiabilité industrielle (temps avant panne), essais cliniques (survie), analyse de churn client, garanties produits, maintenance prédictive. + --- ## Concepts Clés Infer.NET From 8aa340fc6295481d39de8d9a3a3b98f253902611 Mon Sep 17 00:00:00 2001 From: jsboige Date: Thu, 8 Oct 2026 03:10:56 +0200 Subject: [PATCH 2/6] fix(probas,#19814): rebaseline twin pairs Probas-18/19 (DRIFT introduced par c.108) Le CI gate Twin parity audit (#8057) detecte 2 paires en DRIFT introduites par cette PR (sha blob Python et C# changes par la nav-redaction c.108) : - Probas-18 Change-Point (python_sha 69e0d128, csharp_sha 56c6b6d0) - Probas-19 Survival-Analysis (python_sha 9644282a, csharp_sha cf218cdc) Rebaseline chirurgicale (L963/L974) via : python scripts/notebook_tools/check_twin_parity.py --update --pair 'Probas-18 Change-Point' --by 'myia-po-2024:CoursIA-2' python scripts/notebook_tools/check_twin_parity.py --update --pair 'Probas-19 Survival-Analysis' --by 'myia-po-2024:CoursIA-2' Aucune modification de fond sur les notebooks. Le rebaseline atteste la parite au nouveau SHA blob, ne la repare pas. Le strip outille (strip_probe_banner / strip_machine_paths / scrub_papermill) --update va en DERNIER (cf #8957). Co-Authored-By: Claude Haiku 4.5 (1M context) --- .../0009-2026-10-08-myia-po-2024-CoursIA-2.yaml | 6 ++++++ .../0008-2026-10-08-myia-po-2024-CoursIA-2.yaml | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 scripts/notebook_tools/twin_pairs.d/probas-18-change-point/0009-2026-10-08-myia-po-2024-CoursIA-2.yaml create mode 100644 scripts/notebook_tools/twin_pairs.d/probas-19-survival-analysis/0008-2026-10-08-myia-po-2024-CoursIA-2.yaml diff --git a/scripts/notebook_tools/twin_pairs.d/probas-18-change-point/0009-2026-10-08-myia-po-2024-CoursIA-2.yaml b/scripts/notebook_tools/twin_pairs.d/probas-18-change-point/0009-2026-10-08-myia-po-2024-CoursIA-2.yaml new file mode 100644 index 0000000000..2cdbf71477 --- /dev/null +++ b/scripts/notebook_tools/twin_pairs.d/probas-18-change-point/0009-2026-10-08-myia-po-2024-CoursIA-2.yaml @@ -0,0 +1,6 @@ +date: '2026-10-08' +by: myia-po-2024:CoursIA-2 +python_sha: 69e0d128ed5b33b32eb852d3668be941d9f2bb58 +csharp_sha: 56c6b6d08c621bb28d2c25946f3acc5d7da42f04 +content_python_sha: b58b150603924d25040b59b5f44d8fa019189e0853436e4c9cef02e66d4ebf95 +content_csharp_sha: 167e41016f50f5d88e73237f77d09c8c15cf66f8aac675d7b63e7f19cdad5b92 diff --git a/scripts/notebook_tools/twin_pairs.d/probas-19-survival-analysis/0008-2026-10-08-myia-po-2024-CoursIA-2.yaml b/scripts/notebook_tools/twin_pairs.d/probas-19-survival-analysis/0008-2026-10-08-myia-po-2024-CoursIA-2.yaml new file mode 100644 index 0000000000..aed0c422d2 --- /dev/null +++ b/scripts/notebook_tools/twin_pairs.d/probas-19-survival-analysis/0008-2026-10-08-myia-po-2024-CoursIA-2.yaml @@ -0,0 +1,6 @@ +date: '2026-10-08' +by: myia-po-2024:CoursIA-2 +python_sha: 9644282a311939ad230654749e04a2f872644c23 +csharp_sha: cf218cdca39d5e855d6eec947356f7eae6fb38a4 +content_python_sha: f85d60ca11ea1f14c45835aeefc676d543008e669cf52d17ba48241d6b338d95 +content_csharp_sha: 706c95ff6d3685d092bebbcb4d2e4ad961e3fd22e27e549539a1600047cfd4e4 From b2342e01a09c9546244a95f73dc6df9e67fa8fd9 Mon Sep 17 00:00:00 2001 From: jsboige Date: Fri, 9 Oct 2026 00:24:44 +0200 Subject: [PATCH 3/6] =?UTF-8?q?Fix(probas,#18556):=20Infer-18=20intervalle?= =?UTF-8?q?=20credible=20=E2=80=94=20recherche=20exacte=20remplace=20le=20?= =?UTF-8?q?glouton?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review ai-01 sur #19814 : l'expansion gloutonne depuis le mode ne garantit pas la minimalite — contre-exemple p=[.25,.25,.01,.40,.09] (glouton rend [0..4] masse 1 largeur 5 ; minimal = [0..3] masse .91 largeur 4). Remplacee par un balayage exact des intervalles contigus (O(N^2), N=112) avec temoin embarque sur le contre-exemple. Re-exec dotnet_executor : 9/9 cellules, 0 erreur, 18.3 s. Temoin rend [0..3] masse 0.91 ; intervalle reel [1886..1892] / 90.9% — inchange (posterior reel unimodal), les 5 valeurs de la Lecture restent ancrees. Co-Authored-By: Claude Sonnet 5.5 --- .../Probas/Infer/Infer-18-Change-Point.ipynb | 47 ++++++++++++++----- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/MyIA.AI.Notebooks/Probas/Infer/Infer-18-Change-Point.ipynb b/MyIA.AI.Notebooks/Probas/Infer/Infer-18-Change-Point.ipynb index 56c6b6d08c..3abceb8a91 100644 --- a/MyIA.AI.Notebooks/Probas/Infer/Infer-18-Change-Point.ipynb +++ b/MyIA.AI.Notebooks/Probas/Infer/Infer-18-Change-Point.ipynb @@ -357,6 +357,11 @@ "name": "stdout", "text": "Rapport de taux : 3,3x\r\n" }, + { + "output_type": "stream", + "name": "stdout", + "text": "Temoin de minimalite : p=[.25,.25,.01,.40,.09] -> [0..3] masse 0,91 (l'expansion gloutonne depuis le mode rendait [0..4], largeur 5)\r\n" + }, { "output_type": "stream", "name": "stdout", @@ -411,17 +416,37 @@ "Console.WriteLine($\"Annee de rupture detectee : {1851 + modeCp2} (indice {modeCp2})\");\n", "Console.WriteLine($\"Taux AVANT : {tauxAvantPost.GetMean(),5:F2} catastrophes/an\");\n", "Console.WriteLine($\"Taux APRES : {tauxApresPost.GetMean(),5:F2} catastrophes/an\");\n", - "Console.WriteLine($\"Rapport de taux : {tauxAvantPost.GetMean() / Math.Max(1e-6, tauxApresPost.GetMean()):F1}x\");", - "\n", - "// Intervalle credible a 90 % : plus petit intervalle contenant >= 90 % de la masse du posterior\n", - "int cpLo = modeCp2, cpHi = modeCp2;\n", - "double masse = cp2Post[cpLo];\n", - "while (masse < 0.90 && (cpLo > 0 || cpHi < N2 - 1)) {\n", - " double gauche = cpLo > 0 ? cp2Post[cpLo - 1] : -1.0;\n", - " double droite = cpHi < N2 - 1 ? cp2Post[cpHi + 1] : -1.0;\n", - " if (droite >= gauche) { cpHi++; masse += Math.Max(0.0, droite); }\n", - " else { cpLo--; masse += Math.Max(0.0, gauche); }\n", + "Console.WriteLine($\"Rapport de taux : {tauxAvantPost.GetMean() / Math.Max(1e-6, tauxApresPost.GetMean()):F1}x\");\n", + "// Intervalle credible a 90 % : plus petit intervalle CONTIGU contenant >= 90 % de la masse.\n", + "// Recherche exacte par balayage des intervalles contigus (O(N^2), N = 112 ici) :\n", + "// l'expansion gloutonne depuis le mode ne garantit PAS la minimalite -- sur\n", + "// p = [.25, .25, .01, .40, .09] elle achete le .09 de droite avant les .25 de gauche\n", + "// et rend [0..4] (masse 1, largeur 5) alors que [0..3] (masse .91, largeur 4) est\n", + "// l'intervalle minimal. A largeur egale, on garde la masse la plus elevee.\n", + "(int lo, int hi, double m) IntervalleMinimalExact(int n, Func masseDe, double seuil) {\n", + " int bLo = 0, bHi = n - 1; double bMasse = 0.0;\n", + " for (int lo = 0; lo < n; lo++) {\n", + " double cumul = 0.0;\n", + " for (int hi = lo; hi < n; hi++) {\n", + " cumul += masseDe(hi);\n", + " if (cumul >= seuil) {\n", + " if (hi - lo < bHi - bLo || (hi - lo == bHi - bLo && cumul > bMasse)) {\n", + " bLo = lo; bHi = hi; bMasse = cumul;\n", + " }\n", + " break; // elargir hi depuis ce lo ne fait qu'augmenter la largeur\n", + " }\n", + " }\n", + " }\n", + " return (bLo, bHi, bMasse);\n", "}\n", + "\n", + "// Temoin de minimalite (contre-exemple ci-dessus, attendu : [0..3] masse .91) :\n", + "var pTemoin = new double[] { .25, .25, .01, .40, .09 };\n", + "var (tLo, tHi, tM) = IntervalleMinimalExact(pTemoin.Length, i => pTemoin[i], 0.90);\n", + "Console.WriteLine($\"Temoin de minimalite : p=[.25,.25,.01,.40,.09] -> [{tLo}..{tHi}] masse {tM:F2}\"\n", + " + \" (l'expansion gloutonne depuis le mode rendait [0..4], largeur 5)\");\n", + "\n", + "var (cpLo, cpHi, masse) = IntervalleMinimalExact(N2, i => cp2Post[i], 0.90);\n", "Console.WriteLine($\"Intervalle credible a 90 % : [{1851 + cpLo}..{1851 + cpHi}] (masse couverte {masse:P1})\");" ] }, @@ -767,4 +792,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file From 1597bf453349796d381eb3b7356587206a57d19f Mon Sep 17 00:00:00 2001 From: jsboige Date: Fri, 9 Oct 2026 01:05:54 +0200 Subject: [PATCH 4/6] fix(twin,#18556): rebaseline twin probas-18 apres recherche exacte de l'intervalle L'organe check_twin_parity rendait DRIFT (cote C# seulement) depuis le remplacement de l'expansion gloutonne par la recherche exhaustive de l'intervalle contigu minimal (revue #19814). Le cote Python est inchange (PyMC-18 utilise az.summary/HDI, sans objet ici) : la derive est donc attendue et se declare, elle ne se corrige pas. Attestation 0010 ecrite par l'organe (--update --pair), pas a la main. See #19814 --- .../0010-2026-10-09-myia-po-2027-CoursIA.yaml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 scripts/notebook_tools/twin_pairs.d/probas-18-change-point/0010-2026-10-09-myia-po-2027-CoursIA.yaml diff --git a/scripts/notebook_tools/twin_pairs.d/probas-18-change-point/0010-2026-10-09-myia-po-2027-CoursIA.yaml b/scripts/notebook_tools/twin_pairs.d/probas-18-change-point/0010-2026-10-09-myia-po-2027-CoursIA.yaml new file mode 100644 index 0000000000..fe50faf4cb --- /dev/null +++ b/scripts/notebook_tools/twin_pairs.d/probas-18-change-point/0010-2026-10-09-myia-po-2027-CoursIA.yaml @@ -0,0 +1,6 @@ +date: '2026-10-09' +by: myia-po-2027:CoursIA +python_sha: 69e0d128ed5b33b32eb852d3668be941d9f2bb58 +csharp_sha: 3abceb8a911996c60057bdba548436313c45bcaf +content_python_sha: b58b150603924d25040b59b5f44d8fa019189e0853436e4c9cef02e66d4ebf95 +content_csharp_sha: 8016c218a16f80a849588f7f0e0e1f6ffd02d6e5c5472e02b280084831d5306d From 1d8bae4ae5ce7357fea89765a8b84a259659f554 Mon Sep 17 00:00:00 2001 From: jsboige Date: Fri, 9 Oct 2026 13:48:55 +0200 Subject: [PATCH 5/6] fix(twin,#18556): decollision index 0009 probas-18 -- 0010->0011 po-2027, 0009->0010 po-2024 CoursIA-2 Geste coordonne (DM c1115) : main porte 0009-...CoursIA.yaml depuis le merge de la PR soeur ; l'attestation po-2027 passe a 0011, celle de po-2024 CoursIA-2 a 0010. Renommages purs, aucun contenu touche. Co-Authored-By: Claude Sonnet 5.5 --- ...CoursIA-2.yaml => 0010-2026-10-08-myia-po-2024-CoursIA-2.yaml} | 0 ...027-CoursIA.yaml => 0011-2026-10-09-myia-po-2027-CoursIA.yaml} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename scripts/notebook_tools/twin_pairs.d/probas-18-change-point/{0009-2026-10-08-myia-po-2024-CoursIA-2.yaml => 0010-2026-10-08-myia-po-2024-CoursIA-2.yaml} (100%) rename scripts/notebook_tools/twin_pairs.d/probas-18-change-point/{0010-2026-10-09-myia-po-2027-CoursIA.yaml => 0011-2026-10-09-myia-po-2027-CoursIA.yaml} (100%) diff --git a/scripts/notebook_tools/twin_pairs.d/probas-18-change-point/0009-2026-10-08-myia-po-2024-CoursIA-2.yaml b/scripts/notebook_tools/twin_pairs.d/probas-18-change-point/0010-2026-10-08-myia-po-2024-CoursIA-2.yaml similarity index 100% rename from scripts/notebook_tools/twin_pairs.d/probas-18-change-point/0009-2026-10-08-myia-po-2024-CoursIA-2.yaml rename to scripts/notebook_tools/twin_pairs.d/probas-18-change-point/0010-2026-10-08-myia-po-2024-CoursIA-2.yaml diff --git a/scripts/notebook_tools/twin_pairs.d/probas-18-change-point/0010-2026-10-09-myia-po-2027-CoursIA.yaml b/scripts/notebook_tools/twin_pairs.d/probas-18-change-point/0011-2026-10-09-myia-po-2027-CoursIA.yaml similarity index 100% rename from scripts/notebook_tools/twin_pairs.d/probas-18-change-point/0010-2026-10-09-myia-po-2027-CoursIA.yaml rename to scripts/notebook_tools/twin_pairs.d/probas-18-change-point/0011-2026-10-09-myia-po-2027-CoursIA.yaml From 8ed0125fc9fced4ede1a1bae0642a220c4fb10c8 Mon Sep 17 00:00:00 2001 From: jsboige Date: Fri, 9 Oct 2026 18:01:39 +0200 Subject: [PATCH 6/6] fix(twin,#19814): re-attestation Probas-18 Change-Point -- python_sha perime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le gate `Twin parity audit` refusait la tete de cette PR sur « 1 paire mise en DRIFT par cette PR ». Diagnostic mesure avant toute ecriture : c'est un **faux « drift introduit »**, et la cause est dans l'attestation, pas dans un carnet. Mesure (organe git autoritatif, pas `hash-object`) : | cote | blob a `origin/main` | blob a la tete | attestation 0009 (main) | attestation 0011 (tete) | |---|---|---|---|---| | Python `PyMC-18-Change-Point.ipynb` | `3526999f` | `3526999f` | `3526999f` OK | `69e0d128` **perime** | | C# `Infer-18-Change-Point.ipynb` | `0e0ced31` | `3abceb8a` | `0e0ced31` OK | `3abceb8a` OK | Le carnet **Python n'est pas touche par cette PR** : son blob est identique a la base (`3526999f` des deux cotes). Seul le cote C# a change, et legitiment (tranche 18 de #18556 : intervalle credible mesure, censure decrite par son chemin execute). La paire etait donc **saine a la base** et l'est restee ; ce que la tete apportait, c'etait une attestation `0011` portant un `python_sha` **anterieur a l'avancee de `main`** — un SHA releve avant que la base ne bouge. Re-attestation (`--update --pair`, l'attestation precedente reste dans l'historique du registre, qui est append-only) : `0012` enregistre les SHAs courants, verifies contre `git hash-object` apres ecriture — `python_sha 3526999f` et `csharp_sha 3abceb8a` correspondent tous deux aux blobs reels. L'ordre est celui que le gate exige (#8957) : **attester en dernier**. Aucune normalisation outillee n'etait en attente sur ce carnet au moment de l'ecriture — `probeAddresses`, `probingAddresses`, `loadDotnetInteractiveApi`, `C:\Users`, `.nuget` et `papermill` rendent tous **0** occurrence ; un strip posterieur deplacerait le blob et invaliderait cette attestation. Co-Authored-By: Claude Sonnet 5.5 --- .../0012-2026-10-09-myia-po-2027-CoursIA.yaml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 scripts/notebook_tools/twin_pairs.d/probas-18-change-point/0012-2026-10-09-myia-po-2027-CoursIA.yaml diff --git a/scripts/notebook_tools/twin_pairs.d/probas-18-change-point/0012-2026-10-09-myia-po-2027-CoursIA.yaml b/scripts/notebook_tools/twin_pairs.d/probas-18-change-point/0012-2026-10-09-myia-po-2027-CoursIA.yaml new file mode 100644 index 0000000000..f01fa7a7e7 --- /dev/null +++ b/scripts/notebook_tools/twin_pairs.d/probas-18-change-point/0012-2026-10-09-myia-po-2027-CoursIA.yaml @@ -0,0 +1,6 @@ +date: '2026-10-09' +by: myia-po-2027:CoursIA +python_sha: 3526999f14a350a16f4ff3f8c4b76af719e52349 +csharp_sha: 3abceb8a911996c60057bdba548436313c45bcaf +content_python_sha: 38e0f56b3d5bb83c045abcb537dde4131f2400dfdf276fd7ca6e699b8d7b5eec +content_csharp_sha: 8016c218a16f80a849588f7f0e0e1f6ffd02d6e5c5472e02b280084831d5306d