Tweety-6 — Argumentation structuree (twin C# / .NET Interactive)

Twin C# du notebook Python Tweety-06-Structured-Argumentation-Python.ipynb (marathon #4956, parite .NET <-> Python, axe-2 SOTA #3801 Prong B).

Le notebook Python invoque la bibliotheque Java TweetyProject (via jpype + JVM) pour construire des arguments structurees : ASPIC+, DeLP, ABA, argumentation deductive, ASP. Ce twin deroule le moteur ASPIC+ a la main (BCL .NET 9, 0 NuGet, aucune JVM), dans la même lignee que le Tweety-5-Csharp qui implementait le cadre abstrait de Dung from-scratch.

Idea cle

L’argumentation abstraite de Dung (Tweety-5) traite les arguments comme des boites noires : on ne sait pas d’ou ils viennent ni pourquoi ils s’attaquent. L’argumentation structuree (ASPIC+) repond a ces deux questions :

  • Construction : un argument est construit en appliquant des règles (strictes -> ou defaisables =>) a partir d’axiomes. Les arguments se composent recursivement.
  • Attaque : deux arguments s’attaquent quand leurs conclusions se contredisent (rebut), ou quand l’un minconforte une règle defaisable de l’autre (undercut).

On verifie mathematiquement que la conversion ASPIC+ -> Dung redonne le bon cadre abstrait, et que la sémantique grounded isole les arguments defendus.

Note de parite cross-langage : ce notebook est le jumeau C# from-scratch BCL .NET 9 (0 NuGet, 0 JVM) du notebook Python Tweety-06-Structured-Argumentation-Python.ipynb (JVM TweetyProject via jpype, ASPIC+ + DeLP + ABA + Deductive + ASP). Le twin C# couvre ASPIC+ from-scratch BCL (sections 1-5) et declare INTRINSIC les 4 autres formalismes (pas d’equivalent BCL .NET ; le notebook Python + JVM TweetyProject reste la reference d’execution pour DeLP/ABA/ASP). Audit G.1 cross-langage cell-by-cell c.743 : cellules C# verifiees, grounded { -> b, -> c, b, c => a [ -> b, -> c] } coherent avec cell[7] Python (5 memes arguments). Marathon parite .NET <-> Python : EPIC #4956.

// Setup : aucune dependance externe — moteur ASPIC+ from-scratch, uniquement la BCL .NET.
// Meme convention que Tweety-5-Csharp (Dung from-scratch).
using Microsoft.DotNet.Interactive;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;

// Helper de formatage invariant (CultureInfo NE persiste PAS entre cellules -> helper FI).
static string FI(double x, string fmt = "F2")
{
    var s = x.ToString(fmt, CultureInfo.InvariantCulture);
    // Tue les -0.0 parasites (mathematiquement nuls).
    if (s == "-0" || s == "-0.0" || s == "-0.00") s = s.Substring(1);
    return s;
}
// Affichage headless (Console.WriteLine est avale en papermill ; .Display() capture).
static void Show(string s) { s.Display(); }

"Environnement pret — moteur ASPIC+ from-scratch (BCL .NET seule, 0 NuGet, 0 JVM).".Display();
Environnement pret — moteur ASPIC+ from-scratch (BCL .NET seule, 0 NuGet, 0 JVM).

1. Théorie ASPIC+ : règles et axiomes

Une théorie ASPIC+ (en logique propositionnelle) comporte :

  • des axiomes (faits certains, non attaquables) ;
  • des règles strictes premises -> conclusion : deduction inattaquable (logique classique) ;
  • des règles defaisables premises => conclusion : inference remise en cause (le coeur du debat).

Une formule est une proposition (d) ou sa negation (!d). Deux formules sont contradictoires si l’une est la negation de l’autre : Compl(d) = !d, Compl(!d) = d.

// Representation d'une theorie ASPIC+ : axiomes + regles (strictes / defaisables).
// Les formules sont des chaines ("d", "!d") ; la negation est prefixee par '!'.

static string Compl(string f) => f.StartsWith("!") ? f.Substring(1) : "!" + f;

class AspicRule
{
    public string Name { get; set; }
    public List<string> Premises { get; set; } = new();
    public string Conclusion { get; set; }
    public bool Defeasible { get; set; }
    public string Display() =>
        this.Premises.Count == 0
            ? (this.Defeasible ? "T => " : "T -> ") + this.Conclusion
            : string.Join(", ", this.Premises) + (this.Defeasible ? " => " : " -> ") + this.Conclusion;
}

class AspicTheory
{
    public List<string> Axioms { get; } = new();
    public List<AspicRule> Rules { get; } = new();
    public void AddAxiom(string p) { if (!this.Axioms.Contains(p)) this.Axioms.Add(p); }
    public void AddRule(AspicRule r) { this.Rules.Add(r); }
    public override string ToString()
    {
        var parts = new List<string>();
        foreach (var ax in this.Axioms) parts.Add("T -> " + ax);
        foreach (var r in this.Rules) parts.Add(r.Display());
        return "ArgumentationSystem [rules=[" + string.Join(", ", parts) + "]]";
    }
}

Show("Classes pretes : AspicRule (stricte/defaisable), AspicTheory (axiomes + regles).");
Classes pretes : AspicRule (stricte/defaisable), AspicTheory (axiomes + regles).

2. Construction récursive des arguments

Un argument est soit : - un axiome (feuille) : -> c (conclusion c tiree directement d’un axiome) ; - l’application d’une règle a des sous-arguments : premises => conclusion [sous-args].

On construit tous les arguments par point fixe : on part des axiomes, puis on applique iterativement toute règle dont les premisses sont déjà derivables, jusqu’a ne plus rien pouvoir ajouter.

Chaque conclusion n’etant ici produite que par une seule règle, on obtient exactement un argument par conclusion derivee (pas d’explosion combinatoire).

// Argument ASPIC+ : structure recursive (conclusion + regle sommitale + sous-arguments).
// ToString reproduit la convention Tweety :  -> c  (axiome)  |  p1, p2 => c [sub1, sub2]  (regle).

class AspicArgument
{
    public string Conclusion { get; set; }
    public AspicRule TopRule { get; set; }          // null => argument d'axiome
    public List<AspicArgument> Subs { get; set; } = new();  // un sous-argument par premisse de TopRule
    public bool IsAxiom => this.TopRule == null;
    public bool IsDefeasible => this.TopRule != null && this.TopRule.Defeasible;

    public string Display()
    {
        if (this.IsAxiom) return " -> " + this.Conclusion;
        var pre = string.Join(", ", this.TopRule.Premises);
        var arrow = this.TopRule.Defeasible ? " => " : " -> ";
        var subs = string.Join(", ", this.Subs.Select(s => s.Display()));
        return pre + arrow + this.Conclusion + " [" + subs + "]";
    }
    // Signature de canonicalite pour dedoublonner (conclusion + regle + structure des sous-args).
    public string Key() => this.IsAxiom
        ? "AX:" + this.Conclusion
        : this.TopRule.Name + "[" + string.Join("|", this.Subs.Select(s => s.Key())) + "]";
}

static class AspicBuilder
{
    // Construit tous les arguments d'une theorie par point fixe.
    public static List<AspicArgument> Build(AspicTheory t)
    {
        var args = new List<AspicArgument>();
        var byKey = new Dictionary<string, AspicArgument>();
        // Arguments d'axiomes.
        foreach (var ax in t.Axioms)
        {
            var a = new AspicArgument { Conclusion = ax };
            if (!byKey.ContainsKey(a.Key())) { byKey[a.Key()] = a; args.Add(a); }
        }
        // Point fixe : appliquer toute regle dont les premisses sont derivables.
        bool changed = true;
        while (changed)
        {
            changed = false;
            // conclusions deja derivables (cle = conclusion, valeur = un argument canonique).
            var derivable = args.GroupBy(a => a.Conclusion).ToDictionary(g => g.Key, g => g.First());
            foreach (var rule in t.Rules)
            {
                if (!rule.Premises.All(p => derivable.ContainsKey(p))) continue;
                // Un argument par application : sous-argument canonique pour chaque premisse.
                var subs = rule.Premises.Select(p => derivable[p]).ToList();
                var arg = new AspicArgument { Conclusion = rule.Conclusion, TopRule = rule, Subs = subs };
                if (!byKey.ContainsKey(arg.Key()))
                {
                    byKey[arg.Key()] = arg;
                    args.Add(arg);
                    changed = true;
                }
            }
        }
        return args;
    }
}

Show("AspicArgument + AspicBuilder prets (construction recursive par point fixe).");
AspicArgument + AspicBuilder prets (construction recursive par point fixe).

3. Exemple : théorie avec contradiction sur d

Théorie (une seule règle par conclusion, une contradiction sur d) :

  • Axiomes : b, c
  • Règles defaisables :
    • r1 : b, c => a
    • r2 : b => d
    • r3 : a => !d

On derive a (via r1), puis d (via r2) et !d (via r3) : d et !d sont contradictoires -> conflit.

// Construction de l'exemple ASPIC+ et enumeration des arguments.
var t = new AspicTheory();
t.AddAxiom("b");
t.AddAxiom("c");
t.AddRule(new AspicRule { Name = "r1", Premises = new() { "b", "c" }, Conclusion = "a", Defeasible = true });
t.AddRule(new AspicRule { Name = "r2", Premises = new() { "b" },     Conclusion = "d", Defeasible = true });
t.AddRule(new AspicRule { Name = "r3", Premises = new() { "a" },     Conclusion = "!d", Defeasible = true });

var sb = new StringBuilder();
sb.AppendLine("--- Theorie ASPIC+ ---");
sb.AppendLine(t.ToString());

var args = AspicBuilder.Build(t);
sb.AppendLine();
sb.AppendLine("--- Arguments construits (" + args.Count + ") ---");
foreach (var a in args) sb.AppendLine("  - " + a.Display());

Show(sb.ToString());
--- Theorie ASPIC+ ---
ArgumentationSystem [rules=[T -> b, T -> c, b, c => a, b => d, a => !d]]

--- Arguments construits (5) ---
  -  -> b
  -  -> c
  - b, c => a [ -> b,  -> c]
  - b => d [ -> b]
  - a => !d [b, c => a [ -> b,  -> c]]

On obtient 5 arguments :

Argument Conclusion Structure
-> b b axiome
-> c c axiome
b => d [ -> b] d r2 sur l’axiome b
b, c => a [ -> b, -> c] a r1 sur les axiomes b, c
a => !d [b, c => a [...]] !d r3 sur l’argument de a (récursif)

L’argument pour !d emboite l’argument pour a, qui lui-même emboite ceux de b et c : c’est la composition récursive propre a l’argumentation structuree.

4. Attaques : rebut entre arguments defaisables

En ASPIC+, un argument defaisable A rebut un argument defaisable B si la conclusion de A contredit celle de B (Conclusion(A) = Compl(Conclusion(B))). Le rebut est symetrique entre arguments defaisables : si A rebut B et que B est defaisable, B rebut A aussi.

Simplification honnete : on modelise le rebut sur la conclusion sommitale entre arguments defaisables. L’ASPIC+ complet ajoute l’undermine (attaque d’une premisse) et l’undercut (attaque directe d’une règle defaisable) — non necessaire pour cet exemple (pur conflit de conclusion), et signale ici pour transparence.

// Generation des attaques (rebut symetrique entre arguments defaisables).
// A rebut B <=> A defaisable ET B defaisable ET Conclusion(A) = Compl(Conclusion(B)).

static List<(AspicArgument, AspicArgument)> Rebuttals(List<AspicArgument> args)
{
    var atks = new List<(AspicArgument, AspicArgument)>();
    foreach (var a in args)
    {
        if (!a.IsDefeasible) continue;
        foreach (var b in args)
        {
            if (!b.IsDefeasible) continue;
            if (a.Conclusion == Compl(b.Conclusion))
                atks.Add((a, b));
        }
    }
    return atks;
}

var attacks = Rebuttals(args);
var sb2 = new StringBuilder();
sb2.AppendLine("--- Attaques generees (" + attacks.Count + ") ---");
foreach (var (a, b) in attacks)
    sb2.AppendLine("  - (" + a.Display() + ", " + b.Display() + ")");
Show(sb2.ToString());
--- Attaques generees (2) ---
  - (b => d [ -> b], a => !d [b, c => a [ -> b,  -> c]])
  - (a => !d [b, c => a [ -> b,  -> c]], b => d [ -> b])

Deux attaques symetriques : - arg_d (conclut d) rebut arg_!d (conclut !d) - arg_!d rebut arg_d

Ni arg_a, ni les axiomes ne sont attaques (leurs conclusions ne sont contredites par personne).

5. Conversion vers Dung + sémantique grounded

On projette les arguments en noeuds et les attaques en aretes : on retrouve un cadre de Dung abstrait. On lui applique alors la fonction caractéristique de Tweety-5-Csharp :

  • \(F(S)\) = arguments dont tous les attaquants sont contre-attaques par \(S\) ;
  • \(F(\emptyset)\) = arguments sans attaquant ;
  • on itere \(F\) jusqu’au point fixe = extension grounded (sceptique).
// Cadre de Dung abstrait (repris de Tweety-5-Csharp) + fonction caracteristique.
class DungAF
{
    public HashSet<string> Args { get; } = new();
    private readonly Dictionary<string, HashSet<string>> _attackersOf = new();
    private readonly Dictionary<string, HashSet<string>> _attackedBy = new();
    public void AddArg(string a) { this.Args.Add(a); if(!this._attackersOf.ContainsKey(a)) this._attackersOf[a]=new(); if(!this._attackedBy.ContainsKey(a)) this._attackedBy[a]=new(); }
    public void AddAttack(string a, string b) { this.AddArg(a); this.AddArg(b); this._attackersOf[b].Add(a); this._attackedBy[a].Add(b); }
    public IEnumerable<string> AttackersOf(string b) => this._attackersOf[b];
    public bool Attacks(string a, string b) => this._attackedBy[a].Contains(b);
}
static bool DefendedBy(string a, ISet<string> S, DungAF af)
{
    foreach (var b in af.AttackersOf(a)) { bool blocked=false; foreach(var c in S) if(af.Attacks(c,b)){blocked=true;break;} if(!blocked) return false; }
    return true;
}
static HashSet<string> F(ISet<string> S, DungAF af)
{
    var r = new HashSet<string>(); foreach (var a in af.Args) if (DefendedBy(a, S, af)) r.Add(a); return r;
}
// Extension grounded = point fixe de F partant de l'ensemble vide.
static HashSet<string> Grounded(DungAF af)
{
    var E = new HashSet<string>(); var prev = new HashSet<string>();
    do { prev = E; E = F(E, af); } while (E.Count != prev.Count);
    return E;
}

// Conversion ASPIC+ -> Dung : les arguments deviennent des noeuds (id = Display).
var af = new DungAF();
foreach (var a in args) af.AddArg(a.Display());
foreach (var (a, b) in attacks) af.AddAttack(a.Display(), b.Display());

var sb3 = new StringBuilder();
sb3.AppendLine("--- Conversion en AF de Dung ---");
sb3.AppendLine("Arguments (" + af.Args.Count + ") : " + string.Join(", ", af.Args.OrderBy(x => x)));
sb3.AppendLine();
var grounded = Grounded(af);
sb3.AppendLine("--- Extension grounded ---");
sb3.AppendLine("{ " + string.Join(", ", grounded.OrderBy(x => x)) + " }");
Show(sb3.ToString());
--- Conversion en AF de Dung ---
Arguments (5) :  -> b,  -> c, a => !d [b, c => a [ -> b,  -> c]], b => d [ -> b], b, c => a [ -> b,  -> c]

--- Extension grounded ---
{  -> b,  -> c, b, c => a [ -> b,  -> c] }

Lecture du résultat :

  • Les arguments -> b, -> c et b, c => a [...] sont acceptes (sans attaquant, ou defendus).
  • Les arguments b => d [...] et a => !d [...] sont rejetes : ils se rebutent mutuellement, et aucun tiers ne defend l’un contre l’autre. Le doute l’emporte : ni d ni !d n’est conclu.

C’est exactement le verdict sceptique de la sémantique grounded face a un cycle d’attaque symetrique non resoluble — et il correspond a l’extension calculee par TweetyProject sur la même théorie.

6. Au-dela d’ASPIC+ : DeLP, ABA, ASP (sections conceptuelles)

Le notebook Python illustre quatre autres formalismes structurees via TweetyProject (JVM). Aucun n’a d’equivalent BCL .NET direct — ce sont des systèmes dedies (moteurs Java, solveurs ASP). Ce twin les documente honnetement (verdict INTRINSIC au sens #3801) sans maquiller un faux rendu.

Formalisme Idee Fait tourner dans ce twin ?
ASPIC+ Règles strictes/defaisables -> arguments -> Dung Oui, from-scratch (sections 1-5)
DeLP (Defeasible Logic Programming) Debat sur règles defaisables + requêtes dialectiques Non (moteur Java arg.delp) — conceptuel
ABA (Assumption-Based Argumentation) Atttaquer des hypothses (assumptions) inversees Non (moteur Java arg.aba) — conceptuel
Argumentation deductive Arbres de preuve PL + contre-arguments Non (moteur Java arg.deductive) — conceptuel
ASP (Answer Set Programming) Ensembles-reponses d’un programme logique Non (solveur dlv/clingo) — conceptuel

Pourquoi ce plafond est honnete : ASPIC+ est le formalisme le plus expressif dont le moteur (construction d’arguments + rebut + Dung) se reconstruit proprement en BCL. DeLP/ABA/ASP reposent sur des infrastructures dediees (moteurs d’inférence, solveurs) qui n’ont pas d’equivalent .NET leger — invoquer une JVM ou un solver externe depasserait le cadre d’un twin pedagogique BCL. La valeur du twin reside dans l’explication du moteur ASPIC+ (ce que TweetyProject cache), pas dans la reproduction exhaustive des cinq formalismes.

// Synthese comparative des formalismes d'argumentation structuree (conceptuel).
var rows = new[] {
    ("ASPIC+",            "Regles strictes/defaisables, rebut/undercut/undermine -> Dung", "From-scratch (sections 1-5)"),
    ("DeLP",              "Programmation logique defaisable + arbre dialectique",          "Intrinseque (moteur Java arg.delp)"),
    ("ABA",               "Attaque d'hypotheses (assumptions) par contraires",             "Intrinseque (moteur Java arg.aba)"),
    ("Arg. deductive",    "Arbre de preuve PL + contre-arguments",                         "Intrinseque (moteur Java arg.deductive)"),
    ("ASP",               "Ensembles-reponses d'un programme logique",                     "Intrinseque (solveur dlv/clingo)"),
};
var sb4 = new StringBuilder();
sb4.AppendLine("+---------------------+------------------------------------------------+--------------------------------+");
sb4.AppendLine("| Formalisme          | Idee cle                                       | Dans ce twin C# ?              |");
sb4.AppendLine("+---------------------+------------------------------------------------+--------------------------------+");
foreach (var (n, idea, here) in rows)
    sb4.AppendLine("| " + n.PadRight(19) + " | " + idea.PadRight(46) + " | " + here.PadRight(30) + " |");
sb4.AppendLine("+---------------------+------------------------------------------------+--------------------------------+");
sb4.AppendLine();
sb4.AppendLine("Verdict #3801 : ASPIC+ = SOTA-OK (moteur from-scratch). DeLP/ABA/ASP/Arg.deductive = INTRINSIC");
sb4.AppendLine("(pas d'equivalent BCL .NET ; le notebook Python + JVM TweetyProject reste la reference d'execution).");
Show(sb4.ToString());
+---------------------+------------------------------------------------+--------------------------------+
| Formalisme          | Idee cle                                       | Dans ce twin C# ?              |
+---------------------+------------------------------------------------+--------------------------------+
| ASPIC+              | Regles strictes/defaisables, rebut/undercut/undermine -> Dung | From-scratch (sections 1-5)    |
| DeLP                | Programmation logique defaisable + arbre dialectique | Intrinseque (moteur Java arg.delp) |
| ABA                 | Attaque d'hypotheses (assumptions) par contraires | Intrinseque (moteur Java arg.aba) |
| Arg. deductive      | Arbre de preuve PL + contre-arguments          | Intrinseque (moteur Java arg.deductive) |
| ASP                 | Ensembles-reponses d'un programme logique      | Intrinseque (solveur dlv/clingo) |
+---------------------+------------------------------------------------+--------------------------------+

Verdict #3801 : ASPIC+ = SOTA-OK (moteur from-scratch). DeLP/ABA/ASP/Arg.deductive = INTRINSIC
(pas d'equivalent BCL .NET ; le notebook Python + JVM TweetyProject reste la reference d'execution).

Tranche 2 : parite avec la lib Tweety reelle (IKVM / ASPIC+)

La tranche 1 (sections 1 a 3) est un moteur ASPIC+ pedagogique ecrit a la main en .NET : il construit les arguments, genere les attaques et resout le cadre de Dung sans aucune dependance externe. C’est l’objectif : comprendre le formalisme de l’interieur.

La tranche 2 qui suit est le pendant production : la meme theorie ASPIC+, mais traitee par la lib Tweety reelle (Java, via le pont IKVM). L’interet n’est pas de refaire ce que la tranche 1 fait deja, mais de verifier la parite : les deux moteurs doivent s’accorder sur le meme ensemble d’arguments, d’attaques et d’extensions. C’est la parite jpype (Python) <=> IKVM (C#) qui fait la valeur du jumeau C#.

On reprend exactement la theorie de la section 3 (contradiction sur d) : deux axiomes b, c, trois regles defaisables b,c => a, b => d, a => !d.

#r "nuget: IKVM, 8.15.0"
#r "nuget: IKVM.Image, 8.15.0"
Installed Packages
  • IKVM, 8.15.0
  • IKVM.Image, 8.15.0
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
#r "org.tweetyproject.tweety-aspic.dll"
// Constat (#5039) : la directive #r est silencieuse par defaut en .NET Interactive.
// On verifie que la DLL Tweety (IKVM) referencee est bien presente et lisible.
using System.Reflection;
var tweetyDll = "org.tweetyproject.tweety-aspic.dll";
var an = AssemblyName.GetAssemblyName(tweetyDll);
Console.WriteLine($"Tweety (IKVM) charge : {an.Name} v{an.Version} ({new FileInfo(tweetyDll).Length / 1024 / 1024:F1} Mo).");
Tweety (IKVM) charge : org.tweetyproject.tweety-aspic v1.30.0.0 (8.0 Mo).

Refaire l’exemple de la section 3 avec la lib ASPIC+

On reconstruit la theorie ASPIC+ de la section 3 (b,c => a, b => d, a => !d) via les classes de la lib Tweety, puis on laisse la lib generer elle-meme le cadre de Dung equivalent (asDungTheory()). Si la parite tient, la lib doit trouver le meme graphe d’attaque que le moteur from-scratch : deux attaques symetriques entre b => d et a => !d.

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");
var c = new Proposition("c");
var d = new Proposition("d");
var notD = new Negation(d);

aat.addAxiom(b);
aat.addAxiom(c);

// b, c => a
var r1 = new DefeasibleInferenceRule(); r1.setConclusion(a); r1.addPremise(b); r1.addPremise(c);
aat.addRule(r1);
// b => d
var r2 = new DefeasibleInferenceRule(); r2.setConclusion(d); r2.addPremise(b);
aat.addRule(r2);
// a => !d
var r3 = new DefeasibleInferenceRule(); r3.setConclusion(notD); r3.addPremise(a);
aat.addRule(r3);

Console.WriteLine("=== Theorie ASPIC+ (lib Tweety) ===");
Console.WriteLine(aat);
=== Theorie ASPIC+ (lib Tweety) ===
ArgumentationSystem [rules=[ -> c,  -> b, b, c => a, b => d, a => !d]]
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 genere par la lib ===");
Console.WriteLine("nb arguments = " + dung.getNodes().size());
Console.WriteLine("attaques = " + dung.getAttacks());

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);
}
Console.WriteLine();
Dump("Extension fondee",   new SimpleGroundedReasoner(),  dung);
Dump("Extension preferee", new SimplePreferredReasoner(), dung);
Dump("Extension stable",   new SimpleStableReasoner(),    dung);
=== AAF genere par la lib ===
nb arguments = 5
attaques = [(b => d [ -> b],a => !d [b, c => a [ -> b,  -> c]]), (a => !d [b, c => a [ -> b,  -> c]],b => d [ -> b])]

Extension fondee :
  { -> b, -> c,b, c => a [ -> b,  -> c]}
Extension preferee :
  {b => d [ -> b], -> b, -> c,b, c => a [ -> b,  -> c]}
  { -> b, -> c,b, c => a [ -> b,  -> c],a => !d [b, c => a [ -> b,  -> c]]}
Extension stable :
  {b => d [ -> b], -> b, -> c,b, c => a [ -> b,  -> c]}
  { -> b, -> c,b, c => a [ -> b,  -> c],a => !d [b, c => a [ -> b,  -> c]]}

Lecture : parite from-scratch <=> lib Tweety

La lib Tweety retrouve exactement la contradiction que le moteur from-scratch de la section 3 avait identifiee : b => d et a => !d s’attaquent mutuellement (rebuttal sur la conclusion d). L’extension fondee exclut les deux arguments attaques et ne retient que ce qui est incontestable (b, c, et l’argument b,c => a qui n’est attaque par personne). Les extensions preferree et stable exposent les deux mondes possibles : soit b => d survit, soit a => !d.

Parite tenue : le moteur pedagogique ecrit a la main (tranche 1) et le moteur de production (tranche 2) s’accordent. C’est ce cross-check qui justifie d’avoir les deux : le from-scratch enseigne le comment, la lib Tweety valide le quoi et confirme que notre implementation manuelle n’a pas de bug. Le jumeau Python (Tweety-6, section 4.2) obtient le meme resultat via jpype ; les trois chemins (from-scratch C#, IKVM C#, jpype Python) convergent.

Exercices (#2161)

Trois exercices pour manipuler le moteur ASPIC+.

Exercice 1 — Une quatrieme règle defaisable

Ajoutez une règle r4 : c => !a a la théorie de la section 3 (en plus de r1, r2, r3).

  1. Construisez le nouvel argument pour !a.
  2. Identifiez la nouvelle paire d’attaques (rebut entre arg_a et arg_!a).
  3. Recalculez l’extension grounded : a et !a sont-ils acceptes ? Que devient d (r3 depend de a) ?

Indice : un nouveau cycle symetrique apparait au niveau de a. Observez comment l’effet se propage aux arguments qui utilisent a comme premisse.

// Exercice 1 : a completer
// Etape 1 : reconstruire la theorie avec r4 (c => !a).
// Etape 2 : enumerer les arguments et identifier les rebuttals supplementaires.
// Etape 3 : calculer l'extension grounded et comparer avec la section 5.
// var tex1 = new AspicTheory();
// tex1.AddAxiom("b"); tex1.AddAxiom("c");
// tex1.AddRule(... r1, r2, r3 ...);
// tex1.AddRule(new AspicRule { Name = "r4", Premises = new() { "c" }, Conclusion = "!a", Defeasible = true });
// var argsEx1 = AspicBuilder.Build(tex1);
"Exercice 1 a completer : observer la propagation du conflit sur 'a' vers 'd'.".Display();
Exercice 1 a completer : observer la propagation du conflit sur 'a' vers 'd'.

Exercice 2 — Attaque undercut (extension du modèle)

L’ASPIC+ complet définit l’undercut : un argument peut attaquer directement une règle defaisable de l’autre (et non sa conclusion). On ajoute pour cela des formules !r_i signifiant « la règle r_i n’est pas fiable ».

  1. Ajoutez un axiome !r2 (l’adversaire conteste la règle b => d).
  2. Proposez une règle de generation d’attaque : un argument concluant !r_i undercut tout argument dont la règle sommitale est r_i.
  3. Recalculez l’extension grounded : d est-il encore attaqué ? Par qui ?

Indice : contrairement au rebut (symetrique), l’undercut est directif (A undercut B ne signifie pas que B undercut A). Il faut distinguer les deux types d’aretes dans le cadre de Dung.

// Exercice 2 : a completer
// Etape 1 : etendre la theorie avec un axiome !r2.
// Etape 2 : ecrire la regle d'undercut (un argument conclusant !r_i undercut l'argument de top-rule r_i).
// Indice : il faudra peut-etre suivre le Name de la regle sommitale de chaque argument.
"Exercice 2 a completer : modeliser l'undercut (attaque directive d'une regle).".Display();
Exercice 2 a completer : modeliser l'undercut (attaque directive d'une regle).

Exercice 3 — Comparaison ASPIC+ vs ABA sur un exemple medical

On veut modeliser un debat medical : « Le patient a la maladie M (symptome S => M). Mais le traitement T provoque aussi S (T => S). Le patient suit T. »

  1. Modelisez ce debat en ASPIC+ (axiomes + règles defaisables) et lancez le moteur.
  2. Decrivez (en prose) comment ABA representerait le même debat : quelles sont les assomptions ? quels sont leurs contraires ?
  3. Quel formalisme est le plus naturel pour ce cas, et pourquoi ?

Indice : ABA inverse des assumptions (assume(S)) dont le contraire (S -> conflict) peut etre attaqué. ASPIC+ raisonne en avant (règles), ABA en arriere (assumptions).

// Exercice 3 : a completer
// Etape 1 : modeliser en ASPIC+ (axiome 'T', regles 'S => M' et 'T => S', plus une regle 'S => ...').
// Etape 2 : decrire en commentaire la representation ABA equivalent (assumptions + contraires).
// Etape 3 : argumenter (en commentaire) le choix du formalisme.
"Exercice 3 a completer : comparer ASPIC+ (avant) et ABA (arriere, assumptions) sur un cas medical.".Display();
Exercice 3 a completer : comparer ASPIC+ (avant) et ABA (arriere, assumptions) sur un cas medical.

Conclusion

Ce twin C# a deroule le moteur ASPIC+ from-scratch (BCL .NET 9, 0 NuGet, 0 JVM) :

  1. Construction récursive des arguments par point fixe a partir des axiomes et règles (strictes/defaisables).
  2. Attaques par rebut symetrique entre arguments defaisables de conclusions contradictoires.
  3. Conversion vers Dung puis sémantique grounded reutilisant le moteur abstrait de Tweety-5-Csharp.
  4. Verification mathematique : sur la théorie r1,r2,r3 + axiomes b,c, le moteur isole {b, c, a} et rejette {d, !d} (cycle symetrique non resoluble) — conforme au verdict sceptique attendu.

Complementarite (#3801 Prong B) : le notebook Python execute TweetyProject (JVM, boite noire) ; ce twin rend explicite le moteur (construction d’arguments, classification des attaques, projection vers Dung). Les formalismes sans equivalent BCL (DeLP, ABA, ASP) sont documentes honnetement (verdict INTRINSIC) et renvoient au twin Python pour l’exécution.

See #4956, #3801.

Retour au sommet