Lean-36 : structures mathematiques finies — l’Annexe A de Tegmark en Lean
Compagnon natif du lake tegmark_muh_lean : les modules MUH.Structure, MUH.Encoding, MUH.Cyclic, MUH.Boolean, MUH.Aut et MUH.Decidable y sont importes et executes dans un kernel Lean 4 reel (lean4-wsl). Chaque definition et chaque theoreme est interroge par #check, #eval ou #print axioms — les sorties de ce notebook sont des sorties du compilateur Lean, pas de la prose a propos de Lean.
La source
Tegmark (2007), The Mathematical Universe (arXiv:0704.0646), Annexe A : la definition d’une structure mathematique finie, ses exemples canoniques (algebre de Boole, groupes cycliques), son schema d’encodage, et une phrase qui porte tout le reste :
« There is a simple halting algorithm for determining whether any two finite mathematical structure definitions are equivalent. »
C’est la base de la CUH (Computable Universe Hypothesis, §VII.E) : si une structure est un jeu fini d’ensembles et de relations, alors « etre cette structure » est une propriete calculable.
Ce que ce notebook fait, et ce qu’il refuse de faire
Il exhibe ce qui est reellement formalise dans le lake — et il mesure la frontiere la ou elle est. La section 5 execute le decideur livre et montre, par sa sortie, qu’il ne decide pas encore ce que l’article annonce. Un notebook qui ne montrerait que les parties vertes vendrait une bibliotheque plus forte qu’elle n’est ; le lake declare lui-meme son scope, et cette visite le lit tel qu’il est.
Trois exercices ferment la visite (section 6), tous verifiables au noyau.
1. La definition d’une structure finie (Annexe A §1)
Une structure est un triplet : un nombre d’ensembles, le cardinal de chacun, et une liste finie de relations generatrices. Chaque relation porte une arite, le type de chacun de ses arguments et le type de sa sortie — plus la table des valeurs, qui est ce que le papier appelle le value array.
Papier (Annexe A §1)
Lean (MUH.Structure)
Ce qu’il dit
nombre d’ensembles n
Structure.nSets
combien d’ensembles porte la structure
cardinaux sizes
Structure.sizes : Fin nSets → Nat
la taille de chaque ensemble
ensembles non vides
Structure.sizes_pos
∀ i, 0 < sizes i — une relation exige un domaine non vide
relations generatrices
Structure.rels : List (Rel nSets sizes)
la liste finie des generateurs
arite / types
RelSig.arity, RelSig.args, RelSig.out
la signature de la relation
value array
Rel.table
une entree par tuple d’arguments
Le champ table est le point subtil, et c’est une erreur de typage corrigee dans l’histoire du lake : sa source est un produit dependant d’arguments, (i : Fin sig.arity) → Fin (sizes (sig.args i)), pas une famille d’indices. Une version anterieure curryfiait la table, ce qui traitait l’indice d’argument comme un element : la relation NOT y devenait la constante 1, et la table de C₃ ne prenait que 2 valeurs sur 3. La cellule suivante interroge le noyau sur la forme livree.
-- Tete de session : toutes les importations viennent ici.
import MUH.Structure
import MUH.Encoding
import MUH.Cyclic
import MUH.Boolean
import MUH.Aut
import MUH.Decidable
-- La signature telle que le noyau la connait :
#check @Structure
#check @Rel
#check @RelSig
-- Les accesseurs que le module ajoute (namespace Structure) :
#check Structure.arity
#check Structure.out
#check Structure.argType
-- Tete de session : toutes les importations viennent ici.
importMUH.Structure
importMUH.Encoding
importMUH.Cyclic
importMUH.Boolean
importMUH.Aut
importMUH.Decidable
-- La signature telle que le noyau la connait :
Structure:Type
Rel:(n:Nat)→Sizesn→Type
RelSig:Nat→Type
-- Les accesseurs que le module ajoute (namespace Structure) :
#check @Structure rend Structure : Type : la structure est un type, pas une classe de types — deux structures sont donc des objets que l’on peut comparer, encoder et transformer, ce que la suite du notebook exploite.
#check @Rel rend Rel : (n : Nat) → Sizes n → Type. Le Sizes n en argument est ce qui rend la table typable : c’est lui qui fixe Fin (sizes sig.out) au codomaine. Une relation n’a donc pas de sens hors de la structure qui declare ses ensembles — exactement la dependance que le papier exprime en disant que les Sᵢ sont donnes avec la definition.
Les trois accesseurs (arity, out, argType) sont les projections utiles pour lire une relation sans connaitre sa construction.
2. Encodage et complexite (Annexe A §c et §d)
Tegmark propose un encodage aplati d’une structure — un en-tete, la definition de chaque ensemble, puis la definition de chaque relation avec sa table de valeurs — et la complexite H(s) = Σᵢ log₂(2 + kᵢ), ou kᵢ parcourt l’encodage.
Le lake livre Encoding := List Nat et deux fonctions : encodeCardinalities (le preambule de l’encodage, nSets suivi des cardinaux) et complexity. Le lake declare explicitement que l’encodage complet des relations est hors de son scope — la cellule suivante montre ce qui est calcule, et rien de plus.
-- Le preambule de l'encodage (§c), pour deux structures du papier :
#eval Encoding.encodeCardinalities Cyclic.c3
#eval Encoding.encodeCardinalities Boolean.sheffer
-- La complexite H(s) = somme_i log2(2 + k_i) (§d), lue sur l'encodage de C3 :
#eval Encoding.complexity (Encoding.encodeCardinalities Cyclic.c3)
#check @Encoding.complexity
-- Le preambule de l'encodage (§c), pour deux structures du papier :
[1,3]
[1,2]
-- La complexite H(s) = somme_i log2(2 + k_i) (§d), lue sur l'encodage de C3 :
Les deux encodages sont des listes courtes : [1, 3] pour C₃ — un ensemble, de cardinal 3 — et [1, 2] pour l’algebre de Boole de Sheffer — un ensemble, de cardinal 2. C’est exactement le preambule annonce : l’en-tete nSets, puis un entier par ensemble.
complexity rend 3 sur [1, 3] : les deux entiers contribuent log₂(2+1) = 1 et log₂(2+3) = 2 (le Nat.log2 tronque). La fonction est donc bien celle du §d, appliquee a l’encodage qu’elle recoit — mais comme le preambule ne decrit pas les relations, ce 3 mesure la taille declaree des ensembles, pas la richesse de la structure. Le lake l’ecrit : encoder une relation demanderait l’enumeration de son produit d’arguments, ce qu’il ne fait pas encore.
3. Les exemples du papier : C₃, C₂, NAND
Tegmark (§2a, §2b) exhibe deux familles d’exemples : l’algebre de Boole a deux elements, avec ses huit relations generatrices ou son unique generateur de Sheffer (NAND), et les groupes cycliquesC₂ et C₃, donnes par leur table d’addition.
Ces structures sont livrees comme des Structure — donc comme des objets du type interroge en section 1. La cellule suivante evalue leurs tables et les rend en clair : une matrice d’entiers, lisible par un humain.
-- La table d'addition de C3 (3x3), lue comme une matrice d'entiers :
#eval List.ofFn (fun i : Fin 3 => List.ofFn (fun j : Fin 3 => (Cyclic.mult3Table i j).val))
-- Sa diagonale : les elements neutres de chaque ligne
#eval List.ofFn (fun i : Fin 3 => (Cyclic.mult3Table i i).val)
-- La table de C2 (2x2) et celle du seul generateur NAND (2x2) :
#eval List.ofFn (fun i : Fin 2 => List.ofFn (fun j : Fin 2 => (Cyclic.mult2Table i j).val))
#eval List.ofFn (fun i : Fin 2 => List.ofFn (fun j : Fin 2 => (Boolean.nandTable i j).val))
-- Les structures elles-memes, et les cardinaux qui les separent :
#check @Cyclic.c3
#check @Cyclic.c2
#check @Boolean.sheffer
#eval Cyclic.c3Sizes 0
#eval Cyclic.c2Sizes 0
-- La table d'addition de C3 (3x3), lue comme une matrice d'entiers :
[[0,1,2],[1,2,0],[2,0,1]]
-- Sa diagonale : les elements neutres de chaque ligne
[0,2,1]
-- La table de C2 (2x2) et celle du seul generateur NAND (2x2) :
[[0,1],[1,0]]
[[1,1],[1,0]]
-- Les structures elles-memes, et les cardinaux qui les separent :
Cyclic.c3:Structure
Cyclic.c2:Structure
Boolean.sheffer:Structure
3
2
--% env 2
Raw input{"cmd": "-- La table d'addition de C3 (3x3), lue comme une matrice d'entiers :\n#eval List.ofFn (fun i : Fin 3 => List.ofFn (fun j : Fin 3 => (Cyclic.mult3Table i j).val))\n\n-- Sa diagonale : les elements neutres de chaque ligne\n#eval List.ofFn (fun i : Fin 3 => (Cyclic.mult3Table i i).val)\n\n-- La table de C2 (2x2) et celle du seul generateur NAND (2x2) :\n#eval List.ofFn (fun i : Fin 2 => List.ofFn (fun j : Fin 2 => (Cyclic.mult2Table i j).val))\n#eval List.ofFn (fun i : Fin 2 => List.ofFn (fun j : Fin 2 => (Boolean.nandTable i j).val))\n\n-- Les structures elles-memes, et les cardinaux qui les separent :\n#check @Cyclic.c3\n#check @Cyclic.c2\n#check @Boolean.sheffer\n#eval Cyclic.c3Sizes 0\n#eval Cyclic.c2Sizes 0", "env": 1}Raw output{"messages":
[{"severity": "info",
"pos": {"line": 2, "column": 0},
"endPos": {"line": 2, "column": 5},
"data": "[[0, 1, 2], [1, 2, 0], [2, 0, 1]]"},
{"severity": "info",
"pos": {"line": 5, "column": 0},
"endPos": {"line": 5, "column": 5},
"data": "[0, 2, 1]"},
{"severity": "info",
"pos": {"line": 8, "column": 0},
"endPos": {"line": 8, "column": 5},
"data": "[[0, 1], [1, 0]]"},
{"severity": "info",
"pos": {"line": 9, "column": 0},
"endPos": {"line": 9, "column": 5},
"data": "[[1, 1], [1, 0]]"},
{"severity": "info",
"pos": {"line": 12, "column": 0},
"endPos": {"line": 12, "column": 6},
"data": "Cyclic.c3 : Structure"},
{"severity": "info",
"pos": {"line": 13, "column": 0},
"endPos": {"line": 13, "column": 6},
"data": "Cyclic.c2 : Structure"},
{"severity": "info",
"pos": {"line": 14, "column": 0},
"endPos": {"line": 14, "column": 6},
"data": "Boolean.sheffer : Structure"},
{"severity": "info",
"pos": {"line": 15, "column": 0},
"endPos": {"line": 15, "column": 5},
"data": "3"},
{"severity": "info",
"pos": {"line": 16, "column": 0},
"endPos": {"line": 16, "column": 5},
"data": "2"}],
"env": 2}
Lecture du resultat
La table de C₃ sort complete — [[0, 1, 2], [1, 2, 0], [2, 0, 1]] — et cela seul tranche la question du typage soulevee en section 1 : les trois lignes sont la, chacune avec trois valeurs, alors que la version curryfiee n’en produisait que deux. La ligne [2, 0, 1] expose precisement les couples que le typage degenere rendait inaccessibles.
Sa diagonale vaut [0, 2, 1] : 0+0 = 0, 1+1 = 2, 2+2 = 1. Dans un groupe cyclique d’ordre 3, le neutre est 0 et les elements non neutres sont 1 et 2 — 2+2 = 4 ≡ 1 : la diagonale porte donc l’information d’inversion, exactement ce que Tegmark (§2b, in fine) lit sur la table.
C₂ sort [[0, 1], [1, 0]] — son propre inverse, 1+1 = 0. La table de NAND sort [[1, 1], [1, 0]] : une seule entree fausse (0), celle de (1, 1), qui est la definition meme de not-and. Les deux structures ont un ensemble, mais de cardinaux differents — c3Sizes 0 = 3 contre c2Sizes 0 = 2 — et c’est cette difference que la section 5 va confronter au decideur livre.
4. Aut(S) est un groupe — le « easy to see » du papier
Tegmark (Annexe A §1 in fine) expedit l’enonce en une incidente :
« Aut(S) is a group (easy to see). »
Un automorphisme est ici une famille de permutations, une par ensemble, qui preserve la table de chaque relation : la valeur d’une relation sur des arguments transformes est l’image de sa valeur sur les arguments d’origine. Trois objets portent l’enonce dans le lake : le type IsAutomorphism, l’identite autId, et la composition autComp.
-- Le type de l'automorphisme, tel que le module le definit :
#check @Aut.IsAutomorphism
#check @Aut.StructureOn
-- L'identite et la composition : les deux halves du groupe.
#check @Aut.autId
#check @Aut.autComp
-- Audit axiomatique des objets publics du lake (aucun sorry, aucun native_decide) :
#print axioms Cyclic.c3
#print axioms Cyclic.mult3Table
#print axioms Aut.autComp
-- Le type de l'automorphisme, tel que le module le definit :
@Aut.IsAutomorphism:{n:Nat}→Aut.StructureOnn→Type
Aut.StructureOn:Nat→Type
-- L'identite et la composition : les deux halves du groupe.
-- Audit axiomatique des objets publics du lake (aucun sorry, aucun native_decide) :
'Cyclic.c3'dependsonaxioms:[propext]
'Cyclic.mult3Table'dependsonaxioms:[propext]
'Aut.autComp'doesnotdependonanyaxioms
--% env 3
Raw input{"cmd": "-- Le type de l'automorphisme, tel que le module le definit :\n#check @Aut.IsAutomorphism\n#check @Aut.StructureOn\n\n-- L'identite et la composition : les deux halves du groupe.\n#check @Aut.autId\n#check @Aut.autComp\n\n-- Audit axiomatique des objets publics du lake (aucun sorry, aucun native_decide) :\n#print axioms Cyclic.c3\n#print axioms Cyclic.mult3Table\n#print axioms Aut.autComp", "env": 2}Raw output{"messages":
[{"severity": "info",
"pos": {"line": 2, "column": 0},
"endPos": {"line": 2, "column": 6},
"data": "@Aut.IsAutomorphism : {n : Nat} → Aut.StructureOn n → Type"},
{"severity": "info",
"pos": {"line": 3, "column": 0},
"endPos": {"line": 3, "column": 6},
"data": "Aut.StructureOn : Nat → Type"},
{"severity": "info",
"pos": {"line": 6, "column": 0},
"endPos": {"line": 6, "column": 6},
"data":
"@Aut.autId : {n : Nat} → (S : Aut.StructureOn n) → Aut.IsAutomorphism S"},
{"severity": "info",
"pos": {"line": 7, "column": 0},
"endPos": {"line": 7, "column": 6},
"data":
"@Aut.autComp : {n : Nat} → {S : Aut.StructureOn n} → Aut.IsAutomorphism S → Aut.IsAutomorphism S → Aut.IsAutomorphism S"},
{"severity": "info",
"pos": {"line": 10, "column": 0},
"endPos": {"line": 10, "column": 6},
"data": "'Cyclic.c3' depends on axioms: [propext]"},
{"severity": "info",
"pos": {"line": 11, "column": 0},
"endPos": {"line": 11, "column": 6},
"data": "'Cyclic.mult3Table' depends on axioms: [propext]"},
{"severity": "info",
"pos": {"line": 12, "column": 0},
"endPos": {"line": 12, "column": 6},
"data": "'Aut.autComp' does not depend on any axioms"}],
"env": 3}
Lecture du resultat
#check @Aut.IsAutomorphism rend {n : Nat} → Aut.StructureOn n → Type : un automorphisme est indexe par la structure qu’il preserve, il n’existe pas « dans le vide ». Les trois champs annonces en prose y sont : les permutations φ, leur injectivite φ_inj, et la preservation des tables rel_pres.
Les deux moities de l’enonce sortent ensuite. autId rend l’identite : φ := fun _ x => x, injective par Function.injective_id, et la preservation des tables se ferme par rfl — parce que id transporte un argument en lui-meme. autComp compose deux automorphismes : (ψ ∘ φ)_i = ψ_i ∘ φ_i, injective comme composee d’injectives, et la preservation des tables s’obtient en appliquant les deux hypotheses successivement. La fermeture sous composition est tout le « easy to see » : avec l’identite, elle fait de Aut(S) un sous-monoide du groupe symetrique Π i, Sym(S.sizes i).
L’audit axiomatique est le controle qui rend la phrase verifiable plutot que plausible : Cyclic.c3 et Cyclic.mult3Table dependent de [propext] — la propositional extensionality, consommee par les preuves by decide du typage des indices — tandis que Aut.autCompne depend d’aucun axiome. Aucun sorry, aucun native_decide : les preuves de cette section sont des preuves.
5. La frontiere : une decidabilite annoncee, un decideur absent
Voici la partie que beaucoup de notebooks de demonstration omettraient. Le lake MUH.lean annonce, en tete de son propre module racine, que MUH.Decidable est un squelette enumeratif documente — et que le code livre n’est pas l’algorithme halting du papier :
decideEq compare nSets, ClosedUnderComp est trivial — pas un algorithme enumeratif haltant.
La cellule suivante prend cette declaration au mot et l’execute.
-- Le decideur livre, et sa signature :
#check @Decidable.decideEq
-- L'espace de tables annonce pour une relation binaire booleenne :
#check @Decidable.boolBinaryTableCount
#eval Decidable.boolBinaryTableCount
-- Le seul decideur non-stub du module, sur une structure a 1 element :
#eval Decidable.trivialStructure.nSets
-- L'experience : deux structures NON equivalentes, presentees au decideur.
-- C3 a 3 elements par ensemble, l'algebre de Sheffer en a 2.
#eval Decidable.decideEq Cyclic.c3 Cyclic.c2
#eval Decidable.decideEq Cyclic.c3 Boolean.sheffer
-- Controle : le decideur ne distingue pas davantage une structure d'elle-meme.
#eval Decidable.decideEq Cyclic.c3 Cyclic.c3
#print axioms Decidable.decideEq
-- Le decideur livre, et sa signature :
Decidable.decideEq:Structure→Structure→Bool
-- L'espace de tables annonce pour une relation binaire booleenne :
Decidable.boolBinaryTableCount:Nat
16
-- Le seul decideur non-stub du module, sur une structure a 1 element :
1
-- L'experience : deux structures NON equivalentes, presentees au decideur.
-- C3 a 3 elements par ensemble, l'algebre de Sheffer en a 2.
true
true
-- Controle : le decideur ne distingue pas davantage une structure d'elle-meme.
true
'Decidable.decideEq'doesnotdependonanyaxioms
--% env 4
Raw input{"cmd": "-- Le decideur livre, et sa signature :\n#check @Decidable.decideEq\n\n-- L'espace de tables annonce pour une relation binaire booleenne :\n#check @Decidable.boolBinaryTableCount\n#eval Decidable.boolBinaryTableCount\n\n-- Le seul decideur non-stub du module, sur une structure a 1 element :\n#eval Decidable.trivialStructure.nSets\n\n-- L'experience : deux structures NON equivalentes, presentees au decideur.\n-- C3 a 3 elements par ensemble, l'algebre de Sheffer en a 2.\n#eval Decidable.decideEq Cyclic.c3 Cyclic.c2\n#eval Decidable.decideEq Cyclic.c3 Boolean.sheffer\n\n-- Controle : le decideur ne distingue pas davantage une structure d'elle-meme.\n#eval Decidable.decideEq Cyclic.c3 Cyclic.c3\n#print axioms Decidable.decideEq", "env": 3}Raw output{"messages":
[{"severity": "info",
"pos": {"line": 2, "column": 0},
"endPos": {"line": 2, "column": 6},
"data": "Decidable.decideEq : Structure → Structure → Bool"},
{"severity": "info",
"pos": {"line": 5, "column": 0},
"endPos": {"line": 5, "column": 6},
"data": "Decidable.boolBinaryTableCount : Nat"},
{"severity": "info",
"pos": {"line": 6, "column": 0},
"endPos": {"line": 6, "column": 5},
"data": "16"},
{"severity": "info",
"pos": {"line": 9, "column": 0},
"endPos": {"line": 9, "column": 5},
"data": "1"},
{"severity": "info",
"pos": {"line": 13, "column": 0},
"endPos": {"line": 13, "column": 5},
"data": "true"},
{"severity": "info",
"pos": {"line": 14, "column": 0},
"endPos": {"line": 14, "column": 5},
"data": "true"},
{"severity": "info",
"pos": {"line": 17, "column": 0},
"endPos": {"line": 17, "column": 5},
"data": "true"},
{"severity": "info",
"pos": {"line": 18, "column": 0},
"endPos": {"line": 18, "column": 6},
"data": "'Decidable.decideEq' does not depend on any axioms"}],
"env": 4}
Lecture du resultat
Les deux experiences rendent true — et c’est le resultat le plus instructif de ce notebook.
decideEq Cyclic.c3 Cyclic.c2 compare une structure a ensembles de cardinal 3 et une structure de cardinal 2, dont les tables different sur toutes leurs entrees sauf une. Le decideur repond qu’elles sont equivalentes, parce qu’il ne regarde que nSets — le seul champ que son corps teste (s1.nSets == s2.nSets). Le second appel, contre l’algebre de Sheffer, rend le meme true pour la meme raison. Le troisieme appel ne prouve rien : il rend true sur une structure comparee a elle-meme, ce qu’un decideur correct ferait aussi.
Ce n’est pas un bug a corriger dans ce notebook, c’est l’etat declare du module : boolBinaryTableCount rend 16 — l’espace des tables binaires booleennes, celui qu’une enumeration exhaustive parcourrait — mais aucune enumeration n’est ecrite. L’#print axioms de la section precedente est coherent avec cela : Decidable.decideEq ne depend d’aucun axiome, puisqu’il ne prouve rien.
Le lake a choisi de declarer l’ecart plutot que de le masquer : la revision des docstrings (issue #16958, PR #17431) a reecrit l’annonce pour qu’elle corresponde au code, et le module racine porte desormais la phrase « pas un algorithme enumeratif haltant ». C’est l’inverse d’une fuite : c’est une frontiere documentee, et elle est ouverte — la section 6 propose de la franchir.
6. Exemple resolu, puis exercices
La frontiere de la section 5 porte sur les structures generales : le decideur du lake compare des cardinaux et le module le declare. Pour les tables binaires, en revanche, un vrai decideur existe et tient en une ligne — l’exemple resolu ci-dessous le construit et l’execute. Le residuel de #16753 (critere 3) est donc l’arbitraire des cardinalites, pas le cas binaire. Suivent trois exercices, tous verifiables au noyau : chacun est accompagne de la cible telle que Lean la lit, sous la forme d’un #check. Completer l’exercice signifie ecrire du Lean qui compile — pas du pseudo-code.
Exemple resolu — un vrai decideur pour les tables binaires
Le stub de la section 5 compare des cardinaux d’ensembles. Ecrire a la place une egalite de tables, qui parcourt les quatre entrees au lieu de s’arreter au nombre d’ensembles — c’est l’ancien exercice 1, resolu ici parce que la frontiere qu’il touche est plus lisible montee qu’indiquee.
-- Exemple resolu (ancien exercice 1) : un decideur fonctionnel pour les
-- tables binaires. Le stub de la section 5 comparait des cardinaux
-- d'ensembles ; celui-ci parcourt les quatre entrees des tables.
def sameBinaryTable (r₁ r₂ : Fin 2 → Fin 2 → Fin 2) : Bool :=
decide (∀ i j, r₁ i j = r₂ i j)
-- La ou le stub de la section 5 rend `true` pour C₂ vs NAND (meme nombre
-- d'ensembles), le decideur fonctionnel rend `false` : les tables different.
#eval sameBinaryTable Cyclic.mult2Table Boolean.nandTable
-- Et `true` sur une table comparee a elle-meme.
#eval sameBinaryTable Cyclic.mult2Table Cyclic.mult2Table
-- Exemple resolu (ancien exercice 1) : un decideur fonctionnel pour les
-- tables binaires. Le stub de la section 5 comparait des cardinaux
-- d'ensembles ; celui-ci parcourt les quatre entrees des tables.
defsameBinaryTable(r₁r₂:Fin2→Fin2→Fin2):Bool:=
decide(∀ij,r₁ij=r₂ij)
-- La ou le stub de la section 5 rend `true` pour C₂ vs NAND (meme nombre
-- d'ensembles), le decideur fonctionnel rend `false` : les tables different.
false
-- Et `true` sur une table comparee a elle-meme.
true
--% env 5
Raw input{"cmd": "-- Exemple resolu (ancien exercice 1) : un decideur fonctionnel pour les\n-- tables binaires. Le stub de la section 5 comparait des cardinaux\n-- d'ensembles ; celui-ci parcourt les quatre entrees des tables.\ndef sameBinaryTable (r\u2081 r\u2082 : Fin 2 \u2192 Fin 2 \u2192 Fin 2) : Bool :=\n decide (\u2200 i j, r\u2081 i j = r\u2082 i j)\n\n-- La ou le stub de la section 5 rend `true` pour C\u2082 vs NAND (meme nombre\n-- d'ensembles), le decideur fonctionnel rend `false` : les tables different.\n#eval sameBinaryTable Cyclic.mult2Table Boolean.nandTable\n-- Et `true` sur une table comparee a elle-meme.\n#eval sameBinaryTable Cyclic.mult2Table Cyclic.mult2Table\n", "env": 4}Raw output{"messages":
[{"severity": "info",
"pos": {"line": 9, "column": 0},
"endPos": {"line": 9, "column": 5},
"data": "false"},
{"severity": "info",
"pos": {"line": 11, "column": 0},
"endPos": {"line": 11, "column": 5},
"data": "true"}],
"env": 5}
Exercice 1 — la correction du decideur (soundness)
L’exemple resolu ci-dessus decide l’egalite fonctionnelle des tables. Prouver qu’il ne se trompe pas : sameBinaryTable r₁ r₂ = true si et seulement si les tables coincident entree par entree.
-- TODO etudiant (exercice 1) : prouver la correction du decideur.
-- Decommentez l'enonce, puis ecrivez la preuve.
--
-- Etape 1 : le sens direct. De `sameBinaryTable r₁ r₂ = true`, tirez
-- `∀ i j, r₁ i j = r₂ i j`. Que dit exactement la definition
-- du decideur, et quelle tactique l'exploite ?
-- Etape 2 : le sens retour. De `∀ i j, r₁ i j = r₂ i j`, reconstruisez
-- l'egalite de Bool.
--
-- theorem sameBinaryTable_iff (r₁ r₂ : Fin 2 → Fin 2 → Fin 2) :
-- sameBinaryTable r₁ r₂ = true ↔ ∀ i j, r₁ i j = r₂ i j := by
-- TODO etudiant
-- La cible de l'exercice, telle que Lean la lit.
#check (∀ (r₁ r₂ : Fin 2 → Fin 2 → Fin 2),
sameBinaryTable r₁ r₂ = true ↔ ∀ i j, r₁ i j = r₂ i j)
-- Cellule neutre : rien a faire ici, elle sert a garder la session verte.
example : True := trivial
-- TODO etudiant (exercice 1) : prouver la correction du decideur.
-- Decommentez l'enonce, puis ecrivez la preuve.
--
-- Etape 1 : le sens direct. De `sameBinaryTable r₁ r₂ = true`, tirez
-- `∀ i j, r₁ i j = r₂ i j`. Que dit exactement la definition
-- du decideur, et quelle tactique l'exploite ?
-- Etape 2 : le sens retour. De `∀ i j, r₁ i j = r₂ i j`, reconstruisez
-- l'egalite de Bool.
--
-- theorem sameBinaryTable_iff (r₁ r₂ : Fin 2 → Fin 2 → Fin 2) :
-- sameBinaryTable r₁ r₂ = true ↔ ∀ i j, r₁ i j = r₂ i j := by
-- Cellule neutre : rien a faire ici, elle sert a garder la session verte.
example:True:=trivial
--% env 6
Raw input{"cmd": "-- TODO etudiant (exercice 1) : prouver la correction du decideur.\n-- Decommentez l'enonce, puis ecrivez la preuve.\n--\n-- Etape 1 : le sens direct. De `sameBinaryTable r\u2081 r\u2082 = true`, tirez\n-- `\u2200 i j, r\u2081 i j = r\u2082 i j`. Que dit exactement la definition\n-- du decideur, et quelle tactique l'exploite ?\n-- Etape 2 : le sens retour. De `\u2200 i j, r\u2081 i j = r\u2082 i j`, reconstruisez\n-- l'egalite de Bool.\n--\n-- theorem sameBinaryTable_iff (r\u2081 r\u2082 : Fin 2 \u2192 Fin 2 \u2192 Fin 2) :\n-- sameBinaryTable r\u2081 r\u2082 = true \u2194 \u2200 i j, r\u2081 i j = r\u2082 i j := by\n-- TODO etudiant\n\n-- La cible de l'exercice, telle que Lean la lit.\n#check (\u2200 (r\u2081 r\u2082 : Fin 2 \u2192 Fin 2 \u2192 Fin 2),\n sameBinaryTable r\u2081 r\u2082 = true \u2194 \u2200 i j, r\u2081 i j = r\u2082 i j)\n\n-- Cellule neutre : rien a faire ici, elle sert a garder la session verte.\nexample : True := trivial\n", "env": 5}Raw output{"messages":
[{"severity": "info",
"pos": {"line": 15, "column": 0},
"endPos": {"line": 15, "column": 6},
"data":
"∀ (r₁ r₂ : Fin 2 → Fin 2 → Fin 2), sameBinaryTable r₁ r₂ = true ↔ ∀ (i j : Fin 2), r₁ i j = r₂ i j : Prop"}],
"env": 6}
Exercice 2 — l’automorphisme non trivial de C₃
Aut.autComp (section 4) compose des automorphismes deja donnes. Ici il s’agit d’en construire un : le decalage φ(0) = 1, φ(1) = 2, φ(2) = 0, et de prouver qu’il preserve l’addition — c’est-a-dire que φ(a + b) = φ(a) + φ(b) pour les neuf couples.
-- TODO etudiant (exercice 2) : exhiber le decalage comme automorphisme de C3.
-- Decommentez les deux enonces, completez les trous `_`, puis ecrivez la preuve.
--
-- Etape 1 : completez `shift3`. L'automorphisme non trivial du groupe
-- cyclique fait avancer chaque element d'un cran, modulo 3 ;
-- Lean exige une preuve de borne pour chacun des trois cas.
-- Etape 2 : montrez que ce decalage preserve la table livree. Les neuf
-- couples (a, b) se ferment par une tactique de decision.
--
-- def shift3 : Fin 3 → Fin 3 := fun
-- | ⟨0, _⟩ => ⟨_, by decide⟩
-- | ⟨1, _⟩ => ⟨_, by decide⟩
-- | ⟨2, _⟩ => ⟨_, by decide⟩
--
-- example (a b : Fin 3) :
-- shift3 (Cyclic.mult3Table a b) = Cyclic.mult3Table (shift3 a) (shift3 b) := by
-- TODO etudiant
-- La cible, telle que Lean la lit : la commutativite de la table livree.
-- (Le groupe C3 est abelien ; le decalage preserve aussi cette symetrie.)
#check (∀ a b : Fin 3, Cyclic.mult3Table a b = Cyclic.mult3Table b a)
-- Cellule neutre : rien a faire ici, elle sert a garder la session verte.
example : True := trivial
-- TODO etudiant (exercice 2) : exhiber le decalage comme automorphisme de C3.
-- Decommentez les deux enonces, completez les trous `_`, puis ecrivez la preuve.
--
-- Etape 1 : completez `shift3`. L'automorphisme non trivial du groupe
-- cyclique fait avancer chaque element d'un cran, modulo 3 ;
-- Lean exige une preuve de borne pour chacun des trois cas.
-- Etape 2 : montrez que ce decalage preserve la table livree. Les neuf
-- couples (a, b) se ferment par une tactique de decision.
--
-- def shift3 : Fin 3 → Fin 3 := fun
-- | ⟨0, _⟩ => ⟨_, by decide⟩
-- | ⟨1, _⟩ => ⟨_, by decide⟩
-- | ⟨2, _⟩ => ⟨_, by decide⟩
--
-- example (a b : Fin 3) :
-- shift3 (Cyclic.mult3Table a b) = Cyclic.mult3Table (shift3 a) (shift3 b) := by
-- TODO etudiant
-- La cible, telle que Lean la lit : la commutativite de la table livree.
-- (Le groupe C3 est abelien ; le decalage preserve aussi cette symetrie.)
-- Cellule neutre : rien a faire ici, elle sert a garder la session verte.
example:True:=trivial
--% env 7
Raw input{"cmd": "-- TODO etudiant (exercice 2) : exhiber le decalage comme automorphisme de C3.\n-- Decommentez les deux enonces, completez les trous `_`, puis ecrivez la preuve.\n--\n-- Etape 1 : completez `shift3`. L'automorphisme non trivial du groupe\n-- cyclique fait avancer chaque element d'un cran, modulo 3 ;\n-- Lean exige une preuve de borne pour chacun des trois cas.\n-- Etape 2 : montrez que ce decalage preserve la table livree. Les neuf\n-- couples (a, b) se ferment par une tactique de decision.\n--\n-- def shift3 : Fin 3 \u2192 Fin 3 := fun\n-- | \u27e80, _\u27e9 => \u27e8_, by decide\u27e9\n-- | \u27e81, _\u27e9 => \u27e8_, by decide\u27e9\n-- | \u27e82, _\u27e9 => \u27e8_, by decide\u27e9\n--\n-- example (a b : Fin 3) :\n-- shift3 (Cyclic.mult3Table a b) = Cyclic.mult3Table (shift3 a) (shift3 b) := by\n-- TODO etudiant\n\n-- La cible, telle que Lean la lit : la commutativite de la table livree.\n-- (Le groupe C3 est abelien ; le decalage preserve aussi cette symetrie.)\n#check (\u2200 a b : Fin 3, Cyclic.mult3Table a b = Cyclic.mult3Table b a)\n\n-- Cellule neutre : rien a faire ici, elle sert a garder la session verte.\nexample : True := trivial", "env": 6}Raw output{"messages":
[{"severity": "info",
"pos": {"line": 21, "column": 0},
"endPos": {"line": 21, "column": 6},
"data":
"∀ (a b : Fin 3), Cyclic.mult3Table a b = Cyclic.mult3Table b a : Prop"}],
"env": 7}
Exercice 3 — NAND suffit a exprimer la negation
Tegmark (eq. A2) donne les huit relations de l’algebre de Boole a partir du seul NAND. Sa premiere identite est ¬X = X|X. Le lake livre les deux tables (nandTable, notTable) comme des objets separes : les relier est l’exercice.
-- TODO etudiant (exercice 3) : montrer que le NAND de x avec lui-meme rend
-- la negation de x, sur les deux valeurs de l'ensemble a deux elements.
-- Decommentez l'enonce, puis ecrivez la preuve.
--
-- Etape 1 : l'ensemble a deux elements a exactement deux habitants ;
-- une tactique de decision les couvre tous les deux.
--
-- example (x : Fin 2) : Boolean.nandTable x x = Boolean.notTable x := by
-- TODO etudiant
-- La cible, telle que Lean la lit.
#check (∀ x : Fin 2, Boolean.nandTable x x = Boolean.notTable x)
-- Cellule neutre : rien a faire ici, elle sert a garder la session verte.
example : True := trivial
-- TODO etudiant (exercice 3) : montrer que le NAND de x avec lui-meme rend
-- la negation de x, sur les deux valeurs de l'ensemble a deux elements.
-- Decommentez l'enonce, puis ecrivez la preuve.
--
-- Etape 1 : l'ensemble a deux elements a exactement deux habitants ;
-- une tactique de decision les couvre tous les deux.
--
-- example (x : Fin 2) : Boolean.nandTable x x = Boolean.notTable x := by
-- Cellule neutre : rien a faire ici, elle sert a garder la session verte.
example:True:=trivial
--% env 8
Raw input{"cmd": "-- TODO etudiant (exercice 3) : montrer que le NAND de x avec lui-meme rend\n-- la negation de x, sur les deux valeurs de l'ensemble a deux elements.\n-- Decommentez l'enonce, puis ecrivez la preuve.\n--\n-- Etape 1 : l'ensemble a deux elements a exactement deux habitants ;\n-- une tactique de decision les couvre tous les deux.\n--\n-- example (x : Fin 2) : Boolean.nandTable x x = Boolean.notTable x := by\n-- TODO etudiant\n\n-- La cible, telle que Lean la lit.\n#check (\u2200 x : Fin 2, Boolean.nandTable x x = Boolean.notTable x)\n\n-- Cellule neutre : rien a faire ici, elle sert a garder la session verte.\nexample : True := trivial", "env": 7}Raw output{"messages":
[{"severity": "info",
"pos": {"line": 12, "column": 0},
"endPos": {"line": 12, "column": 6},
"data": "∀ (x : Fin 2), Boolean.nandTable x x = Boolean.notTable x : Prop"}],
"env": 8}
Conclusion
Ce que cette visite a etabli, et avec quelle preuve :
Section
Ce qui est sur main
Comment on le sait
1
une structure finie est un Type portant ensembles, relations et tables
#check @Structure, #check @Rel
2
l’encodage du §c est livre comme preambule, la complexite du §d est calculee
#eval des deux fonctions
3
C₃, C₂ et NAND sont des Structure reelles, tables completes
#eval des matrices, 9 + 4 + 4 entrees
4
Aut(S) est ferme sous composition, l’identite est dedans
#check de autId/autComp, #print axioms
5
la decidabilite du §1 est annoncee, non implementee
#eval decideEq C₃ C₂ → true
La derniere ligne est celle qui donne sa valeur au notebook : elle n’aurait pas pu etre ecrite par lecture seule du titre de l’article, ni en recopiant le README du lake. Il fallait executer le decideur sur deux structures dont on connait les cardinaux.
Ce que ce notebook n’est pas
Pas une preuve de la CUH. Il visite une bibliotheque qui formalise la definition du §1 et deux familles d’exemples ; l’algorithme halting du §1 reste a ecrire, et c’est une autre histoire que celle de la definition.
Pas un cours de tactiques. Les preuves du lake sont courtes (rfl, decide, simp only) ; l’interet est dans le typage — un produit dependant d’arguments, et ce qu’une curryfication degenere casse.
Pas une visite de Mathlib. Le lake est sans dependance Mathlib : ses neuf fichiers se buildent en une trentaine de secondes, ce qui est precisement ce qui rend la frontiere observable sans infrastructure.
Pour aller plus loin
le lake lui-meme : tegmark_muh_lean — MUH.lean (module racine bilingue) et ses six modules ;
l’encodage et la complexite : MUH/Encoding.lean, §c et §d du papier ;
la frontiere du decideur : MUH/Decidable.lean, et l’issue #16958 qui a choisi de realigner l’annonce sur le code plutot que d’implementer ;