Acte 1 - Les backends embarques decident pour de vrai
Trois familles logiques, trois raisonneurs Tweety in-JVM. Aucun binaire externe : le contrat de livraison est structurellement fiable (tout se passe dans la JVM). Chaque cellule produit une vraie decision (entailment ou extension), pas une simulation.
# Modal K : axiome K sur des propositions 0-aires via SimpleMlReasoner.from java.util import ArrayListFolSignature = JClass("org.tweetyproject.logics.fol.syntax.FolSignature")Sort = JClass("org.tweetyproject.logics.commons.syntax.Sort")Predicate = JClass("org.tweetyproject.logics.commons.syntax.Predicate")MlParser = JClass("org.tweetyproject.logics.ml.parser.MlParser")MlBeliefSet = JClass("org.tweetyproject.logics.ml.syntax.MlBeliefSet")SimpleMlReasoner = JClass("org.tweetyproject.logics.ml.reasoner.SimpleMlReasoner")sig_m = FolSignature()thing = Sort("thing"); sig_m.add(thing)empty = ArrayList()sig_m.add(Predicate("p", empty)); sig_m.add(Predicate("q", empty))mparser = MlParser(); mparser.setSignature(sig_m)mbs = MlBeliefSet(); mbs.setSignature(sig_m)mbs.add(mparser.parseFormula("[](p => q)"))mbs.add(mparser.parseFormula("[](p)"))mreasoner = SimpleMlReasoner()box_q =bool(mreasoner.query(mbs, mparser.parseFormula("[](q)")))plain_q =bool(mreasoner.query(mbs, mparser.parseFormula("q")))print("--- Modal K ---")print(f" {{[](p=>q), [](p)}} |= [](q) ? {box_q} (attendu True, par K)")print(f" {{[](p=>q), [](p)}} |= q ? {plain_q} (attendu False, K n'a pas T)")
--- Modal K ---
{[](p=>q), [](p)} |= [](q) ? True (attendu True, par K)
{[](p=>q), [](p)} |= q ? False (attendu False, K n'a pas T)
# Dung : reinstatement grounded sur a -> b -> c (cas non trivial : c est reinstaure).DungTheory = JClass("org.tweetyproject.arg.dung.syntax.DungTheory")Attack = JClass("org.tweetyproject.arg.dung.syntax.Attack")Argument = JClass("org.tweetyproject.arg.dung.syntax.Argument")SimpleGroundedReasoner = JClass("org.tweetyproject.arg.dung.reasoner.SimpleGroundedReasoner")theory = DungTheory()a = Argument("a"); b = Argument("b"); c = Argument("c")for x in (a, b, c): theory.add(x)theory.add(Attack(a, b)); theory.add(Attack(b, c))grounded = SimpleGroundedReasoner().getModels(theory)it = grounded.iterator(); ext = []while it.hasNext(): ext.append(str(it.next()))print("--- Dung : extension grounded ---")print(f" graphe a->b->c ; extension grounded = {ext}")print(" attendu {a, c} : a non attaque, b rejete, c reinstaure.")
--- Dung : extension grounded ---
graphe a->b->c ; extension grounded = ['{a,c}']
attendu {a, c} : a non attaque, b rejete, c reinstaure.
Acte 2 - La sentinelle de contrat de livraison (le coeur)
Pour passer a l’echelle, on route les requêtes FOL vers un prouveur externe comme EProver. Tweety (EFOLReasoner) construit la commande eprover --auto-schedule --tptp3-format <fichier> et la lance via Runtime.exec(String) - la surcharge mono-String qui re-tokenise sur les espaces. Sur certaines combinaisons plateforme/version, le fichier de problème n’atteint jamais le binaire : E lit une entree vide, prouve la théorie videSatisfiable, et une base incoherente est rapportee coherente. C’est un verdict fabrique - exactement ce que la doctrine interdit.
Le principe de la sentinelle
Avant de faire confiance a un verdict d’un prouveur externe, on lui soumet une sentinelle : un problème dont on connait déjà la reponse. Pour un prouveur par refutation (EProver, Prover9), la sentinelle est une base connue incoherente{P(a), !P(a)} : si le prouveur ne la rapporte PAS incoherente, le contrat est rompu sur cette plateforme -> on refuse de le croire (fail-loud). Pour un chercheur de modèles (Mace4), la sentinelle exerce sa capacite sound : une base connue coherente{ptest(a)} DOIT donner un modèle fini.
On commence par montrer que le backend embarque passe trivialement sa propre sentinelle (contrat in-JVM toujours fiable), puis on essaie de cabler EProver.
# Sentinelle sur le backend embarque SimpleFolReasoner : {P(a), !P(a)} DOIT etre incoherent.# Contrat in-JVM : pas de subprocess => toujours fiable. C'est une vraie decision FOL.from java.util import ArrayListFolParser = JClass("org.tweetyproject.logics.fol.parser.FolParser")FolBeliefSet = JClass("org.tweetyproject.logics.fol.syntax.FolBeliefSet")SimpleFolReasoner = JClass("org.tweetyproject.logics.fol.reasoner.SimpleFolReasoner")Constant = JClass("org.tweetyproject.logics.commons.syntax.Constant")def build_inconsistent_sentinel(): sig = FolSignature() th = Sort("thing"); sig.add(th) sig.add(Constant("a", th)) arg_sorts = ArrayList(); arg_sorts.add(th) sig.add(Predicate("P", arg_sorts)) parser = FolParser(); parser.setSignature(sig) bs = FolBeliefSet() bs.add(parser.parseFormula("P(a)")) bs.add(parser.parseFormula("!P(a)"))return bs, parsersentinel_bs, sentinel_parser = build_inconsistent_sentinel()sentinel_parser.setSignature(sentinel_bs.getMinimalSignature())bottom = sentinel_parser.parseFormula("-") # symbole de contradictioninconsistent =bool(SimpleFolReasoner().query(sentinel_bs, bottom))print("--- Sentinelle sur backend embarque (SimpleFolReasoner) ---")print(f" {{P(a), !P(a)}} rapporte incoherent ? {inconsistent} (attendu True)")assert inconsistent, "contrat de livraison in-JVM rompu : ne devrait jamais arriver"print(" Contrat in-JVM fiable : la sentinelle est passee.")
--- Sentinelle sur backend embarque (SimpleFolReasoner) ---
{P(a), !P(a)} rapporte incoherent ? True (attendu True)
Contrat in-JVM fiable : la sentinelle est passee.
# Cablage d'EProver externe, garde par la sentinelle. Fail-loud HONNETE si absent.import shutil, osdef detect_eprover():"""Retourne le chemin du binaire EProver, ou None s'il n'est pas cable."""for name in ("eprover", "eprover.exe"): p = shutil.which(name)if p:return p env = os.environ.get("EPROVER_HOME")if env:for name in ("eprover", "eprover.exe"): cand = os.path.join(env, name)if os.path.isfile(cand):return candreturnNonedef eprover_delivery_is_reliable(eprover_path):"""Sentinelle de contrat de livraison Tweety->EProver (distille de fol_handler.py, #1204). Soumet la base connue incoherente {P(a),!P(a)} via EFOLReasoner(eprover_path). True ssi EProver la rapporte correctement incoherente. Toute exception ou un 'coherent' => contrat rompu => on ne fait PAS confiance (fail-loud)."""try: EFOLReasoner = JClass("org.tweetyproject.logics.fol.reasoner.EFOLReasoner") reasoner = EFOLReasoner(eprover_path) # Tweety 1.28+ : chemin = unique argument bs, parser = build_inconsistent_sentinel() parser.setSignature(bs.getMinimalSignature()) contradiction = parser.parseFormula("-")returnbool(reasoner.query(bs, contradiction))exceptExceptionas e: # noqa: BLE001 - on veut justement tout capturer iciprint(f" sentinelle EProver : exception ({e}) -> traite comme non fiable.")returnFalseeprover_path = detect_eprover()print("--- Cablage EProver (gardee par sentinelle) ---")if eprover_path isNone: EPROVER_USABLE =Falseprint(" EProver : NON cable sur cette machine (PATH + EPROVER_HOME vides).")print(" -> Le routeur REFUSERA le backend FOL externe : aucun verdict fabrique.")print(" -> Recuperable : installer EProver sur une machine au bon env (RECOVERABLE-MACHINE).")else: EPROVER_USABLE = eprover_delivery_is_reliable(eprover_path)print(f" EProver detecte : {os.path.basename(eprover_path)}")print(f" Sentinelle {{P(a),!P(a)}} passee ? {EPROVER_USABLE}")ifnot EPROVER_USABLE:print(" -> Contrat rompu : le routeur refusera ce backend (fail-loud).")print(f"\nEPROVER_USABLE = {EPROVER_USABLE}")
--- Cablage EProver (gardee par sentinelle) ---
EProver : NON cable sur cette machine (PATH + EPROVER_HOME vides).
-> Le routeur REFUSERA le backend FOL externe : aucun verdict fabrique.
-> Recuperable : installer EProver sur une machine au bon env (RECOVERABLE-MACHINE).
EPROVER_USABLE = False
Acte 3 - Le routeur : decider ou echouer bruyamment
On reunit tout dans un routeurdecide(...) qui choisit le backend selon la famille logique et refuse de fabriquer un verdict. La règle d’or : un backend externe n’est utilise que si sa sentinelle est passee ; sinon le routeur leve une exception explicite (fail-loud) plutot que de renvoyer une reponse douteuse. Le FOL petit reste decidable par le backend embarque SimpleFolReasoner, toujours fiable.
# Routeur multi-backend. Verdict structure ; jamais de fabrication silencieuse.class SolverUnavailable(RuntimeError):"""Leve quand le seul backend capable a un contrat de livraison non verifie."""def decide_fol_consistency(belief_set, parser, prefer_external=False):"""Decide la coherence d'une base FOL. Route vers EProver si demande ET fiable, sinon vers le backend embarque. Fail-loud si on exige l'externe et qu'il est rompu.""" parser.setSignature(belief_set.getMinimalSignature()) contradiction = parser.parseFormula("-")if prefer_external:ifnot EPROVER_USABLE:raise SolverUnavailable("Backend FOL externe (EProver) exige mais contrat non verifie ""(sentinelle absente/echouee). Refus de servir un verdict possiblement ""fabrique. Installez EProver (RECOVERABLE-MACHINE) ou retombez sur l'embarque.") reasoner = JClass("org.tweetyproject.logics.fol.reasoner.EFOLReasoner")(eprover_path) backend ="EProver (externe, sentinelle OK)"else: reasoner = SimpleFolReasoner() backend ="SimpleFolReasoner (embarque)" inconsistent =bool(reasoner.query(belief_set, contradiction))return {"consistent": not inconsistent, "backend": backend}# Cas 1 : syllogisme coherent, decide par l'embarque (vraie decision FOL).sig = FolSignature()th = Sort("thing"); sig.add(th); sig.add(Constant("tweety", th))asorts = ArrayList(); asorts.add(th)sig.add(Predicate("Bird", asorts)); sig.add(Predicate("Flies", asorts))p = FolParser(); p.setSignature(sig)kb = FolBeliefSet()kb.add(p.parseFormula("forall X: (Bird(X) => Flies(X))"))kb.add(p.parseFormula("Bird(tweety)"))res_embedded = decide_fol_consistency(kb, p, prefer_external=False)print("--- Routeur, cas coherent (embarque) ---")print(f" base {{forall X: Bird(X)=>Flies(X), Bird(tweety)}} coherente ? {res_embedded['consistent']}")print(f" backend utilise : {res_embedded['backend']}")# Cas 2 : on EXIGE l'externe pour la meme base -> fail-loud honnete si EProver absent.print("\n--- Routeur, cas 'exiger l'externe' ---")try: res_ext = decide_fol_consistency(kb, p, prefer_external=True)print(f" decide par {res_ext['backend']} : coherente ? {res_ext['consistent']}")except SolverUnavailable as e:print(f" fail-loud (correct) : {e}")
--- Routeur, cas coherent (embarque) ---
base {forall X: Bird(X)=>Flies(X), Bird(tweety)} coherente ? True
backend utilise : SimpleFolReasoner (embarque)
--- Routeur, cas 'exiger l'externe' ---
fail-loud (correct) : Backend FOL externe (EProver) exige mais contrat non verifie (sentinelle absente/echouee). Refus de servir un verdict possiblement fabrique. Installez EProver (RECOVERABLE-MACHINE) ou retombez sur l'embarque.
Refutation vs recherche de modèle : deux capacites complementaires
Deux familles de prouveurs externes se completent :
Refutation (EProver, Prover9) : prouve qu’une base est incoherente en derivant une contradiction. Sentinelle = base connue incoherente {P(a), !P(a)}.
Recherche de modèle (Mace4) : prouve qu’une base est coherente en exhibant un modèle fini. Sentinelle = base connue coherente {ptest(a)} qui DOIT donner un modèle.
Les croiser permet de decider la coherence dans les deux sens avec certitude. Sur cette machine, EProver et Mace4 sont absents : le routeur le declare honnetement (“impossible de cross-valider a l’echelle”) plutot que de fabriquer un verdict. Le backend embarque, lui, decide toujours le cas a petite signature - c’est le filet de securite in-JVM.
Exercices
Trois exercices pour t’approprier le routage et la doctrine fail-loud. Complete les TODO sans introduire d’erreur volontaire : le notebook doit toujours s’executer de bout en bout.
# Exercice 1 (PL via routeur) - modus tollens.# Objectif : croire {rain => wet, !wet} et verifier que !rain est entaille (modus tollens).# Indice : reutilise pl_parser et pl_reasoner de l'acte 1.# Etape 1 : nouveau belief setbs_ex1 =None# TODO etudiant : PlBeliefSet()# Etape 2 : ajouter 'rain => wet' puis '!wet'# TODO etudiant# Etape 3 : requete '!rain'result_ex1 =None# TODO etudiant : bool(pl_reasoner.query(bs_ex1, pl_parser.parseFormula('!rain')))print(f"Modus tollens : !rain entaille ? {result_ex1} (attendu True une fois complete)")print("Exercice a completer")
Modus tollens : !rain entaille ? None (attendu True une fois complete)
Exercice a completer
# Exercice 2 (Dung) - extension preferred sur un cycle a deux arguments.# Objectif : sur le graphe a <-> b (a attaque b ET b attaque a), calculer les extensions# PREFERRED (il y en a deux : {a} et {b}) avec SimplePreferredReasoner.# Indice : JClass('org.tweetyproject.arg.dung.reasoner.SimplePreferredReasoner').# Etape 1 : construire la theorie a <-> btheory_ex2 =None# TODO etudiant : DungTheory(), ajouter a, b, Attack(a,b), Attack(b,a)# Etape 2 : instancier le raisonneur preferredreasoner_ex2 =None# TODO etudiant# Etape 3 : recuperer et afficher les extensionsextensions_ex2 =None# TODO etudiant : iterer getModels(theory_ex2)print(f"Extensions preferred du cycle a<->b : {extensions_ex2} (attendu {{a}} et {{b}})")print("Exercice a completer")
Extensions preferred du cycle a<->b : None (attendu {a} et {b})
Exercice a completer
# Exercice 3 (sentinelle) - contrat de livraison d'un chercheur de modeles facon Mace4.# Un chercheur de modeles prouve la COHERENCE : sa sentinelle exerce sa capacite sound.# Objectif : ecrire la sentinelle d'un model-finder. Base connue COHERENTE {ptest(a)} :# elle DOIT donner un modele fini. Si le finder n'en trouve pas, contrat rompu -> non fiable.# Indice : ici on simule le verdict du finder par la variable finder_found_model.def model_finder_delivery_is_reliable(finder_found_model):# Etape 1 : la sentinelle coherente {ptest(a)} doit donner un modele.# Le contrat est fiable SSI le finder a bien trouve un modele. reliable =None# TODO etudiant : renvoyer bool(finder_found_model)return reliable# Etape 2 : tester les deux cas. Sur un finder sain finder_found_model=True.print(f"finder trouve un modele -> fiable ? {model_finder_delivery_is_reliable(True)} (attendu True)")print(f"finder n'en trouve pas -> fiable ? {model_finder_delivery_is_reliable(False)} (attendu False)")print("Exercice a completer")
finder trouve un modele -> fiable ? None (attendu True)
finder n'en trouve pas -> fiable ? None (attendu False)
Exercice a completer
Conclusion
Tu as construit un routeur multi-backend qui incarne la doctrine anti-theatre / fail-loud :
Decider pour de vrai quand c’est possible : PL, Modal K, Dung et FOL a petite
signature sont decides par les raisonneurs embarques de Tweety, dont le contrat de
livraison in-JVM est structurellement fiable.
Garder les backends externes par une sentinelle de contrat de livraison : avant
de croire EProver, on lui soumet {P(a), !P(a)} ; avant de croire Mace4, on lui soumet
{ptest(a)}. Si la reponse connue n’est pas reproduite, le contrat est rompu sur cette
plateforme et le routeur refuse ce backend.
Echouer bruyamment plutot que fabriquer : sur cette machine EProver/Mace4 sont
absents, et le routeur le declare honnetement (verdict SolverUnavailable), au lieu
de servir un “coherent” fabrique a partir d’une théorie vide.
Echelle de recuperabilite (doctrine CoursIA, règle F / SOTA-not-workaround)
L’indisponibilite d’EProver/Mace4 ici est RECOVERABLE-MACHINE : sur une machine au bon
environnement (Linux CI du projet source, ou poste avec EProver/Mace4 installes et cables),
le même code route vers le prouveur externe, la sentinelle passe, et le verdict a
l’echelle devient reel. Le notebook ne maquille pas cette limite : il l’expose comme une
sortie executee honnete - c’est précisément ce que la sentinelle est faite pour produire.