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)
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 ---publicenum Tri { T, F, U }publicstaticclass Kleene{publicstatic Tri Not(Tri x)=> x == Tri.T? Tri.F:(x == Tri.F? Tri.T: Tri.U);publicstatic 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;}publicstatic 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;}publicstaticstringStr(Tri x)=> x == Tri.T?"t":(x == Tri.F?"f":"u");}publicstaticvoidShow(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))}");
The below script needs to be able to find the current output cell; this is an easy method to get it.
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’acceptationC(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>;publicsealedclass ADF{public List<string> Args {get;}=new();public Dictionary<string, Func<Interpretation, Tri>> Conditions {get;}=new();publicvoidAdd(string a, Func<Interpretation, Tri> cond){ Args.Add(a); Conditions[a]= cond;}// Grounded = fixed-point depuis tout-U.public Interpretation Grounded(){var I =newInterpretation();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 couranteif(val != cur){ I[a]= val; changed =true;}}}return I;}}// Helpers : references a un argument, constantes T/FFunc<Interpretation, Tri>Var(string x)=> I => I.TryGetValue(x,outvar 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 =newADF();adf.Add("a",Neg(Var("b")));// a : not badf.Add("c",Const(Tri.T));// c : Tadf.Add("b",Neg(Var("c")));// b : not cvar g = adf.Grounded();var sb =newStringBuilder();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).
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 ---publicsealedclass SetAF{public List<string> Args {get;}=new();// (ensemble attaquants, cible)public List<(HashSet<string> Attackers,string Target)> Attacks {get;}=new();publicSetAF(IEnumerable<string> args){ Args.AddRange(args);}publicvoidAddAttack(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 =newSetAF(new[]{"a","b","c","d"});setaf.AddAttack(new[]{"b","d"},"a");setaf.AddAttack(new[]{"a","c"},"c");var(sIN, sOUT)= setaf.Grounded();var sb4 =newStringBuilder();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-attaquec -> (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 ---publicsealedclass EAF{public List<string> Args {get;}=new();public List<(string Src,string Dst)> Attacks {get;}=new();// a -> bpublic List<(string Src,string SrcAtk,string DstAtk)> MetaAttacks {get;}=new();// c -> (a,b)publicEAF(IEnumerable<string> args){ Args.AddRange(args);}publicvoidAddAttack(string src,string dst)=> Attacks.Add((src, dst));publicvoidAddMetaAttack(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.boolIsValid((string Src,string Dst) at, HashSet<string> IN,bool useMeta){if(!useMeta)returntrue;// 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 courantvar 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 attaquesvar eaf =newEAF(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 =newStringBuilder();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.
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) ---publicsealedclass VAF{public List<string> Args {get;}=new();public Dictionary<string,string> ValueOf {get;}=new();// arg -> valeurpublic List<(string Src,string Dst)> Attacks {get;}=new();public Dictionary<(string,string),int> ValuePref {get;}=new();// (vA, vB) -> +1 si vA prefere a vBpublicvoidAdd(string arg,string value){ Args.Add(arg); ValueOf[arg]= value;}publicvoidAddAttack(string src,string dst)=> Attacks.Add((src, dst));// Definit un ordre : v1 preferee a v2publicvoidSetPref(string v1,string v2){ ValuePref[(v1, v2)]=1; ValuePref[(v2, v1)]=-1;}// Attack valide (defeat) ssi value(src) preferee ou egale a value(dst).boolIsValid((string Src,string Dst) at){string vy = ValueOf[at.Src], vx = ValueOf[at.Dst];if(vy == vx)returntrue;// meme valeur -> attaque reussitif(ValuePref.TryGetValue((vy, vx),outint p))return p >=0;// vy pref a vxreturnfalse;// 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 =newVAF(); vaf.Add("a","Economie"); vaf.Add("b","Environnement"); vaf.AddAttack("a","b");setPref(vaf);var g = vaf.Grounded();var sb =newStringBuilder(); 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"));
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
ADF : generaliser l’attaque en formule logique sur les arguments (logique 3-valued de Kleene pour le grounded).
SetAF : une attaque peut necessiter une coalition complete ; l’auto-reference la desactive naturellement.
EAF : un argument peut desactiver une attaque (préférence/exception), restaurant sa cible.
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.
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.
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 vraisSimpleGroundedReasoner / 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.Booleann’est pasjava.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))thrownewFileNotFoundException("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");}
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 bout, 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 =newDungTheory();var a =newArgument("a");var b =newArgument("b");var c =newArgument("c");dt.add(a);dt.add(b);dt.add(c);dt.addAttack(a, b);dt.addAttack(b, c);var gr =newSimpleGroundedReasoner();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'Argumentsvar 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 =newBooleanSemiring();var waf =newWeightedArgumentationFramework(bs);var aW =newArgument("a");var bW =newArgument("b");var cW =newArgument("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 =newSimpleWeightedGroundedReasoner();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 =newSocialAbstractArgumentationFramework();var aS =newArgument("a");var bS =newArgument("b");var cS =newArgument("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 =newSimpleProductSemantics(0.5,0.5);// 2 params Doublevar iss =newIssReasoner(semantics,0.5);// threshold d'acceptabilitevar 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 =newNativeLingelingSolver(); solverVerdict = $"NativeLingelingSolver instancie (type={solver.GetType().FullName}) -> etonnant, JNI reussi";}catch(System.TypeInitializationException ex)when(ex.InnerExceptionis 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 0par 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();}
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.eafet 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)
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)
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 groundedvar adfExo1 =newADF();// 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 groundedvar setafExo2 =newSetAF(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 groundedvar eafExo3 =newEAF(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 ordresvar vafExo4 =newVAF();// 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.
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 IncrementalSatSolvernatif (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 ! »
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 :
IssReasonerne rend pas une extension mais unSocialMapping, soit une valeur dans[0, 1]par argument.Le meme graphe est repris, avec les votes
a+3,b-1 etca 0, sousSimpleProductSemantics(0.5, 0.5)et un seuil d’acceptabilite de0.5. L’argument a surveiller estc: il n’est attaque par personne et ne recoit aucun vote negatif.