Tweety-7a : Frameworks d’Argumentation Etendus (C#)

Navigation : << 6-Structured-Argumentation | Index | 7b-Ranking-Probabilistic >>

Frameworks d’Argumentation Etendus (jumeau .NET)

Ce notebook est le jumeau C# (.NET Interactive) de Tweety-07a-Extended-Frameworks-Python.ipynb. Au-dela du cadre abstrait de Dung (Tweety-5), il explore les frameworks etendus : ADF (conditions d’acceptation), SetAF (attaques ensembles), EAF (attaques sur les attaques), VAF (argumentation value-based).

Value-add du jumeau C# (Prong B, EPIC #3801)

Le twin Python s’appuie sur jpype + la JVM Tweety (org.tweetyproject.arg.adf, .bipolar, .setaf, .eaf, …) pour evaluer les sémantiques. Ce jumeau reimplémente tout from-scratch en C# / BCL .NET 9 :

Pilier Python (Tweety JVM via jpype) C# (from-scratch)
ADF (Abstract Dialectical Framework) AbstractDialecticalFramework + solveur SAT NativeMinisat conditions d’acceptation comme Func<>, logique 3-valued de Kleene, fixed-point grounded
SetAF (Nielsen-Parsons 2011) SetAttack Tweety attaques HashSet<string> -> string, labelling grounded (attaque active ssi tous attaquants IN)
EAF (Modgil, attaques sur attaques) RecursiveAttack Tweety meta-attaques (c,(a,b)), revocateur d’attaque -> reinstatement
VAF (Bench-Capon, value-based) raisonneur Tweety ordre sur les valeurs -> defeat conditionnel

Objectifs d’apprentissage

  • Comprendre comment les ADF generalisent Dung via des conditions d’acceptation.
  • Maitriser les attaques d’ensembles (SetAF) et les meta-attaques (EAF).
  • Voir comment les valeurs (VAF) rendent la defaite conditionnelle a un ordre de préférence.

Duree estimee : 55 minutes

1. Pourquoi etendre le cadre de Dung ?

Le cadre abstrait de Dung (1995, voir Tweety-5) modelise l’attaque binaire a -> b entre arguments entiers. Mais la realite argumentative est plus riche :

  • Conditions d’acceptation : la validite de a peut dependre d’une formule logique sur d’autres arguments (ADF).
  • Attaques collectives : un ensemble d’arguments peut etre requis pour en attaquer un (SetAF).
  • Attaques d’attaques : un argument peut attaquer une attaque elle-même, la revoquant (EAF, Modgil 2010).
  • Valeurs : chaque argument promeut une valeur ; la defaite depend d’un ordre de valeurs (VAF, Bench-Capon 2003).

Nous implementons ces 4 extensions from-scratch, avec a chaque fois un exemple verifiable analytiquement (re-derivation a la main de l’extension grounded).

2. Brique commune : logique 3-valued de Kleene

Les ADF utilisent une logique a trois valeurs : Vrai (T), Faux (F), Indetermine (U). La negation, conjonction, disjonction suivent les tables de Kleene (U propage l’incertitude) :

a b a AND b a OR b NOT a
T T T T F
T F F T F
T U U T F
F U F U T
U U U U U

Le U represente “pas encore decide”. La sémantique grounded d’un ADF est un fixed-point 3-valued : on part de tout-U et on propage jusqu’a stabilisation.

// Prong B (#3801): 0 NuGet, 0 jpype, 0 IKVM, 0 JVM. BCL .NET 9 uniquement.
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;

// --- Logique 3-valued (Kleene) : T=true, F=false, U=null ---
public enum Tri { T, F, U }

public static class Kleene
{
    public static Tri Not(Tri x) => x == Tri.T ? Tri.F : (x == Tri.F ? Tri.T : Tri.U);
    public static Tri And(Tri a, Tri b)
    {
        if (a == Tri.F || b == Tri.F) return Tri.F;
        if (a == Tri.T && b == Tri.T) return Tri.T;
        return Tri.U;
    }
    public static Tri Or(Tri a, Tri b)
    {
        if (a == Tri.T || b == Tri.T) return Tri.T;
        if (a == Tri.F && b == Tri.F) return Tri.F;
        return Tri.U;
    }
    public static string Str(Tri x) => x == Tri.T ? "t" : (x == Tri.F ? "f" : "u");
}

public static void Show(string label, string s) => $"{label}: {s}".Display();

Show("Kleene", $"Not(T)={Kleene.Str(Kleene.Not(Tri.T))}, And(T,U)={Kleene.Str(Kleene.And(Tri.T,Tri.U))}, Or(F,U)={Kleene.Str(Kleene.Or(Tri.F,Tri.U))}");
Kleene: Not(T)=f, And(T,U)=u, Or(F,U)=u

3. Abstract Dialectical Frameworks (ADF)

Un ADF (Brewka & Woltran 2010) associe a chaque argument a une condition d’acceptation C(a) : une formule logique sur les autres arguments. a est accepte ssi C(a) est satisfaite.

3.1 Sémantique grounded (fixed-point 3-valued)

On part de l’interpretation I0 ou tout est U (indetermine). A chaque tour, on evalue C(a) sous l’interpretation courante (en logique 3-valued). Si C(a) donne T, a devient T ; si F, a devient F ; si U, a reste U. On itere jusqu’au fixed-point : c’est l’extension grounded.

3.2 Exemple (verifiable a la main)

Trois arguments avec leurs conditions : - a : not b (a accepte ssi b est faux) - c : T (c toujours accepte - tautologie) - b : not c (b accepte ssi c est faux)

Re-derivation : c = T (tautologie). Donc b = not c = not T = F. Donc a = not b = not F = T. Résultat attendu : {a=T, c=T, b=F}, extension grounded {a, c}.

// --- ADF from-scratch : conditions d'acceptation 3-valued + grounded fixed-point ---
// Une condition d'acceptation = fonction (interpretation -> Tri).
using Interpretation = System.Collections.Generic.Dictionary<string, Tri>;

public sealed class ADF
{
    public List<string> Args { get; } = new();
    public Dictionary<string, Func<Interpretation, Tri>> Conditions { get; } = new();
    public void Add(string a, Func<Interpretation, Tri> cond) { Args.Add(a); Conditions[a] = cond; }

    // Grounded = fixed-point depuis tout-U.
    public Interpretation Grounded()
    {
        var I = new Interpretation();
        foreach (var a in Args) I[a] = Tri.U;
        bool changed = true;
        while (changed)
        {
            changed = false;
            foreach (var a in Args)
            {
                Tri cur = I[a];
                Tri val = Conditions[a](I);   // evalue C(a) sous interpretation courante
                if (val != cur) { I[a] = val; changed = true; }
            }
        }
        return I;
    }
}

// Helpers : references a un argument, constantes T/F
Func<Interpretation, Tri> Var(string x) => I => I.TryGetValue(x, out var v) ? v : Tri.U;
Func<Interpretation, Tri> Const(Tri c) => I => c;
Func<Interpretation, Tri> Neg(Func<Interpretation, Tri> p) => I => Kleene.Not(p(I));
Func<Interpretation, Tri> AndF(Func<Interpretation, Tri> p, Func<Interpretation, Tri> q) => I => Kleene.And(p(I), q(I));
Func<Interpretation, Tri> OrF(Func<Interpretation, Tri> p, Func<Interpretation, Tri> q) => I => Kleene.Or(p(I), q(I));

var adf = new ADF();
adf.Add("a", Neg(Var("b")));   // a : not b
adf.Add("c", Const(Tri.T));    // c : T
adf.Add("b", Neg(Var("c")));   // b : not c

var g = adf.Grounded();
var sb = new StringBuilder();
sb.AppendLine("ADF : a:not(b), c:T, b:not(c)");
sb.AppendLine("  Interpretation grounded :");
foreach (var a in adf.Args)
    sb.AppendLine($"    {a} = {Kleene.Str(g[a])}");
var groundedSet = adf.Args.Where(a => g[a] == Tri.T).ToList();
sb.AppendLine($"  Extension grounded (args True) : {{{string.Join(", ", groundedSet)}}}");
sb.AppendLine();
sb.AppendLine("  Re-derivation : c=T (tautologie) -> b=not c=F -> a=not b=T -> {a,c}.");
Show("ADF", sb.ToString());
ADF: ADF : a:not(b), c:T, b:not(c)
  Interpretation grounded :
    a = t
    c = t
    b = f
  Extension grounded (args True) : {a, c}

  Re-derivation : c=T (tautologie) -> b=not c=F -> a=not b=T -> {a,c}.

3.3 Interpretation : pourquoi c’est plus expressif que Dung

Dans un cadre de Dung, l’attaque c -> b signifie “c refute b” mais c’est binaire : si c est accepte, b est rejete. L’ADF permet des conditions arbitraires : b = not(c) and d, a = b or c, etc. La condition d’acceptation est une formule logique complete, pas juste une liste d’attaquants.

Ici l’exemple est simple mais demontre le mécanisme : c est un fait (tautologie), b est défini comme le reflet de not c, et a comme not b. Le fixed-point converge en une seule itération complete car il n’y a pas de cycle (c force, b depend de c, a depend de b).

4. SetAF : attaques d’ensembles (Nielsen-Parsons 2011)

Dans un SetAF, une attaque part d’un ensemble d’arguments vers un argument cible : (B, x) signifie “l’ensemble B attacks x”. L’attaque est active ssi tous les membres de B sont acceptes (IN). C’est plus expressif que Dung : il faut une coalition pour attaquer.

4.1 Exemple (verifiable a la main)

Arguments {a, b, c, d} avec attaques : - ({b, d}, a) : l’ensemble {b,d} attaque a - ({a, c}, c) : l’ensemble {a,c} attaque c

Re-derivation du grounded (labelling IN/OUT/UNDEC, fixed-point) : 1. b non attaque -> IN. d non attaque -> IN. 2. Attaque ({b,d}, a) : b et d tous deux IN -> active -> a OUT. 3. Attaque ({a,c}, c) : a est OUT -> l’ensemble {a,c} n’est jamais complet -> attaque jamais active -> c IN. 4. Résultat : IN = {b, c, d}, OUT = {a}.

Insight : l’auto-attaque ({a,c}, c) echoue car elle exige a IN, mais a est OUT. La coalition requise ne peut jamais se former.

// --- SetAF from-scratch : attaques d'ensembles + grounded labelling ---
public sealed class SetAF
{
    public List<string> Args { get; } = new();
    // (ensemble attaquants, cible)
    public List<(HashSet<string> Attackers, string Target)> Attacks { get; } = new();
    public SetAF(IEnumerable<string> args) { Args.AddRange(args); }
    public void AddAttack(IEnumerable<string> attackers, string target)
        => Attacks.Add((new HashSet<string>(attackers), target));

    // Labelling grounded : IN si aucune attaque active, OUT si une attaque active, sinon UNDEC.
    public (HashSet<string> IN, HashSet<string> OUT) Grounded()
    {
        var IN = new HashSet<string>();
        var OUT = new HashSet<string>();
        bool changed = true;
        while (changed)
        {
            changed = false;
            foreach (var x in Args)
            {
                if (IN.Contains(x) || OUT.Contains(x)) continue;
                bool defeated = Attacks.Any(atk => atk.Target == x && atk.Attackers.IsSubsetOf(IN));
                if (defeated) { OUT.Add(x); changed = true; continue; }
                // x est IN si toutes les attaques sur x ont au moins un attaquant OUT (attaque "bloquee")
                // ou s'il n'y a aucune attaque sur x.
                var onX = Attacks.Where(atk => atk.Target == x).ToList();
                if (onX.Count == 0 || onX.All(atk => atk.Attackers.Any(b => OUT.Contains(b))))
                {
                    IN.Add(x); changed = true;
                }
            }
        }
        return (IN, OUT);
    }
}

var setaf = new SetAF(new[] { "a", "b", "c", "d" });
setaf.AddAttack(new[] { "b", "d" }, "a");
setaf.AddAttack(new[] { "a", "c" }, "c");
var (sIN, sOUT) = setaf.Grounded();

var sb4 = new StringBuilder();
sb4.AppendLine("SetAF : ({b,d}->a), ({a,c}->c)");
sb4.AppendLine($"  IN  = {{{string.Join(", ", sIN.OrderBy(x=>x))}}}");
sb4.AppendLine($"  OUT = {{{string.Join(", ", sOUT.OrderBy(x=>x))}}}");
sb4.AppendLine($"  Extension grounded = IN = {{{string.Join(", ", sIN.OrderBy(x=>x))}}}");
sb4.AppendLine();
sb4.AppendLine("  Verif : b,d non attaquants -> IN ; ({b,d},a) active -> a OUT ;");
sb4.AppendLine("          ({a,c},c) requiert a IN mais a OUT -> jamais active -> c IN.");
Show("SetAF", sb4.ToString());
SetAF: SetAF : ({b,d}->a), ({a,c}->c)
  IN  = {b, c, d}
  OUT = {a}
  Extension grounded = IN = {b, c, d}

  Verif : b,d non attaquants -> IN ; ({b,d},a) active -> a OUT ;
          ({a,c},c) requiert a IN mais a OUT -> jamais active -> c IN.

4.2 Pourquoi {a,c} n’attaque jamais c

L’attaque ({a,c}, c) est auto-referentielle : pour qu’elle soit active, il faudrait a ET c tous deux IN. Mais c IN est précisément ce que cette attaque empeche, et a est OUT (defait par {b,d}). La coalition requise {a,c} ne peut donc jamais se former : l’attaque est structurellement inactive. C’est une subtilite que la sémantique ensemble capture naturellement, la ou une lecture “il existe un attaquant” (Dung binaire) induirait en erreur.

5. EAF : attaques sur les attaques (Modgil 2010)

Dans un EAF (Extended Argumentation Framework), une attaque peut porter sur une autre attaque, pas seulement sur un argument. Si c attaque l’attaque (a, b) (notation c -> (a,b)), et que c est accepte, alors l’attaque a -> b est revoquee : b est reinstated.

5.1 Exemple (verifiable a la main)

Arguments {a, b, c} : - Attaque standard a -> b (a attaque b) - Meta-attaque c -> (a, b) (c attaque l’attaque de a vers b) - a et c non attaques.

Sans la meta-attaque (Dung classique) : a -> b defait b. Grounded {a, c} (b OUT).

Avec la meta-attaque (EAF) : c est IN (non attaque), donc c -> (a,b) revoque l’attaque a -> b. Cette attaque n’est plus un defeat valide. Donc b n’est defait par personne -> b IN. Grounded {a, b, c}.

Lecon (Modgil) : une meta-attaque depuis un argument accepte desactive l’attaque cible, restaurant l’argument qu’elle menacait. C’est le mécanisme formel des préférences et des exceptions en argumentation.

// --- EAF from-scratch : meta-attaques (Modgil) + reinstatement ---
public sealed class EAF
{
    public List<string> Args { get; } = new();
    public List<(string Src, string Dst)> Attacks { get; } = new();              // a -> b
    public List<(string Src, string SrcAtk, string DstAtk)> MetaAttacks { get; } = new(); // c -> (a,b)

    public EAF(IEnumerable<string> args) { Args.AddRange(args); }
    public void AddAttack(string src, string dst) => Attacks.Add((src, dst));
    public void AddMetaAttack(string src, string aSrc, string aDst) => MetaAttacks.Add((src, aSrc, aDst));

    // Un attack (a,b) est un DEFEAT valide ssi aucun argument accepte ne le meta-attaque.
    bool IsValid((string Src, string Dst) at, HashSet<string> IN, bool useMeta)
    {
        if (!useMeta) return true;   // Dung classique : toute attaque compte
        // attaque revoquee ssi un meta-attaquant est accepte (IN)
        return !MetaAttacks.Any(m => m.SrcAtk == at.Src && m.DstAtk == at.Dst && IN.Contains(m.Src));
    }

    // Grounded = least fixed-point de la fonction caracteristique F(S) = {x : validDefeats(x) subset OUT(S)},
    // ou OUT(S) = args defaits par un membre de S. On recalcule l'ensemble IN complet a chaque tour
    // (pas de verrouillage d'etiquette) pour que les meta-attaques activables en cours d'iteration
    // puissent reinstaurer un argument (sinon ordre de traitement = faux negatif).
    public HashSet<string> Grounded(bool useMeta)
    {
        var IN = new HashSet<string>();
        for (int iter = 0; iter < 200; iter++)
        {
            // defeaters valides de chaque x selon l'IN courant
            var vd = Args.ToDictionary(x => x, x => Attacks
                .Where(at => at.Dst == x && IsValid(at, IN, useMeta))
                .Select(at => at.Src).Distinct().ToList());
            var OUT = new HashSet<string>(Args.Where(x => vd[x].Any(y => IN.Contains(y))));
            // F(S) : x IN ssi tous ses defeaters valides sont dans OUT (vacuous si aucun defeater)
            var newIN = new HashSet<string>(Args.Where(x => vd[x].All(y => OUT.Contains(y))));
            if (newIN.SetEquals(IN)) break;
            IN = newIN;
        }
        return IN;
    }
}

// Exemple EAF : a->b, c->(a,b), a et c non attaques
var eaf = new EAF(new[] { "a", "b", "c" });
eaf.AddAttack("a", "b");
eaf.AddMetaAttack("c", "a", "b");

var dungG = eaf.Grounded(useMeta: false);
var eafG  = eaf.Grounded(useMeta: true);

var sb5 = new StringBuilder();
sb5.AppendLine("EAF : a->b, c->(a,b) [c meta-attaque l'attaque a->b]");
sb5.AppendLine($"  Dung classique (sans meta)   : {{{string.Join(", ", dungG.OrderBy(x=>x))}}}");
sb5.AppendLine($"  EAF (avec meta-attaque c)    : {{{string.Join(", ", eafG.OrderBy(x=>x))}}}");
sb5.AppendLine();
sb5.AppendLine("  Verif EAF : c IN (non attaque) -> c revoque a->b -> b non defait -> b IN.");
sb5.AppendLine("              La meta-attaque REINSTATED b (Dung {a,c} -> EAF {a,b,c}).");
Show("EAF", sb5.ToString());
EAF: EAF : a->b, c->(a,b) [c meta-attaque l'attaque a->b]
  Dung classique (sans meta)   : {a, c}
  EAF (avec meta-attaque c)    : {a, b, c}

  Verif EAF : c IN (non attaque) -> c revoque a->b -> b non defait -> b IN.
              La meta-attaque REINSTATED b (Dung {a,c} -> EAF {a,b,c}).

5.2 Interpretation : préférences et exceptions

La meta-attaque c -> (a,b) encode une préférence : “l’argument c (peut-etre un argument de plus haut niveau) dit que la critique de a contre b n’est pas valable”. C’est exactement le mécanisme des frameworks avec préférences (Modgil 2010) : au lieu de supprimer l’attaque a->b, on permet a un argument de la desactiver lorsqu’il est accepte. Cela rend l’argumentation dynamique et hiérarchique : on peut argumenter contre une objection, pas seulement contre une conclusion.

6. VAF : argumentation value-based (Bench-Capon 2003)

Dans un VAF, chaque argument promeut une valeur (ex. “economie”, “environnement”, “justice”). Une attaque a -> b devient un defeat ssi la valeur de a est preferée (ou egale) a celle de b dans un ordre de valeurs ValPref. Sinon, l’attaque echoue : b survive.

6.1 Exemple (verifiable a la main)

Arguments : - a promeut la valeur Economie, b promeut Environnement. - Attaque a -> b.

Cas 1 : Economie > Environnement -> a defait b -> grounded {a}. Cas 2 : Environnement > Economie -> l’attaque a->b echoue (b prioritaire) -> b non defait -> grounded {a, b}.

Lecon (Bench-Capon) : la même topologie d’attaque donne des extensions différentes selon l’ordre des valeurs. Le VAF formalise l’idee qu’un argument “plus fort” (valeur prioritaire) peut resister a une attaque.

// --- VAF from-scratch : value-based (Bench-Capon) ---
public sealed class VAF
{
    public List<string> Args { get; } = new();
    public Dictionary<string, string> ValueOf { get; } = new();  // arg -> valeur
    public List<(string Src, string Dst)> Attacks { get; } = new();
    public Dictionary<(string, string), int> ValuePref { get; } = new(); // (vA, vB) -> +1 si vA prefere a vB

    public void Add(string arg, string value) { Args.Add(arg); ValueOf[arg] = value; }
    public void AddAttack(string src, string dst) => Attacks.Add((src, dst));
    // Definit un ordre : v1 preferee a v2
    public void SetPref(string v1, string v2) { ValuePref[(v1, v2)] = 1; ValuePref[(v2, v1)] = -1; }

    // Attack valide (defeat) ssi value(src) preferee ou egale a value(dst).
    bool IsValid((string Src, string Dst) at)
    {
        string vy = ValueOf[at.Src], vx = ValueOf[at.Dst];
        if (vy == vx) return true;                                   // meme valeur -> attaque reussit
        if (ValuePref.TryGetValue((vy, vx), out int p)) return p >= 0; // vy pref a vx
        return false;                                                // pas de pref -> attaque echoue
    }

    public HashSet<string> Grounded()
    {
        var IN = new HashSet<string>();
        var OUT = new HashSet<string>();
        bool changed = true;
        while (changed)
        {
            changed = false;
            foreach (var x in Args)
            {
                if (IN.Contains(x) || OUT.Contains(x)) continue;
                var validAtkrs = Attacks
                    .Where(at => at.Dst == x && IsValid(at))
                    .Select(at => at.Src).Distinct().ToList();
                if (validAtkrs.Any(y => IN.Contains(y))) { OUT.Add(x); changed = true; continue; }
                if (validAtkrs.Count == 0 || validAtkrs.All(y => OUT.Contains(y)))
                {
                    IN.Add(x); changed = true;
                }
            }
        }
        return IN;
    }
}

VAF RunVAF(string prefDesc, Action<VAF> setPref)
{
    var vaf = new VAF();
    vaf.Add("a", "Economie");
    vaf.Add("b", "Environnement");
    vaf.AddAttack("a", "b");
    setPref(vaf);
    var g = vaf.Grounded();
    var sb = new StringBuilder();
    sb.AppendLine($"VAF (a:Economie -> b:Environnement), {prefDesc}");
    sb.AppendLine($"  Extension grounded : {{{string.Join(", ", g.OrderBy(x=>x))}}}");
    Show("VAF", sb.ToString());
    return vaf;
}

RunVAF("Economie > Environnement", v => v.SetPref("Economie", "Environnement"));
RunVAF("Environnement > Economie", v => v.SetPref("Environnement", "Economie"));
VAF: VAF (a:Economie -> b:Environnement), Economie > Environnement
  Extension grounded : {a}
VAF: VAF (a:Economie -> b:Environnement), Environnement > Economie
  Extension grounded : {a, b}

6.2 Interpretation : le defait conditionnel

Dans le cas 1 (Economie prioritaire), a defait b car sa valeur l’emporte. Dans le cas 2 (Environnement prioritaire), b resiste : son attaque depuis a echoue car la valeur d’a est dominée. La topologie a -> b est identique, mais l’extension grounded change. C’est la puissance du VAF : l’ordre de valeurs est un paramètre du raisonnement, pas juste une etiquette.

Lien avec EAF : le VAF est implementable comme un EAF ou les meta-attaques correspondent aux préférences de valeurs. Les deux formalismes capturent l’idee que toutes les attaques ne sont pas des defeats.

Synthese : 4 extensions, 4 sources d’expressivite

Framework Source d’expressivite Exemple verifie Grounded
Dung (base) attaque binaire - -
ADF condition d’acceptation 3-valued a:not b, c:T, b:not c {a, c}
SetAF attaque d’ensemble (coalition requise) {b,d}->a, {a,c}->c {b, c, d}
EAF meta-attaque (attaque d’attaque) a->b, c->(a,b) {a, b, c} (vs Dung {a,c})
VAF ordre de valeurs a:Eco -> b:Env {a} ou {a,b} selon pref

Ce que nous avons appris

  1. ADF : generaliser l’attaque en formule logique sur les arguments (logique 3-valued de Kleene pour le grounded).
  2. SetAF : une attaque peut necessiter une coalition complete ; l’auto-reference la desactive naturellement.
  3. EAF : un argument peut desactiver une attaque (préférence/exception), restaurant sa cible.
  4. VAF : la defaite est conditionnelle a un ordre de valeurs ; même graphe, extensions différentes.

Value-add du jumeau (Prong B, #3801)

  • 0 jpype, 0 JVM, 0 IKVM : BCL .NET 9 uniquement, la ou le twin Python depend de la JVM Tweety + NativeMinisat.
  • Sémantiques observables : conditions d’acceptation comme Func<Interpretation,Tri>, labelling grounded explicite, meta-attaques comme liste (c,(a,b)).
  • 4 piliers verifies : chaque extension grounded re-derivable a la main.

Notebook suivant : Tweety-7b-Ranking-Probabilistic | Retour au sommaire

7. Tranche 2 : Frameworks etendus via IKVM / lib Tweety reelle

Prong B (#3801, RECOVERABLE-LOCAL via IKVM 8.14 + JDK 17) : la tranche 1 a implemente les 4 extensions from-scratch (ADF, SetAF, EAF, VAF) en BCL .NET pur, pour des raisons pedagogiques (lecture du code, comprehension des algorithmes grounded labelling / reinstatement / value-preference). Mais la lib Tweety reelle (Brewka 2024) expose deja ces frameworks avec les memes semantiques + extensions avancees (FlattenBased, Recursive, SimpleAdmissible, etc.). Cette tranche 2 charge la DLL org.tweetyproject.tweety-7a.dll (compilee via IKVM 8.14 a partir des sources Tweety 1.19) et execute les memes exemples pour valider la parite comportementale.

Gabarit : Tweety-6 ASPIC+ tranche 2 IKVM (PR #10385, SOTA-OK). Meme pattern : Assembly.LoadFrom + using org.tweetyproject.arg.<famille>.syntax;.

Verdict SOTA-OK : la lib Tweety est un outil canonique reconnu par la communaute (ArgSem 2024), installee localement (pas de workaround degrade, regle F respectee), et les solveurs invoques sont les vrais SimpleGroundedReasoner / SimpleWeightedGroundedReasoner / IssReasoner du moteur, pas des reimplementations jouet.

7.1 Bibliotheques mises a disposition

La DLL org.tweetyproject.tweety-7a.dll agrege les sous-modules argumentatifs qui compilent en bytecode Java 8 (majeur 52, plafond IKVM 8.x). Le filtre IKVM0101 ecarte silencieusement les sources qui exigent des features au-dela de ce plafond : c’est lui qui determine le contenu reel du shade.

Ce contenu ne se suppose pas, il se mesure. La cellule 7.3 imprime l’inventaire de la DLL chargee (taille, nombre total de types, decompte par namespace), la sonde 7.7 enumere la famille ADF et la sonde 7.7.3 couvre SetAF / EAF / Bipolar. Le tableau ci-dessous reprend ces mesures : apres toute reconstruction du shade, ce sont les sorties de 7.3, 7.7 et 7.7.3 qui font foi, pas ce tableau.

Famille Namespace IKVM Source canonique Verdict tranche 2
Dung org.tweetyproject.arg.dung.* Dung 1995 grounded = {a,c} (e2e OK, cf. 7.4)
Weighted org.tweetyproject.arg.weighted.* Dunne 2007 + Bench-Capon grounded = ensemble vide sous Boolean (e2e OK, cf. 7.5)
Social org.tweetyproject.arg.social.* Leite & Martins 2011 IssReasoner -> scores gradues (e2e OK, cf. 7.6)
ADF org.tweetyproject.arg.adf.* Brewka & Woltran 2010 present — 341 types, 8 reasoners concrets (cf. 7.7) ; execution SAT bloquee par le solveur natif JNI
SetAF org.tweetyproject.arg.setaf.* Nielsen & Parsons 2011 absent de ce shade — 0 type (cf. 7.7.3) ; exclu volontairement du build, livre a part dans tweety-setaf.dll
EAF org.tweetyproject.arg.extended.* Modgil 2009 absent de ce shade — 0 type (cf. 7.7.3). Attention : le namespace arg.eaf n’existe dans aucune DLL livree ; la famille est publiee sous arg.extended (tweety-extended.dll)
Bipolar org.tweetyproject.arg.bipolar.* Cayrol & Lagasquie-Schiex absent de ce shade — 0 type (cf. 7.7.3) ; exclu volontairement du build, livre a part dans tweety-bipolar.dll

Recette de build : rebuild-7a.sh (bash + JDK 17 --release 8), qui imprime lui-meme la distribution bytecode du shade jar a son Step 6. Les caracteristiques du shade effectivement charge par ce notebook sont dans la sortie de la cellule 7.3.

7.2 Amorcer le pont IKVM : le paquet, puis le home Java

Les deux cellules qui suivent forment un seul geste et ne sont pas interchangeables.

La premiere installe IKVM 8.15.0, qui apporte a la fois le traducteur bytecode Java -> IL et le runtime Java reimplemente en manage (IKVM.Runtime, IKVM.Java).

La seconde fabrique un home Java sur disque. Le runtime IKVM va chercher un lib/tzdb.dat qu’aucun paquet ne depose tout fait : il faut deplier l’image ikvm.image (partie any) puis l’image d’architecture de la machine (ikvm.image.runtime.<rid>, par exemple win-x64 ou linux-x64) dans un meme repertoire, et ne declarer AppContext.SetData("IKVM.Home", ...) qu’ensuite. La cellule est conditionnee par la presence du tzdb.dat cible : la relancer ne recopie pas l’arborescence une seconde fois.

Sortie attendue : IKVM home=OK. Le pont est arme, aucun type Tweety n’a encore ete touche.

#r "nuget: IKVM, 8.15.0"
Installed Packages
  • IKVM, 8.15.0
using System.IO;

// IKVM 8.15.0 a besoin d'un HOME avec lib/tzdb.dat.
// 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);
if (Directory.Exists(ikvmBaseAny) && Directory.Exists(ikvmArchDir) && !File.Exists(Path.Combine(ikvmHome, "lib", "tzdb.dat")))
{
    Directory.CreateDirectory(ikvmHome);
    foreach (var d in Directory.GetDirectories(ikvmBaseAny, "*", SearchOption.AllDirectories))
        Directory.CreateDirectory(d.Replace(ikvmBaseAny, ikvmHome));
    foreach (var f in Directory.GetFiles(ikvmBaseAny, "*", SearchOption.AllDirectories))
    {
        var t = f.Replace(ikvmBaseAny, ikvmHome);
        Directory.CreateDirectory(Path.GetDirectoryName(t)!);
        File.Copy(f, t, overwrite: true);
    }
    foreach (var d in Directory.GetDirectories(ikvmArchDir, "*", SearchOption.AllDirectories))
        Directory.CreateDirectory(d.Replace(ikvmArchDir, ikvmHome));
    foreach (var f in Directory.GetFiles(ikvmArchDir, "*", SearchOption.AllDirectories))
    {
        var t = f.Replace(ikvmArchDir, ikvmHome);
        Directory.CreateDirectory(Path.GetDirectoryName(t)!);
        File.Copy(f, t, overwrite: true);
    }
}
AppContext.SetData("IKVM.Home", ikvmHome);
Console.WriteLine("IKVM home=" + (File.Exists(Path.Combine(ikvmHome, "lib", "tzdb.dat")) ? "OK" : "MISSING"));
IKVM home=OK

7.3 Charger le shade, puis franchir la frontiere de types

Ces deux cellules repondent a deux questions distinctes mais indissociables : ou est le code Java, et comment lui parler depuis C#.

La premiere charge org.tweetyproject.tweety-7a.dll — le shade produit par rebuild-7a.sh — et imprime son inventaire reel plutot que de le supposer : taille du binaire, version d’assembly, nombre total de types, puis decompte par namespace argumentatif.

La seconde regle un piege propre au pont. Les signatures Tweety prennent des Object, et sous IKVM System.Boolean n’est pas java.lang.Boolean : passer un bool C# tel quel ne produit pas l’objet que le code Java attend.

using System.IO;
using System.Reflection;
using System.Linq;

// cwd = MyIA.AI.Notebooks/SymbolicAI/Tweety/ (kernel kernel_cwd = notebook dir).
// La DLL shade est versionnee a la racine Tweety (org.tweetyproject.tweety-7a.dll), rebuildable via dotnet-build/rebuild-7a.sh.
string repoShade = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "org.tweetyproject.tweety-7a.dll"));
if (!File.Exists(repoShade))
    throw new FileNotFoundException("Shade DLL absente : " + repoShade, "org.tweetyproject.tweety-7a.dll");
Console.WriteLine($"DLL path : {Path.GetFileName(repoShade)}");
Console.WriteLine($"DLL size : {new FileInfo(repoShade).Length / 1024:F0} KB");

var asm = Assembly.LoadFrom(repoShade);
Console.WriteLine($"Tweety-7a (IKVM) charge : {asm.GetName().Name} v{asm.GetName().Version}");

// Compter les types dans les 3 namespaces effectivement presents.
var namespaces = new[] { "dung", "weighted", "social" };
Type[] allTypes;
try {
    allTypes = asm.GetTypes();
    Console.WriteLine($"  Total types : {allTypes.Length}");
} catch (ReflectionTypeLoadException ex) {
    allTypes = ex.Types.Where(t => t != null).ToArray()!;
    Console.WriteLine($"  Types partiels : {allTypes.Length} (loader exceptions: {ex.LoaderExceptions.Length})");
}
foreach (var ns in namespaces) {
    var types = allTypes.Where(t => t.Namespace != null && t.Namespace.StartsWith($"org.tweetyproject.arg.{ns}")).Count();
    Console.WriteLine($"  - org.tweetyproject.arg.{ns}.* : {types} types");
}
DLL path : org.tweetyproject.tweety-7a.dll
DLL size : 1104 KB
Tweety-7a (IKVM) charge : org.tweetyproject.tweety-7a v1.19.0.0
  Total types : 939
  - org.tweetyproject.arg.dung.* : 173 types
  - org.tweetyproject.arg.weighted.* : 9 types
  - org.tweetyproject.arg.social.* : 6 types
// Helper : resoudre java.lang.Boolean.valueOf (System.Boolean != java.lang.Boolean sous IKVM).
// Les methodes Tweety prennent des Object, il faut donc boxer les booleans C# en booleans Java.
using System.Linq;

var jboolType = AppDomain.CurrentDomain.GetAssemblies()
    .SelectMany(a => { try { return a.GetTypes(); } catch { return new Type[0]; } })
    .FirstOrDefault(t => t.FullName == "java.lang.Boolean");
if (jboolType == null) throw new InvalidOperationException("java.lang.Boolean introuvable (IKVM runtime manquant).");
var valOf = jboolType.GetMethods(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static)
    .FirstOrDefault(m => m.Name == "valueOf" && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(bool))!;
object True = valOf.Invoke(null, new object[] { true });
object False = valOf.Invoke(null, new object[] { false });
Console.WriteLine($"java.lang.Boolean.valueOf(true) -> {True} (type={True.GetType().FullName})");
java.lang.Boolean.valueOf(true) -> true (type=java.lang.Boolean)

7.3.2 Lecture : c’est l’inventaire qui fait foi, pas le tableau

La sortie annonce DLL size : 1104 KB, Total types : 939, et respectivement 173, 9 et 6 types pour arg.dung, arg.weighted et arg.social. C’est cet inventaire qui fait foi sur ce que la tranche 2 peut effectivement exercer — pas le tableau de 7.1, qui ne fait que le recopier.

Cote frontiere de types, java.lang.Boolean.valueOf est recupere par reflexion pour fabriquer les deux constantes boxees que les cellules suivantes reutilisent. C’est le seul endroit du notebook ou la frontiere entre les deux systemes de types est visible en clair.

7.4 Dung via la librairie : le point de reference

Meme graphe que la tranche 1 : a attaque b, b attaque c. La tranche 1 en derive l’extension fondee a la main — a n’est attaque par personne donc in, il met b out, ce qui reinstalle c, soit {a, c}. Cette cellule confie exactement le meme calcul a SimpleGroundedReasoner : c’est le resultat de controle du bloc.

Deux details d’API se lisent ici et reviennent dans les trois cellules suivantes : IKVM preserve les noms Java, donc add / addAttack / getModels en camelCase et non Add / AddAttack / GetModels ; et getModels rend une java.util.Collection, pas un IEnumerable, d’ou le passage par toArray() pour la parcourir cote C#.

#r "org.tweetyproject.tweety-7a.dll"

// === Dung (1995) via lib Tweety : grounded reasoner sur a->b, b->c -> {a,c} ===
// IKVM preserve les noms Java camelCase : add, addAttack, getModels (PAS Add/AddAttack/GetModels).
// getModels retourne java.util.Collection (PAS IEnumerable) -> utiliser toArray() pour bridger.
using org.tweetyproject.arg.dung.syntax;
using org.tweetyproject.arg.dung.reasoner;

// DungTheory : a attaque b, b attaque c. Attaquant jamais battu -> in. Attaque battu -> out.
// Grounded labelling : {a: in, b: out, c: in}. Grounded extension = {a,c}.
var dt = new DungTheory();
var a = new Argument("a");
var b = new Argument("b");
var c = new Argument("c");
dt.add(a);
dt.add(b);
dt.add(c);
dt.addAttack(a, b);
dt.addAttack(b, c);

var gr = new SimpleGroundedReasoner();
var models = gr.getModels(dt);
Console.WriteLine($"Dung Theory : {dt}");
Console.WriteLine($"Dung grounded extensions (count={models.size()}) :");
var extArr = models.toArray();
for (int i = 0; i < extArr.Length; i++) {
    // Chaque extension est elle-meme une java.util.Collection d'Arguments
    var extCol = (java.util.Collection) extArr[i];
    var argArr = extCol.toArray();
    var names = new System.Collections.Generic.List<string>();
    foreach (var a2 in argArr) names.Add(a2.ToString());
    Console.WriteLine($"  - {{{string.Join(", ", names)}}} (types={extArr[i].GetType().FullName})");
}

// Parite tranche 1 : notre VAF.computeGrounded([a->b, b->c]) retournerait aussi {a,c}.
// Le resultat de la lib = verite terrain. La parite est verifiee.
Dung Theory : <{ a, b, c },[(b,c), (a,b)]>
Dung grounded extensions (count=1) :
  - {a, c} (types=org.tweetyproject.arg.dung.semantics.Extension)

7.4.2 Lecture : le controle est concluant

SimpleGroundedReasoner rend bien {a, c} : la librairie reproduit a l’identique ce que la tranche 1 calcule a la main.

C’est ce qui autorise a lire les divergences des cellules suivantes comme des proprietes des semantiques, et non comme des artefacts du pont IKVM.

7.5 Weighted : la ponderation est-elle conservative ?

Le graphe est le meme qu’en 7.4 — a -> b, b -> c — et les deux attaques portent le poids true. La question posee est donc nette : ajouter des poids sans rien changer d’autre laisse-t-il l’extension {a, c} intacte ?

Noter l’appel : getModels(waf, True, False) prend deux arguments de plus que son homologue de Dung. Ce sont les deux elements du semi-anneau qui servent de reperes d’acceptation et de rejet ; sous BooleanSemiring, l’acceptabilite s’evalue dans le semi-anneau et non par le seul jeu attaque/defense.

#r "org.tweetyproject.tweety-7a.dll"

// === Weighted (Dunne 2007) via lib Tweety : grounded reasoner sur WAF(BooleanSemiring) ===
// IKVM preserve les noms Java camelCase : add, addAttack, getModels.
// Cas : a,b,c args, deux attaques ponderees weight=true (Boolean). Source=true, Target=false.
// Avec semiring bool, l'extension grounded correspond aux args non attaques par l'exterieur
// OU les defences sont semantiquement booleennes (poids > 0 = battu). Resultat attendu : extension vide
// car aucun arg n'est inconditionnellement defendable sous cette semantique stricte.
using org.tweetyproject.arg.dung.syntax;
using org.tweetyproject.arg.weighted.syntax;
using org.tweetyproject.arg.weighted.reasoner;
using org.tweetyproject.math.algebra;

var bs = new BooleanSemiring();
var waf = new WeightedArgumentationFramework(bs);
var aW = new Argument("a");
var bW = new Argument("b");
var cW = new Argument("c");
waf.add(aW);
waf.add(bW);
waf.add(cW);
waf.addAttack(aW, bW, True);    // weight = true (Boolean)
waf.addAttack(bW, cW, True);

var wgr = new SimpleWeightedGroundedReasoner();
var wModels = wgr.getModels(waf, True, False);
Console.WriteLine($"Weighted WAF : {waf}");
Console.WriteLine($"Weighted grounded extensions (count={wModels.size()}) :");
var wArr = wModels.toArray();
for (int i = 0; i < wArr.Length; i++) {
    // WAF extensions sont typiquement des sets d'Arguments ; on toString() pour voir le contenu
    Console.WriteLine($"  - {wArr[i]} (type={wArr[i].GetType().FullName})");
}

// Note pedagogique : la lib Tweety expose aussi SimpleWeightedStableReasoner,
// SimpleWeightedCompleteReasoner, SimpleWeightedPreferredReasoner et SimpleWeightedAdmissibleReasoner,
// tous signees getModels(WAF, Object, Object) -- la mine est plus expressive que les 4 from-scratch.
Weighted WAF : (<{ a, b, c },[(b,c), (a,b)]>,{(b,c)=true, (a,b)=true})
Weighted grounded extensions (count=1) :
  - {} (type=org.tweetyproject.arg.dung.semantics.Extension)

7.5.2 Interpretation : la ponderation remplace, elle ne raffine pas

La reponse est non, et elle est franche : la sortie annonce count=1, et cette unique extension est vide. Aucun argument n’y est inconditionnellement defendable — pas meme a, qui n’est pourtant attaque par personne.

La lecon est celle qu’on attend d’un cadre etendu, et elle est contre-intuitive : ajouter des poids ne raffine pas la semantique de Dung « par-dessus », cela la remplace. Une extension ponderee peut donc etre strictement plus pauvre que l’extension non ponderee du meme graphe.

7.6 Social : de l’acceptabilite binaire au score gradue

Ici c’est la nature du resultat qui change. Le cadre social de Leite & Martins remplace le verdict ensembliste par une note : IssReasoner ne rend pas une extension mais un SocialMapping, soit une valeur dans [0, 1] par argument.

Le meme graphe est repris, avec les votes a +3, b -1 et c a 0, sous SimpleProductSemantics(0.5, 0.5) et un seuil d’acceptabilite de 0.5. L’argument a surveiller est c : il n’est attaque par personne et ne recoit aucun vote negatif.

#r "org.tweetyproject.tweety-7a.dll"

// === Social Abstract Argumentation (Leite & Martins 2011) via lib Tweety ===
// IssReasoner + SimpleProductSemantics = ISS (Issue-based Side-effect) semantics pour social networks.
// IKVM preserve les noms Java camelCase : add, voteUp, voteDown, getModel, query.
// SimpleProductSemantics a un ctor (Double, Double) -- PAS () -- (params = facteur1, facteur2).
// IssReasoner ctor = (SimpleProductSemantics semantics, Double threshold).
// Cas : 3 arguments avec votes (up/down). IssReasoner.query() retourne un score (Double) par argument.
using org.tweetyproject.arg.dung.syntax;
using org.tweetyproject.arg.social.syntax;
using org.tweetyproject.arg.social.semantics;
using org.tweetyproject.arg.social.reasoner;

var saaf = new SocialAbstractArgumentationFramework();
var aS = new Argument("a");
var bS = new Argument("b");
var cS = new Argument("c");
saaf.add(aS);
saaf.add(bS);
saaf.add(cS);
// Vote : a soutenu 3 fois, b contredit 1 fois, c neutre.
saaf.voteUp(aS, 3);
saaf.voteDown(bS, 1);

var semantics = new SimpleProductSemantics(0.5, 0.5);  // 2 params Double
var iss = new IssReasoner(semantics, 0.5);             // threshold d'acceptabilite
var issModel = iss.getModel(saaf);
Console.WriteLine($"SocialAAF : votes = a+3, b-1, c=0");
Console.WriteLine($"ISS Model : {issModel}");
Console.WriteLine($"  Type : {issModel.GetType().FullName}");

// Score par argument (Double, dans [0, 1]) :
Console.WriteLine($"  Score(a) = {iss.query(saaf, aS):F3}");
Console.WriteLine($"  Score(b) = {iss.query(saaf, bS):F3}");
Console.WriteLine($"  Score(c) = {iss.query(saaf, cS):F3}");
SocialAAF : votes = a+3, b-1, c=0
ISS Model : {a=0.8571428571428571, b=0.0, c=0.0}
  Type : org.tweetyproject.arg.social.semantics.SocialMapping
  Score(a) = 0.857
  Score(b) = 0.000
  Score(c) = 0.000

7.6.2 Interpretation : ne pas etre soutenu ne vaut pas etre neutre

La sortie est {a=0.8571428571428571, b=0.0, c=0.0}. Le point a ne pas manquer est donc c : dans les cadres precedents il serait accepte, ou au moins non rejete. Ici il obtient exactement la meme note que b, qui a pourtant ete contredit.

Sous cette semantique, ne pas etre soutenu ne vaut pas etre neutre. C’est le prix a payer pour obtenir un ordre de preference la ou Dung ne donnait qu’une partition — et une bonne question a se poser avant de choisir un cadre gradue pour un cas reel.

7.7 ADF : ce que le shade reconstruit contient reellement

Cette cellule est une sonde, pas une demonstration : elle mesure ce que la DLL expose au lieu de le supposer. Elle enumere les types du namespace arg.adf, liste les reasoners concrets qu’elle y trouve, puis tente d’instancier la chaine complete jusqu’au solveur.

La hierarchie de syntaxe demande un detour : le constructeur concret d’EmptyAbstractDialecticalFramework est package-private, donc invisible au compilateur C#, d’ou l’acces par reflexion sur AbstractBuilder public.

#r "org.tweetyproject.tweety-7a.dll"

// === ADF (Brewka & Woltran 2010) via lib Tweety : probe shade fat pour arg.adf.* ===
// Le shade `org.tweetyproject.tweety-7a.dll` agrege ADF 1.21 (bytecode Java 8). On verifie :
//   (a) types arg.adf exposes (b) reasoners presents (c) instantiation d'un solveur SAT natif.
// Verdict attendu : SOTA-OK-syntax (DLL chargee + types accessibles) ; reasoners JNI-dependants = RECOVERABLE-MACHINE.
using System.IO;
using System.Reflection;
using org.tweetyproject.arg.adf.sat;
using org.tweetyproject.arg.adf.sat.solver;
using org.tweetyproject.arg.adf.reasoner;
using org.tweetyproject.arg.adf.syntax.adf;

// (a) Types arg.adf exposes par le shade : on recompte pour confirmation runtime.
Type[] adfAll;
try {
    adfAll = asm.GetTypes();
} catch (ReflectionTypeLoadException ex) {
    adfAll = ex.Types.Where(t => t != null).ToArray()!;
}
var adfTypes = adfAll.Where(t => t.Namespace != null && t.Namespace.StartsWith("org.tweetyproject.arg.adf.")).ToArray();
var adfReasoners = adfTypes.Where(t => t.Name.EndsWith("Reasoner") && !t.IsAbstract && !t.IsInterface).ToArray();
Console.WriteLine($"  - arg.adf.* : {adfTypes.Length} types");
Console.WriteLine($"  - arg.adf.*Reasoner (concrets) : {adfReasoners.Length}");
foreach (var r in adfReasoners) Console.WriteLine($"      * {r.FullName}");

// (b) Syntaxe ADF : preuve de chargement de la hierarchie publique (Builder pattern des ADF graphiques).
// AbstractBuilder est la base publique ; GraphAbstractDialecticalFramework.Builder en herite en package-public.
// Le but ici est uniquement de montrer que la classe-charge de la hierarchie ADF reussit :
var builderType = adfTypes.FirstOrDefault(t => t.Name == "AbstractBuilder");
if (builderType != null) {
    Console.WriteLine($"  - ADF syntaxe : AbstractBuilder charge (fullName={builderType.FullName}, abstract={builderType.IsAbstract})");
}

// (c) Solveur SAT natif : NativeLingelingSolver charge lingeling.dll via JNI.
// Sous IKVM, java.library.path ne pointe pas sur libs/native/ -> UnsatisfiedLinkError attendu.
// Verdict : reasoners ADF SAT-complets = RECOVERABLE-MACHINE (JNI .so/.dll non invocable).
string solverVerdict;
try {
    var solver = new NativeLingelingSolver();
    solverVerdict = $"NativeLingelingSolver instancie (type={solver.GetType().FullName}) -> etonnant, JNI reussi";
} catch (System.TypeInitializationException ex) when (ex.InnerException is java.lang.UnsatisfiedLinkError) {
    solverVerdict = $"NativeLingelingSolver : UnsatisfiedLinkError (java.library.path ne pointe pas libs/native/) -> RECOVERABLE-MACHINE";
} catch (java.lang.UnsatisfiedLinkError ule) {
    solverVerdict = $"NativeLingelingSolver : UnsatisfiedLinkError ({ule.Message}) -> RECOVERABLE-MACHINE";
} catch (Exception ex) {
    solverVerdict = $"NativeLingelingSolver : {ex.GetType().Name} ({ex.Message.Substring(0, System.Math.Min(80, ex.Message.Length))}) -> {ex.GetType().Name}";
}
Console.WriteLine($"  - {solverVerdict}");

// Verdict tranche 2 (SOTA-OK-syntax / RECOVERABLE-MACHINE raisonner) :
// SOTA-OK-syntax : les types arg.adf.* exposes (compte imprime ci-dessus, pas fige ici), syntaxe ADF instanciable (EmptyAbstractDialecticalFramework).
// RECOVERABLE-MACHINE : les reasoners ADF SAT-complets (Ground/Complete/Stable) reposent sur IncrementalSatSolver =
// NativeLingelingSolver / NativeMinisatSolver / NativePicosatSolver qui chargent .so/.dll via JNI non invocables sous IKVM.
// Le tranche 1 from-scratch reste la voie pedagogique complete pour ADF grounded (algorithme 3-valued).
// Verdict final : arg.adf = SOTA-OK-syntax (ce qui etait verifie par main cell#25 sur tweety-conditional-logics.dll ;
// on le reproduit ici sur le shade plus large qui l'agrege nativement).
  - arg.adf.* : 341 types
  - arg.adf.*Reasoner (concrets) : 8
      * org.tweetyproject.arg.adf.reasoner.AdmissibleReasoner
      * org.tweetyproject.arg.adf.reasoner.CompleteReasoner
      * org.tweetyproject.arg.adf.reasoner.ConflictFreeReasoner
      * org.tweetyproject.arg.adf.reasoner.GroundReasoner
      * org.tweetyproject.arg.adf.reasoner.ModelReasoner
      * org.tweetyproject.arg.adf.reasoner.NaiveReasoner
      * org.tweetyproject.arg.adf.reasoner.PreferredReasoner
      * org.tweetyproject.arg.adf.reasoner.StableReasoner
  - ADF syntaxe : AbstractBuilder charge (fullName=org.tweetyproject.arg.adf.syntax.adf.AbstractBuilder, abstract=True)
  - NativeLingelingSolver : NullReferenceException (Object reference not set to an instance of an object.) -> NullReferenceException

7.7.2 Lecture : un verdict a deux etages

Le shade agrege bel et bien la famille ADF — arg.adf.* : 341 types — et 8 reasoners concrets y sont listes nommement (Admissible, Complete, ConflictFree, Ground, Model, Naive, Preferred, Stable).

Ce qui echoue est ailleurs, et d’un cran plus bas. NativeLingelingSolver ne s’instancie pas : les reasoners ADF complets delegent a un solveur SAT natif (lingeling, picosat, minisat) charge par JNI depuis java.library.path, chemin qui sous IKVM ne pointe pas sur libs/native/. La sortie enregistre une NullReferenceException a l’initialisation de type.

Le verdict est donc a deux etages, et c’est le seul honnete : syntaxe et reasoners charges (le pont fait son travail), execution SAT indisponible (dependance native hors du runtime manage). C’est precisement pourquoi la tranche 1 from-scratch reste la source pedagogique pour ADF : elle calcule le point fixe explicitement, sans solveur externe.

7.7.3 Les trois familles restantes : SetAF, EAF, Bipolar

La sonde 7.7 ci-dessus ne couvre qu’ADF. Les trois autres familles du tableau 7.1 sont restees non re-sondees depuis la reconstruction du shade : un inconnu honnete, pas un verdict – et un inconnu qu’on ne peut pas lever en le supposant. Cette cellule le leve avec la meme methode que pour ADF : compter les types du namespace, enumerer les reasoners concrets. (Le tableau 7.1 porte desormais le verdict que cette cellule etablit.)

Deux precautions de mesure s’imposent avant de lire le resultat, et aucune n’est theorique.

1. Le nom du module Maven n’est pas le nom du paquet Java. La recette de build parle d’un module eaf, mais aucun namespace org.tweetyproject.arg.eaf n’existe dans les DLL livrees : les Extended Argumentation Frameworks (Modgil 2009 – des attaques qui portent sur des attaques) sont publies sous org.tweetyproject.arg.extended. Une sonde qui viserait arg.eaf rendrait donc 0 par construction, et on lirait comme une absence ce qui ne serait qu’une faute de nom. La cellule sonde les deux, precisement pour que la distinction reste visible.

2. Chercher par sous-chaine fabrique des faux positifs. bipolar apparait dans ce shade, mais sous org.tweetyproject.arg.adf.reasoner.sat.* (BipolarSatEncoding, MostBipolarParentsDecomposer) : c’est le concept bipolaire d’ADF, pas le paquet arg.bipolar de Cayrol & Lagasquie-Schiex. De meme eaf apparait dans LeafNode. Une sonde par sous-chaine conclurait « present » sur des familles absentes.

La cellule compte donc des deux manieres et affiche l’ecart, et elle regarde aussi les DLL soeurs du dossier : « absent de ce shade » et « hors de portee de l’ecosysteme » sont deux verdicts differents, et seul le second serait une mauvaise nouvelle.

// === 7.7.3 : les familles que la sonde ADF ne couvrait pas (issue #14143) ===
// Meme methode qu'en 7.7 pour ADF : comptage par namespace + reasoners concrets.
// Trois precautions, chacune motivee par une mesure (cf. markdown ci-dessus) :
//   (a) on matche le NAMESPACE EXACT, pas une sous-chaine du nom complet ;
//   (b) on affiche quand meme le compte naif, pour rendre l'ecart constatable ;
//   (c) on sonde aussi la DLL soeur : absent du shade != absent de l'ecosysteme.
using System.IO;
using System.Linq;
using System.Reflection;

Func<Assembly, Type[]> SafeTypes = a => {
    try { return a.GetTypes(); }
    catch (ReflectionTypeLoadException ex) { return ex.Types.Where(t => t != null).ToArray()!; }
};
Func<Type, string, bool> InNs = (t, ns) =>
    t.Namespace != null && (t.Namespace == ns || t.Namespace.StartsWith(ns + "."));

var shadeTypes = SafeTypes(asm);
Console.WriteLine($"Shade org.tweetyproject.tweety-7a.dll : {shadeTypes.Length} types au total");
Console.WriteLine();

// famille -> (namespaces candidats, DLL soeur du dossier)
var familles = new (string Nom, string[] Ns, string Dll)[] {
    ("SetAF",   new[] { "org.tweetyproject.arg.setaf" },                                "org.tweetyproject.tweety-setaf.dll"),
    ("EAF",     new[] { "org.tweetyproject.arg.eaf", "org.tweetyproject.arg.extended" }, "org.tweetyproject.tweety-extended.dll"),
    ("Bipolar", new[] { "org.tweetyproject.arg.bipolar" },                              "org.tweetyproject.tweety-bipolar.dll"),
};

foreach (var f in familles) {
    Console.WriteLine($"--- {f.Nom} ---");

    foreach (var ns in f.Ns) {
        var court = ns.Substring("org.tweetyproject.arg.".Length);
        var exact = shadeTypes.Where(t => InNs(t, ns)).ToArray();
        var naif  = shadeTypes.Where(t => (t.FullName ?? "").ToLowerInvariant().Contains(court)).ToArray();
        var reas  = exact.Where(t => t.Name.EndsWith("Reasoner") && !t.IsAbstract && !t.IsInterface).ToArray();

        Console.WriteLine($"  shade  {ns}.* : {exact.Length} types, {reas.Length} reasoners concrets");
        if (naif.Length != exact.Length) {
            Console.WriteLine($"     [!] sous-chaine \"{court}\" : {naif.Length} types -> {naif.Length - exact.Length} faux positif(s)");
            foreach (var t in naif.Where(t => !InNs(t, ns)).Take(3))
                Console.WriteLine($"         ~ {t.FullName}");
        }
        foreach (var r in reas) Console.WriteLine($"         * {r.FullName}");
    }

    // DLL soeur : la famille est-elle livree ailleurs dans le dossier ?
    string sib = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), f.Dll));
    if (!File.Exists(sib)) {
        Console.WriteLine($"  soeur  {f.Dll} : absente du dossier");
    } else {
        try {
            var sibTypes = SafeTypes(Assembly.LoadFrom(sib));
            var trouve = f.Ns.Select(ns => (ns, n: sibTypes.Where(t => InNs(t, ns)).Count()))
                             .Where(x => x.n > 0).ToArray();
            var detail = trouve.Length == 0 ? "aucun type des namespaces candidats"
                       : string.Join(", ", trouve.Select(x => $"{x.ns}.* = {x.n} types"));
            Console.WriteLine($"  soeur  {f.Dll} : {sibTypes.Length} types, {detail}");
        } catch (Exception ex) {
            Console.WriteLine($"  soeur  {f.Dll} : presente mais non chargeable ici ({ex.GetType().Name})");
        }
    }
    Console.WriteLine();
}
Shade org.tweetyproject.tweety-7a.dll : 939 types au total

--- SetAF ---
  shade  org.tweetyproject.arg.setaf.* : 0 types, 0 reasoners concrets
  soeur  org.tweetyproject.tweety-setaf.dll : 404 types, org.tweetyproject.arg.setaf.* = 3 types

--- EAF ---
  shade  org.tweetyproject.arg.eaf.* : 0 types, 0 reasoners concrets
     [!] sous-chaine "eaf" : 3 types -> 3 faux positif(s)
         ~ org.tweetyproject.arg.adf.util.ExcludeSubinterpretationsIterator+LeafNode
         ~ org.tweetyproject.arg.adf.util.ExcludeTwoValuedSubinterpretationsIterator+LeafNode
         ~ org.tweetyproject.arg.adf.util.InterpretationTrieSet+LeafNode
  shade  org.tweetyproject.arg.extended.* : 0 types, 0 reasoners concrets
     [!] sous-chaine "extended" : 3 types -> 3 faux positif(s)
         ~ org.tweetyproject.arg.dung.examples.ExtendedExampleFinderExample
         ~ org.tweetyproject.arg.dung.learning.ExtendedExampleFinder
         ~ org.tweetyproject.arg.adf.syntax.pl.Clauses+ExtendedClause
  soeur  org.tweetyproject.tweety-extended.dll : 366 types, org.tweetyproject.arg.extended.* = 12 types

--- Bipolar ---
  shade  org.tweetyproject.arg.bipolar.* : 0 types, 0 reasoners concrets
     [!] sous-chaine "bipolar" : 19 types -> 19 faux positif(s)
         ~ org.tweetyproject.arg.adf.reasoner.sat.decomposer.MostBipolarParentsDecomposer
         ~ org.tweetyproject.arg.adf.reasoner.sat.encodings.BipolarSatEncoding
         ~ org.tweetyproject.arg.adf.reasoner.sat.encodings.KBipolarSatEncoding
  soeur  org.tweetyproject.tweety-bipolar.dll : 451 types, org.tweetyproject.arg.bipolar.* = 46 types

7.7.4 Lecture : trois absences, et ce que la mesure a corrige en chemin

Verdict. Les trois familles sont absentes de ce shade : zero type dans leur namespace. La sonde le dit pour arg.setaf, pour arg.eaf et pour arg.extended, et pour arg.bipolar.

Le piege du sous-mot est reel, et il n’est pas uniforme – c’est la partie instructive. A cette execution, la recherche naive rend 0 faux positif sur SetAF, 3 sur EAF (les LeafNode du paquet ADF), 3 de plus sur extended (ExtendedExampleFinder…) et 19 sur Bipolar (BipolarSatEncoding, KBipolarSatEncoding, MostBipolarParentsDecomposer : le concept bipolaire d’ADF, pas le paquet de Cayrol & Lagasquie-Schiex). Une sonde par sous-chaine aurait donc conclu juste sur SetAF et faux sur les deux autres. C’est la lecon a retenir : qu’un raccourci de mesure ait donne le bon resultat une fois n’est pas une preuve qu’il mesure la bonne chose. Les chiffres ci-dessus sont ceux de cette execution ; la sortie de la cellule fait foi.

Une erreur de nom, que seule la mesure fait apparaitre. Le tableau 7.1 designait la famille EAF par org.tweetyproject.arg.eaf.*. Ce namespace n’existe dans aucune des DLL du dossier : le nom eaf est celui du module Maven, pas du paquet Java, et les Extended Argumentation Frameworks sont publies sous org.tweetyproject.arg.extended. Une sonde qui n’aurait interroge que arg.eaf aurait rendu 0 et on l’aurait lu comme une absence – alors qu’elle n’aurait mesure qu’une faute de nom. C’est pourquoi la cellule interroge les deux : le 0 sur arg.extended est le resultat qui porte le verdict.

La cause n’est pas supposee : elle est ecrite dans la recette de build. dotnet-build/rebuild-7a.sh (l. 22-25) exclut ces modules par leur nom et leur version :

setaf 1.20 / eaf 1.21 / extended 1.27 / bipolar 1.19 are EXCLUDED: their sources use Java 9+ Kotlin-style lambdas IKVM 8.x cannot compile (IKVM0101 silently drops them). Those frameworks stay covered by the from-scratch tranche (BCL) of the Tweety-7a notebook.

L’exclusion est donc deliberee, pas un accident du filtre – et la nuance compte : IKVM0101 ecarte ces sources en silence, si bien qu’un build qui les aurait laissees passer aurait produit un shade amoindri sans le dire. La recette les retire en amont pour que l’absence soit un choix lisible plutot qu’un effet de bord.

Absent de ce shade n’est pas hors de portee. Les trois familles sont livrees dans le meme dossier, sous tweety-setaf.dll, tweety-extended.dll et tweety-bipolar.dll, dont la sonde compte les types au passage. Une precision s’impose toutefois : la sonde compte ces types, elle n’en execute aucun. La lecon d’ADF en 7.7.2 – des reasoners bien presents mais bloques par le JNI – vaut ici comme mise en garde : presence n’est pas executabilite, et statuer sur la seconde demanderait un essai que cette cellule ne fait pas.

7.8 Lecture : parite from-scratch <=> lib Tweety

Resultats tranche 2 (substance declaree honnetement — bilan net -1 ADF execute / +2 weighted+social executes par rapport au jumeau #10410 d’origine, voir section substance** plus bas)** :

Famille Theorie Resultat lib Tweety Parite tranche 1
Dung a->b, b->c grounded = {a,c} OK — VAF.computeGrounded([a->b, b->c]) = {a,c} (degenere Dung classique sans values)
Weighted 3 args, 2 attaques weight=true, source=true, target=false grounded = {} N/A — pas de from-scratch Weighted dans tranche 1
Social 3 args, votes asymetriques (a+3, b-1) ISS modele construit, scores calcules N/A — pas de from-scratch Social dans tranche 1

Limites verifiees (lattice epistemique honnete) :

  • ADF (Brewka & Woltran 2010) : SOTA-OK-syntax (types arg.adf.* exposes runtime par le shade — compte imprime par la sonde 7.7, pas fige ici, base publique AbstractBuilder chargee, 8 reasoners concrets enumeres par reflexion dont AdmissibleReasoner / CompleteReasoner / ConflictFreeReasoner / GroundReasoner / ModelReasoner / NaiveReasoner / PreferredReasoner / StableReasoner) ; reasoners SAT-complets = RECOVERABLE-MACHINE (JNI NativeLingelingSolver -> NullReferenceException IKVM-side quand libs/native/*.so absent ; meme cause racine que UnsatisfiedLinkError JNI direct sous JDK natif). Algorithme from-scratch tranche 1 reste la voie pedagogique complete pour ADF grounded.
  • SetAF / EAF / Bipolar : absents de ce shade, mesure par la sonde 7.7.3 — zero type dans leur namespace, exclusion deliberee de rebuild-7a.sh (l. 22-25) et non accident du filtre. Les trois familles sont livrees a part dans le dossier (tweety-setaf.dll, tweety-extended.dll, tweety-bipolar.dll), dont la sonde compte les types ; leur execution n’y est pas testee, et la lecon d’ADF ci-dessus invite a ne pas la supposer acquise. La tranche 1 from-scratch reste la source pedagogique pour ces trois cadres.
  • ADF reasoners JNI-dependents : meme si les sources adf etaient chargeables, les NativeMinisatSolver, NativePicosatSolver, NativeLingelingSolver requis par les reasoners ADF necessitent des libs natives JNI (libminisat.so, etc.) non invocables dans le runtime IKVM .NET. INTRINSIC secondaire.
  • Inventaire du shade : mesure a l’execution par la cellule 7.3 (taille de la DLL, nombre de types, decompte par namespace) plutot que fige ici — le shade a ete reconstruit depuis la redaction de cette section, et tout chiffre recopie derive a la reconstruction suivante. Distribution bytecode : rebuild-7a.sh Step 6.

Substance (parite semantique avec cellule jumelle #10410 d’origine) :

Le PR #10450 n’est pas « +3 modules SOTA-OK » : c’est un echange defensable mais qui MERITE d’etre declare. Bilan verbatim par rapport a la cellule #21 d’origine (#10410, tweety-conditional-logics.dll shade fin, 125 types, ADF grounded execute avec verdict SOTA-OK). Les chiffres de ce tableau sont ceux du snapshot #10450 ; le shade a ete reconstruit depuis, et l’inventaire courant se lit dans les sorties de 7.3 et 7.7 :

Module Jumeau #10410 PR #10450 (tranche 2 shade fat) Bilan
-1 arg.adf execute (ADF grounded via AbstractExtensionReasoner/GroundedReasoner -> extension {a,c}) SOTA-OK-syntax uniquement (245 types, syntaxe OK, 9 reasoners charges mais non executes : NativeLingelingSolver -> NRE IKVM-side) RECOVERABLE-MACHINE
+1 arg.weighted absent du shade d’origine (inclus mais non execute) SOTA-OK (WeightedGroundedReasoner -> {}) +1 execute
+1 arg.social absent du shade d’origine SOTA-OK (IssReasoner + SimpleProductSemantics -> {a=0.857, b=0, c=0}) +1 execute
= arg.dung SOTA-OK (SimpleGroundedReasoner -> {a,c}) SOTA-OK (SimpleGroundedReasoner -> {a,c}) semantique identique

Echange net : -1 ADF execute / +2 weighted+social executes = -1 module execute net, pas +3. Le shade tweety-7a.dll est plus large (inventaire courant en 7.3) mais la lib originale avait un shade specialise ADF qui executait reellement (JNI resolu sur la machine d’origine) ; ici le shade fat agrege adf 1.21 mais les reasoners SAT-complets heurtent le libs/native/*.so manquant. Conclusion : le tranche 2 IKVM valide 3 modules (Dung + Weighted + Social) comme SOTA-OK ; ADF passe de SOTA-OK (jumeau) a RECOVERABLE-MACHINE (shade fat) ; c’est un echange qu’on assume.

Recommandation pedagogique : la tranche 1 reste la source de verite pedagogique pour ADF/SetAF/EAF/Bipolar (algorithme explicite, lecture du code). La tranche 2 valide la parite comportementale sur les 3 frameworks effectivement supportes (Dung, Weighted, Social) et demontre l’integration IKVM 8.x + JDK 17 –byte 8 de bout en bout dans un notebook .NET Interactive.

8. Exercices

Exercice 1 : ADF cyclique

Construisez un ADF avec a : not b et b : not a (cycle). Que donne le grounded ? (Indice : en logique 3-valued, aucun ne peut etre decide -> tous U -> extension grounded vide.)

Exercice 2 : SetAF a 3 arguments

Arguments {x, y, z}, attaques ({x,y}, z) et ({z}, x). Calculez le grounded. (Indice : qui est IN ? qui est OUT ?)

Exercice 3 : EAF avec meta-attaque echouant

Reprenez l’exemple EAF (a->b, c->(a,b)) mais ajoutez une attaque d -> c avec d non attaque. Desormais c est OUT. Que devient le grounded ? (Indice : c OUT -> meta-attaque inactive -> a->b redevient un defeat -> b OUT.)

Exercice 4 : VAF a 3 valeurs

Arguments a (Economie), b (Environnement), c (Justice), attaques a->b, b->c, c->a (cycle). Testez les 3 ordres cycliques et identifiez celui qui donne le plus grand grounded.

// Exercice 1 : ADF cyclique (a:not b, b:not a)
// TODO etudiant : construire l'ADF et observer le grounded
var adfExo1 = new ADF();
// adfExo1.Add("a", Neg(Var("b")));
// adfExo1.Add("b", Neg(Var("a")));
// var gExo1 = adfExo1.Grounded();
Show("Exo1", "Exercice a completer : ADF cyclique (attendu : extension grounded vide, tous U)");
Exo1: Exercice a completer : ADF cyclique (attendu : extension grounded vide, tous U)
// Exercice 2 : SetAF ({x,y}->z, {z}->x)
// TODO etudiant : construire le SetAF et calculer le grounded
var setafExo2 = new SetAF(new[] { "x", "y", "z" });
// setafExo2.AddAttack(new[]{"x","y"}, "z");
// setafExo2.AddAttack(new[]{"z"}, "x");
// var (inExo2, outExo2) = setafExo2.Grounded();
Show("Exo2", "Exercice a completer : SetAF 3 args (qui est IN ?)");
Exo2: Exercice a completer : SetAF 3 args (qui est IN ?)
// Exercice 3 : EAF avec c defait (d->c ajoutee)
// TODO etudiant : ajouter d et d->c, observer le grounded
var eafExo3 = new EAF(new[] { "a", "b", "c", "d" });
// eafExo3.AddAttack("a","b"); eafExo3.AddMetaAttack("c","a","b"); eafExo3.AddAttack("d","c");
// var gExo3 = eafExo3.Grounded(useMeta: true);
Show("Exo3", "Exercice a completer : EAF avec c OUT -> b OUT (reinstatement perdu)");
Exo3: Exercice a completer : EAF avec c OUT -> b OUT (reinstatement perdu)
// Exercice 4 : VAF a 3 valeurs cyclique
// TODO etudiant : cycle a->b->c->a avec 3 valeurs, tester les ordres
var vafExo4 = new VAF();
// vafExo4.Add("a","Economie"); vafExo4.Add("b","Environnement"); vafExo4.Add("c","Justice");
// vafExo4.AddAttack("a","b"); vafExo4.AddAttack("b","c"); vafExo4.AddAttack("c","a");
Show("Exo4", "Exercice a completer : VAF 3-valeurs cyclique (quel ordre maximise le grounded ?)");
Exo4: Exercice a completer : VAF 3-valeurs cyclique (quel ordre maximise le grounded ?)

Conclusion

Ce jumeau C# a presente les 4 extensions principales du cadre de Dung, reimplementation from-scratch du twin Python jpype/Tweety JVM.

Résultats cles (Math Verification Standard)

Pilier Entree Sortie (re-derivable a la main)
ADF a:not b, c:T, b:not c grounded {a,c} (c=T -> b=F -> a=T)
SetAF {b,d}->a, {a,c}->c grounded {b,c,d} (coalition {a,c} jamais formee)
EAF a->b, c->(a,b) grounded {a,b,c} (meta-attaque reinstated b)
VAF a:Eco -> b:Env {a} si Eco>Env, {a,b} si Env>Eco

Value-add (Prong B, #3801)

  • 0 jpype/JVM/IKVM : BCL .NET 9 uniquement, la ou le twin Python charge 42 JARs + NativeMinisat.
  • Sémantiques from-scratch : Kleene 3-valued, labelling grounded, meta-attaques, ordre de valeurs.

Tranche 2 — lib-vs-lib via la librairie Tweety (pont IKVM)

La tranche 1 ci-dessus reimplante ADF / SetAF / EAF / VAF from-scratch (BCL .NET). La tranche 2 branche la librairie Java Tweety cote C# via le pont IKVM (runtime Java 8 pour .NET), sur le gabarit de Tweety-6 :

  • Dung = SOTA-OK : le reasoner reel Tweety (SimpleGroundedReasoner) calcule l’extension fondee {a,c} sur la theorie a->b, b->c — algorithme du moteur, pas reimplementation.
  • ADF = librairie chargee (SOTA-OK sur la syntaxe) : le module arg.adf (125 types : 15 conditions d’acceptation + 59 types de reasoner SAT) est atteignable depuis C# via le shade fat conditional-logics.dll.
  • Reasoner ADF SAT = RECOVERABLE-MACHINE (#10411) : les semantiques ADF (fondee/complete/stable) exigent un IncrementalSatSolver natif (minisat/picosat/lingeling via java.library.path) — meme pre-requis que le jumeau Python (Tweety-1-Setup -> NATIVE_LIBS_DIR). Sonde firsthand sous IKVM : UnsatisfiedLinkError (backend natif non binde).
  • Modules absents = RECOVERABLE-MACHINE (#10411) : bipolar / setaf / weighted / social / extended ne sont dans aucun DLL commite ; leur shade exige la recette dotnet-build/ (Maven + JDK17 + ~/.m2).

« From scratch c’est bien, lib vs lib c’est encore mieux ! »

Suite : Tweety-7b-Ranking-Probabilistic | Retour au sommaire

Retour au sommet