#r "nuget: IKVM, 8.15.0"
#r "nuget: IKVM.Image, 8.15.0"
- IKVM, 8.15.0
- IKVM.Image, 8.15.0
Série Tweety — port C#/.NET natif (EPIC #4667). Ce notebook exploite le module
arg-aspicde TweetyProject sans JVM : la librairie Java est compilée vers un fat-jar Maven shade puis exécutée sur le runtime .NET via IKVM.
Navigation : Tweety-2-Basic-Logics-Csharp (propositionnel) · Tweety-2b-Semantics-Csharp (mondes possibles) · Tweety-2c-FOL-Csharp (premier ordre) · Tweety-3-Dung-Csharp (argumentation abstraite) · Tweety-4-Aspic (ce notebook) (argumentation structurée).
Le notebook Tweety-3-Dung traitait l’argumentation abstraite de Dung : un argument y est un nœud opaque, on ne regarde que la structure d’attaque. Mais d’où viennent ces arguments ? Sont-ils bien fondés ? L’argumentation structurée ASPIC+ (Modgil & Prakken) répond à ces questions en donnant aux arguments une structure interne construite à partir d’une base de connaissances :
| Composant | Rôle | Notation Tweety |
|---|---|---|
| Axiome (fait) | une vérité tenue pour acquise | -> p (règle sans prémisse) |
| Règle stricte | déduction certaine (si prémisses vraies, conclusion vraie) | p -> q |
| Règle défaisable | déduction réfutable (typiquement vraie, mais des exceptions existent) | p => q |
| Conclusion | la formule déduite (ici propositionnelle) | p, q, !p (négation) |
Convention de notation Tweety (piège fréquent). À l’inverse de la convention mathématique usuelle (
=>= implication stricte), Tweety affiche la flèche simple->pour les règles strictes et la double flèche=>pour les règles défaisables. Le drapeau booléenrule.isDefeasible()lève toute ambiguïté (false= strict,true= défaisable).
La question centrale d’ASPIC+ : étant donné une base de connaissances (faits + règles strictes + règles défaisables), quels arguments peut-on construire, quelles attaques en découlent, et finalement quelles conclusions sont acceptées ? ASPIC+ génère un cadre d’argumentation abstraite de Dung (AAF) à partir de la base structurée, puis on lui applique les sémantiques vues dans Tweety-3 (fondée, préférée, stable).
Ce notebook construit des théories ASPIC+ en C#, génère l’AAF sous-jacente, et montre sur l’exemple canonique (Tweety le pingouin) la divergence des sémantiques : prudente (fondée) vs tranchantes (préférée/stable).
arg-aspicOn installe le runtime IKVM, on fusionne l’image (base + arch), puis on charge la DLL org.tweetyproject.tweety-aspic.dll (compilée côté build à partir d’un fat-jar shade embarquant arg-aspic + ses dépendances transitives : arg-dung, arg-comparator, graphs, commons, math, logics-fol, logics-pl).
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.15.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);
}
AppContext.SetData("IKVM.Home", ikvmHome);
Console.WriteLine("IKVM home=" + (File.Exists(Path.Combine(ikvmHome, "lib", "tzdb.dat")) ? "OK" : "MISSING"));
IKVM home=OK
// Constat 3 (#5039) : la directive #r est silencieuse par defaut en .NET Interactive.
// On verifie que la DLL IKVM/Tweety referencee est bien presente et lisible (metadata).
using System.Reflection;
var tweetyDll = "org.tweetyproject.tweety-aspic.dll";
var an = AssemblyName.GetAssemblyName(tweetyDll);
Console.WriteLine($"Tweety (IKVM) reference chargee : {an.Name} v{an.Version} ({new FileInfo(tweetyDll).Length / 1024 / 1024:F1} Mo).");Tweety (IKVM) reference chargee : org.tweetyproject.tweety-aspic v1.30.0.0 (8.0 Mo).
Une théorie ASPIC+ se construit avec AspicArgumentationTheory en lui passant un générateur de formules de règle (RuleFormulaGenerator) qui sait traduire les règles en formules logiques — pour la logique propositionnelle, c’est PlFormulaGenerator. On y ajoute ensuite :
addAxiom ;addRule, instanciées comme StrictInferenceRule ou DefeasibleInferenceRule.Détail de portage C#/.NET (IKVM). En Java, ces classes sont génériques (
AspicArgumentationTheory<T extends Invertable>). Le port IKVM efface le paramètre de type : on les utilise sans argument générique (sinon erreurCS0308). Le typeTest alors effacé vers sa borneInvertable; uneProposition(logique propositionnelle) est unInvertable.
Construisons une petite théorie : un fait a, et une règle stricte « si a alors b ».
using org.tweetyproject.arg.aspic.syntax;
using org.tweetyproject.arg.aspic.ruleformulagenerator;
using org.tweetyproject.logics.pl.syntax;
var aat = new AspicArgumentationTheory(new PlFormulaGenerator());
var a = new Proposition("a");
var b = new Proposition("b");
aat.addAxiom(a); // fait : a est vrai
// regle stricte : a -> b (a implique b avec certitude)
var strict = new StrictInferenceRule();
strict.setConclusion(b);
strict.addPremise(a);
aat.addRule(strict);
Console.WriteLine("theorie = " + aat);
Console.WriteLine("strict.isDefeasible() = " + strict.isDefeasible() + " (false => STRICT, fleche '->')");
theorie = ArgumentationSystem [rules=[ -> a, a -> b]]
strict.isDefeasible() = False (false => STRICT, fleche '->')
theorie = ArgumentationSystem [rules=[ -> a, a -> b]] : l’objet construit s’imprime sous forme de liste de règles, et la première règle -> a est un fait (conclusion sans prémisse). La ligne suivante est le vrai signal : strict.isDefeasible() = False (false => STRICT, fleche '->') — la flèche affichée est une donnée de sortie, pas une convention visuelle. Les règles fléchées -> forment le socle strict de la théorie : la proposition a est un fait, b dérive de a par une règle totale, et aucune des deux n’est contestable. C’est cette base que la cellule 2.2 va desserrer en introduisant la première règle défaisable.
Ajoutons une règle défaisable « si a alors c » : c est typiquement dérivable, mais peut être réfuté par un argument contradictoire. On le vérifie avec isDefeasible().
var c = new Proposition("c");
// regle défaisable : a => c (a implique c sauf exception)
var def = new DefeasibleInferenceRule();
def.setConclusion(c);
def.addPremise(a);
aat.addRule(def);
Console.WriteLine("theorie = " + aat);
Console.WriteLine("def.isDefeasible() = " + def.isDefeasible() + " (true => DEFAISABLE, fleche '=>')");
theorie = ArgumentationSystem [rules=[ -> a, a => c, a -> b]]
def.isDefeasible() = True (true => DEFAISABLE, fleche '=>')
Comparez la liste des règles à la cellule précédente : rules=[ -> a, a => c, a -> b] — une seule différence, a => c, et la flèche double inverse le statut. La sortie le dit mot pour mot : def.isDefeasible() = True (true => DEFAISABLE, fleche '=>'). La règle « si a alors c » est une généralisation — en l’absence d’information contraire elle se tient, mais elle est attaquable : c’est exactement le matériau dont la section 3 aura besoin pour produire une vraie contradiction, et que la section 4 mettra en scène avec bird => flies contre penguin => !flies. Le couple des deux cellules pose ainsi les deux flèches de ASPIC+ par contraste de sortie avant tout discours théorique.
asDungTheory()ASPIC+ génère automatiquement les arguments (combinaisons de règles + faits) et les attaques entre eux, puis expose le résultat comme un cadre d’argumentation abstraite de Dung via asDungTheory(). On retrouve exactement les types de Tweety-3-Dung : DungTheory, Argument, Attack, Extension.
Sur notre théorie sans conflit (pas de conclusion contradictoire), on obtient plusieurs arguments sans aucune attaque — tous sont donc acceptés, quelle que soit la sémantique.
using org.tweetyproject.arg.dung.syntax;
using org.tweetyproject.arg.dung.semantics;
using org.tweetyproject.arg.dung.reasoner;
DungTheory dung = aat.asDungTheory();
Console.WriteLine("AAF generee = " + dung);
Console.WriteLine("nb arguments = " + dung.getNodes().size());
Console.WriteLine("attaques = " + dung.getAttacks()); // vide : pas de conflit
// Toutes les semantiques acceptent tout (zero attaque)
AbstractExtensionReasoner gr = new SimpleGroundedReasoner();
foreach (Extension ext in gr.getModels(dung).toArray(new Extension[0]))
Console.WriteLine("extension fondee = " + ext);
AAF generee = <{ -> a, a => c [ -> a], a -> b [ -> a] },[]>
nb arguments = 3
attaques = []
extension fondee = { -> a,a => c [ -> a],a -> b [ -> a]}
La génération de l’AAF est lisible ligne à ligne. AAF generee = <{ -> a, a => c [ -> a], a -> b [ -> a] },[]> : la notation Dung — un ensemble d’arguments, puis l’ensemble d’attaques (vide ici), et chaque argument affiche la règle dont il dérive avec le support entre crochets — [ -> a] rappelle que l’argument s’appuie sur le fait a. nb arguments = 3 : chaque règle de la théorie a produit exactement un argument. attaques = [] : aucun conflit — la règle défaisable n’est pas contredite par une règle adverse. Conséquence dans la sortie : extension fondee = { -> a, a => c [ -> a], a -> b [ -> a]} contient les trois arguments. Sans contradiction, les trois sémantiques de la section 3.1 coïncident — la divergence viendra de la section 4.
Pour qu’une attaque naisse, deux arguments doivent conclure des formules contradictoires — par exemple flies et sa négation !flies. Deux atomes distincts comme flies et not_flies ne sont pas reconnus comme contradictoires par la logique propositionnelle : ils ne s’attaquent pas.
La négation se construit avec Negation(formula) (le complément au sens d’Invertable) : c’est elle qui déclenche les attaques de type rebuttal (un argument en concluant !p en attaque un autre concluant p).
Mettons tout en œuvre sur l’exemple classique du raisonnement défaisable :
penguin) et un oiseau (bird).penguin -> bird), taxonomie certaine.bird => flies).penguin => !flies).On a donc deux arguments concluant des choses contradictoires sur le fait de voler : l’un (« Tweety vole ») depuis la règle des oiseaux, l’autre (« Tweety ne vole pas ») depuis la règle des pingouins. Ils s’attaquent mutuellement : c’est un conflit défaisable.
var aat2 = new AspicArgumentationTheory(new PlFormulaGenerator());
var bird = new Proposition("bird");
var peng = new Proposition("penguin");
var flies = new Proposition("flies");
var notFlies = new Negation(flies); // complément reel -> declenche le rebuttal
aat2.addAxiom(bird);
aat2.addAxiom(peng);
// strict : penguin -> bird (taxonomie certaine)
var s1 = new StrictInferenceRule(); s1.setConclusion(bird); s1.addPremise(peng);
aat2.addRule(s1);
// defaisable : bird => flies (les oiseaux volent en general)
var d1 = new DefeasibleInferenceRule(); d1.setConclusion(flies); d1.addPremise(bird);
aat2.addRule(d1);
// defaisable : penguin => !flies (les pingouins ne volent pas)
var d2 = new DefeasibleInferenceRule(); d2.setConclusion(notFlies); d2.addPremise(peng);
aat2.addRule(d2);
Console.WriteLine("=== Theorie ASPIC+ ===\n" + aat2);
DungTheory tweety = aat2.asDungTheory();
Console.WriteLine("\n=== AAF generee ===\n" + tweety);
Console.WriteLine("\nattaques = " + tweety.getAttacks());
=== Theorie ASPIC+ ===
ArgumentationSystem [rules=[ -> penguin, -> bird, bird => flies, penguin => !flies, penguin -> bird]]
=== AAF generee ===
<{ -> bird, bird => flies [ -> bird], penguin => !flies [ -> penguin], -> penguin, penguin -> bird [ -> penguin], bird => flies [penguin -> bird [ -> penguin]] },[(bird => flies [ -> bird],penguin => !flies [ -> penguin]), (penguin => !flies [ -> penguin],bird => flies [ -> bird]), (penguin => !flies [ -> penguin],bird => flies [penguin -> bird [ -> penguin]]), (bird => flies [penguin -> bird [ -> penguin]],penguin => !flies [ -> penguin])]>
attaques = [(bird => flies [ -> bird],penguin => !flies [ -> penguin]), (penguin => !flies [ -> penguin],bird => flies [ -> bird]), (penguin => !flies [ -> penguin],bird => flies [penguin -> bird [ -> penguin]]), (bird => flies [penguin -> bird [ -> penguin]],penguin => !flies [ -> penguin])]
La théorie du pingouin met le pont Dung au travail. La liste des règles affichée d’abord — cinq règles, dont bird => flies et penguin => !flies — puis l’AAF générée : six arguments, parce que la règle dérivée du penguin -> bird strict donne naissance à un argument de plus (le flies dont la chaîne de prémisses passe par le fait penguin). Les attaques = [...] alignent quatre paires — et la lecture la plus instructive est leur symétrie : chaque paire d’arguments contradictoires (flies vs !flies, dans leurs deux dérivations) apparaît dans les deux sens. L’AAF imprimée est un graphe non orienté en pratique : la contradiction entre conclusions est réciproque, et c’est la sémantique qui décidera lequel des deux survivra.
Les deux arguments sur le vol (flies vs !flies) s’attaquent mutuellement sans qu’aucun ne soit défendu. C’est exactement la situation d’attaque mutuelle vue dans Tweety-3, et les sémantiques divergent :
flies ni !flies ne sont acceptés. Les seuls arguments acceptés sont ceux non attaqués (bird, penguin, et penguin -> bird).flies, l’autre !flies.void Dump(string label, AbstractExtensionReasoner r, DungTheory theory)
{
Console.WriteLine(label + " :");
foreach (Extension ext in r.getModels(theory).toArray(new Extension[0]))
Console.WriteLine(" " + ext);
}
Dump("Tweety fondée", new SimpleGroundedReasoner(), tweety);
Dump("Tweety préférée", new SimplePreferredReasoner(), tweety);
Dump("Tweety stable", new SimpleStableReasoner(), tweety);
Console.WriteLine("\n-- Lecture --");
Console.WriteLine("Fondee : Tweety vole-t-il ? -> non tranche (flies ET !flies absents)");
Console.WriteLine("Preferee : deux mondes possibles (flies OU !flies), il faut choisir");
Tweety fondée :
{ -> bird, -> penguin,penguin -> bird [ -> penguin]}
Tweety préférée :
{ -> bird,bird => flies [ -> bird], -> penguin,penguin -> bird [ -> penguin],bird => flies [penguin -> bird [ -> penguin]]}
{ -> bird,penguin => !flies [ -> penguin], -> penguin,penguin -> bird [ -> penguin]}
Tweety stable :
{ -> bird,bird => flies [ -> bird], -> penguin,penguin -> bird [ -> penguin],bird => flies [penguin -> bird [ -> penguin]]}
{ -> bird,penguin => !flies [ -> penguin], -> penguin,penguin -> bird [ -> penguin]}
-- Lecture --
Fondee : Tweety vole-t-il ? -> non tranche (flies ET !flies absents)
Preferee : deux mondes possibles (flies OU !flies), il faut choisir
Les onze lignes de sortie comparées font la leçon de la section 4.1. Fondée : une seule extension { -> bird, -> penguin, penguin -> bird [ -> penguin]} — les faits et la taxonomie stricte, ni flies ni !flies : le scepticisme ne tranche pas sur le vol. Préférée : deux extensions — l’une avec bird => flies [ -> bird] (et sa dérivée), l’autre avec penguin => !flies [ -> penguin] — deux mondes maximaux, chacun excluant l’argument adverse. Stable : les deux mêmes ensembles — ici stable et préférée coïncident, car chaque extension attaque tout argument extérieur. Le « non tranché » de la fondée et le « il faut choisir » des préférées sont déjà imprimés par la cellule elle-même (section -- Lecture -- de la sortie) : les extensions illustrent mot pour mot ces deux verdicts.
Stubs sans
throw/raise(convention C.1) : le notebook s’exécute de bout en bout même non complété.
Dans l’exemple Tweety ci-dessus, la sémantique fondée ne tranche pas : ni flies ni !flies ne sont acceptés (conflit défaisable symétrique). Ajoutez à aat2 une règle stricte penguin -> !flies (« les pingouins ne volent pas, c’est un fait certain ») et ré-affichez l’extension fondée.
Question : !flies est-il maintenant accepté sceptiquement ? Pourquoi une conclusion strictement dé rivable ne peut-elle pas être réfutée par un argument défaisable ?
Indice : créez un nouveau StrictInferenceRule de conclusion notFlies et de prémisse peng, ajoutez-le à aat2, régénérez le DungTheory, et relancez SimpleGroundedReasoner.
// TODO etudiant : ajouter la regle stricte penguin -> !flies, puis re-afficher la fondee
// Indice : new StrictInferenceRule(); .setConclusion(notFlies); .addPremise(peng); aat2.addRule(...)
string verdictFlies = null; // TODO etudiant : "accepte" si !flies dans l'extension fondee, sinon "rejete"/"non tranche"
Console.WriteLine("!flies skeptiquement accepte ? " + (verdictFlies ?? "Exercice a completer"));
!flies skeptiquement accepte ? Exercice a completer
Construisez une théorie ASPIC+ avec trois faits p, q, r et trois règles défaisables formant un cycle de contradictions : p => q, q => r, r => !p (de sorte que p et !p coexistent via la chaîne). Générez l’AAF et comparez l’extension fondée et les extensions préférées.
Question : combien d’extensions préférées obtient-on ? L’extension fondée contient-elle une conclusion sur p ?
Indice : comme dans Tweety-3, un cycle d’attaques mutuelles sans argument défendu donne une fondée prudente et plusieurs préférées maximales.
// TODO etudiant : cycle defaisable p => q => r => !p, comparer fondee vs preferee
// Indice : 3 axiomes p,q,r + 3 DefeasibleInferenceRule. asDungTheory() puis Dump(...).
object nbPrefereesCycle = null; // TODO etudiant : nombre d'extensions preferees (int)
Console.WriteLine("extensions preferees du cycle = " + (nbPrefereesCycle ?? "Exercice a completer"));
extensions preferees du cycle = Exercice a completer
Construisez une théorie ASPIC+ de votre choix sur le modèle suivant : un fait initial, une règle stricte et deux règles défaisables aboutissant à des conclusions contradictoires (usage de Negation). Affichez la théorie, l’AAF générée et les attaques.
Exemple : « un patient a de la fièvre » (fever), règle stricte « infection ⇒ fièvre n’est pas une règle, plutôt : flu -> fever », défaisable « fièvre ⇒ antibiotique », défaisable « allergie ⇒ ! antibiotique ». Prédisez l’extension fondée avant de l’exécuter, puis vérifiez.
Indice : traduisez en Proposition + une Negation. La conclusion du raisonnement fondé est la même que pour Tweety : suspension tant que les deux camps défaisables s’équilibrent.
// TODO etudiant : mini-theorie (1 fait + 1 regle stricte + 2 defaisables contradictoires)
// Indice : AspicArgumentationTheory + addAxiom + StrictInferenceRule + 2 DefeasibleInferenceRule (conclusions p et Negation(p)).
string predictionVerdict = null; // TODO etudiant : ce que vous PREDISEZ pour la fondee (puis verifiez)
Console.WriteLine("verdict fondee prédit = " + (predictionVerdict ?? "Exercice a completer"));
verdict fondee prédit = Exercice a completer
On a porté en C#/.NET natif (sans JVM) l’argumentation structurée ASPIC+ via le module arg-aspic et IKVM, en montrant le pont vers l’argumentation abstraite de Dung :
->, certaines) et règles défaisables (=>, réfutables) — via AspicArgumentationTheory, addAxiom, StrictInferenceRule/DefeasibleInferenceRule.asDungTheory() produit les arguments (combinaisons de règles) et les attaques (conflits par complémentarité, Negation).| Aspect | Dung (Tweety-3) | ASPIC+ (Tweety-4) |
|---|---|---|
| Argument | nœud opaque (boîte noire) | structure interne (règles + prémisses → conclusion) |
| Origine des arguments | donnée (à la main) | générée depuis une base de connaissances |
| Origine des attaques | déclarées (Attack) |
déduites des contradictions (compléments) |
| Force des arguments | uniforme | dépend des règles (strict / défaisable, ordres) |
Le bilan du pont Dung en chiffres de sortie : théorie simple — trois arguments, zéro attaque, une extension fondée pleine ; théorie du pingouin — six arguments, quatre attaques en paires symétriques, une fondée sceptique et deux préférées/stables. Le même mécanisme de traduction produit l’unanimité ou le conflit selon l’ontologie : c’est la quantification exacte de la phrase d’ouverture — ASPIC+ génère le débat, la sémantique le juge.