GameTheory-06g — Agents à budget explicite (companion Lean natif)
Ce notebook est le companion Lean natif du module ProgramGames.Bounded livré par la PR #15395 dans le lake game_theory_lean. Le module représente le code public (ProgramCode) et le budget de raisonnement fini (BoundedAgent) d’un agent-programme — modèle structurel inspiré de Barasz et al. (2014) et Critch (2016) — avec un interprète total (act) et un paramétrage canonique du Dilemme du prisonnier (canonicalPD, T=5, R=3, P=1, S=0).
Convention de vérification — #checknatif dans le kernel Lean
Ce notebook est un notebook Lean natif (kernel lean4-wsl) : il importe le lake directement et le compilateur Lean est censé rendre les signatures #check/#reducedans le notebook dès lors que le lake game_theory_lean est prébuildé via lake build ProgramGames. Sur la machine worker (myia-po-2026), ce lake build n’a pas pu être mené (réseau Mathlib fatal: fetch-pack: invalid index-pack output, absence de .lake/ préexistant — voir verdict INTRINSIC côté exécution kernel dans le body PR). C’est rendu possible par l’UNLOCK (patch lean4_jupyter + jonction Mathlib).
⚠️ À l’exécution, la première cellule (import) peut prendre plusieurs minutes : le kernel charge les oleans Mathlib via la jonction NTFS. Les suivantes sont instantanées.
Distinction explicite entre calcul fini et preuve Lean
Le module fournit deux registres :
Preuve : les théorèmes cooperate_cooperate, defect_defect, mirror_mirror, defectBotBounded_unexploitable, mirror_basicFamily_unexploitable, defect_profile_programNash portent sur toute famille, tout adversaire — quantification universelle.
Organe booléen fini : les Check (mutualCooperationCheck, unexploitableCheck, programNashCheck) sont des décideurs Bool sur des entrées concrètes ; leur exactitude est elle-même prouvée (programNashCheck_eq_true) par équivalence avec la propriété universelle.
Le notebook ne formalise ni logique de prouvabilité ni théorème de Löb ni Gödel — le module lui-même s’en garde explicitement dans son en-tête. Aucune cellule ne franchit cette limite.
1. Import du module ProgramGames.Bounded
On cible directement la lib ProgramGames.Bounded du lake game_theory_lean (le module racine ProgramGames est l’aggregator et dépend transitivement de Basic, mais on veut éviter de charger inutilement les autres libs sociales pour rester dans le scope du notebook).
import ProgramGames.Bounded
open ProgramGames
open RepeatedGames PDAction
importProgramGames.Bounded
openProgramGames
openRepeatedGamesPDAction
--% env 0
Raw input{"cmd": "import ProgramGames.Bounded\nopen ProgramGames\nopen RepeatedGames PDAction\n"}Raw output{"env": 0}
2. Famille de certificats n°1 — coopération mutuelle
Trois bots témoins définis dans ProgramGames.Bounded :
cooperateBot (code cooperateBot, budget 0) — coopère sans inspection.
defectBotBounded (code defectBot, budget 0) — dévie sans inspection.
mirrorBot (code mirror, budget 1) — examine l’adversaire : coopère sauf face à defectBot.
Les théorèmes suivants établissent la coopération mutuelle de deux cooperateBot, et de deux mirrorBot.
Ce que la cellule vérifie : la paire, le profil, le témoin négatif
La cellule mobilise trois niveaux. Les deux #check nomment les preuves universelles — cooperate_cooperate, mirror_mirror : des théorèmes sur toute paire d’arguments, pas sur un cas particulier. Les #reduce du milieu restreignent à des entrées concrètes : mutualCooperationCheck cooperateBot cooperateBot réduit à la forme normale — le profil joué par la paire — et la ligne miroir fait de même. Le dernier #reduce installe un témoin négatif : la référence commentée exige que defect_defect retourne (defect, defect), pas (cooperate, cooperate) — un diagnostic visible si le module confondait les profils. Le couple preuve-universelle / organe booléen posé par l’en-tête est à l’œuvre dès la première famille.
3. Famille de certificats n°2 — inexploitation
UnexploitableInFamily quantifie sur une famille finie d’adversaires : un agent est inexploitable s’il ne coopère jamais face à un adversaire qui dévie. Le théorème defectBotBounded_unexploitable est universel sur la famille ; le théorème mirror_basicFamily_unexploitable est l’instanciation concrète sur basicFamily.
L’inexploitation en deux registres, sur la famille témoin
Même structure que la famille 1 : deux théorèmes (defectBotBounded_unexploitable, mirror_basicFamily_unexploitable) et deux réductions sur des entrées concrètes. L’entrée typique de l’organe est un couple (adversaire, famille) : unexploitableCheck defectBotBounded basicFamily demande s’il existe, dans la famille donnée, un programme qui exploite le bot testé — le Bool rend la réponse computationnelle. La cohérence entre l’organe et la propriété universelle n’est pas une affaire de foi : la conclusion du notebook la rappelle famille par famille, et l’exercice 3 testera la fragilité du couple quand la famille s’étend.
4. Famille de certificats n°3 — équilibre de Nash borné
ProgramNashBounded pose l’équilibre relatif sur une famille finie : aucune substitution unilatérale dans la famille n’améliore strictement le paiement. Le théorème defect_profile_programNash prouve la défection mutuelle (defectBotBounded, defectBotBounded) comme équilibre dans le PD canonique. La version booléenne defect_profile_check et l’équivalence programNashCheck_eq_true sont des organes calculables.
Nash borné : un profil candidat, un organe, une équivalence prouvée
La famille 3 pose la question de l’équilibre dans le cadre borné. Les #check nomment la propriété universelle (defect_profile_programNash) et l’équivalence qui la relie à l’organe décidable (programNashCheck_eq_true) ; le #reduce du milieu teste le profil candidat — (defectBotBounded, defectBotBounded) — sur la famille témoin via programNashCheck basicFamily. Le point conceptuel est préparé par la famille suivante : le classement payoffRank des paiements canoniques est ce qui rend une déviation profitable détectable — sans ordre fini sur les gains, pas de Nash décidable à budget borné.
5. Famille de certificats n°4 — ordre fini des gains
Le rang fini payoffRank : PDAction × PDAction → Nat associe (cooperate, cooperate) → 3 (R), (defect, defect) → 1 (P), (cooperate, defect) → 0 (S), (defect, cooperate) → 5 (T). Le théorème payoffRank_le_iff prouve que ce rang préserve exactement l’ordre des paiements du PD canonique — pas une approximation, pas un résidu, l’équivalence sur les 16 cas.
C’est ce rang fini qui justifie l’organe programNashCheck : passer par un classement décidable évite de prétendre décider un ordre sur les réels arbitraires.
L’ordre fini des gains : la graduation qui rend le Nash décidable
payoffRank_le_iff formalise le classement des issues du dilemme ; les #reduce de la cellule visent le paramétrage canonique posé par l’en-tête (canonicalPD, valeurs T, R, P, S — 5, 3, 1, 0). L’intérêt du rang est d’être un ordre total fini : chaque sortie de partie a une position, deux gains quelconques sont comparables, et la comparaison se réduit algorithmiquement — c’est la graduation qui rend le Nash décidable sur les entrées bornées du module, là où une quantification sur des paiements non ordonnés resterait hors d’atteinte du reduce.
Exemple guidé 1 — Modifier cooperateBot pour explorer un budget non nul
Consigne. Construire un BoundedAgent nommé cooperateBudget avec le code cooperateBot et un budget 2. Vérifier avec #reduce que act cooperateBudget defectBotBounded retourne bien cooperate (le code cooperateBot ignore le budget et l’adversaire). Comparer avec act cooperateBudget mirrorBot qui doit aussi retourner cooperate.
Indice : la structure BoundedAgent se construit avec la notation ⟨code, budget⟩.
Exemple guidé 2 — Vérifier que mirrorBot à budget nul se comporte comme defectBotBounded
Consigne. Définir mirrorBudget0 : BoundedAgent := ⟨.mirror, 0⟩. Vérifier avec #reduce que outcomeBounded mirrorBudget0 cooperateBot = (defect, cooperate) (la branche budget=0 de act rend defect peu importe l’adversaire). Indice : lire la définition de act dans Bounded.lean lignes 44-48.
L’exemple guidé 2 : le miroir à budget nul, bascule en défection
Le constructeur est le point d’attention : ⟨.mirror, 0⟩ assemble un programme — le miroir qui répond à la dernière action adverse — avec un budget nul. La consigne de la section attend la vérification que ce bot se comporte comme defectBotBounded : sans budget, le miroir ne peut refléter la moindre action, et son jeu coïncide avec la défection systématique. La cellule dispose du bon instrument — #reduce outcomeBounded mirrorBudget0 cooperateBot et ses deux variantes contre defectBotBounded puis mirrorBot : l’issue de chaque paire est un terme qui se réduit, la comparaison des trois profils départage la lecture. La conclusion B du notebook annonce la bascule ; l’exercice la fait constater à l’exécution.
Exemple guidé 3 — Étendre basicFamily et vérifier l’inexploitabilité
Consigne. Définir extendedFamily : List BoundedAgent := basicFamily ++ [cooperateBudget, mirrorBudget0]. Vérifier avec #reduce unexploitableCheck mirrorBot extendedFamily. Le résultat doit être false car mirrorBudget0 (code .mirror à budget 0) force mirrorBot à dévier, ce qui rend l’inexploitabilité fausse. Comparer avec #reduce unexploitableCheck defectBotBounded extendedFamily qui doit rester true.
L’exemple guidé 3 : étendre la famille, re-scanner l’inexploitabilité
Le geste est minimal : basicFamily ++ [cooperateBudget, mirrorBudget0] — une liste, deux ajouts — et toute la question se repose. Les trois #reduce relancent unexploitableCheck sur la famille étendue contre les trois bots témoins. La conclusion C du notebook annonce la leçon : l’inexploitabilité est fragile à l’ajout d’un bot miroir à budget nul — un théorème universel (le miroir n’exploite personne dans basicFamily) et un fait booléen peuvent diverger dès qu’une entrée nouvelle entre dans le domaine. C’est le sens du couple preuve/organe : l’organe ne s’étend pas automatiquement, il se re-réduit.
Exercice 1 — Budget et code : un defectBot à budget élevé change-t-il de comportement ?
Les exemples guidés 1 et 2 ont fait varier le budget sur cooperateBot et mirror. Mais dans act, seul le code .mirror inspecte le budget — les deux autres codes l’ignorent. Construisez un defectBot doté d’un gros budget et vérifiez que son action est invariante sur les trois codes adverses, puis expliquez pourquoi le budget ne lui apporte rien.
-- Exercice 1 : un defectBot a budget eleve change-t-il de comportement ?
-- TODO etudiant : definir l'agent a gros budget
-- Indice : def defectBudget5 : BoundedAgent := ⟨.defectBot, 5⟩
-- Etape 1 : evaluer son action contre chacun des trois codes adverses
-- #reduce act defectBudget5 .cooperateBot
-- #reduce act defectBudget5 .defectBot
-- #reduce act defectBudget5 .mirror
-- Etape 2 : verifier le profil complet contre mirrorBot (budget 1)
-- #reduce outcomeBounded defectBudget5 mirrorBot
-- Etape 3 : conclure -- dans act, quelle branche du match lit le budget ?
-- Un code non conditionnel peut-il distinguer budget 0 et budget 5 ?
#check Nat -- placeholder pour verifier que la cellule compile
-- Exercice 1 : un defectBot a budget eleve change-t-il de comportement ?
-- Etape 1 : evaluer son action contre chacun des trois codes adverses
-- #reduce act defectBudget5 .cooperateBot
-- #reduce act defectBudget5 .defectBot
-- #reduce act defectBudget5 .mirror
-- Etape 2 : verifier le profil complet contre mirrorBot (budget 1)
-- #reduce outcomeBounded defectBudget5 mirrorBot
-- Etape 3 : conclure -- dans act, quelle branche du match lit le budget ?
-- Un code non conditionnel peut-il distinguer budget 0 et budget 5 ?
Nat:Type
--% env 9
Raw input{"cmd": "-- Exercice 1 : un defectBot a budget eleve change-t-il de comportement ?\n\n-- TODO etudiant : definir l'agent a gros budget\n-- Indice : def defectBudget5 : BoundedAgent := \u27e8.defectBot, 5\u27e9\n\n-- Etape 1 : evaluer son action contre chacun des trois codes adverses\n-- #reduce act defectBudget5 .cooperateBot\n-- #reduce act defectBudget5 .defectBot\n-- #reduce act defectBudget5 .mirror\n\n-- Etape 2 : verifier le profil complet contre mirrorBot (budget 1)\n-- #reduce outcomeBounded defectBudget5 mirrorBot\n\n-- Etape 3 : conclure -- dans act, quelle branche du match lit le budget ?\n-- Un code non conditionnel peut-il distinguer budget 0 et budget 5 ?\n\n#check Nat -- placeholder pour verifier que la cellule compile\n", "env": 8}Raw output{"messages":
[{"severity": "info",
"pos": {"line": 17, "column": 0},
"endPos": {"line": 17, "column": 6},
"data": "Nat : Type"}],
"env": 9}
Exercice 2 — La graduation des quatre issues : ordonner S, P, R, T par payoffRank
La section 5 a énoncé payoffRank_le_iff sans jamais manipuler la graduation. Évaluez les quatre rangs, ordonnez-les, puis vérifiez une instance du théorème d’ordre par decide : la graduation finie doit refléter exactement S < P < R < T.
-- Exercice 2 : ordonner les quatre issues par payoffRank
-- TODO etudiant : evaluer les quatre rangs (convention : payoffRank propre adversaire)
-- #reduce payoffRank cooperate cooperate
-- #reduce payoffRank cooperate defect
-- #reduce payoffRank defect cooperate
-- #reduce payoffRank defect defect
-- Etape 2 : les ordonner a la main -- quel rang joue le role de S, P, R, T ?
-- Etape 3 : verifier une instance de l'ordre par decide
-- example : payoffRank cooperate defect ≤ payoffRank defect defect := by decide
-- Indice : pourquoi decide suffit-il ici, sans reparcourir les 16 cas du theoreme ?
#check Nat -- placeholder pour verifier que la cellule compile
-- Exercice 2 : ordonner les quatre issues par payoffRank
-- TODO etudiant : evaluer les quatre rangs (convention : payoffRank propre adversaire)
-- #reduce payoffRank cooperate cooperate
-- #reduce payoffRank cooperate defect
-- #reduce payoffRank defect cooperate
-- #reduce payoffRank defect defect
-- Etape 2 : les ordonner a la main -- quel rang joue le role de S, P, R, T ?
-- Etape 3 : verifier une instance de l'ordre par decide
-- example : payoffRank cooperate defect ≤ payoffRank defect defect := by decide
-- Indice : pourquoi decide suffit-il ici, sans reparcourir les 16 cas du theoreme ?
Nat:Type
--% env 10
Raw input{"cmd": "-- Exercice 2 : ordonner les quatre issues par payoffRank\n\n-- TODO etudiant : evaluer les quatre rangs (convention : payoffRank propre adversaire)\n-- #reduce payoffRank cooperate cooperate\n-- #reduce payoffRank cooperate defect\n-- #reduce payoffRank defect cooperate\n-- #reduce payoffRank defect defect\n\n-- Etape 2 : les ordonner a la main -- quel rang joue le role de S, P, R, T ?\n\n-- Etape 3 : verifier une instance de l'ordre par decide\n-- example : payoffRank cooperate defect \u2264 payoffRank defect defect := by decide\n\n-- Indice : pourquoi decide suffit-il ici, sans reparcourir les 16 cas du theoreme ?\n\n#check Nat -- placeholder pour verifier que la cellule compile\n", "env": 9}Raw output{"messages":
[{"severity": "info",
"pos": {"line": 16, "column": 0},
"endPos": {"line": 16, "column": 6},
"data": "Nat : Type"}],
"env": 10}
Exercice 3 — Nash program-borné : le profil (defect, defect) survit-il à la famille étendue ?
L’exemple guidé 3 a scanné l’inexploitabilité de la famille étendue. Ici, mesurez l’autre prédicat sur la même famille : appliquez l’organe programNashCheck au couple (defectBotBounded, defectBotBounded), d’abord sur basicFamily seul, puis sur extendedFamily — et interprétez l’éventuelle différence de verdict.
-- Exercice 3 : programNashCheck sur la famille etendue
-- TODO etudiant : appliquer l'organe au couple (defect, defect) sur la famille de base
-- #reduce programNashCheck basicFamily defectBotBounded defectBotBounded
-- Etape 2 : recommencer sur la famille etendue de l'exemple guide 3
-- #reduce programNashCheck extendedFamily defectBotBounded defectBotBounded
-- Etape 3 : interpreter -- un cooperateBudget ou un mirrorBudget0 pourrait-il
-- devier profitablement contre defectBotBounded ? Relire act sur .defectBot.
#check Nat -- placeholder pour verifier que la cellule compile
-- Exercice 3 : programNashCheck sur la famille etendue
-- TODO etudiant : appliquer l'organe au couple (defect, defect) sur la famille de base
-- Etape 3 : interpreter -- un cooperateBudget ou un mirrorBudget0 pourrait-il
-- devier profitablement contre defectBotBounded ? Relire act sur .defectBot.
Nat:Type
--% env 11
Raw input{"cmd": "-- Exercice 3 : programNashCheck sur la famille etendue\n\n-- TODO etudiant : appliquer l'organe au couple (defect, defect) sur la famille de base\n-- #reduce programNashCheck basicFamily defectBotBounded defectBotBounded\n\n-- Etape 2 : recommencer sur la famille etendue de l'exemple guide 3\n-- #reduce programNashCheck extendedFamily defectBotBounded defectBotBounded\n\n-- Etape 3 : interpreter -- un cooperateBudget ou un mirrorBudget0 pourrait-il\n-- devier profitablement contre defectBotBounded ? Relire act sur .defectBot.\n\n#check Nat -- placeholder pour verifier que la cellule compile\n", "env": 10}Raw output{"messages":
[{"severity": "info",
"pos": {"line": 12, "column": 0},
"endPos": {"line": 12, "column": 6},
"data": "Nat : Type"}],
"env": 11}
Conclusion — ce que ce notebook a rendu visible
Quatre familles de certificats sont rendues par le noyau lean4-wsl, sur une machine où le lake game_theory_lean a été construit, chacune à deux niveaux — preuve universelle et organe booléen fini :
A. cooperateBot à budget non nul — comportement indépendant du budget. B. mirrorBot à budget nul — bascule en défection systématique. C. Famille étendue — l’inexploitabilité est fragile à l’ajout d’un bot miroir à budget nul.
Les trois exercices non résolus qui suivent reprennent le flambeau sur des facettes laissées ouvertes : l’invariance du budget sur un code non conditionnel, la graduation payoffRank manipulée à la main, et le statut Nash de la famille étendue.
Verdict :EXEC_PROVED — obtenu par exécution authentique de ce notebook, sur le noyau lean4-wsl, après construction du lake game_theory_lean (Mathlib db584cd6d46c, toolchain v4.33.0). Les sorties visibles ici sont celles de cette exécution : chaque #check rend le type annoncé, chaque #reduce rend la valeur booléenne attendue. Le module ProgramGames.Bounded est entièrement chargé, ses théorèmes (cooperate_cooperate, defect_defect, mirror_mirror, defectBotBounded_unexploitable, mirror_basicFamily_unexploitable, defect_profile_programNash, payoffRank_le_iff, plus l’organe defect_profile_check et l’équivalence programNashCheck_eq_true) compilent dans le noyau, et aucune cellule ne franchit la limite Löb/Gödel posée en en-tête du module.