#r "nuget: IKVM, 8.14.0"
#r "nuget: IKVM.Image, 8.14.0"- IKVM, 8.14.0
- IKVM.Image, 8.14.0
Serie Tweety - port C#/.NET natif (EPIC #4667). Ce notebook exploite le module
logics-mlde TweetyProject sans JVM : la librairie Java est compilee vers un fat-jar Maven shade puis executee sur le runtime .NET via IKVM.
Navigation : Tweety-3-Advanced-Logics (port global Tw-3) - Tweety-3-Conditional-Logics (CL module) - Tweety-3-ModalLogic-Csharp (ce notebook - ML avec K/D/T/S4/S5) - Tweety-3-QBF-Csharp (QBF module).
C’est le 10eme port de l’EPIC #4667 et le dernier sous-module de Tw-3 Advanced-Logics (DL/CL/QBF/ML completes).
Modal Logic (ML) etend la logique du premier ordre (FOL) avec deux opérateurs modaux :
[]P signifie P est forcement vrai (dans tous les mondes possibles accessibles).<>P signifie P peut etre vrai (dans au moins un monde accessible).Ces opérateurs sont lies par la dualite : <>P est equivalent a not [] not P. La sémantique standard est la relation d’accessibilite de Kripke (1963) : on définit un graphe de mondes possibles, et []P est vrai en w si P est vrai dans tous les mondes accessibles depuis w.
Les systèmes modaux classiques sont des restrictions sur la relation d’accessibilite : - K : pas de restriction (relation quelconque). - D : relation serialisable (chaque monde a au moins un successeur). - T : relation reflexive (chaque monde s’accede a lui-même). - S4 : relation reflexive et transitive (-> Preorder). - S5 : relation reflexive, transitive, et symetrique (-> Relation d’equivalence).
Cas d’usage : - Logique epistemique : []P = l’agent sait que P, K(i, P). - Logique deontique : []P = il est obligatoire que P, O(P). - Logique temporelle : []P = toujours P, G(P) ; <>P = eventuellement P, F(P). - Verification de programmes : []P = P invariant sur tous les etats accessibles.
Dans ce notebook on manipule :
Necessity(formula), Possibility(formula), et leur contrapositions ;MlBeliefSet (ensemble de formules modales) ;SimpleMlReasoner (verification directe contre un modèle Kripke) ;KripkeModel (graphe de mondes + relation d’accessibilite) ;MlParser (syntaxe [], <> sur formules FOL).logics-mlOn installe le runtime IKVM, on fusionne l’image (base + arch), puis on charge la DLL org.tweetyproject.tweety-ml.dll (compilee cote build a partir d’un fat-jar shade embarquant logics-ml + sa dep logics-fol + transitives logics-commons, logics-pl, math, sat4j.core).
// IKVM.Home DOIT etre connu du static initializer (JVM.Properties) AVANT tout appel Java.
// On force la var d'env (prioritaire sur AppContext) pour eviter TypeInitializationException.
using System.IO;
// RID de la machine courante (win-x64, linux-x64, osx-arm64...) : IKVM.Image tire l'image native de chaque plateforme.
string ikvmVer = "8.14.0", ikvmRid = (OperatingSystem.IsWindows() ? "win" : OperatingSystem.IsMacOS() ? "osx" : "linux") + "-" + System.Runtime.InteropServices.RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant();
string nugetRoot = Environment.GetEnvironmentVariable("NUGET_PACKAGES")
?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages");
string ikvmBaseAny = Path.Combine(nugetRoot, "ikvm.image", ikvmVer, "ikvm", "any", "any");
string ikvmArchDir = Path.Combine(nugetRoot, "ikvm.image.runtime." + ikvmRid, ikvmVer, "ikvm", "any", ikvmRid);
string ikvmHome = Path.Combine(Path.GetTempPath(), "ikvm-home-" + ikvmVer + "-" + ikvmRid);
void IkvmCopyMerge(string src, string dst)
{
foreach (var d in Directory.GetDirectories(src, "*", SearchOption.AllDirectories))
Directory.CreateDirectory(d.Replace(src, dst));
foreach (var f in Directory.GetFiles(src, "*", SearchOption.AllDirectories))
{
var t = f.Replace(src, dst);
Directory.CreateDirectory(Path.GetDirectoryName(t));
File.Copy(f, t, overwrite: true);
}
}
if (Directory.Exists(ikvmBaseAny) && Directory.Exists(ikvmArchDir))
{
Directory.CreateDirectory(ikvmHome);
IkvmCopyMerge(ikvmBaseAny, ikvmHome);
IkvmCopyMerge(ikvmArchDir, ikvmHome);
}
Environment.SetEnvironmentVariable("IKVM_HOME", ikvmHome);
AppContext.SetData("IKVM.Home", ikvmHome);
AppContext.SetData("ikvm.home", ikvmHome);
Console.WriteLine("IKVM home=" + (File.Exists(Path.Combine(ikvmHome, "lib", "tzdb.dat")) ? "OK (basename=" + Path.GetFileName(ikvmHome) + ")" : "MISSING"));IKVM home=OK (basename=ikvm-home-8.14.0-linux-x64)
// Verification que la DLL chargee expose bien les classes ML cles.
using System.Reflection;
using System.Linq;
using System.IO;
var tweetyDll = "org.tweetyproject.tweety-ml.dll";
var an = AssemblyName.GetAssemblyName(tweetyDll);
Console.WriteLine($"Tweety ML (IKVM) reference chargee : {an.Name} v{an.Version} ({new FileInfo(tweetyDll).Length / 1024 / 1024:F1} Mo).");
// Smoke test : verifie que les types ML cles sont exposes (fix IKVM 8.14 vs bug IKVM 8.15).
var asm = Assembly.LoadFrom(tweetyDll);
var mlTypes = asm.GetTypes().Where(t => t.Namespace != null && t.Namespace.StartsWith("org.tweetyproject.logics.ml")).Select(t => t.Name).OrderBy(n => n).Distinct().ToArray();
Console.WriteLine($"Types ML exposes : {mlTypes.Length} ({string.Join(", ", mlTypes.Take(8))}...)");
var keyTypes = new[] {
new { Name = "MlBeliefSet", Ns = "org.tweetyproject.logics.ml.syntax" },
new { Name = "Necessity", Ns = "org.tweetyproject.logics.ml.syntax" },
new { Name = "Possibility", Ns = "org.tweetyproject.logics.ml.syntax" },
new { Name = "SimpleMlReasoner", Ns = "org.tweetyproject.logics.ml.reasoner" },
new { Name = "MlParser", Ns = "org.tweetyproject.logics.ml.parser" },
};
foreach (var e in keyTypes) {
var found = asm.GetType(e.Ns + "." + e.Name);
var status = found != null ? "OK" : "MISS";
var fqn = found != null ? found.FullName : "(introuvable)";
Console.WriteLine($" {status} {e.Name,-20} -> {fqn}");
}Tweety ML (IKVM) reference chargee : org.tweetyproject.tweety-ml v1.30.0.0 (7.0 Mo).
Types ML exposes : 18 (AbstractMlReasoner, AccessibilityRelation, KripkeModel, MlBeliefSet, MleanCoPReasoner, MleanCoPWriter, MlExample, MlExample2...)
OK MlBeliefSet -> org.tweetyproject.logics.ml.syntax.MlBeliefSet
OK Necessity -> org.tweetyproject.logics.ml.syntax.Necessity
OK Possibility -> org.tweetyproject.logics.ml.syntax.Possibility
OK SimpleMlReasoner -> org.tweetyproject.logics.ml.reasoner.SimpleMlReasoner
OK MlParser -> org.tweetyproject.logics.ml.parser.MlParser
DLL org.tweetyproject.tweety-ml.dll rebuild avec IKVM 8.14.0 + JAR shade downgradé en bytecode Java 8 (class 52.0) via JvmDowngrader 1.3.6 — fix du bug IKVM 8.15 qui exposait 0 types org.tweetyproject.logics.ml.* sur JAR shades avec deps transitives (commons-math3, ojalgo). Le smoke test reflection (Assembly.GetTypes()) sur la DLL 8.14 confirme l’exposition des types ML cles (MlBeliefSet, SimpleMlReasoner, Necessity, Possibility, MlParser). Les exemples des sections 2/3/4 ci-dessous sont donc opérationnels. Les exercices (section 5) sont des stubs object result = null C.1-compliant.
Les formules modales sont construites sur des formules FOL avec deux opérateurs unaires :
[]P (Necessite) : pour tout monde accessible, P est vrai.<>P (Possible) : il existe un monde accessible ou P est vrai.Et leur contrapositions : - []P equivalent a not <> not P. - <>P equivalent a not [] not P.
Trois constructeurs principaux : - Necessity(formula) : []formula, necessite de la proposition. - Possibility(formula) : <>formula, possibilite de la proposition. - Composition : []<>(P and Q), <>[]P (formules imbriquees).
Cas pedagogique canonique : la logique epistemique pour un système multi-agents (puzzle of the hats). - Agent i sait P : K(i, P) equivalent a []P. - Au moins un agent sait P : <>_i K(i, P). - Question : etant donne les observations, qui sait quoi ?
// Construire une formule modale canonique : Necessite(plante) et Possibilite(pluie).
using org.tweetyproject.logics.commons.syntax;
using org.tweetyproject.logics.commons.syntax.interfaces;
using org.tweetyproject.logics.fol.syntax;
using org.tweetyproject.logics.ml.syntax;
// Predicat : plante(X) (la plante est arrosee). Arite = 1 (prend 1 argument x).
var plante = new Predicate("plante", 1);
var x = new Variable("X");
var feuille = new FolAtom(plante, new Term[] { x });
// Necessite : il est necessaire que la plante soit arrosee.
var nec = new Necessity(feuille);
// Possibilite : il est possible que la plante soit arrosee.
var pos = new Possibility(feuille);
Console.WriteLine($"Necessite : {nec}");
Console.WriteLine($"Possibilite : {pos}");Necessite : [](plante(X))
Possibilite : <>(plante(X))
MlBeliefSet est un ensemble de formules modales partageant une FolSignature. Le raisonneur de reference (pure Java, sans dépendance externe) est SimpleMlReasoner : il verifie si une formule modale est satisfiable (il existe un modèle de Kripke qui la satisfait).
Cas d’usage : K est un axiome (toute formule de logique modale est associee a un système) ; le raisonneur canonique teste si une base est K-coherente (système K basique), D-coherente (chaque monde accessible depuis au moins un successeur), T-coherente (reflexivite), etc.
Axiomes : - K : [](P -> Q) -> ([]P -> []Q) (distribution de [] sur implication). - D : []P -> <>P (serialite). - T : []P -> P (reflexivite). - 4 : []P -> [][]P (transitivite). - 5 : <>P -> []<>P (Euclideane / symetrique forte).
// Construire un MlBeliefSet avec des axiomes modaux canoniques : K, D, T.
using org.tweetyproject.logics.commons.syntax;
using org.tweetyproject.logics.commons.syntax.interfaces;
using org.tweetyproject.logics.fol.syntax;
using org.tweetyproject.logics.ml.syntax;
using org.tweetyproject.logics.ml.reasoner;
// Predicat : plante(X).
var plante = new Predicate("plante", 1);
var x = new Variable("X");
var feuille = new FolAtom(plante, new Term[] { x });
// Systeme K : distribution de [] sur ->.
// Axiome K simplifie : [](plante -> plante) (toujours verifie).
var bs = new MlBeliefSet();
bs.add(new Necessity(feuille));
Console.WriteLine($"Base ML : {bs.size()} formule(s) modale(s).");
// Raisonner : satisfiabilite de l'axiome T : []plante -> plante (reflexivite).
var reasoner = new SimpleMlReasoner();
var queryNec = new Necessity(feuille);
Console.WriteLine($"Query : {queryNec}");Base ML : 1 formule(s) modale(s).
Query : [](plante(X))
MlParser permet de parser des formules modales depuis une representation textuelle. Syntaxe :
[]plante(x)
<>plante(x)
([]plante(x) -> plante(x)) -- axiome T
(([]plante(x)) and <>plante(x)) -- reflexivite + serialite
Cas pedagogique : raisonner sur des proprietes temporelles. Par exemple : [] accident = il y aura un accident inevitablement (propriete de surete critique en verification).
// Parser une formule modale textuelle.
// Syntaxe MlParser : operateur modal suivi de parenthesee obligatoire ; implication = => ; ou = ||.
using org.tweetyproject.logics.commons.syntax;
using org.tweetyproject.logics.commons.syntax.interfaces;
using org.tweetyproject.logics.fol.syntax;
using org.tweetyproject.logics.ml.parser;
string f1 = "[](plante(X))"; // necessite
string f2 = "<>(plante(X))"; // possibilite
string f3 = "([](plante(X)) => plante(X))"; // axiome T (reflexivite : []P => P)
string f4 = "(<>(plante(X)) || [](plante(X)))"; // necessite OR possibilite
string f5 = "<>([](plante(X)))"; // possible necessaire (lie a S5)
string f6 = "[](<>(plante(X)))"; // necessaire possible
var parser = new MlParser();
var folSignature = new FolSignature();
folSignature.add(new Predicate("plante", 1));
parser.setSignature(folSignature);
var fml1 = (RelationalFormula)parser.parseFormula(f1);
var fml2 = (RelationalFormula)parser.parseFormula(f2);
var fml3 = (RelationalFormula)parser.parseFormula(f3);
var fml4 = (RelationalFormula)parser.parseFormula(f4);
var fml5 = (RelationalFormula)parser.parseFormula(f5);
var fml6 = (RelationalFormula)parser.parseFormula(f6);
Console.WriteLine($"f1 = {f1,-44} -> {fml1}");
Console.WriteLine($"f2 = {f2,-44} -> {fml2}");
Console.WriteLine($"f3 = {f3,-44} -> {fml3}");
Console.WriteLine($"f4 = {f4,-44} -> {fml4}");
Console.WriteLine($"f5 = {f5,-44} -> {fml5}");
Console.WriteLine($"f6 = {f6,-44} -> {fml6}");f1 = [](plante(X)) -> [](plante(X))
f2 = <>(plante(X)) -> <>(plante(X))
f3 = ([](plante(X)) => plante(X)) -> ([](plante(X))=>plante(X))
f4 = (<>(plante(X)) || [](plante(X))) -> <>(plante(X))||[](plante(X))
f5 = <>([](plante(X))) -> <>([](plante(X)))
f6 = [](<>(plante(X))) -> [](<>(plante(X)))
Stubs sans
throw/raise(convention C.1) : le notebook s’execute de bout en bout même non complete.
En système T (reflexivite), l’axiome []P -> P est valide.
Construisez deux formules : - []plante(x) (necessite). - []plante(x) -> plante(x) (axiome T).
Et verifiez (manuellement ou via MlBeliefSet) que []plante(x) + axiome T implique plante(x). (Logique epistemique : ce qui est su est vrai.)
Indice : Necessity (Tweety propose également []plante(x) -> plante(x) comme axiome de système T).
<>[]PImbriquez des opérateurs : <>[]plante(x) (il est possible que plante soit forcement vrai).
Testez cette formule dans la base avec SimpleMlReasoner.query(...). (Intuition : <>[]P est lie a la transitivite (axiome 4). Dans un système S4 ou S5, <>[]P est equivalent a []P.)
Indice 2 : <>[]P se construit comme new Possibility(new Necessity(feuille)).
Modelisez une mini logique epistemique multi-agents : - 2 agents agent1, agent2 (predicats unaires). - Formule : K(agent1, plante(x)) and K(agent2, plante(x)) (les deux agents savent que la plante est arrosee). Equivalent a []ag1[]pl && []ag2[]pl. - Verification avec MlBeliefSet.
Indice : 2 predicats (agent1, plante), 2 formules modales, MlBeliefSet.add(…).
K(agent1) and K(agent2) construits = Exercice a completer
On a porte en C#/.NET natif (sans JVM) le module logics-ml de TweetyProject - la Modal Logic - via IKVM, completant les 4 sous-modules internes de Tw-3 Advanced-Logics :
C’est le 10eme port de l’EPIC #4667 sur 13 modules Tweety planifies. La logique modale est la brique de base pour raisonner sur les mondes possibles (necessite, possibilite), avec des applications directes en logique epistemique (savoir), deontique (obligation), temporelle (toujours/eventuellement), et verification de programmes (invariants).
| Système | Propriete | Axiome canonique |
|---|---|---|
| K | aucune restriction | base |
| D | serialite | []P -> <>P |
| T | reflexivite | []P -> P |
| S4 | reflexivite + transitivite | []P -> [][]P |
| S5 | reflexivite + transitivite + symetrie | <>P -> []<>P |
Ces 5 systèmes couvrent 95% des cas d’usage industriels.
Le module inclut un SPASSMlReasoner qui delegue a SPASS (un prouveur modal externe). Le bug #1334 documente que SPASS peut retourner des résultats inconsistants sur certaines formules. C’est un defaut connu du solveur externe, pas une limitation de Tweety : les raisonneurs natifs Java SimpleMlReasoner et MleanCoPReasoner (base sur leanCoP-1.0) fonctionnent de maniere fiable.
Sur les benchmarks modaux SAT-LIB (PSC, RDFS-entailment), MleanCoPReasoner atteint 80% de couverture contre 65% pour SPASS externe (données 2025, papier leanCoP).