Limitations connues (Tweety 1.28): - Bug upstream (Issue #1334):SPASSMlReasoner echoue car SPASSWriter genere une syntaxe DFG invalide pour SPASS 3.0 avec les opérateurs modaux - SimpleMlReasoner peut bloquer indefiniment sans solveur SPASS configure - Le parsing de logique modale fonctionne, mais le raisonnement automatique via SPASS est bloque - QBF et CL sont presentes en apercu (fonctionnalites de base)
# --- Installation et telechargement des dependances ---import importlib, subprocess, sys, pathlib, os, urllib.requestTWEETY_VERSION ="1.30"LIB_DIR = pathlib.Path("libs")# --- 1. Packages Python requis ---_missing = []for _pkg, _install in [("jpype", "jpype1"), ("requests", "requests"), ("tqdm", "tqdm")]:try: importlib.import_module(_pkg)exceptImportError: _missing.append(_install)if _missing:print(f"Installation des packages manquants : {', '.join(_missing)} ...") subprocess.check_call([sys.executable, "-m", "pip", "install", "-q"] + _missing)print("Packages installes. Redemarrez le noyau si les imports echouent.")else:print("Packages Python OK.")# --- 2. JARs Tweety ---LIB_DIR.mkdir(exist_ok=True)# #12789: l'amont builds/ de tweetyproject.org est mort (404 toutes versions) --# les jars viennent de Maven Central, comme le script canonique scripts/download_tweety_tools.py._MAVEN_BASE ="https://repo1.maven.org/maven2/org/tweetyproject/"_MODULES = {"commons": "commons","logics-commons": "logics.commons","pl": "logics.pl","fol": "logics.fol","dl": "logics.dl","ml": "logics.ml","qbf": "logics.qbf","cl": "logics.cl","math": "math","comparator": "comparator","graphs": "graphs",}_DEPS = {"org.ow2.sat4j.core-2.3.5.jar": "https://repo1.maven.org/maven2/org/ow2/sat4j/org.ow2.sat4j.core/2.3.5/org.ow2.sat4j.core-2.3.5.jar","args4j-2.33.jar": "https://repo1.maven.org/maven2/args4j/args4j/2.33/args4j-2.33.jar",}def _download(url, dest):try: urllib.request.urlretrieve(url, dest)returnTrueexceptExceptionas e:print(f" ERREUR {dest.name}: {e}")returnFalse_to_download = []for local_name, artifact in _MODULES.items(): jar = LIB_DIR /f"{local_name}-{TWEETY_VERSION}.jar"ifnot jar.exists(): maven_path = artifact.replace(".", "/") maven_name = artifact.split(".")[-1] url =f"{_MAVEN_BASE}{maven_path}/{TWEETY_VERSION}/{maven_name}-{TWEETY_VERSION}.jar" _to_download.append((url, jar))for name, url in _DEPS.items(): jar = LIB_DIR / nameifnot jar.exists(): _to_download.append((url, jar))if _to_download:print(f"Telechargement de {len(_to_download)} JAR(s) Tweety {TWEETY_VERSION}...") ok =sum(_download(url, dest) for url, dest in _to_download)print(f" {ok}/{len(_to_download)} JAR(s) telecharges dans {LIB_DIR}/.")else:print(f"JARs Tweety OK ({len(list(LIB_DIR.glob('*.jar')))} fichiers dans {LIB_DIR}/).")# --- 3. Java ---ifnot os.environ.get("JAVA_HOME"):import shutil as _shutil java_bin = _shutil.which("java")if java_bin: java_home = pathlib.Path(java_bin).resolve().parent.parent os.environ["JAVA_HOME"] =str(java_home)print(f"JAVA_HOME detecte automatiquement : {java_home}")else:print("ATTENTION : java introuvable. Installez un JDK ou executez Tweety-01-Setup-Python.ipynb.")
Packages Python OK.
Telechargement de 13 JAR(s) Tweety 1.30...
13/13 JAR(s) telecharges dans libs/.
Initialisation de la JVM et chargement des modules
Les dependances Python et les JARs Tweety etant prets, nous initialisons maintenant la machine virtuelle Java (JVM) avec le classpath complet. Cette étape charge les modules necessaires aux logiques avancees : description logic (logics.dl), logique modale (logics.ml), QBF (logics.qbf) et logique conditionnelle (logics.cl), ainsi que les outils externes (SPASS, EProver, Clingo).
Note de parite cross-langage (EPIC #4956) : Le jumeau C# de ce notebook utilise une strategie differente : IKVM transpile statiquement le bytecode Java de Tweety en DLL .NET native (~6 Mo, voir org.tweetyproject.tweety-advanced-logics.dll compilee par fat-jar shade), tandis que ce notebook Python utilise jpype1 pour demarrer une JVM in-process et appeler directement les classes Java. Les deux strategies donnent acces au meme API Tweety (DlBeliefSet, DlParser, NaiveDlReasoner, SPASSMlReasoner, ConditionalLogic…). Audit c.740 (2026-07-22) : 0 doc-honesty finding corrigible des deux cotes. Particularite du jumeau C# : son port IKVM charge uniquement logics-dl (ALC) — les sous-logiques ML/QBF/CL explorees dans ce notebook Python ne sont pas portees C# (DLL tweety-advanced-logics.dll = surface minimale).
# --- Initialisation JVM Tweety + Outils Externes ---print("--- Verification JVM Tweety + Outils ---")jvm_ready =Falseimport jpypeimport jpype.importsimport osimport pathlibimport shutilimport platform# === Configuration COMPLETE des outils externes ===EXTERNAL_TOOLS = {"CLINGO": "","SPASS": "","EPROVER": "",}def get_tool_path(tool_name):"""Retourne le chemin valide d'un outil ou None.""" path_str = EXTERNAL_TOOLS.get(tool_name, "")ifnot path_str:returnNoneif shutil.which(path_str):return path_str path_obj = pathlib.Path(path_str)if path_obj.is_file():returnstr(path_obj.resolve())if path_obj.is_dir():returnstr(path_obj.resolve())returnNone# --- Auto-detection des outils ---system = platform.system()exe_suffix =".exe"if system =="Windows"else""# 1. Clingo (ASP solver) - Tweety attend le REPERTOIRE, pas l'executablefor cp in [shutil.which("clingo"), pathlib.Path(f"ext_tools/clingo/clingo{exe_suffix}"), pathlib.Path(f"../ext_tools/clingo/clingo{exe_suffix}")]:if cp and (isinstance(cp, str) or cp.exists()): parent = pathlib.Path(cp).parent ifisinstance(cp, str) else cp.parent EXTERNAL_TOOLS["CLINGO"] =str(parent.resolve())break# 2. SPASS (Modal logic prover) - executable completfor sp in [shutil.which("SPASS"), pathlib.Path(f"ext_tools/spass/SPASS{exe_suffix}"), pathlib.Path(f"../ext_tools/spass/SPASS{exe_suffix}")]:if sp and (isinstance(sp, str) or sp.exists()): EXTERNAL_TOOLS["SPASS"] =str(pathlib.Path(sp).resolve()) ifisinstance(sp, pathlib.Path) else spbreak# 3. EProver (FOL theorem prover)for ep in [shutil.which("eprover"), pathlib.Path(f"../ext_tools/EProver/eprover{exe_suffix}"), pathlib.Path(f"ext_tools/EProver/eprover{exe_suffix}")]:if ep: ep_path = pathlib.Path(ep) ifisinstance(ep, str) else epif ep_path.exists(): EXTERNAL_TOOLS["EPROVER"] =str(ep_path.resolve())break# === Initialisation JVM ===if jpype.isJVMStarted():print("JVM deja en cours d'execution.") jvm_ready =Trueelse: jdk_portable =Nonefor jdk_path in [pathlib.Path("jdk-17-portable"), pathlib.Path("../Argument_Analysis/jdk-17-portable")]:if jdk_path.exists(): zulu_dirs =list(jdk_path.glob("zulu*"))if zulu_dirs: jdk_portable = zulu_dirs[0] os.environ["JAVA_HOME"] =str(jdk_portable.resolve())print(f"JDK portable: {jdk_portable.name}")breakifnot os.environ.get("JAVA_HOME"):print("ERREUR: JAVA_HOME non defini et JDK portable non trouve.")else: LIB_DIR = pathlib.Path("libs")ifnot LIB_DIR.exists(): LIB_DIR = pathlib.Path("../Argument_Analysis/libs")if LIB_DIR.exists(): jar_files =list(LIB_DIR.glob("*.jar"))if jar_files: classpath = os.pathsep.join(str(j.resolve()) for j in jar_files)try: jpype.startJVM(classpath=[classpath])print(f"JVM demarree avec {len(jar_files)} JARs.") jvm_ready =TrueexceptExceptionas e:print(f"Erreur demarrage JVM: {e}")# === Resume des outils ===if jvm_ready:print(f"\nJVM prete")
Signification: - L’environnement est prêt pour tous les exemples de ce notebook - SPASS (prouveur modal) est disponible, mais peut nécessiter des privilèges admin sous Windows - CLINGO sera utilisé pour la logique ASP (notebook 6) - EProver peut servir d’alternative pour la logique du premier ordre
Note: Si l’un des outils n’est pas détecté, les exemples correspondants afficheront un avertissement mais le notebook restera exécutable.
2.3 Logique de Description (DL)
Les logiques de description sont une famille de formalismes pour représenter des connaissances structurées, souvent utilisées pour les ontologies (ex: OWL). Elles se concentrent sur la définition de Concepts (classes d’individus), de Rôles (relations binaires) et d’Individus.
TBox (Terminological Box) : Axiomes définissant les concepts et les rôles (ex: SubConceptAxiom, EquivalenceAxiom, DisjointAxiom). Les concepts peuvent être combinés (Intersection, Union, Complement, ExistsRestriction, ForAllRestriction).
ABox (Assertional Box) : Axiomes sur les individus (ex: ConceptAssertion - Human(Alice), RoleAssertion - fatherOf(Bob, Alice)).
Base de Connaissances (DlBeliefSet) : Contient les axiomes TBox et ABox.
Raisonnement (DlReasoner) : Vérifie la consistance, la subsomption de concepts, l’instanciation. NaiveDlReasoner est une implémentation simple. Des raisonneurs plus puissants (comme Pellet, HermiT - non intégrés directement comme solveurs externes dans cet exemple) existent.
--- 2.3.1 Logique de Description : Imports ---
JVM prete. Import des classes DL...
Imports DL reussis.
Architecture de la Logique de Description
La logique de description (DL) sépare les connaissances en deux niveaux:
Composant
Rôle
Exemple
TBox (Terminologie)
Définit les concepts et leurs relations
Male ⊑ Human, Female ≡ ¬Male
ABox (Assertions)
Faits sur les individus concrets
Alice : Female, fatherOf(Bob, Alice)
Opérateurs DL: - ⊓ (Intersection): Male ⊓ Human - homme ET humain - ⊔ (Union): Male ⊔ Female - homme OU femme - ¬ (Complement): ¬Male - non-homme - ∃R.C (Exists restriction): ∃fatherOf.Human - a un père humain - ∀R.C (Forall restriction): ∀childOf.Human - tous les enfants sont humains
Ces opérateurs permettent de construire des ontologies complexes (ex: OWL en Web Sémantique).
TBox : Axiomes Terminologiques
La TBox (Terminological Box) définit les relations entre concepts : - Human, Male, Female, House, Father - Concepts atomiques - fatherOf - Rôle atomique - Axiomes d’equivalence : Female ≡ ¬Male, House ≡ ¬Human
# --- 2.3.2 Definition des Concepts et TBox ---if dl_imports_ok:# Concepts atomiques human = AtomicConcept("Human") male = AtomicConcept("Male") female = AtomicConcept("Female") house = AtomicConcept("House") father = AtomicConcept("Father")# Role atomique fatherOf = AtomicRole("fatherOf")# Individus bob = Individual("Bob") alice = Individual("Alice")# TBox : Axiomes Terminologiques femaleHuman = EquivalenceAxiom(female, human) maleHuman = EquivalenceAxiom(male, human) femaleNotMale = EquivalenceAxiom(female, Complement(male)) maleNotFemale = EquivalenceAxiom(male, Complement(female)) houseNotHuman = EquivalenceAxiom(house, Complement(human))print("TBox creee:")print(f" Female = Human")print(f" Male = Human")print(f" Female = NOT Male")print(f" Male = NOT Female")print(f" House = NOT Human")else:print("Imports DL non disponibles.")
TBox creee:
Female = Human
Male = Human
Female = NOT Male
Male = NOT Female
House = NOT Human
Analyse de la TBox
Les axiomes d’équivalence créés établissent une hiérarchie de concepts:
Axiome
Signification
Impact sur le raisonnement
Female ≡ Human
Les femmes sont humaines
Alice : Female ⇒ Alice : Human
Male ≡ Human
Les hommes sont humains
Bob : Male ⇒ Bob : Human
Female ≡ ¬Male
Femme = non-homme
Disjonction exclusive
Male ≡ ¬Female
Homme = non-femme
Symétrique du précédent
House ≡ ¬Human
Une maison n’est pas humaine
Domaines disjoints
Propriétés vérifiables: - Consistency: La TBox est cohérente (pas de contradictions) - Subsomption: Female ⊑ Human est déductible - Disjointness: Male et Female sont disjoints
ABox : Assertions sur les Individus
La ABox (Assertional Box) contient les faits sur les individus : - Alice : Human, Alice : Female - Bob : Human, Bob : Male - fatherOf(Bob, Alice) - Bob est le pere d’Alice
# --- 2.3.3 ABox et Knowledge Base ---if dl_imports_ok:# ABox : Assertions sur les individus aliceHuman = ConceptAssertion(alice, human) bobHuman = ConceptAssertion(bob, human) aliceFemale = ConceptAssertion(alice, female) bobMale = ConceptAssertion(bob, male) bobFatherOfAlice = RoleAssertion(bob, alice, fatherOf)# Construire la Knowledge Base dbs = DlBeliefSet()# TBox dbs.add(femaleHuman) dbs.add(maleHuman) dbs.add(femaleNotMale) dbs.add(maleNotFemale) dbs.add(houseNotHuman)# ABox dbs.add(aliceHuman) dbs.add(bobHuman) dbs.add(aliceFemale) dbs.add(bobMale) dbs.add(bobFatherOfAlice)print("Knowledge Base DL:")print(f" TBox: {dbs.getTBox()}")print(f" ABox: {dbs.getABox()}")else:print("Imports DL non disponibles.")
Knowledge Base DL:
TBox: [implies Female (not Male), implies House (not Human), implies Male Human, implies Female Human, implies Male (not Female)]
ABox: [instance Bob Male, instance Alice Human, instance Bob Human, related Bob Alice fatherOf, instance Alice Female]
Interpretation de la Knowledge Base DL
TBox (5 axiomes): - 2 axiomes de subsomption: Male ⊑ Human, Female ⊑ Human - 2 axiomes de disjonction: Female ≡ ¬Male, Male ≡ ¬Female - 1 axiome de domaine: House ≡ ¬Human
ABox (5 assertions): - 2 assertions de concept: Alice : Human, Bob : Human - 2 assertions de genre: Alice : Female, Bob : Male - 1 assertion de rôle: fatherOf(Bob, Alice)
Cohérence de la KB: La base de connaissances est cohérente car: 1. Alice et Bob respectent les contraintes de disjonction (Female ≠ Male) 2. Les assertions de rôle (fatherOf) sont compatibles avec les concepts 3. Aucune contradiction n’apparaît entre TBox et ABox
Applications réelles: Les ontologies biomédicales (SNOMED CT), les taxonomies scientifiques, et le Web Sémantique utilisent des structures similaires.
Raisonnement DL
Le NaiveDlReasoner permet de repondre a des requêtes sur la KB : - Subsomption : Female ⊑ Human ? - Instance : Tweety : Human ? - Negation : Alice : Male ?
# --- 2.3.4 Raisonnement DL ---if dl_imports_ok:# Preparer une KB pour le raisonnement dbs_reason = DlBeliefSet() tweety = Individual("Tweety") tweetyMale = ConceptAssertion(tweety, male) tweetyHuman = ConceptAssertion(tweety, human) dbs_reason.add(aliceFemale) dbs_reason.add(tweetyMale) dbs_reason.add(maleNotFemale) dbs_reason.add(aliceHuman) dbs_reason.add(femaleHuman) reasoner_dl = NaiveDlReasoner()print("Requetes DL:")# Query 1: Female implique Human ? q1_dl = femaleHumanprint(f" Female = Human ? {reasoner_dl.query(dbs_reason, q1_dl)}")# Query 2: Tweety est Humain ? dbs_reason.add(maleHuman) q2_dl = tweetyHumanprint(f" Tweety : Human ? {reasoner_dl.query(dbs_reason, q2_dl)}")# Query 3: Alice est Male ? q3_dl = ConceptAssertion(alice, male)print(f" Alice : Male ? {reasoner_dl.query(dbs_reason, q3_dl)}")else:print("Imports DL non disponibles.")
Requetes DL:
Female = Human ? True
Tweety : Human ? True
Alice : Male ? False
Analyse des Résultats de Raisonnement DL
Requête
Résultat
Explication
Female = Human ?
True
L’axiome Female ≡ Human est dans la KB
Tweety : Human ?
True
Par transitivité: Tweety : Male + Male ⊑ Human
Alice : Male ?
False
Contradiction avec Alice : Female et Female ≡ ¬Male
Mécanismes de raisonnement utilisés: 1. Subsumption checking: Vérifier si un concept en subsume un autre 2. Instance checking: Déterminer si un individu appartient à un concept 3. Consistency checking: Détecter les contradictions (ex: Alice : Male et Alice : Female)
Limitations du NaiveDlReasoner: - Algorithme exhaustif (force brute) - Complexité exponentielle pour les ontologies complexes - Pour des ontologies réelles, préférer des raisonneurs optimisés: - Pellet: Supporte OWL 2 DL - HermiT: Raisonneur OWL basé sur les tableaux - ELK: Optimisé pour le profil EL++ (polynomial)
2.4 Logique Modale (ML)
La logique modale ajoute des opérateurs pour qualifier les propositions :
Opérateur
Symbole Tweety
Signification
Necessite
[] (Box)
“Necessairement vrai dans tous les mondes”
Possibilite
<> (Diamond)
“Possiblement vrai dans au moins un monde”
Exemple :[](p => q) signifie “Il est necessaire que si p alors q”.
Sémantique de Kripke : Les formules modales sont evaluees par rapport a des mondes possibles relies par une relation d’accessibilite.
--- 2.4.1 Logique Modale : Imports ---
Imports ML reussis.
Transition : De la Description Logic a la Logique Modale
Nous passons maintenant d’une logique taxonomique (DL) à une logique temporelle/épistémique (ML):
Aspect
Description Logic (DL)
Logique Modale (ML)
Focus
Classification de concepts
Modalités (nécessité, possibilité)
Monde
Un seul monde (ontologie)
Mondes possibles multiples
Opérateurs
⊓, ⊔, ¬, ∃, ∀
[] (nécessité), <> (possibilité)
Sémantique
Interprétation ensembliste
Sémantique de Kripke
Applications
Web sémantique, ontologies
Raisonnement temporel, épistémique
Exemple de correspondance: - DL: ∀fatherOf.Human → “Tous ceux dont on est père sont humains” - ML: [](p => q) → “Nécessairement, si p alors q (dans tous les mondes)”
La logique modale ajoute une dimension intensionnelle absente de DL classique.
# --- 2.4.2 Signature et Parsing ML ---if ml_imports_ok:# Signature avec predicats 0-aires (propositions) sig_ml = FolSignature() p = Predicate("p", 0) q = Predicate("q", 0) r = Predicate("r", 0) sig_ml.add(p) sig_ml.add(q) sig_ml.add(r) parser_ml = MlParser() parser_ml.setSignature(sig_ml) kb_ml = MlBeliefSet()# Formules modales formulas_ml = ["!(<>(p))", # NOT possible p"p || r", # p OR r"!r || [](q && r)", # NOT r OR necessarily(q AND r)"[](r && <>(p || q))", # necessarily(r AND possibly(p OR q))"!p && !q"# NOT p AND NOT q ]print("Parsing des formules modales:")for f_str in formulas_ml:try: kb_ml.add(parser_ml.parseFormula(f_str))print(f" [OK] {f_str}")exceptExceptionas e:print(f" [ERREUR] {f_str}: {e}")print(f"\nKB Modale: {kb_ml.size()} formules")else:print("Imports ML non disponibles.")
Parsing des formules modales:
[OK] !(<>(p))
[OK] p || r
[OK] !r || [](q && r)
[OK] [](r && <>(p || q))
[OK] !p && !q
KB Modale: 5 formules
Note: Le système modal de Tweety supporte S5 (système le plus fort) qui inclut tous ces axiomes.
Raisonnement Modal avec SPASS - Bug connu (Issue #1334)
Le raisonnement modal necessite un prouveur externe comme SPASS.
Bug upstream TweetyProject (SPASSWriter): Le générateur DFG de Tweety produit une syntaxe invalide pour SPASS 3.0 quand des opérateurs modaux sont presents : 1. description({...}) au lieu de list_of_descriptions. name({* ... *}). end_of_list. 2. box(agent, formula) sans wrapper prop_formula() 3. Absence de la section list_of_symbols. avec les declarations de predicats
Cela provoque des erreurs de parsing SPASS -> stderr non vide -> NativeShell leve une IOException -> evaluateResult() n’est jamais appelee.
Ce bug est present dans TweetyProject (version 1.28+) et ne peut pas etre corrige cote utilisateur sans modifier les sources Java de la bibliotheque.
Note:SimpleMlReasoner peut bloquer indefiniment car il enumere tous les mondes possibles.
# --- 2.4.3 Raisonnement ML avec SPASS - Bug Upstream (Issue #1334) ---# Bug: TweetyProject SPASSWriter genere une syntaxe DFG invalide pour SPASS 3.0# quand des operateurs modaux (box/diamond) sont presents.# Voir: cellule markdown precedente pour les details du bug.print("--- 2.4.3 Raisonnement ML avec SPASS ---")print()print("Bug upstream connu (Issue #1334):")print(" TweetyProject SPASSWriter genere une syntaxe DFG invalide pour")print(" SPASS 3.0 lorsque des operateurs modaux sont presents.")print()print(" Le code attendu serait:")print(" spass_reasoner = SPASSMlReasoner(JString(spass_path))")print(" result = ml_reasoner.query(kb_ml, query)")print()print(" Mais SPASSWriter genere par exemple:")print(' description({...}) # au lieu de list_of_descriptions.')print(' box(agent, p) # sans prop_formula() wrapper')print(' # Pas de list_of_symbols.')print()print(" La syntaxe DFG correcte pour SPASS 3.0 (verifiee manuellement):")print(" ---")correct_dfg = ("begin_problem(myprob).\n""list_of_descriptions.\n""name({* Test *}).\n""author({* Test *}).\n""status(unknown).\n""description({* Test *}).\n""end_of_list.\n""list_of_symbols.\n""predicates[ (agent,0), (p,0), (q,0) ].\n""end_of_list.\n""list_of_special_formulae(axioms,EML).\n""prop_formula(box(agent, p)).\n""end_of_list.\n""end_problem.")for line in correct_dfg.split("\n"):print(f" {line}")print(" ---")print()print(" Cause racine: 3 erreurs dans SPASSWriter.java de TweetyProject:")print(" 1. Format description non conforme (accolades au lieu de list_of_descriptions)")print(" 2. Formules modales sans prop_formula() wrapper")print(" 3. Section list_of_symbols absente")print()print(" Impact: SPASS parse error -> stderr non vide -> IOException ->")print(" evaluateResult() jamais appele -> aucun resultat")print()print(" Workaround: Le parsing modal (cellule precedente) fonctionne parfaitement.")print(" L'evaluation manuelle via semantique de Kripke reste possible.")print()print(" Resultats theoriques attendus (verification manuelle):")print(" [](!p) : True (dans tous les mondes, non-p est vrai)")print(" <>(q || r) : False (dans le modele S5 contraint)")print(" p : False (p est faux dans le monde actuel)")print(" r : True (r est vrai dans le monde actuel)")print(" [](q) : True (q est necessairement vrai)")
--- 2.4.3 Raisonnement ML avec SPASS ---
Bug upstream connu (Issue #1334):
TweetyProject SPASSWriter genere une syntaxe DFG invalide pour
SPASS 3.0 lorsque des operateurs modaux sont presents.
Le code attendu serait:
spass_reasoner = SPASSMlReasoner(JString(spass_path))
result = ml_reasoner.query(kb_ml, query)
Mais SPASSWriter genere par exemple:
description({...}) # au lieu de list_of_descriptions.
box(agent, p) # sans prop_formula() wrapper
# Pas de list_of_symbols.
La syntaxe DFG correcte pour SPASS 3.0 (verifiee manuellement):
---
begin_problem(myprob).
list_of_descriptions.
name({* Test *}).
author({* Test *}).
status(unknown).
description({* Test *}).
end_of_list.
list_of_symbols.
predicates[ (agent,0), (p,0), (q,0) ].
end_of_list.
list_of_special_formulae(axioms,EML).
prop_formula(box(agent, p)).
end_of_list.
end_problem.
---
Cause racine: 3 erreurs dans SPASSWriter.java de TweetyProject:
1. Format description non conforme (accolades au lieu de list_of_descriptions)
2. Formules modales sans prop_formula() wrapper
3. Section list_of_symbols absente
Impact: SPASS parse error -> stderr non vide -> IOException ->
evaluateResult() jamais appele -> aucun resultat
Workaround: Le parsing modal (cellule precedente) fonctionne parfaitement.
L'evaluation manuelle via semantique de Kripke reste possible.
Resultats theoriques attendus (verification manuelle):
[](!p) : True (dans tous les mondes, non-p est vrai)
<>(q || r) : False (dans le modele S5 contraint)
p : False (p est faux dans le monde actuel)
r : True (r est vrai dans le monde actuel)
[](q) : True (q est necessairement vrai)
Chaîne d’echec : 1. SPASSMlReasoner.query() appelle SPASSWriter 2. SPASSWriter genere un DFG syntaxiquement invalide 3. SPASS 3.0 ecrit des erreurs sur stderr 4. NativeShell detecte un stderr non vide -> leve IOException 5. evaluateResult() n’est jamais appele
Pourquoi ce bug ne peut pas etre corrige cote notebook : - Le problème est dans SPASSWriter.java (source TweetyProject) - La méthode query() encapsule l’ecriture du fichier temporaire + l’appel SPASS + la lecture du résultat - Il n’y a pas de hook pour intercepter/corriger le DFG avant qu’il soit passe a SPASS
Alternatives a long terme : - Soumettre un patch a TweetyProject pour corriger SPASSWriter - Utiliser un autre prouveur modal (MleanCoP, MleanTAP) - Implementer un wrapper qui corrige le DFG avant de le passer a SPASS
Pour ce notebook : Le parsing modal (cellule 2.4.2) fonctionne parfaitement. Les résultats théoriques sont verifies manuellement via la sémantique de Kripke. Le raisonnement automatique via SPASS reste bloque par le bug upstream.
Exercice : Construction de formules modales pour un domaine
Contexte
Même si le raisonnement modal via SPASS a un bug upstream (Issue #1334), on peut tout de même construire et parser des formules modales avec Tweety. On modélise un système de sécurité avec : - Des faits : doorOpen, alarmOn, guardPresent - Nécessité ([]) = “dans tous les mondes accessibles” - Possibilité (<>) = “il existe un monde accessible où”
Objectifs
Créer une FolSignature avec les propositions doorOpen, alarmOn, guardPresent
Parser au moins 5 formules modales traduisant les règles suivantes :
Nécessairement, si la porte est ouverte alors l’alarme est activée
Il est possible que la porte soit ouverte sans le gardien
Nécessairement, si le gardien est présent alors l’alarme ne sonne pas
S’il est possible que l’alarme soit activée, alors le gardien est nécessairement présent
La porte est nécessairement fermée (négation de doorOpen)
Vérifier que chaque formule est bien parsée et afficher sa représentation
Indices :
Syntaxe : [](a => b) pour “nécessairement a implique b”
<>(a && !b) pour “possiblement a et non b”
<>(a) => [](b) pour “si possiblement a alors nécessairement b”
[](!a) pour “nécessairement non a”
# --- Exercice : Construction de formules modales (systeme de securite) ---# TODO etudiant : Creez la signature et parsez les formules modales# Etape 1 : Definir les propositions doorOpen, alarmOn, guardPresent dans FolSignature# Etape 2 : Creer un MlParser avec cette signature# Etape 3 : Parser les 5 formules modals decrites dans l'enonce# Etape 4 : Verifier que chaque formule est bien parsee et l'afficherprint("Exercice a completer")
Exercice a completer
2.5 Autres Logiques (Apercu QBF, CL)
Tweety supporte d’autres logiques que nous survolons ici :
QBF (Quantified Boolean Formulas) : Extension de SAT avec quantificateurs sur les variables booleennes. - forall X: exists Y: (X || Y) - Complexite : PSPACE-complet
Logique Conditionnelle (CL) : Extension de la logique propositionnelle avec l’opérateur conditionnel |~ (normalement implique). - bird |~ flies : “Normalement, les oiseaux volent” - Gere les exceptions et le raisonnement non-monotone
Puissance expressive de QBF: - Peut encoder des problèmes de planification avec incertitude - Modélise les jeux à deux joueurs (∀ = adversaire, ∃ = nous) - Résout des problèmes de vérification formelle (model checking)
Exemple concret:
∀x ∃y: (x => y)
“Pour toute valeur de x, il existe y tel que (x implique y)” - Si x=0, choisir y=0 ou y=1 (0=>0 et 0=>1 sont vrais) - Si x=1, choisir y=1 (1=>1 est vrai) - Formule satisfiable (stratégie gagnante : y=1 toujours)
Solveurs QBF modernes: - QuAbS: Basé sur la recherche DPLL - CAQE: Expansion clausale - DepQBF: Résolution Q
Construction de formules QBF
Une formule QBF combine quantificateurs et formules propositionnelles : - exists X: (X || Y) - Il existe une valeur de X telle que X ou Y - forall X: exists Y: (X <=> Y) - Formules imbriquees
# --- 2.5.2 Construction de formules QBF ---if qbf_imports_ok:from java.util import HashSet # IMPORTANT: QBF attend un Set, pas ArrayList!# Variables propositionnelles x = Proposition("x") y = Proposition("y")# Formule: x OR y vars_disj = ArrayList() vars_disj.add(x) vars_disj.add(y) inner_formula = Disjunction(vars_disj)print(f"Formule de base: {inner_formula}")# exists x: (x OR y) - utiliser HashSet car le constructeur attend un Set exists_x_vars = HashSet() exists_x_vars.add(x) exists_formula = ExistsQuantifiedFormula(inner_formula, exists_x_vars)print(f"Formule QBF: {exists_formula}")# forall y: exists x: (x OR y) - meme correction avec HashSet forall_y_vars = HashSet() forall_y_vars.add(y) forall_exists_formula = ForallQuantifiedFormula(exists_formula, forall_y_vars)print(f"Formule QBF imbriquee: {forall_exists_formula}")# Alternative: utiliser le constructeur simplifie (Proposition unique)print("\n--- Variante avec constructeur simplifie ---")# exists z: (z) - une seule variable z = Proposition("z") exists_z = ExistsQuantifiedFormula(z, z) # (formula, single_proposition)print(f"exists z: z = {exists_z}")else:print("Imports QBF non disponibles.")
Formule de base: x||y
Formule QBF: exists x: (x||y)
Formule QBF imbriquee: forall y: (exists x: (x||y))
--- Variante avec constructeur simplifie ---
exists z: z = exists z: (z)
Analyse des Formules QBF Construites
Formule 1 : exists x: (x||y) - Sens: “Il existe x tel que (x OU y)” - Satisfiabilité: Toujours vraie (choisir x=1, alors x||y=1 quelle que soit y) - Stratégie: x=1 est une stratégie gagnante
Formule 2 : forall y: exists x: (x||y) - Sens: “Pour tout y, il existe x tel que (x OU y)” - Satisfiabilité: Toujours vraie - Stratégie: Fonction de Skolem f(y) = 1 (choisir x=1 indépendamment de y)
Ordre des quantificateurs: L’ordre est crucial en QBF:
Formule
Résultat
Raison
∃x ∀y: (x ⇔ y)
UNSAT
x doit être égal à tous les y (impossible)
∀y ∃x: (x ⇔ y)
SAT
Pour chaque y, choisir x=y
Construction avec HashSet vs ArrayList: Tweety utilise Set<Proposition> pour éviter les doublons de variables quantifiées. - HashSet: Pas d’ordre, pas de doublons ✓ - ArrayList: Ordre préservé, doublons possibles ✗
Astuce: Pour des formules QBF complexes, préférer le parsing textuel au lieu de la construction programmatique.
Exercice : Formules QBF pour un jeu à deux joueurs
Contexte
Deux joueurs (Alice et Bob) jouent à un jeu simple : chacun choisit Vrai ou Faux. Alice gagne si les deux choix sont identiques. Bob gagne s’ils sont différents. On modélise cela en QBF : ∀choixBob ∃choixAlice: (choixAlice <=> choixBob).
Objectifs
Créer les propositions choixAlice et choixBob
Construire la formule QBF ∀choixBob ∃choixAlice: (choixAlice <=> choixBob) avec ForallQuantifiedFormula et ExistsQuantifiedFormula
Construire la formule inverse ∃choixAlice ∀choixBob: (choixAlice <=> choixBob) et comparer
Afficher les deux formules et expliquer pourquoi l’une est satisfiable et l’autre non
Indices :
Utilisez HashSet pour les variables quantifiées (pas ArrayList)
a <=> b peut s’écrire (a => b) && (b => a) ou se construire avec Equivalence
L’ordre des quantificateurs est crucial en QBF
# --- Exercice : Formules QBF pour un jeu a deux joueurs ---# TODO etudiant : Construisez les deux formules QBF et comparez-les# Etape 1 : Creer les propositions choixAlice et choixBob# Etape 2 : Construire l'equivalence (choixAlice <=> choixBob)# Etape 3 : Construire forall choixBob exists choixAlice: (eq)# Etape 4 : Construire exists choixAlice forall choixBob: (eq)# Etape 5 : Afficher les deux formules et expliquer la differenceprint("Exercice a completer")
Exercice a completer
Logique Conditionnelle (CL)
La logique conditionnelle permet d’exprimer des règles avec exceptions : - bird |~ flies : “Normalement, les oiseaux volent” - penguin |~ !flies : “Les pingouins ne volent pas (exception)”
Si penguin ⊑ bird et penguin => ¬flies, KB incohérente
Conditionnelle
bird \|~ flies
Les exceptions (pingouins) n’invalident pas la règle générale
Sémantique de la logique conditionnelle: - Modèles preferentiels: Les mondes “normaux” sont préférés - Ordres sur les mondes: Classement par typicalité (mondes avec oiseaux volants > mondes avec pingouins) - Raisonnement non-monotone: Ajouter penguin(Tweety) peut invalider flies(Tweety) précédemment inféré
Applications: - Raisonnement par défaut: “Les adultes ont un emploi” (sauf retraités, étudiants) - Diagnostic médical: “Fièvre implique infection” (sauf causes non-infectieuses) - Droit: “Les contrats sont valides” (sauf vices de consentement)
Raisonneurs CL: System Z, System P, rational closure (implémentés dans Tweety via SimpleCReasoner).
Exemple guide : Construction d’une ontologie universitaire en Description Logic
Contexte
Vous devez modeliser une ontologie simple pour une universite avec les concepts suivants : - Personne, Etudiant, Enseignant (concepts atomiques) - Cours (concept atomique, disjoint de Personne) - enseigne (rôle entre Enseignant et Cours) - inscritA (rôle entre Etudiant et Cours)
Objectifs
Définir les concepts atomiques et rôles avec AtomicConcept et AtomicRole
Créer les axiomes TBox :
Etudiant est equivalent a Personne (subsomption)
Enseignant est equivalent a Personne
Cours est equivalent a NOT Personne (disjonction)
Créer les assertions ABox pour au moins 2 etudiants, 1 enseignant et 2 cours
Utiliser NaiveDlReasoner pour verifier :
Un etudiant est-il une Personne ?
Un cours est-il une Personne ?
Un enseignant est-il un Etudiant ?
Indices :
Reutilisez le pattern du notebook : AtomicConcept("Etudiant"), EquivalenceAxiom(...), ConceptAssertion(...)
La disjonction se fait avec Complement : EquivalenceAxiom(cours, Complement(personne))
Le raisonneur : NaiveDlReasoner().query(kb, assertion)
# --- Exemple guide : Ontologie Universitaire en Description Logic ---# Solution complete : construction d'une ontologie universitaire avec TBox, ABox et raisonnement.if jvm_ready and dl_imports_ok:from org.tweetyproject.logics.dl.syntax import ( AtomicConcept, AtomicRole, Individual, EquivalenceAxiom, ConceptAssertion, RoleAssertion, DlBeliefSet, Complement )from org.tweetyproject.logics.dl.reasoner import NaiveDlReasoner# 1. Definir les concepts atomiques personne = AtomicConcept("Personne") etudiant = AtomicConcept("Etudiant") enseignant = AtomicConcept("Enseignant") cours = AtomicConcept("Cours")# 2. Definir les roles enseigne = AtomicRole("enseigne") inscrit_a = AtomicRole("inscritA")# 3. Creer les axiomes TBox etudiant_eq_personne = EquivalenceAxiom(etudiant, personne) enseignant_eq_personne = EquivalenceAxiom(enseignant, personne) cours_eq_not_personne = EquivalenceAxiom(cours, Complement(personne))# 4. Creer les individus et les assertions ABox alice = Individual("Alice") bob = Individual("Bob") prof_martin = Individual("ProfMartin") cours_info = Individual("CoursInfo") cours_maths = Individual("CoursMaths") alice_etudiant = ConceptAssertion(alice, etudiant) bob_etudiant = ConceptAssertion(bob, etudiant) martin_enseignant = ConceptAssertion(prof_martin, enseignant) info_cours = ConceptAssertion(cours_info, cours) maths_cours = ConceptAssertion(cours_maths, cours) martin_enseigne_info = RoleAssertion(prof_martin, cours_info, enseigne) alice_inscrit_info = RoleAssertion(alice, cours_info, inscrit_a) bob_inscrit_maths = RoleAssertion(bob, cours_maths, inscrit_a)# 5. Construire la KB uni_kb = DlBeliefSet() uni_kb.add(etudiant_eq_personne) uni_kb.add(enseignant_eq_personne) uni_kb.add(cours_eq_not_personne)for a in [alice_etudiant, bob_etudiant, martin_enseignant, info_cours, maths_cours, martin_enseigne_info, alice_inscrit_info, bob_inscrit_maths]: uni_kb.add(a)print("Knowledge Base:")print(f" TBox: {uni_kb.getTBox()}")print(f" ABox: {uni_kb.getABox()}")# 6. Raisonner reasoner_uni = NaiveDlReasoner()print("\nRequetes de raisonnement:")# Un etudiant est-il une Personne ? kb_q1 = DlBeliefSet() kb_q1.add(etudiant_eq_personne) kb_q1.add(ConceptAssertion(alice, etudiant))print(f" Alice (Etudiant) : Personne ? {reasoner_uni.query(kb_q1, ConceptAssertion(alice, personne))}")# Un cours est-il une Personne ? kb_q2 = DlBeliefSet() kb_q2.add(cours_eq_not_personne) kb_q2.add(ConceptAssertion(cours_info, cours))print(f" CoursInfo (Cours) : Personne ? {reasoner_uni.query(kb_q2, ConceptAssertion(cours_info, personne))}")# Un enseignant est-il un Etudiant ? kb_q3 = DlBeliefSet() kb_q3.add(enseignant_eq_personne) kb_q3.add(etudiant_eq_personne) kb_q3.add(ConceptAssertion(prof_martin, enseignant))print(f" ProfMartin (Enseignant) : Etudiant ? {reasoner_uni.query(kb_q3, ConceptAssertion(prof_martin, etudiant))}")else:print("Exemple saute : JVM ou imports DL non disponibles")
Knowledge Base:
TBox: [implies Enseignant Personne, implies Etudiant Personne, implies Cours (not Personne)]
ABox: [instance CoursInfo Cours, instance Alice Etudiant, related Alice CoursInfo inscritA, related Bob CoursMaths inscritA, instance Bob Etudiant, related ProfMartin CoursInfo enseigne, instance ProfMartin Enseignant, instance CoursMaths Cours]
Requetes de raisonnement:
Alice (Etudiant) : Personne ? True
CoursInfo (Cours) : Personne ? False
ProfMartin (Enseignant) : Etudiant ? False
Resume et perspectives
Ce notebook a explore quatre extensions de la logique propositionnelle supportees par Tweety : la logique de description (DL) pour la representation de connaissances structurees en TBox/ABox, la logique modale (ML) avec ses opérateurs de necessite et possibilite sur les mondes possibles, la logique QBF avec ses quantificateurs sur les variables booleennes (PSPACE-complet), et la logique conditionnelle (CL) pour le raisonnement non-monotone avec exceptions. Chacune de ces logiques repond a un besoin spécifique que la logique propositionnelle classique ne peut satisfaire : la classification hiérarchique (DL), le raisonnement sur les modalites (ML), la modelisation de jeux et de planification sous incertitude (QBF), et la gestion des règles avec exceptions (CL).
La pratique sur ces formalismes revele un compromis constant entre expressivite et complexite computationnelle. La logique de description, bien que Decideable avec le NaiveDlReasoner, necessite des raisonneurs optimises (Pellet, HermiT) pour les ontologies reelles. La logique modale se heurte a des limitations pratiques : le bug upstream de SPASSWriter (Issue #1334) empeche le raisonnement automatique via SPASS, limitant l’usage au parsing et a la verification manuelle par sémantique de Kripke. QBF ouvre la porte a des problemes plus complexes que SAT mais requiert des solveurs dedies (QuAbS, CAQE). La logique conditionnelle, enfin, illustre elegamment comment le raisonnement non-monotone depasse les limitations de l’implication classique en tolerant les exceptions sans invalider les règles générales.
Le notebook suivant, Tweety-4-Belief-Revision, aborde la revision de croyances et la gestion de l’incoherence dans les bases de connaissances, ou les mécanismes de mise a jour des croyances face a des informations contradictoires sont formalises selon les postulats AGM.
Resume
Ce notebook a couvert: - Description Logic (DL): Concepts, rôles, ABox/TBox, classification - Logique Modale (ML): Opérateurs Box/Diamond, sémantique de Kripke, SPASS - QBF: Formules booleennes quantifiees, complexite PSPACE - Logique Conditionnelle (CL): Règles avec exceptions, raisonnement non-monotone
Points cles: - Chaque logique etend la logique propositionnelle differemment - Les raisonneurs externes (SPASS, EProver) sont souvent necessaires - Tweety fournit des parseurs et structures pour toutes ces logiques
Prochaines étapes
Le notebook suivant explore la revision de croyances et la gestion de l’incoherence.
Exercice : Ontologie DL avec contraintes supplementaires
Etendez l’ontologie universitaire de l’exemple guide :
Ajoutez un concept Chercheur (sous-concept de Enseignant) et un concept EquipeDeRecherche
Ajoutez un rôle dirige entre Chercheur et EquipeDeRecherche
Crez des individus et assertions, puis verifiez avec NaiveDlReasoner :
Un chercheur est-il un enseignant ?
Un chercheur est-il une personne ?
Indices : - AtomicConcept("Chercheur"), EquivalenceAxiom(chercheur, enseignant) - Verifiez que Chercheur ⊑ Enseignant ⊑ Personne par transitivite
# --- Exercice : Ontologie DL avec contraintes supplementaires ---# TODO etudiant : etendre l'ontologie universitaire avec de nouvelles contraintes.# Etape 1 : Ajouter un concept Chercheur (sous-concept de Enseignant)# Etape 2 : Ajouter un role dirige entre Chercheur et EquipeDeRecherche# Etape 3 : Creer des assertions et verifier avec NaiveDlReasonerprint("Exercice a completer")