From f69096429f31d612835abd8e0222022541d4f607 Mon Sep 17 00:00:00 2001 From: jsboige Date: Thu, 10 Sep 2026 07:54:39 +0200 Subject: [PATCH 1/5] chore(notebooks,#14209): tranche T7 du split -- normalisation source str->list sur GameTheory/Sudoku/RL/QC See #15429 (T7/T8). Contenu identique cellule par cellule : 10 conversions str->list verifiees par script (source joinee == source str d'origine, zero drift outputs/execution_count/metadata). Reprise du contenu de chore/14209-normalize-residue a1e83ff941 sur origin/main frais. Co-Authored-By: Claude Sonnet 5 --- .../GameTheory-10-ForwardInduction-SPE.ipynb | 27 +++- .../projects/Research-Executor/runner.ipynb | 77 +++++++++- .../research/research_m11ef_ensemble.ipynb | 140 +++++++++++++++++- .../research/research_rl_multi_asset.ipynb | 89 ++++++++++- .../RL/rl_12_distributional_rl.ipynb | 12 +- .../Sudoku/Sudoku-14-BDD-Csharp.ipynb | 23 ++- 6 files changed, 352 insertions(+), 16 deletions(-) diff --git a/MyIA.AI.Notebooks/GameTheory/GameTheory-10-ForwardInduction-SPE.ipynb b/MyIA.AI.Notebooks/GameTheory/GameTheory-10-ForwardInduction-SPE.ipynb index e0fbc58833..a15dbaa3ac 100644 --- a/MyIA.AI.Notebooks/GameTheory/GameTheory-10-ForwardInduction-SPE.ipynb +++ b/MyIA.AI.Notebooks/GameTheory/GameTheory-10-ForwardInduction-SPE.ipynb @@ -2065,13 +2065,34 @@ "cell_type": "markdown", "id": "2277837f", "metadata": {}, - "source": "**Lien avec la formalisation Lean**\n\nL'equilibre de sous-jeux parfaits (SPE) raffine le concept d'equilibre de Nash en imposant la rationalite dans chaque sous-jeu. La structure `GameTree` et l'algorithme de minimax dans [`lean_game_defs/Combinatorial.lean`](lean_game_defs/Combinatorial.lean) formalisent l'induction a rebours pour les jeux a information parfaite -- le cas particulier zero-somme du backward induction que SPE generalise. Les concepts de forward induction et de perfection en main tremblante restent a formaliser." + "source": [ + "**Lien avec la formalisation Lean**\n", + "\n", + "L'equilibre de sous-jeux parfaits (SPE) raffine le concept d'equilibre de Nash en imposant la rationalite dans chaque sous-jeu. La structure `GameTree` et l'algorithme de minimax dans [`lean_game_defs/Combinatorial.lean`](lean_game_defs/Combinatorial.lean) formalisent l'induction a rebours pour les jeux a information parfaite -- le cas particulier zero-somme du backward induction que SPE generalise. Les concepts de forward induction et de perfection en main tremblante restent a formaliser." + ] }, { "cell_type": "markdown", "id": "cfa25ede", "metadata": {}, - "source": "## Resume et perspectives\n\nCe notebook a constitue une étude approfondie des raffinements d'equilibre pour les jeux sous forme extensive, en progressant dans une hiérarchie de pouvoir discriminant croissant. L'equilibre parfait de sous-jeux (SPE) a elimine les menaces non credibles en exigeant un Nash dans chaque sous-jeu, comme l'a illustre le jeu d'entree ou la menace de combat n'est pas soutenable. La perfection en mains tremblantes a renforce cette sélection en eliminant les equilibres reposant sur des stratégies faiblement dominees, ne conservant que les profils robustes aux petites erreurs. L'induction avant a introduit un raisonnement d'une nature différente : plutot que de partir de la fin du jeu, elle exploite les actions observees en debut de partie pour inferer les intentions des joueurs, selectionnant ainsi l'equilibre efficient dans la Chasse au Cerf avec option exterieure.\n\nLe jeu du \"Burn Money\" a synthetise ces concepts de maniere frappante : la simple possibilite de bruler de l'argent modifie l'equilibre même si personne ne brule effectivement, un phenomene qui rappelle que les options non utilisees peuvent etre aussi informatives que les actions realisees. La hiérarchie des raffinements -- Nash, SPE, trembling-hand, forward induction -- forme un outil d'analyse progressif dont le choix depend du contexte et de la richesse informationnelle du jeu etudie.\n\nL'étape suivante introduit les jeux bayesiens et l'information incomplete, ou les joueurs ne connaissent pas les types de leurs adversaires : [GameTheory-11-BayesianGames](GameTheory-11-BayesianGames.ipynb).\n\n\n\n### References academiques\n\n- Selten, R. (1965). Spieltheoretische Behandlung eines Oligopolmodells mit Nachfragetragheit. Jahrbucher fur Nationalokonomie und Statistik 183:301-324.\n- Selten, R. (1975). Reexamination of the Perfectness Concept for Equilibrium Points in Extensive Games. International Journal of Game Theory 4(1):25-55.\n- Kohlberg, E. & Mertens, J.-F. (1986). On the Strategic Stability of Equilibria. Journal of Economic Theory 41(2):353-379.\n\n" + "source": [ + "## Resume et perspectives\n", + "\n", + "Ce notebook a constitue une étude approfondie des raffinements d'equilibre pour les jeux sous forme extensive, en progressant dans une hiérarchie de pouvoir discriminant croissant. L'equilibre parfait de sous-jeux (SPE) a elimine les menaces non credibles en exigeant un Nash dans chaque sous-jeu, comme l'a illustre le jeu d'entree ou la menace de combat n'est pas soutenable. La perfection en mains tremblantes a renforce cette sélection en eliminant les equilibres reposant sur des stratégies faiblement dominees, ne conservant que les profils robustes aux petites erreurs. L'induction avant a introduit un raisonnement d'une nature différente : plutot que de partir de la fin du jeu, elle exploite les actions observees en debut de partie pour inferer les intentions des joueurs, selectionnant ainsi l'equilibre efficient dans la Chasse au Cerf avec option exterieure.\n", + "\n", + "Le jeu du \"Burn Money\" a synthetise ces concepts de maniere frappante : la simple possibilite de bruler de l'argent modifie l'equilibre même si personne ne brule effectivement, un phenomene qui rappelle que les options non utilisees peuvent etre aussi informatives que les actions realisees. La hiérarchie des raffinements -- Nash, SPE, trembling-hand, forward induction -- forme un outil d'analyse progressif dont le choix depend du contexte et de la richesse informationnelle du jeu etudie.\n", + "\n", + "L'étape suivante introduit les jeux bayesiens et l'information incomplete, ou les joueurs ne connaissent pas les types de leurs adversaires : [GameTheory-11-BayesianGames](GameTheory-11-BayesianGames.ipynb).\n", + "\n", + "\n", + "\n", + "### References academiques\n", + "\n", + "- Selten, R. (1965). Spieltheoretische Behandlung eines Oligopolmodells mit Nachfragetragheit. Jahrbucher fur Nationalokonomie und Statistik 183:301-324.\n", + "- Selten, R. (1975). Reexamination of the Perfectness Concept for Equilibrium Points in Extensive Games. International Journal of Game Theory 4(1):25-55.\n", + "- Kohlberg, E. & Mertens, J.-F. (1986). On the Strategic Stability of Equilibria. Journal of Economic Theory 41(2):353-379.\n", + "\n" + ] } ], "metadata": { @@ -2120,4 +2141,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/MyIA.AI.Notebooks/QuantConnect/projects/Research-Executor/runner.ipynb b/MyIA.AI.Notebooks/QuantConnect/projects/Research-Executor/runner.ipynb index 95ed8fa321..e49fc63347 100644 --- a/MyIA.AI.Notebooks/QuantConnect/projects/Research-Executor/runner.ipynb +++ b/MyIA.AI.Notebooks/QuantConnect/projects/Research-Executor/runner.ipynb @@ -12,7 +12,80 @@ } }, "outputs": [], - "source": "import json\nimport sys\nimport os\nfrom io import StringIO\nimport traceback\n\ncwd = os.getcwd()\nfiles = sorted(os.listdir('.'))\nprint(f'Working directory: {cwd}')\nprint(f'Files: {files}')\n\nnotebooks = [\n 'research_asset_class_momentum.ipynb',\n 'research_volatility_regime_ml.ipynb',\n 'research_defensive_etf_rotation.ipynb',\n 'research_commodity_term_structure.ipynb',\n 'research_piotroski_fscore.ipynb',\n 'research_long_short_harvest.ipynb',\n 'research_macro_factor_rotation.ipynb',\n 'research_puppies_of_dow.ipynb',\n]\n\nfor nb_name in notebooks:\n print(f'\\n{\"=\" * 60}')\n print(f'Processing: {nb_name}')\n try:\n with open(nb_name, 'r') as f:\n nb = json.load(f)\n except FileNotFoundError:\n print(f'SKIP: {nb_name} not found')\n continue\n exec_count = 0\n success = 0\n errors = 0\n for cell in nb['cells']:\n if cell['cell_type'] != 'code':\n continue\n exec_count += 1\n code = ''.join(cell['source'])\n old_stdout, old_stderr = sys.stdout, sys.stderr\n sys.stdout, sys.stderr = StringIO(), StringIO()\n try:\n exec(code, globals())\n cell['execution_count'] = exec_count\n out = sys.stdout.getvalue()\n err = sys.stderr.getvalue()\n outputs = []\n if out:\n lines = out.splitlines(True)\n if lines and not lines[-1].endswith('\\n'):\n lines[-1] += '\\n'\n outputs.append({'output_type': 'stream', 'name': 'stdout', 'text': lines})\n if err:\n outputs.append({'output_type': 'stream', 'name': 'stderr', 'text': err.splitlines(True)})\n cell['outputs'] = outputs\n success += 1\n except Exception as e:\n cell['execution_count'] = exec_count\n cell['outputs'] = [{\n 'output_type': 'error',\n 'ename': type(e).__name__,\n 'evalue': str(e),\n 'traceback': [traceback.format_exc()]\n }]\n errors += 1\n finally:\n sys.stdout, sys.stderr = old_stdout, old_stderr\n out_name = nb_name.replace('.ipynb', '_executed.ipynb')\n with open(out_name, 'w') as f:\n json.dump(nb, f, indent=1)\n print(f' {success} OK, {errors} ERR -> {out_name}')\nprint('\\n\\nALL DONE')" + "source": [ + "import json\n", + "import sys\n", + "import os\n", + "from io import StringIO\n", + "import traceback\n", + "\n", + "cwd = os.getcwd()\n", + "files = sorted(os.listdir('.'))\n", + "print(f'Working directory: {cwd}')\n", + "print(f'Files: {files}')\n", + "\n", + "notebooks = [\n", + " 'research_asset_class_momentum.ipynb',\n", + " 'research_volatility_regime_ml.ipynb',\n", + " 'research_defensive_etf_rotation.ipynb',\n", + " 'research_commodity_term_structure.ipynb',\n", + " 'research_piotroski_fscore.ipynb',\n", + " 'research_long_short_harvest.ipynb',\n", + " 'research_macro_factor_rotation.ipynb',\n", + " 'research_puppies_of_dow.ipynb',\n", + "]\n", + "\n", + "for nb_name in notebooks:\n", + " print(f'\\n{\"=\" * 60}')\n", + " print(f'Processing: {nb_name}')\n", + " try:\n", + " with open(nb_name, 'r') as f:\n", + " nb = json.load(f)\n", + " except FileNotFoundError:\n", + " print(f'SKIP: {nb_name} not found')\n", + " continue\n", + " exec_count = 0\n", + " success = 0\n", + " errors = 0\n", + " for cell in nb['cells']:\n", + " if cell['cell_type'] != 'code':\n", + " continue\n", + " exec_count += 1\n", + " code = ''.join(cell['source'])\n", + " old_stdout, old_stderr = sys.stdout, sys.stderr\n", + " sys.stdout, sys.stderr = StringIO(), StringIO()\n", + " try:\n", + " exec(code, globals())\n", + " cell['execution_count'] = exec_count\n", + " out = sys.stdout.getvalue()\n", + " err = sys.stderr.getvalue()\n", + " outputs = []\n", + " if out:\n", + " lines = out.splitlines(True)\n", + " if lines and not lines[-1].endswith('\\n'):\n", + " lines[-1] += '\\n'\n", + " outputs.append({'output_type': 'stream', 'name': 'stdout', 'text': lines})\n", + " if err:\n", + " outputs.append({'output_type': 'stream', 'name': 'stderr', 'text': err.splitlines(True)})\n", + " cell['outputs'] = outputs\n", + " success += 1\n", + " except Exception as e:\n", + " cell['execution_count'] = exec_count\n", + " cell['outputs'] = [{\n", + " 'output_type': 'error',\n", + " 'ename': type(e).__name__,\n", + " 'evalue': str(e),\n", + " 'traceback': [traceback.format_exc()]\n", + " }]\n", + " errors += 1\n", + " finally:\n", + " sys.stdout, sys.stderr = old_stdout, old_stderr\n", + " out_name = nb_name.replace('.ipynb', '_executed.ipynb')\n", + " with open(out_name, 'w') as f:\n", + " json.dump(nb, f, indent=1)\n", + " print(f' {success} OK, {errors} ERR -> {out_name}')\n", + "print('\\n\\nALL DONE')" + ] } ], "metadata": { @@ -36,4 +109,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file diff --git a/MyIA.AI.Notebooks/QuantConnect/research/research_m11ef_ensemble.ipynb b/MyIA.AI.Notebooks/QuantConnect/research/research_m11ef_ensemble.ipynb index b39514d846..21b392cd56 100644 --- a/MyIA.AI.Notebooks/QuantConnect/research/research_m11ef_ensemble.ipynb +++ b/MyIA.AI.Notebooks/QuantConnect/research/research_m11ef_ensemble.ipynb @@ -121,7 +121,67 @@ ] } ], - "source": "# Section 3: Kelly threshold-band position sizing\ndef threshold_band_signal(close_prices, window=60):\n \"\"\"Generate signal: long if price > rolling mean, short otherwise.\"\"\"\n prices = pd.Series(close_prices)\n rolling_mean = prices.rolling(window).mean()\n signal = np.where(prices.values > rolling_mean.values, 1.0, 0.0)\n return signal\n\ndef kelly_sleeve(close_prices, cap=0.6, window=60, fee_bps=50):\n \"\"\"Single Kelly sleeve with threshold-band signal.\"\"\"\n prices = pd.Series(close_prices)\n returns = prices.pct_change().dropna().values\n\n # Signal aligned to returns length (returns starts at index 1)\n signal_full = threshold_band_signal(close_prices, window)\n signal = signal_full[1:][:len(returns)] # skip first element (no return), trim to match\n\n # Rolling vol for Kelly sizing\n rolling_vol = pd.Series(returns).rolling(window).std().values * np.sqrt(252)\n rolling_vol = np.where(rolling_vol < 0.01, 0.2, rolling_vol) # floor\n\n # Kelly weight: cap * (signal / vol)\n kelly_w = np.minimum(cap / rolling_vol, cap) * signal\n\n # Net returns\n weighted_returns = kelly_w * returns\n\n # Fee deduction\n fee = fee_bps / 10000\n trades = np.abs(np.diff(np.concatenate([[0], kelly_w])))\n fee_costs = np.concatenate([[0], trades * fee])[:len(weighted_returns)]\n net_returns = weighted_returns - fee_costs\n\n sharpe = np.mean(net_returns) / (np.std(net_returns) + 1e-10) * np.sqrt(252)\n return sharpe, net_returns\n\ndef vol_targeting_sleeve(close_prices, target_vol=0.15, window=60, fee_bps=50):\n \"\"\"Vol-targeting sleeve: scale position to maintain target volatility.\"\"\"\n prices = pd.Series(close_prices)\n returns = prices.pct_change().dropna().values\n\n rolling_vol = pd.Series(returns).rolling(window).std().values * np.sqrt(252)\n rolling_vol = np.where(rolling_vol < 0.01, 0.2, rolling_vol)\n\n # Scale to target vol\n scale = target_vol / rolling_vol\n scale = np.minimum(scale, 2.0) # cap leverage\n\n weighted_returns = scale * returns\n\n fee = fee_bps / 10000\n trades = np.abs(np.diff(np.concatenate([[0], scale])))\n fee_costs = np.concatenate([[0], trades * fee])[:len(weighted_returns)]\n net_returns = weighted_returns - fee_costs\n\n sharpe = np.mean(net_returns) / (np.std(net_returns) + 1e-10) * np.sqrt(252)\n return sharpe, net_returns\n\nprint(\"Sleeve functions defined\")", + "source": [ + "# Section 3: Kelly threshold-band position sizing\n", + "def threshold_band_signal(close_prices, window=60):\n", + " \"\"\"Generate signal: long if price > rolling mean, short otherwise.\"\"\"\n", + " prices = pd.Series(close_prices)\n", + " rolling_mean = prices.rolling(window).mean()\n", + " signal = np.where(prices.values > rolling_mean.values, 1.0, 0.0)\n", + " return signal\n", + "\n", + "def kelly_sleeve(close_prices, cap=0.6, window=60, fee_bps=50):\n", + " \"\"\"Single Kelly sleeve with threshold-band signal.\"\"\"\n", + " prices = pd.Series(close_prices)\n", + " returns = prices.pct_change().dropna().values\n", + "\n", + " # Signal aligned to returns length (returns starts at index 1)\n", + " signal_full = threshold_band_signal(close_prices, window)\n", + " signal = signal_full[1:][:len(returns)] # skip first element (no return), trim to match\n", + "\n", + " # Rolling vol for Kelly sizing\n", + " rolling_vol = pd.Series(returns).rolling(window).std().values * np.sqrt(252)\n", + " rolling_vol = np.where(rolling_vol < 0.01, 0.2, rolling_vol) # floor\n", + "\n", + " # Kelly weight: cap * (signal / vol)\n", + " kelly_w = np.minimum(cap / rolling_vol, cap) * signal\n", + "\n", + " # Net returns\n", + " weighted_returns = kelly_w * returns\n", + "\n", + " # Fee deduction\n", + " fee = fee_bps / 10000\n", + " trades = np.abs(np.diff(np.concatenate([[0], kelly_w])))\n", + " fee_costs = np.concatenate([[0], trades * fee])[:len(weighted_returns)]\n", + " net_returns = weighted_returns - fee_costs\n", + "\n", + " sharpe = np.mean(net_returns) / (np.std(net_returns) + 1e-10) * np.sqrt(252)\n", + " return sharpe, net_returns\n", + "\n", + "def vol_targeting_sleeve(close_prices, target_vol=0.15, window=60, fee_bps=50):\n", + " \"\"\"Vol-targeting sleeve: scale position to maintain target volatility.\"\"\"\n", + " prices = pd.Series(close_prices)\n", + " returns = prices.pct_change().dropna().values\n", + "\n", + " rolling_vol = pd.Series(returns).rolling(window).std().values * np.sqrt(252)\n", + " rolling_vol = np.where(rolling_vol < 0.01, 0.2, rolling_vol)\n", + "\n", + " # Scale to target vol\n", + " scale = target_vol / rolling_vol\n", + " scale = np.minimum(scale, 2.0) # cap leverage\n", + "\n", + " weighted_returns = scale * returns\n", + "\n", + " fee = fee_bps / 10000\n", + " trades = np.abs(np.diff(np.concatenate([[0], scale])))\n", + " fee_costs = np.concatenate([[0], trades * fee])[:len(weighted_returns)]\n", + " net_returns = weighted_returns - fee_costs\n", + "\n", + " sharpe = np.mean(net_returns) / (np.std(net_returns) + 1e-10) * np.sqrt(252)\n", + " return sharpe, net_returns\n", + "\n", + "print(\"Sleeve functions defined\")" + ], "id": "cell-a896b9f2" }, { @@ -162,7 +222,43 @@ ] } ], - "source": "# Section 4: Individual sleeve backtests\nfee_levels = [0, 5, 10, 25, 50, 100]\nsleeve_results = []\n\nfor symbol, hist in crypto_data.items():\n close = hist[\"close\"].values\n \n for fee in fee_levels:\n # K60\n sharpe_k60, rets_k60 = kelly_sleeve(close, cap=0.6, fee_bps=fee)\n # K30\n sharpe_k30, rets_k30 = kelly_sleeve(close, cap=0.3, fee_bps=fee)\n # VTH\n sharpe_vth, rets_vth = vol_targeting_sleeve(close, target_vol=0.15, fee_bps=fee)\n \n # Buy-and-hold baseline (round-trip fee: entry + exit)\n bh_returns = np.diff(np.log(close))\n bh_fee = 2 * fee / 10000 # round-trip: entry + exit\n bh_net = bh_returns - bh_fee\n sharpe_bh = np.mean(bh_net) / (np.std(bh_net) + 1e-10) * np.sqrt(252)\n \n sleeve_results.append({\n \"coin\": symbol,\n \"fee_bps\": fee,\n \"sharpe_k60\": sharpe_k60,\n \"sharpe_k30\": sharpe_k30,\n \"sharpe_vth\": sharpe_vth,\n \"sharpe_bh\": sharpe_bh,\n })\n \n print(f\"{symbol} @ {fee}bps: K60={sharpe_k60:.3f}, K30={sharpe_k30:.3f}, \"\n f\"VTH={sharpe_vth:.3f}, BH={sharpe_bh:.3f}\")\n\nsleeve_df = pd.DataFrame(sleeve_results)\nprint(f\"\\nTotal combos: {len(sleeve_df)}\")", + "source": [ + "# Section 4: Individual sleeve backtests\n", + "fee_levels = [0, 5, 10, 25, 50, 100]\n", + "sleeve_results = []\n", + "\n", + "for symbol, hist in crypto_data.items():\n", + " close = hist[\"close\"].values\n", + " \n", + " for fee in fee_levels:\n", + " # K60\n", + " sharpe_k60, rets_k60 = kelly_sleeve(close, cap=0.6, fee_bps=fee)\n", + " # K30\n", + " sharpe_k30, rets_k30 = kelly_sleeve(close, cap=0.3, fee_bps=fee)\n", + " # VTH\n", + " sharpe_vth, rets_vth = vol_targeting_sleeve(close, target_vol=0.15, fee_bps=fee)\n", + " \n", + " # Buy-and-hold baseline (round-trip fee: entry + exit)\n", + " bh_returns = np.diff(np.log(close))\n", + " bh_fee = 2 * fee / 10000 # round-trip: entry + exit\n", + " bh_net = bh_returns - bh_fee\n", + " sharpe_bh = np.mean(bh_net) / (np.std(bh_net) + 1e-10) * np.sqrt(252)\n", + " \n", + " sleeve_results.append({\n", + " \"coin\": symbol,\n", + " \"fee_bps\": fee,\n", + " \"sharpe_k60\": sharpe_k60,\n", + " \"sharpe_k30\": sharpe_k30,\n", + " \"sharpe_vth\": sharpe_vth,\n", + " \"sharpe_bh\": sharpe_bh,\n", + " })\n", + " \n", + " print(f\"{symbol} @ {fee}bps: K60={sharpe_k60:.3f}, K30={sharpe_k30:.3f}, \"\n", + " f\"VTH={sharpe_vth:.3f}, BH={sharpe_bh:.3f}\")\n", + "\n", + "sleeve_df = pd.DataFrame(sleeve_results)\n", + "print(f\"\\nTotal combos: {len(sleeve_df)}\")" + ], "id": "cell-64086bd7" }, { @@ -203,7 +299,43 @@ ] } ], - "source": "# Section 5: Ensemble (equal-weight) and fee sweep\nensemble_results = []\n\nfor symbol, hist in crypto_data.items():\n close = hist[\"close\"].values\n \n for fee in fee_levels:\n _, rets_k60 = kelly_sleeve(close, cap=0.6, fee_bps=fee)\n _, rets_k30 = kelly_sleeve(close, cap=0.3, fee_bps=fee)\n _, rets_vth = vol_targeting_sleeve(close, target_vol=0.15, fee_bps=fee)\n \n min_len = min(len(rets_k60), len(rets_k30), len(rets_vth))\n ensemble_rets = (rets_k60[:min_len] + rets_k30[:min_len] + rets_vth[:min_len]) / 3\n \n sharpe_ensemble = np.mean(ensemble_rets) / (np.std(ensemble_rets) + 1e-10) * np.sqrt(252)\n \n # BH baseline (round-trip fee)\n bh_returns = np.diff(np.log(close))[:min_len]\n bh_net = bh_returns - 2 * fee / 10000\n sharpe_bh = np.mean(bh_net) / (np.std(bh_net) + 1e-10) * np.sqrt(252)\n \n ensemble_results.append({\n \"coin\": symbol,\n \"fee_bps\": fee,\n \"sharpe_ensemble\": sharpe_ensemble,\n \"sharpe_bh\": sharpe_bh,\n \"delta_sharpe\": sharpe_ensemble - sharpe_bh,\n \"beats_bh\": int(sharpe_ensemble > sharpe_bh),\n })\n \n print(f\"{symbol} @ {fee}bps: Ensemble={sharpe_ensemble:.3f}, BH={sharpe_bh:.3f}, \"\n f\"delta={sharpe_ensemble - sharpe_bh:+.3f}\")\n\nensemble_df = pd.DataFrame(ensemble_results)\nprint(f\"\\nTotal combos: {len(ensemble_df)}\")", + "source": [ + "# Section 5: Ensemble (equal-weight) and fee sweep\n", + "ensemble_results = []\n", + "\n", + "for symbol, hist in crypto_data.items():\n", + " close = hist[\"close\"].values\n", + " \n", + " for fee in fee_levels:\n", + " _, rets_k60 = kelly_sleeve(close, cap=0.6, fee_bps=fee)\n", + " _, rets_k30 = kelly_sleeve(close, cap=0.3, fee_bps=fee)\n", + " _, rets_vth = vol_targeting_sleeve(close, target_vol=0.15, fee_bps=fee)\n", + " \n", + " min_len = min(len(rets_k60), len(rets_k30), len(rets_vth))\n", + " ensemble_rets = (rets_k60[:min_len] + rets_k30[:min_len] + rets_vth[:min_len]) / 3\n", + " \n", + " sharpe_ensemble = np.mean(ensemble_rets) / (np.std(ensemble_rets) + 1e-10) * np.sqrt(252)\n", + " \n", + " # BH baseline (round-trip fee)\n", + " bh_returns = np.diff(np.log(close))[:min_len]\n", + " bh_net = bh_returns - 2 * fee / 10000\n", + " sharpe_bh = np.mean(bh_net) / (np.std(bh_net) + 1e-10) * np.sqrt(252)\n", + " \n", + " ensemble_results.append({\n", + " \"coin\": symbol,\n", + " \"fee_bps\": fee,\n", + " \"sharpe_ensemble\": sharpe_ensemble,\n", + " \"sharpe_bh\": sharpe_bh,\n", + " \"delta_sharpe\": sharpe_ensemble - sharpe_bh,\n", + " \"beats_bh\": int(sharpe_ensemble > sharpe_bh),\n", + " })\n", + " \n", + " print(f\"{symbol} @ {fee}bps: Ensemble={sharpe_ensemble:.3f}, BH={sharpe_bh:.3f}, \"\n", + " f\"delta={sharpe_ensemble - sharpe_bh:+.3f}\")\n", + "\n", + "ensemble_df = pd.DataFrame(ensemble_results)\n", + "print(f\"\\nTotal combos: {len(ensemble_df)}\")" + ], "id": "cell-9a420cbf" }, { @@ -346,4 +478,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/MyIA.AI.Notebooks/QuantConnect/research/research_rl_multi_asset.ipynb b/MyIA.AI.Notebooks/QuantConnect/research/research_rl_multi_asset.ipynb index 1f063fe0c7..8e1cc5815a 100644 --- a/MyIA.AI.Notebooks/QuantConnect/research/research_rl_multi_asset.ipynb +++ b/MyIA.AI.Notebooks/QuantConnect/research/research_rl_multi_asset.ipynb @@ -149,7 +149,92 @@ ] } ], - "source": "class PortfolioEnv:\n \"\"\"Multi-asset portfolio environment with continuous actions (weights).\n\n State: flattened LOOKBACK x N_ASSETS return matrix.\n Action: N_ASSETS logits (applied softmax to get weights).\n Reward: rolling Sharpe of portfolio - rolling Sharpe of equal-weight.\n \"\"\"\n\n def __init__(self, returns, lookback=LOOKBACK, episode_len=EPISODE_LEN,\n fee_bps=FEE_BPS, n_assets=n_assets):\n self.returns = returns\n self.lookback = lookback\n self.episode_len = episode_len\n self.fee = fee_bps / 10000\n self.n_assets = n_assets\n self.ew_weight = np.ones(n_assets) / n_assets\n\n @property\n def state_dim(self):\n return self.lookback * self.n_assets\n\n @property\n def action_dim(self):\n return self.n_assets\n\n def reset(self, start_idx=None):\n n = len(self.returns) - self.lookback - 2\n if start_idx is None:\n start_idx = np.random.randint(0, max(1, n - self.episode_len))\n self.t = start_idx + self.lookback\n self.end = min(self.t + self.episode_len, len(self.returns) - 1)\n self.prev_weights = self.ew_weight.copy()\n self.port_returns = []\n self.ew_returns = []\n return self._get_state()\n\n def _get_state(self):\n window = self.returns[self.t - self.lookback:self.t] # (L, A)\n # Normalize each asset's returns\n std = np.std(window, axis=0, keepdims=True) + 1e-8\n normalized = window / std\n return normalized.flatten()\n\n def step(self, weights):\n \"\"\"weights: array of shape (n_assets,), sums to ~1.\"\"\"\n r = self.returns[self.t] # (A,)\n\n # Portfolio return\n port_ret = np.dot(weights, r)\n\n # Equal-weight return\n ew_ret = np.mean(r)\n\n # Transaction cost: L1 change in weights\n turnover = np.sum(np.abs(weights - self.prev_weights))\n fee_cost = turnover * self.fee * 0.5 # half-spread model\n\n port_ret_net = port_ret - fee_cost\n\n self.port_returns.append(port_ret_net)\n self.ew_returns.append(ew_ret)\n\n # Reward: differential Sharpe (rolling)\n reward = self._compute_reward(port_ret_net, ew_ret)\n\n self.prev_weights = weights.copy()\n self.t += 1\n done = self.t >= self.end\n\n next_state = self._get_state() if not done else np.zeros(self.state_dim)\n return next_state, reward, done, {\"port_ret\": port_ret_net, \"ew_ret\": ew_ret, \"turnover\": turnover}\n\n def _compute_reward(self, port_ret, ew_ret):\n if len(self.port_returns) < 5:\n return port_ret - ew_ret # simple differential\n window_p = np.array(self.port_returns[-SHARPE_WINDOW:])\n window_e = np.array(self.ew_returns[-SHARPE_WINDOW:])\n sp = np.mean(window_p) / (np.std(window_p) + 1e-8) * np.sqrt(252)\n se = np.mean(window_e) / (np.std(window_e) + 1e-8) * np.sqrt(252)\n return (sp - se) * 0.01 # scaled differential Sharpe\n\n\nprint(f\"PortfolioEnv: state_dim={LOOKBACK * n_assets}, action_dim={n_assets}\")\nprint(f\"Baseline: equal-weight ({n_assets} assets, 1/{n_assets:.4f} each)\")" + "source": [ + "class PortfolioEnv:\n", + " \"\"\"Multi-asset portfolio environment with continuous actions (weights).\n", + "\n", + " State: flattened LOOKBACK x N_ASSETS return matrix.\n", + " Action: N_ASSETS logits (applied softmax to get weights).\n", + " Reward: rolling Sharpe of portfolio - rolling Sharpe of equal-weight.\n", + " \"\"\"\n", + "\n", + " def __init__(self, returns, lookback=LOOKBACK, episode_len=EPISODE_LEN,\n", + " fee_bps=FEE_BPS, n_assets=n_assets):\n", + " self.returns = returns\n", + " self.lookback = lookback\n", + " self.episode_len = episode_len\n", + " self.fee = fee_bps / 10000\n", + " self.n_assets = n_assets\n", + " self.ew_weight = np.ones(n_assets) / n_assets\n", + "\n", + " @property\n", + " def state_dim(self):\n", + " return self.lookback * self.n_assets\n", + "\n", + " @property\n", + " def action_dim(self):\n", + " return self.n_assets\n", + "\n", + " def reset(self, start_idx=None):\n", + " n = len(self.returns) - self.lookback - 2\n", + " if start_idx is None:\n", + " start_idx = np.random.randint(0, max(1, n - self.episode_len))\n", + " self.t = start_idx + self.lookback\n", + " self.end = min(self.t + self.episode_len, len(self.returns) - 1)\n", + " self.prev_weights = self.ew_weight.copy()\n", + " self.port_returns = []\n", + " self.ew_returns = []\n", + " return self._get_state()\n", + "\n", + " def _get_state(self):\n", + " window = self.returns[self.t - self.lookback:self.t] # (L, A)\n", + " # Normalize each asset's returns\n", + " std = np.std(window, axis=0, keepdims=True) + 1e-8\n", + " normalized = window / std\n", + " return normalized.flatten()\n", + "\n", + " def step(self, weights):\n", + " \"\"\"weights: array of shape (n_assets,), sums to ~1.\"\"\"\n", + " r = self.returns[self.t] # (A,)\n", + "\n", + " # Portfolio return\n", + " port_ret = np.dot(weights, r)\n", + "\n", + " # Equal-weight return\n", + " ew_ret = np.mean(r)\n", + "\n", + " # Transaction cost: L1 change in weights\n", + " turnover = np.sum(np.abs(weights - self.prev_weights))\n", + " fee_cost = turnover * self.fee * 0.5 # half-spread model\n", + "\n", + " port_ret_net = port_ret - fee_cost\n", + "\n", + " self.port_returns.append(port_ret_net)\n", + " self.ew_returns.append(ew_ret)\n", + "\n", + " # Reward: differential Sharpe (rolling)\n", + " reward = self._compute_reward(port_ret_net, ew_ret)\n", + "\n", + " self.prev_weights = weights.copy()\n", + " self.t += 1\n", + " done = self.t >= self.end\n", + "\n", + " next_state = self._get_state() if not done else np.zeros(self.state_dim)\n", + " return next_state, reward, done, {\"port_ret\": port_ret_net, \"ew_ret\": ew_ret, \"turnover\": turnover}\n", + "\n", + " def _compute_reward(self, port_ret, ew_ret):\n", + " if len(self.port_returns) < 5:\n", + " return port_ret - ew_ret # simple differential\n", + " window_p = np.array(self.port_returns[-SHARPE_WINDOW:])\n", + " window_e = np.array(self.ew_returns[-SHARPE_WINDOW:])\n", + " sp = np.mean(window_p) / (np.std(window_p) + 1e-8) * np.sqrt(252)\n", + " se = np.mean(window_e) / (np.std(window_e) + 1e-8) * np.sqrt(252)\n", + " return (sp - se) * 0.01 # scaled differential Sharpe\n", + "\n", + "\n", + "print(f\"PortfolioEnv: state_dim={LOOKBACK * n_assets}, action_dim={n_assets}\")\n", + "print(f\"Baseline: equal-weight ({n_assets} assets, 1/{n_assets:.4f} each)\")" + ] }, { "cell_type": "markdown", @@ -685,4 +770,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} +} \ No newline at end of file diff --git a/MyIA.AI.Notebooks/RL/rl_12_distributional_rl.ipynb b/MyIA.AI.Notebooks/RL/rl_12_distributional_rl.ipynb index 6c68e74c32..6ede6bd81d 100644 --- a/MyIA.AI.Notebooks/RL/rl_12_distributional_rl.ipynb +++ b/MyIA.AI.Notebooks/RL/rl_12_distributional_rl.ipynb @@ -761,7 +761,15 @@ { "cell_type": "markdown", "id": "ffe2297b", - "source": "### Lecture : que nous apprend la distribution apprise ?\n\nLa politique greedy choisit **l'action 1 (droite)** : c'est simplement l'action dont l'espérance $\\mathbb{E}[Z]$ est la plus élevée — la ligne rouge pointillée sur chaque sous-figure. Un DQN scalaire aurait pris la **même** décision ; jusqu'ici, rien de neuf.\n\nLa différence est dans les **barres**. Pour chaque action, C51 expose la *forme complète* du retour : sa concentration, sa dispersion, son asymétrie, ses éventuels modes. L'espérance (la ligne rouge) résume cette distribution en un seul nombre et **jette tout le reste**. Or deux distributions de même moyenne peuvent avoir des risques radicalement différents : l'une concentrée autour de sa moyenne (retour prévisible), l'autre bimodale ou étalée (jackpot ou effondrement). Un DQN les verrait comme **identiques** ; C51 les distingue.\n\nC'est précisément cette information que l'**Exercice 3** exploite : au lieu de maximiser l'espérance, on peut maximiser la **CVaR** (la moyenne conditionnelle des pires scénarios) et obtenir une politique *averse au risque* — strictement impossible avec un scalaire $Q$. La section suivante explique pourquoi, empiriquement, ce signal d'apprentissage plus riche stabilise aussi l'entraînement.", + "source": [ + "### Lecture : que nous apprend la distribution apprise ?\n", + "\n", + "La politique greedy choisit **l'action 1 (droite)** : c'est simplement l'action dont l'espérance $\\mathbb{E}[Z]$ est la plus élevée — la ligne rouge pointillée sur chaque sous-figure. Un DQN scalaire aurait pris la **même** décision ; jusqu'ici, rien de neuf.\n", + "\n", + "La différence est dans les **barres**. Pour chaque action, C51 expose la *forme complète* du retour : sa concentration, sa dispersion, son asymétrie, ses éventuels modes. L'espérance (la ligne rouge) résume cette distribution en un seul nombre et **jette tout le reste**. Or deux distributions de même moyenne peuvent avoir des risques radicalement différents : l'une concentrée autour de sa moyenne (retour prévisible), l'autre bimodale ou étalée (jackpot ou effondrement). Un DQN les verrait comme **identiques** ; C51 les distingue.\n", + "\n", + "C'est précisément cette information que l'**Exercice 3** exploite : au lieu de maximiser l'espérance, on peut maximiser la **CVaR** (la moyenne conditionnelle des pires scénarios) et obtenir une politique *averse au risque* — strictement impossible avec un scalaire $Q$. La section suivante explique pourquoi, empiriquement, ce signal d'apprentissage plus riche stabilise aussi l'entraînement." + ], "metadata": {} }, { @@ -1116,4 +1124,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/MyIA.AI.Notebooks/Sudoku/Sudoku-14-BDD-Csharp.ipynb b/MyIA.AI.Notebooks/Sudoku/Sudoku-14-BDD-Csharp.ipynb index 80bf03ab34..a1f445c8ef 100644 --- a/MyIA.AI.Notebooks/Sudoku/Sudoku-14-BDD-Csharp.ipynb +++ b/MyIA.AI.Notebooks/Sudoku/Sudoku-14-BDD-Csharp.ipynb @@ -356,7 +356,15 @@ "text": "Exercice a completer\r\n" } ], - "source": "// EXERCICE : Construire un BDD pour la contrainte \"exactement un parmi N\"\npublic BDDNode ExactlyOneOfN(BDDNode[] variables)\n{\n // TODO: Construisez un BDD qui est vrai si exactement une des N variables est vraie\n return null; // TODO etudiant\n}\nConsole.WriteLine(\"Exercice a completer\");", + "source": [ + "// EXERCICE : Construire un BDD pour la contrainte \"exactement un parmi N\"\n", + "public BDDNode ExactlyOneOfN(BDDNode[] variables)\n", + "{\n", + " // TODO: Construisez un BDD qui est vrai si exactement une des N variables est vraie\n", + " return null; // TODO etudiant\n", + "}\n", + "Console.WriteLine(\"Exercice a completer\");" + ], "id": "cell-a18373c4" }, { @@ -1111,7 +1119,16 @@ "text": "Exercice a completer\r\n" } ], - "source": "// EXERCICE : Construire un MDD pour la contrainte \"tous différents\"\npublic MDDNode BuildAllDifferentMDD(int numCells, int maxValue)\n{\n // TODO: Construisez un MDD qui accepte uniquement les assignments\n // ou toutes les valeurs sont différentes\n return null; // TODO etudiant\n}\nConsole.WriteLine(\"Exercice a completer\");", + "source": [ + "// EXERCICE : Construire un MDD pour la contrainte \"tous différents\"\n", + "public MDDNode BuildAllDifferentMDD(int numCells, int maxValue)\n", + "{\n", + " // TODO: Construisez un MDD qui accepte uniquement les assignments\n", + " // ou toutes les valeurs sont différentes\n", + " return null; // TODO etudiant\n", + "}\n", + "Console.WriteLine(\"Exercice a completer\");" + ], "id": "cell-fff6d6dd" }, { @@ -2875,4 +2892,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file From 79e597cde25d6fbf0f3d3e6632fb8812e5b25712 Mon Sep 17 00:00:00 2001 From: jsboige Date: Thu, 10 Sep 2026 12:36:15 +0200 Subject: [PATCH 2/5] chore(notebooks,#14209): restore final newline on split tranche T7 (EOF drift #15429) Le writer de normalisation source str->list a perdu la newline finale des notebooks modifies (fin `}` au lieu de `}\n` comme sur main). Restauration d'un octet par fichier : les blobs redeviennent suffixe-identiques a main, contain JSON/nbformat inchange. Co-Authored-By: Claude Sonnet 5 --- .../GameTheory/GameTheory-10-ForwardInduction-SPE.ipynb | 2 +- .../QuantConnect/projects/Research-Executor/runner.ipynb | 2 +- .../QuantConnect/research/research_m11ef_ensemble.ipynb | 2 +- .../QuantConnect/research/research_rl_multi_asset.ipynb | 2 +- MyIA.AI.Notebooks/RL/rl_12_distributional_rl.ipynb | 2 +- MyIA.AI.Notebooks/Sudoku/Sudoku-14-BDD-Csharp.ipynb | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/MyIA.AI.Notebooks/GameTheory/GameTheory-10-ForwardInduction-SPE.ipynb b/MyIA.AI.Notebooks/GameTheory/GameTheory-10-ForwardInduction-SPE.ipynb index a15dbaa3ac..52e9839246 100644 --- a/MyIA.AI.Notebooks/GameTheory/GameTheory-10-ForwardInduction-SPE.ipynb +++ b/MyIA.AI.Notebooks/GameTheory/GameTheory-10-ForwardInduction-SPE.ipynb @@ -2141,4 +2141,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} \ No newline at end of file +} diff --git a/MyIA.AI.Notebooks/QuantConnect/projects/Research-Executor/runner.ipynb b/MyIA.AI.Notebooks/QuantConnect/projects/Research-Executor/runner.ipynb index e49fc63347..dc140996f7 100644 --- a/MyIA.AI.Notebooks/QuantConnect/projects/Research-Executor/runner.ipynb +++ b/MyIA.AI.Notebooks/QuantConnect/projects/Research-Executor/runner.ipynb @@ -109,4 +109,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +} diff --git a/MyIA.AI.Notebooks/QuantConnect/research/research_m11ef_ensemble.ipynb b/MyIA.AI.Notebooks/QuantConnect/research/research_m11ef_ensemble.ipynb index 21b392cd56..f1c3a2ccd8 100644 --- a/MyIA.AI.Notebooks/QuantConnect/research/research_m11ef_ensemble.ipynb +++ b/MyIA.AI.Notebooks/QuantConnect/research/research_m11ef_ensemble.ipynb @@ -478,4 +478,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} \ No newline at end of file +} diff --git a/MyIA.AI.Notebooks/QuantConnect/research/research_rl_multi_asset.ipynb b/MyIA.AI.Notebooks/QuantConnect/research/research_rl_multi_asset.ipynb index 8e1cc5815a..100bf9735c 100644 --- a/MyIA.AI.Notebooks/QuantConnect/research/research_rl_multi_asset.ipynb +++ b/MyIA.AI.Notebooks/QuantConnect/research/research_rl_multi_asset.ipynb @@ -770,4 +770,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} \ No newline at end of file +} diff --git a/MyIA.AI.Notebooks/RL/rl_12_distributional_rl.ipynb b/MyIA.AI.Notebooks/RL/rl_12_distributional_rl.ipynb index 6ede6bd81d..93261d6030 100644 --- a/MyIA.AI.Notebooks/RL/rl_12_distributional_rl.ipynb +++ b/MyIA.AI.Notebooks/RL/rl_12_distributional_rl.ipynb @@ -1124,4 +1124,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} \ No newline at end of file +} diff --git a/MyIA.AI.Notebooks/Sudoku/Sudoku-14-BDD-Csharp.ipynb b/MyIA.AI.Notebooks/Sudoku/Sudoku-14-BDD-Csharp.ipynb index a1f445c8ef..6921b6974a 100644 --- a/MyIA.AI.Notebooks/Sudoku/Sudoku-14-BDD-Csharp.ipynb +++ b/MyIA.AI.Notebooks/Sudoku/Sudoku-14-BDD-Csharp.ipynb @@ -2892,4 +2892,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} \ No newline at end of file +} From d663f8a89bd67b97f4c23c9dd67dce712848a70b Mon Sep 17 00:00:00 2001 From: jsboige Date: Thu, 10 Sep 2026 13:15:10 +0200 Subject: [PATCH 3/5] chore(twin-parity,#15429): rebaseline T7 twin pairs after source normalization The split converted notebook sources to the list form, moving the blob SHAs of 2 registered twin pairs (GameTheory-10 ForwardInduction-SPE, Sudoku-14 BDD) without a re-audit. Canonical rebaseline via check_twin_parity.py --update (after the EOF newline repair), per the twin-parity gate contract (#8057). Co-Authored-By: Claude Sonnet 5 --- .../0010-2026-09-10-myia-po-2023-CoursIA.yaml | 6 ++++++ .../sudoku-14-bdd/0010-2026-09-10-myia-po-2023-CoursIA.yaml | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0010-2026-09-10-myia-po-2023-CoursIA.yaml create mode 100644 scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0010-2026-09-10-myia-po-2023-CoursIA.yaml diff --git a/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0010-2026-09-10-myia-po-2023-CoursIA.yaml b/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0010-2026-09-10-myia-po-2023-CoursIA.yaml new file mode 100644 index 0000000000..4c6a046a85 --- /dev/null +++ b/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0010-2026-09-10-myia-po-2023-CoursIA.yaml @@ -0,0 +1,6 @@ +date: '2026-09-10' +by: myia-po-2023:CoursIA +python_sha: 52e983924607fcad4d797cf092f9adfa55f225fd +csharp_sha: 11c2595eaac6c95201712789073e19e4731694a0 +content_python_sha: 41aacfc35d4184ae63dadb1345ee8f8027f33ef795ce376da9e49e2f54c2989d +content_csharp_sha: af0b6d8d123fa15c25af1bf8ab4b9de2b9c33d876b9c14b54f8fe55279ffbc58 diff --git a/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0010-2026-09-10-myia-po-2023-CoursIA.yaml b/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0010-2026-09-10-myia-po-2023-CoursIA.yaml new file mode 100644 index 0000000000..b06f2b4128 --- /dev/null +++ b/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0010-2026-09-10-myia-po-2023-CoursIA.yaml @@ -0,0 +1,6 @@ +date: '2026-09-10' +by: myia-po-2023:CoursIA +python_sha: 8672bbe7b5508c8dbca73e17d6d1a0af2f68ab00 +csharp_sha: 6921b6974a72d9cc3d05f6e2fe96dce5d49ec3ae +content_python_sha: 1bfc5722a332acb62b38fc19af0f8a32cdddb604b0867e3949607b1897c9db07 +content_csharp_sha: 5504c03af3e6ad940c6ad20bf3ded4739c3e21643c411921eed985da1884df2c From e05c111080cd0df09f1bfd7f7759a1ec85c05156 Mon Sep 17 00:00:00 2001 From: jsboige Date: Fri, 11 Sep 2026 12:03:40 +0200 Subject: [PATCH 4/5] chore(ci,#15446): attestation twin parity des 2 paires T7 + renumeration du journal d'audit local (See #15429) Le garde Twin parity audit (#8057) rougissait sur 2 paires du champ de cette PR : GameTheory-10 ForwardInduction-SPE et Sudoku-14 BDD. Re-audit firsthand AVANT attestation (le garde exige d'attester la parite, pas de la reparer). Mesure sur les 4 jumeaux : le SHA canonique du carnet -- toutes les listes-de-chaines jointes, metadata de niveau carnet exclue -- est IDENTIQUE base (origin/main) vs head, et les champs `text` des sorties sont inchanges. L'empreinte de forme montre la seule difference : source_str passe a 0 et source_list gagne exactement les cellules correspondantes (GameTheory-10 Python 2->0 / 35->37 ; Sudoku-14 C# 2->0 / 42->44). Aucun contenu pedagogique touche. Le content_sha du registre (_content_sha hache la forme brute du JSON, donc un re-encodage str->list le deplace autant qu'une edition de prose) bougeait sans divergence : d'ou attestation via --update. Collision d'index propre a la branche : les deux repertoires portaient DEUX fichiers au prefixe 0010 (un po-2024 09-08 herite de main, un po-2023 09-10 local). L'index zero-padde NNNN est la cle de tri de _load_audits_from_files (#14911/#15345), donc renumeration 0011->0012 puis 0010->0011, en preservant l'ordre du journal. Verification : le contenu de chaque fichier renomme est byte-identique a sa source (sha256 : 7b6e04a864f1 et 386e2daac1a0 sur gametheory, 0208891eec5d et 00f600db8795 sur sudoku). Note de lecture : git apparie 0010 -> 0013 dans l'affichage du diff (les YAML d'attestation sont quasi identiques, l'heuristique de renommage est arbitraire ici). Le contenu de l'arbre est le seul verdict et il est verifie : ordre final du repertoire = 0009, 0010 (po-2024 09-08), 0011, 0012, 0013. - 2 entrees d'audit 2026-09-11 (myia-po-2023:CoursIA), forme file-per-audit #14911 - ligne explicative en tete de known_differences de chaque paire - per-pair vs origin/main : INTRO=0 (etait 2), OK=154/157, PRE=3 inchange (Probas-16, SW-2, SW-7 : DRIFT pre-existants sur main, hors champ de cette PR) Co-Authored-By: Claude Sonnet 5 --- .../twin_pairs.d/gametheory-10-forwardinduction-spe.yaml | 1 + .../0011-2026-09-10-myia-po-2023-CoursIA.yaml | 4 ++-- .../0012-2026-09-10-myia-po-2023-CoursIA.yaml | 6 ++++++ ...ursIA.yaml => 0013-2026-09-11-myia-po-2023-CoursIA.yaml} | 2 +- scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd.yaml | 1 + .../sudoku-14-bdd/0011-2026-09-10-myia-po-2023-CoursIA.yaml | 4 ++-- .../sudoku-14-bdd/0012-2026-09-10-myia-po-2023-CoursIA.yaml | 6 ++++++ ...ursIA.yaml => 0013-2026-09-11-myia-po-2023-CoursIA.yaml} | 2 +- 8 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0012-2026-09-10-myia-po-2023-CoursIA.yaml rename scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/{0010-2026-09-10-myia-po-2023-CoursIA.yaml => 0013-2026-09-11-myia-po-2023-CoursIA.yaml} (94%) create mode 100644 scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0012-2026-09-10-myia-po-2023-CoursIA.yaml rename scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/{0010-2026-09-10-myia-po-2023-CoursIA.yaml => 0013-2026-09-11-myia-po-2023-CoursIA.yaml} (94%) diff --git a/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe.yaml b/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe.yaml index 40b0163059..0e66f43d1c 100644 --- a/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe.yaml +++ b/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe.yaml @@ -6,6 +6,7 @@ bridge_verdict: SOTA-OK bridge_verdict_reason: "Modele #10382 solver-free par conception : theorie des jeux algorithmique enseignee from-scratch des deux cotes (l algorithme - induction arriere, existence Nash, SPE, formes extensive/combinatoire/cooperative - EST la lecon ; il n existe pas de bibliotheque .NET canonique de solveur de jeu a invoquer, contrairement aux paires lib-vs-lib comme PyMC/Infer.NET ou scikit/ML.NET). Les deux cotes atteignent le SOTA de leur ecosysteme pour cet objectif pedagogique : reimplementation didactique du formalisme mathematique. Pair verifiee firsthand (census moteur : CS=scratch, PY=scratch)." known_differences: + - "Re-baseline 2026-09-11 (myia-po-2023:CoursIA, #15446 / chore #15429) : suite de la normalisation str->list des cellules `source` (representation seule, aucun contenu pedagogique touche) et reparation d'une collision d'index d'audit propre a la branche (deux fichiers au meme prefixe 0010, renumerotes 0011/0012 pour preserver l'ordre de tri du journal, #14911/#15345). Verifie firsthand : le SHA canonique du carnet (toutes listes-de-chaines jointes, metadata de niveau carnet exclue) est IDENTIQUE base vs head, et les champs `text` des sorties sont inchanges ; seul le content_sha du registre bouge. Le garde #8057 demande une attestation de parite, pas une reparation." - "2026-08-22 (po-2025) #12349 tranche A (PR #12356) : cote C# uniquement -- chemin Gambit code en dur (Program Files (x86) + throw) remplace par le helper FindGambitEfg() (probe GAMBIT_HOME puis candidats Windows du pattern GT-5, puis Unix /usr/bin /usr/local/bin /opt/homebrew/bin sans suffixe .exe), cote C# re-execute 14/14 (gambit-lcp trouve toujours 1 equilibre, outputs frais), cote Python inchange. Changement d'infrastructure multiplateforme #10643, aucune prose/socle pedagogique touche -- parite semantique intacte." - "2026-08-17 (po-2026) re-exec seq etage 3 #11112 (PR #11347) : cote python re-execute (sequences 1..N propres, outputs frais, sources byte-identiques), cote C# inchange. Aucune prose ajoutee -- parite semantique intacte, seuls les blob SHA ont bouge." - "Socle pedagogique commun : les raffinements d'equilibre au-dela du SPE (Selten 1965) -- sous-jeux et equilibre parfait, menaces et promesses credibles, induction avant (forward induction), perfection en mains tremblantes (trembling-hand perfection, Selten 1975), application canonique du jeu du « Burn Money » (signal costly), comparaison des raffinements. Distinct du GT-9 (induction arriere + paradoxes) : le GT-10 porte sur les RAFFINEMENTS de la notion d'equilibre (forward induction, trembling-hand), pas sur l'algorithme de resolution." diff --git a/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0011-2026-09-10-myia-po-2023-CoursIA.yaml b/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0011-2026-09-10-myia-po-2023-CoursIA.yaml index 61423c1e6b..4c6a046a85 100644 --- a/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0011-2026-09-10-myia-po-2023-CoursIA.yaml +++ b/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0011-2026-09-10-myia-po-2023-CoursIA.yaml @@ -1,6 +1,6 @@ date: '2026-09-10' by: myia-po-2023:CoursIA -python_sha: e0fbc58833245a1b657e721ee05668cb04884e8b +python_sha: 52e983924607fcad4d797cf092f9adfa55f225fd csharp_sha: 11c2595eaac6c95201712789073e19e4731694a0 -content_python_sha: e0cc5d3f18ed29011d923ba97b23a29ef573beaafa0687f9efac152528694d05 +content_python_sha: 41aacfc35d4184ae63dadb1345ee8f8027f33ef795ce376da9e49e2f54c2989d content_csharp_sha: af0b6d8d123fa15c25af1bf8ab4b9de2b9c33d876b9c14b54f8fe55279ffbc58 diff --git a/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0012-2026-09-10-myia-po-2023-CoursIA.yaml b/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0012-2026-09-10-myia-po-2023-CoursIA.yaml new file mode 100644 index 0000000000..61423c1e6b --- /dev/null +++ b/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0012-2026-09-10-myia-po-2023-CoursIA.yaml @@ -0,0 +1,6 @@ +date: '2026-09-10' +by: myia-po-2023:CoursIA +python_sha: e0fbc58833245a1b657e721ee05668cb04884e8b +csharp_sha: 11c2595eaac6c95201712789073e19e4731694a0 +content_python_sha: e0cc5d3f18ed29011d923ba97b23a29ef573beaafa0687f9efac152528694d05 +content_csharp_sha: af0b6d8d123fa15c25af1bf8ab4b9de2b9c33d876b9c14b54f8fe55279ffbc58 diff --git a/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0010-2026-09-10-myia-po-2023-CoursIA.yaml b/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0013-2026-09-11-myia-po-2023-CoursIA.yaml similarity index 94% rename from scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0010-2026-09-10-myia-po-2023-CoursIA.yaml rename to scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0013-2026-09-11-myia-po-2023-CoursIA.yaml index 4c6a046a85..dca99d09c4 100644 --- a/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0010-2026-09-10-myia-po-2023-CoursIA.yaml +++ b/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0013-2026-09-11-myia-po-2023-CoursIA.yaml @@ -1,4 +1,4 @@ -date: '2026-09-10' +date: '2026-09-11' by: myia-po-2023:CoursIA python_sha: 52e983924607fcad4d797cf092f9adfa55f225fd csharp_sha: 11c2595eaac6c95201712789073e19e4731694a0 diff --git a/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd.yaml b/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd.yaml index b49d5eb8fa..556215ebeb 100644 --- a/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd.yaml +++ b/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd.yaml @@ -6,6 +6,7 @@ bridge_verdict: SOTA-OK bridge_verdict_reason: "Solver-free by design : les deux jumeaux implementent les BDD/MDD (Binary/Multi-valued Decision Diagrams) from-scratch en pure stdlib. Python = `__future__/dataclasses/time/typing` uniquement (zero solveur externe, confirme par grep `import`/`from` = {`__future__`, `dataclasses`, `time`, `typing`}, aucune lib BDD tierce). C# = `System.Collections.Generic/System.Diagnostics/System.Linq` uniquement (zero solveur externe, confirme par grep `using` = {`System.Collections.Generic`, `System.Diagnostics`, `System.Linq`}, aucune lib BDD tierce). Verifie firsthand (c.235 audit 2026-08-12, myia-po-2023:CoursIA-2) : 33 cells Python / 40 cells C#, divergence d'implementation documentee dans known_differences (Python hand-roll, C# hand-roll avec idiomes differents). Pas de moteur SOTA a brancher : les BDD sont la demonstration (construction recursive de l'arbre de decisions, application des contraintes Sudoku comme noeuds BDD, evaluation par reduction). Verdict SOTA-OK : solver-free by design des deux cotes, aucune asymetrie de machinerie a corriger. parity_level `semantic` confirmee par audit (meme concept BDD, implementation differente)." known_differences: + - "Re-baseline 2026-09-11 (myia-po-2023:CoursIA, #15446 / chore #15429) : suite de la normalisation str->list des cellules `source` (representation seule, aucun contenu pedagogique touche) et reparation d'une collision d'index d'audit propre a la branche (deux fichiers au meme prefixe 0010, renumerotes 0011/0012 pour preserver l'ordre de tri du journal, #14911/#15345). Verifie firsthand : le SHA canonique du carnet (toutes listes-de-chaines jointes, metadata de niveau carnet exclue) est IDENTIQUE base vs head, et les champs `text` des sorties sont inchanges ; seul le content_sha du registre bouge. Le garde #8057 demande une attestation de parite, pas une reparation." - "2026-09-02 (grain hr-separateurs famille Sudoku, myia-po-2027:CoursIA) : conversion des separateurs markdown --- vers *** via fix_hr_separator.py (incident #11451 : un --- seul ouvre un bloc YAML Pandoc et casse quarto render) -- jumeau C# seul (13 separateurs). Cellules markdown uniquement, aucune source de code ni output modifiee, pas de re-exec (C.2 exception markdown-only). Les blob et content SHAs deplaces dans l audit ci-dessus sont des artefacts de cette conversion markdown (le content_sha hashe le JSON canonique des cellules), pas une divergence pedagogique." - "Socle pedagogique commun : resolution du Sudoku par automates a decision binaire (BDD / MDD, Binary/Multi-valued Decision Diagrams), meme concept (codage booleen des contraintes, reduction canonique du graphe de decision, satisfaction via parcours). Le notebook Python declare explicitement etre le jumeau du C# (« Jumeau Python du notebook Sudoku-14-BDD-Csharp.ipynb »)." - "Divergence d'implementation : les deux cotes hand-rollent les BDD/MDD from scratch (pas de lib BDD tierce) -- le Python via dataclasses/typing, le C# via System.Collections.Generic + System.Linq. Meme reconstruction pedagogique de l'automate, idiomes de langage differents." diff --git a/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0011-2026-09-10-myia-po-2023-CoursIA.yaml b/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0011-2026-09-10-myia-po-2023-CoursIA.yaml index d81d8f83b6..b06f2b4128 100644 --- a/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0011-2026-09-10-myia-po-2023-CoursIA.yaml +++ b/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0011-2026-09-10-myia-po-2023-CoursIA.yaml @@ -1,6 +1,6 @@ date: '2026-09-10' by: myia-po-2023:CoursIA python_sha: 8672bbe7b5508c8dbca73e17d6d1a0af2f68ab00 -csharp_sha: 80bf03ab347e3efeff79c3817953f944b5b7d040 +csharp_sha: 6921b6974a72d9cc3d05f6e2fe96dce5d49ec3ae content_python_sha: 1bfc5722a332acb62b38fc19af0f8a32cdddb604b0867e3949607b1897c9db07 -content_csharp_sha: ec8c27c28f678c871ec3786bcf3beac455424aeb87d37343b2df4efb0adfb4e1 +content_csharp_sha: 5504c03af3e6ad940c6ad20bf3ded4739c3e21643c411921eed985da1884df2c diff --git a/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0012-2026-09-10-myia-po-2023-CoursIA.yaml b/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0012-2026-09-10-myia-po-2023-CoursIA.yaml new file mode 100644 index 0000000000..d81d8f83b6 --- /dev/null +++ b/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0012-2026-09-10-myia-po-2023-CoursIA.yaml @@ -0,0 +1,6 @@ +date: '2026-09-10' +by: myia-po-2023:CoursIA +python_sha: 8672bbe7b5508c8dbca73e17d6d1a0af2f68ab00 +csharp_sha: 80bf03ab347e3efeff79c3817953f944b5b7d040 +content_python_sha: 1bfc5722a332acb62b38fc19af0f8a32cdddb604b0867e3949607b1897c9db07 +content_csharp_sha: ec8c27c28f678c871ec3786bcf3beac455424aeb87d37343b2df4efb0adfb4e1 diff --git a/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0010-2026-09-10-myia-po-2023-CoursIA.yaml b/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0013-2026-09-11-myia-po-2023-CoursIA.yaml similarity index 94% rename from scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0010-2026-09-10-myia-po-2023-CoursIA.yaml rename to scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0013-2026-09-11-myia-po-2023-CoursIA.yaml index b06f2b4128..1bb955404f 100644 --- a/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0010-2026-09-10-myia-po-2023-CoursIA.yaml +++ b/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0013-2026-09-11-myia-po-2023-CoursIA.yaml @@ -1,4 +1,4 @@ -date: '2026-09-10' +date: '2026-09-11' by: myia-po-2023:CoursIA python_sha: 8672bbe7b5508c8dbca73e17d6d1a0af2f68ab00 csharp_sha: 6921b6974a72d9cc3d05f6e2fe96dce5d49ec3ae From 72c7c0dca21727ba094fab9f41806d4da8326995 Mon Sep 17 00:00:00 2001 From: jsboige Date: Fri, 11 Sep 2026 12:07:10 +0200 Subject: [PATCH 5/5] =?UTF-8?q?chore(ci,#15446):=20attestation=20twin=20pa?= =?UTF-8?q?rity=20T7=20a=20l'ordinal=200012=20=E2=80=94=20corrige=20la=20c?= =?UTF-8?q?ollision=20d'ordinal=20(See=20#15429)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Suite au diagnostic firsthand d'ai-01 sur #15446 : le rouge du garde n'etait pas la normalisation str->list mais une COLLISION D'ORDINAL. La tete portait, dans chaque repertoire, deux fichiers au meme prefixe 0010 (celui de cette PR et le 0010-2026-09-08-myia-po-2024-CoursIA-2.yaml deja sur main), et le 0011 venu de main (#15449, T8) attestait l'etat PRE-normalisation sous un ordinal superieur a celui que cette PR avait alloue. L'outil lit la derniere attestation : il voyait e0cc5d3 la ou l'arbre porte 41aacfc. Geste applique, conformement a la prescription d'ai-01 (on RETIRE l'entree dupliquee et on re-atteste au-dessus, pas de renumeration en cascade) : - git rm des deux 0010-2026-09-10-myia-po-2023-CoursIA.yaml (collision d'ordinal) - le 0011 de main (T8) est conserve tel quel, byte-identique : sha256 386e2daac1a0 (gametheory) et 00f600db8795 (sudoku) verifies contre origin/main - --update --pair alloue 0012-2026-09-11 dans les deux repertoires Verification: - garde --check : les 2 paires passent. DRIFT=3 residuel = Probas-16, SW-2, SW-7, collisions PRE-existantes sur main introduites par a1ae006524 (T5, #15445) et couvertes par la PR #15576 ouverte -- hors champ de cette PR. - integrite registre : 45 passes ; le seul echec ne liste que ces 3 paires de main, aucune des deux de cette PR. - diff net vs origin/main = +2 fichiers d'attestation + 2 lignes known_differences (le 0011 restaure etant byte-identique a celui de main). - notebooks non touches : leur contenu est bon, la neutralite semantique de la conversion source str->list est verifiee (SHA canonique identique base vs head). Co-Authored-By: Claude Sonnet 5 --- .../twin_pairs.d/gametheory-10-forwardinduction-spe.yaml | 2 +- .../0011-2026-09-10-myia-po-2023-CoursIA.yaml | 4 ++-- .../0012-2026-09-10-myia-po-2023-CoursIA.yaml | 6 ------ ...ursIA.yaml => 0012-2026-09-11-myia-po-2023-CoursIA.yaml} | 0 scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd.yaml | 2 +- .../sudoku-14-bdd/0011-2026-09-10-myia-po-2023-CoursIA.yaml | 4 ++-- .../sudoku-14-bdd/0012-2026-09-10-myia-po-2023-CoursIA.yaml | 6 ------ ...ursIA.yaml => 0012-2026-09-11-myia-po-2023-CoursIA.yaml} | 0 8 files changed, 6 insertions(+), 18 deletions(-) delete mode 100644 scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0012-2026-09-10-myia-po-2023-CoursIA.yaml rename scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/{0013-2026-09-11-myia-po-2023-CoursIA.yaml => 0012-2026-09-11-myia-po-2023-CoursIA.yaml} (100%) delete mode 100644 scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0012-2026-09-10-myia-po-2023-CoursIA.yaml rename scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/{0013-2026-09-11-myia-po-2023-CoursIA.yaml => 0012-2026-09-11-myia-po-2023-CoursIA.yaml} (100%) diff --git a/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe.yaml b/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe.yaml index 0e66f43d1c..19e6fb323d 100644 --- a/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe.yaml +++ b/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe.yaml @@ -6,7 +6,7 @@ bridge_verdict: SOTA-OK bridge_verdict_reason: "Modele #10382 solver-free par conception : theorie des jeux algorithmique enseignee from-scratch des deux cotes (l algorithme - induction arriere, existence Nash, SPE, formes extensive/combinatoire/cooperative - EST la lecon ; il n existe pas de bibliotheque .NET canonique de solveur de jeu a invoquer, contrairement aux paires lib-vs-lib comme PyMC/Infer.NET ou scikit/ML.NET). Les deux cotes atteignent le SOTA de leur ecosysteme pour cet objectif pedagogique : reimplementation didactique du formalisme mathematique. Pair verifiee firsthand (census moteur : CS=scratch, PY=scratch)." known_differences: - - "Re-baseline 2026-09-11 (myia-po-2023:CoursIA, #15446 / chore #15429) : suite de la normalisation str->list des cellules `source` (representation seule, aucun contenu pedagogique touche) et reparation d'une collision d'index d'audit propre a la branche (deux fichiers au meme prefixe 0010, renumerotes 0011/0012 pour preserver l'ordre de tri du journal, #14911/#15345). Verifie firsthand : le SHA canonique du carnet (toutes listes-de-chaines jointes, metadata de niveau carnet exclue) est IDENTIQUE base vs head, et les champs `text` des sorties sont inchanges ; seul le content_sha du registre bouge. Le garde #8057 demande une attestation de parite, pas une reparation." + - "Re-baseline 2026-09-11 (myia-po-2023:CoursIA, #15446 / chore #15429) : suite de la normalisation str->list des cellules `source` (representation seule, aucun contenu pedagogique touche) l'attestation a ete refaite a l'ordinal 0012, au-dessus du 0011 venu de main (#15449, T8) qui portait l'etat pre-normalisation sous un ordinal superieur et masquait celle de cette PR — collision d'ordinal diagnostiquee firsthand par ai-01 sur #15446 ; l'ancien 0010 de cette PR, en collision avec le 0010-2026-09-08 de main, est retire. Verifie firsthand : le SHA canonique du carnet (toutes listes-de-chaines jointes, metadata de niveau carnet exclue) est IDENTIQUE base vs head, et les champs `text` des sorties sont inchanges ; seul le content_sha du registre bouge. Le garde #8057 demande une attestation de parite, pas une reparation." - "2026-08-22 (po-2025) #12349 tranche A (PR #12356) : cote C# uniquement -- chemin Gambit code en dur (Program Files (x86) + throw) remplace par le helper FindGambitEfg() (probe GAMBIT_HOME puis candidats Windows du pattern GT-5, puis Unix /usr/bin /usr/local/bin /opt/homebrew/bin sans suffixe .exe), cote C# re-execute 14/14 (gambit-lcp trouve toujours 1 equilibre, outputs frais), cote Python inchange. Changement d'infrastructure multiplateforme #10643, aucune prose/socle pedagogique touche -- parite semantique intacte." - "2026-08-17 (po-2026) re-exec seq etage 3 #11112 (PR #11347) : cote python re-execute (sequences 1..N propres, outputs frais, sources byte-identiques), cote C# inchange. Aucune prose ajoutee -- parite semantique intacte, seuls les blob SHA ont bouge." - "Socle pedagogique commun : les raffinements d'equilibre au-dela du SPE (Selten 1965) -- sous-jeux et equilibre parfait, menaces et promesses credibles, induction avant (forward induction), perfection en mains tremblantes (trembling-hand perfection, Selten 1975), application canonique du jeu du « Burn Money » (signal costly), comparaison des raffinements. Distinct du GT-9 (induction arriere + paradoxes) : le GT-10 porte sur les RAFFINEMENTS de la notion d'equilibre (forward induction, trembling-hand), pas sur l'algorithme de resolution." diff --git a/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0011-2026-09-10-myia-po-2023-CoursIA.yaml b/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0011-2026-09-10-myia-po-2023-CoursIA.yaml index 4c6a046a85..61423c1e6b 100644 --- a/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0011-2026-09-10-myia-po-2023-CoursIA.yaml +++ b/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0011-2026-09-10-myia-po-2023-CoursIA.yaml @@ -1,6 +1,6 @@ date: '2026-09-10' by: myia-po-2023:CoursIA -python_sha: 52e983924607fcad4d797cf092f9adfa55f225fd +python_sha: e0fbc58833245a1b657e721ee05668cb04884e8b csharp_sha: 11c2595eaac6c95201712789073e19e4731694a0 -content_python_sha: 41aacfc35d4184ae63dadb1345ee8f8027f33ef795ce376da9e49e2f54c2989d +content_python_sha: e0cc5d3f18ed29011d923ba97b23a29ef573beaafa0687f9efac152528694d05 content_csharp_sha: af0b6d8d123fa15c25af1bf8ab4b9de2b9c33d876b9c14b54f8fe55279ffbc58 diff --git a/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0012-2026-09-10-myia-po-2023-CoursIA.yaml b/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0012-2026-09-10-myia-po-2023-CoursIA.yaml deleted file mode 100644 index 61423c1e6b..0000000000 --- a/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0012-2026-09-10-myia-po-2023-CoursIA.yaml +++ /dev/null @@ -1,6 +0,0 @@ -date: '2026-09-10' -by: myia-po-2023:CoursIA -python_sha: e0fbc58833245a1b657e721ee05668cb04884e8b -csharp_sha: 11c2595eaac6c95201712789073e19e4731694a0 -content_python_sha: e0cc5d3f18ed29011d923ba97b23a29ef573beaafa0687f9efac152528694d05 -content_csharp_sha: af0b6d8d123fa15c25af1bf8ab4b9de2b9c33d876b9c14b54f8fe55279ffbc58 diff --git a/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0013-2026-09-11-myia-po-2023-CoursIA.yaml b/scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0012-2026-09-11-myia-po-2023-CoursIA.yaml similarity index 100% rename from scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0013-2026-09-11-myia-po-2023-CoursIA.yaml rename to scripts/notebook_tools/twin_pairs.d/gametheory-10-forwardinduction-spe/0012-2026-09-11-myia-po-2023-CoursIA.yaml diff --git a/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd.yaml b/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd.yaml index 556215ebeb..58fc2b05ea 100644 --- a/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd.yaml +++ b/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd.yaml @@ -6,7 +6,7 @@ bridge_verdict: SOTA-OK bridge_verdict_reason: "Solver-free by design : les deux jumeaux implementent les BDD/MDD (Binary/Multi-valued Decision Diagrams) from-scratch en pure stdlib. Python = `__future__/dataclasses/time/typing` uniquement (zero solveur externe, confirme par grep `import`/`from` = {`__future__`, `dataclasses`, `time`, `typing`}, aucune lib BDD tierce). C# = `System.Collections.Generic/System.Diagnostics/System.Linq` uniquement (zero solveur externe, confirme par grep `using` = {`System.Collections.Generic`, `System.Diagnostics`, `System.Linq`}, aucune lib BDD tierce). Verifie firsthand (c.235 audit 2026-08-12, myia-po-2023:CoursIA-2) : 33 cells Python / 40 cells C#, divergence d'implementation documentee dans known_differences (Python hand-roll, C# hand-roll avec idiomes differents). Pas de moteur SOTA a brancher : les BDD sont la demonstration (construction recursive de l'arbre de decisions, application des contraintes Sudoku comme noeuds BDD, evaluation par reduction). Verdict SOTA-OK : solver-free by design des deux cotes, aucune asymetrie de machinerie a corriger. parity_level `semantic` confirmee par audit (meme concept BDD, implementation differente)." known_differences: - - "Re-baseline 2026-09-11 (myia-po-2023:CoursIA, #15446 / chore #15429) : suite de la normalisation str->list des cellules `source` (representation seule, aucun contenu pedagogique touche) et reparation d'une collision d'index d'audit propre a la branche (deux fichiers au meme prefixe 0010, renumerotes 0011/0012 pour preserver l'ordre de tri du journal, #14911/#15345). Verifie firsthand : le SHA canonique du carnet (toutes listes-de-chaines jointes, metadata de niveau carnet exclue) est IDENTIQUE base vs head, et les champs `text` des sorties sont inchanges ; seul le content_sha du registre bouge. Le garde #8057 demande une attestation de parite, pas une reparation." + - "Re-baseline 2026-09-11 (myia-po-2023:CoursIA, #15446 / chore #15429) : suite de la normalisation str->list des cellules `source` (representation seule, aucun contenu pedagogique touche) l'attestation a ete refaite a l'ordinal 0012, au-dessus du 0011 venu de main (#15449, T8) qui portait l'etat pre-normalisation sous un ordinal superieur et masquait celle de cette PR — collision d'ordinal diagnostiquee firsthand par ai-01 sur #15446 ; l'ancien 0010 de cette PR, en collision avec le 0010-2026-09-08 de main, est retire. Verifie firsthand : le SHA canonique du carnet (toutes listes-de-chaines jointes, metadata de niveau carnet exclue) est IDENTIQUE base vs head, et les champs `text` des sorties sont inchanges ; seul le content_sha du registre bouge. Le garde #8057 demande une attestation de parite, pas une reparation." - "2026-09-02 (grain hr-separateurs famille Sudoku, myia-po-2027:CoursIA) : conversion des separateurs markdown --- vers *** via fix_hr_separator.py (incident #11451 : un --- seul ouvre un bloc YAML Pandoc et casse quarto render) -- jumeau C# seul (13 separateurs). Cellules markdown uniquement, aucune source de code ni output modifiee, pas de re-exec (C.2 exception markdown-only). Les blob et content SHAs deplaces dans l audit ci-dessus sont des artefacts de cette conversion markdown (le content_sha hashe le JSON canonique des cellules), pas une divergence pedagogique." - "Socle pedagogique commun : resolution du Sudoku par automates a decision binaire (BDD / MDD, Binary/Multi-valued Decision Diagrams), meme concept (codage booleen des contraintes, reduction canonique du graphe de decision, satisfaction via parcours). Le notebook Python declare explicitement etre le jumeau du C# (« Jumeau Python du notebook Sudoku-14-BDD-Csharp.ipynb »)." - "Divergence d'implementation : les deux cotes hand-rollent les BDD/MDD from scratch (pas de lib BDD tierce) -- le Python via dataclasses/typing, le C# via System.Collections.Generic + System.Linq. Meme reconstruction pedagogique de l'automate, idiomes de langage differents." diff --git a/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0011-2026-09-10-myia-po-2023-CoursIA.yaml b/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0011-2026-09-10-myia-po-2023-CoursIA.yaml index b06f2b4128..d81d8f83b6 100644 --- a/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0011-2026-09-10-myia-po-2023-CoursIA.yaml +++ b/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0011-2026-09-10-myia-po-2023-CoursIA.yaml @@ -1,6 +1,6 @@ date: '2026-09-10' by: myia-po-2023:CoursIA python_sha: 8672bbe7b5508c8dbca73e17d6d1a0af2f68ab00 -csharp_sha: 6921b6974a72d9cc3d05f6e2fe96dce5d49ec3ae +csharp_sha: 80bf03ab347e3efeff79c3817953f944b5b7d040 content_python_sha: 1bfc5722a332acb62b38fc19af0f8a32cdddb604b0867e3949607b1897c9db07 -content_csharp_sha: 5504c03af3e6ad940c6ad20bf3ded4739c3e21643c411921eed985da1884df2c +content_csharp_sha: ec8c27c28f678c871ec3786bcf3beac455424aeb87d37343b2df4efb0adfb4e1 diff --git a/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0012-2026-09-10-myia-po-2023-CoursIA.yaml b/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0012-2026-09-10-myia-po-2023-CoursIA.yaml deleted file mode 100644 index d81d8f83b6..0000000000 --- a/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0012-2026-09-10-myia-po-2023-CoursIA.yaml +++ /dev/null @@ -1,6 +0,0 @@ -date: '2026-09-10' -by: myia-po-2023:CoursIA -python_sha: 8672bbe7b5508c8dbca73e17d6d1a0af2f68ab00 -csharp_sha: 80bf03ab347e3efeff79c3817953f944b5b7d040 -content_python_sha: 1bfc5722a332acb62b38fc19af0f8a32cdddb604b0867e3949607b1897c9db07 -content_csharp_sha: ec8c27c28f678c871ec3786bcf3beac455424aeb87d37343b2df4efb0adfb4e1 diff --git a/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0013-2026-09-11-myia-po-2023-CoursIA.yaml b/scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0012-2026-09-11-myia-po-2023-CoursIA.yaml similarity index 100% rename from scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0013-2026-09-11-myia-po-2023-CoursIA.yaml rename to scripts/notebook_tools/twin_pairs.d/sudoku-14-bdd/0012-2026-09-11-myia-po-2023-CoursIA.yaml