Serie Tweety – port C#/.NET natif (EPIC #4667). Ce notebook exploite le module org.tweetyproject.preferences de TweetyProjectsans JVM : la librairie Java est compilee vers un fat-jar Maven shade puis executee sur le runtime .NET via IKVM.
Objectifs pedagogiques
Comprendre la representation des ordres de préférence
Decouvrir les méthodes d’agregation de préférences (théorie du choix social)
Explorer les règles de vote classiques (Borda, Condorcet, Copeland)
Comprendre le lien entre préférences et argumentation
org.tweetyproject.preferences – Ordres de préférence et aggregation
Initialisation de l’environnement
Cette cellule initialise le runtime IKVM (implementation .NET de la machine virtuelle Java) et charge la DLL org.tweetyproject.tweety-preferences.dll compilee depuis le module préférences de TweetyProject 1.30.
Pipeline de build : 1. mvn package (Maven shade) produit tweety-preferences-full-1.30.jar (fat-jar avec dependances transitives) 2. dotnet build (IKVM 8.15) compile le JAR vers org.tweetyproject.tweety-preferences.dll (7.7 Mo, net8.0) 3. Le notebook charge la DLL via Assembly.LoadFrom dans le runtime .NET
** vs Python (JPype)** : ou le notebook Python demarre une JVM externe via JPype, le port C# utilise IKVM qui execute le bytecode Java en processus dans le runtime .NET – aucun pont JNI, aucune JVM externe.
Note sur l’effacement des generiques : IKVM 8.15 efface les paramètres de type Java <T> dans les metadonnees .NET. PreferenceOrder<T> devient PreferenceOrder (non-genrique). On utilise dynamic et System.Activator.CreateInstance pour instancier et appeler les méthodes. Cf. cellule 4 pour les details.
#r "nuget: IKVM, 8.15.0"#r "nuget: IKVM.Image, 8.15.0"using System.IO;// RID de la machine courante (win-x64, linux-x64, osx-arm64...) : IKVM.Image tire l'image native de chaque plateforme.string ikvmVer ="8.15.0", ikvmRid =(OperatingSystem.IsWindows()?"win": OperatingSystem.IsMacOS()?"osx":"linux")+"-"+ System.Runtime.InteropServices.RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant();string nugetRoot = Environment.GetEnvironmentVariable("NUGET_PACKAGES")?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),".nuget","packages");string ikvmBaseAny = Path.Combine(nugetRoot,"ikvm.image", ikvmVer,"ikvm","any","any");string ikvmArchDir = Path.Combine(nugetRoot,"ikvm.image.runtime."+ ikvmRid, ikvmVer,"ikvm","any", ikvmRid);string ikvmHome = Path.Combine(Path.GetTempPath(),"ikvm-home-"+ ikvmVer +"-"+ ikvmRid);voidIkvmCopyMerge(string src,string dst){foreach(var d in Directory.GetDirectories(src,"*", SearchOption.AllDirectories)) Directory.CreateDirectory(d.Replace(src, dst));foreach(var f in Directory.GetFiles(src,"*", SearchOption.AllDirectories)){var t = f.Replace(src, dst); Directory.CreateDirectory(Path.GetDirectoryName(t)); File.Copy(f, t, overwrite:true);}}if(Directory.Exists(ikvmBaseAny)&& Directory.Exists(ikvmArchDir)){if(!Directory.Exists(ikvmHome)) Directory.CreateDirectory(ikvmHome);IkvmCopyMerge(ikvmBaseAny, ikvmHome);IkvmCopyMerge(ikvmArchDir, ikvmHome);}AppContext.SetData("IKVM.Home", ikvmHome);bool ikvmReady = File.Exists(Path.Combine(ikvmHome,"lib","tzdb.dat"));Console.WriteLine(ikvmReady?"IKVM home configure : "+ ikvmHome:"ATTENTION : IKVM home incomplet (tzdb.dat manquant)");Console.WriteLine("NuGet IKVM "+ ikvmVer +" ("+ ikvmRid +") : "+(ikvmReady ?"OK":"PARTIEL"));
The below script needs to be able to find the current output cell; this is an easy method to get it.
Installed Packages
IKVM, 8.15.0
IKVM.Image, 8.15.0
IKVM home configure : /tmp/ikvm-home-8.15.0-linux-x64
NuGet IKVM 8.15.0 (linux-x64) : OK
// --- Chargement DLL preferences + verification des types exposes ---using System;using System.IO;using System.Reflection;using System.Linq;string dllRelative = Path.Combine("dotnet-build","bin","Release","net8.0","org.tweetyproject.tweety-preferences.dll");string dllPath = Path.Combine(Environment.CurrentDirectory, dllRelative);if(!File.Exists(dllPath)){// Fallback 1 : DLL livree a la racine de la serie Tweety (commitee, cf. tweety-dung)string rootDll = Path.Combine(Environment.CurrentDirectory,"org.tweetyproject.tweety-preferences.dll");// Fallback 2 : resolution relative depuis le dossier du notebook dllPath = File.Exists(rootDll)? rootDll : Path.GetFullPath(dllRelative);}// Chemin relatif au dossier du notebook : la sortie ne depend pas de la machine.Console.WriteLine("DLL path : "+ Path.GetRelativePath(Environment.CurrentDirectory, dllPath));Console.WriteLine("DLL size : "+(newFileInfo(dllPath).Length/1024.0/1024.0).ToString("F1")+" Mo");Assembly prefsAsm = Assembly.LoadFrom(dllPath);Console.WriteLine("Assembly chargee : "+ prefsAsm.GetName().Name+" v"+ prefsAsm.GetName().Version);// Lister tous les types du namespace preferences (IKVM efface les generiques)var allTypes = prefsAsm.GetTypes().Where(t => t.Namespace!=null&& t.Namespace.StartsWith("org.tweetyproject.preferences")).OrderBy(t => t.FullName).ToList();Console.WriteLine("\n=== Types exposes dans org.tweetyproject.preferences ===");foreach(var t in allTypes){string kind = t.IsInterface?"interface": t.IsAbstract?"abstract": t.IsEnum?"enum":"class"; Console.WriteLine($" [{kind,8}] {t.FullName}");}Console.WriteLine($"\nTotal : {allTypes.Count} types exposes (attendu : 15).");// Verification : BordaScoringPreferenceAggregator peut etre instancievar bordaType = allTypes.FirstOrDefault(t => t.Name=="BordaScoringPreferenceAggregator");if(bordaType !=null){try{ dynamic testAggregator = Activator.CreateInstance(bordaType,newobject[]{3}); Console.WriteLine("\n[OK] BordaScoringPreferenceAggregator(3) instancie avec succes.");}catch(Exception ex){ Console.WriteLine("\n[ECHEC] Instanciation Borda : "+ ex.GetType().Name);}}Console.WriteLine("\nIKVM runtime + Tweety preferences 1.30 charges.");
La théorie des préférences est fondamentale en choix social, economie et argumentation. TweetyProject fournit des outils pour representer et agreger les préférences individuelles en préférences collectives.
7.1 Ordres de Préférence
Un ordre de préférence est une relation binaire sur un ensemble d’alternatives, généralement : - Totale : Toutes les paires sont comparables - Transitive : Si A > B et B > C, alors A > C - Antisymetrique : Si A > B, alors non B > A
TweetyProject represente les préférences via : - PreferenceOrder<T> : Ordre de préférence sur des éléments de type T - BinaryRelation : Relation binaire de base - ranking.RankingFunction : Fonction de classement
Sous-modules disponibles (15 types dans la DLL compilee) : - io.POParser / io.POWriter : Parsing et ecriture d’ordres de préférence - aggregation.* : Agregateurs de préférences (Borda, Plurality, Veto) - ranking.* : Fonctions de ranking et leveling - update.* : Mise a jour dynamique des préférences
Limitation importante : Tweety n’implemente pas nativement les règles de Condorcet et Copeland. Ces concepts seront illustres via des simulations C# (LINQ) dans la section suivante.
// --- 7.1 Exploration du module preferences via reflexion .NET ---using System;using System.Reflection;using System.Linq;Console.WriteLine("--- 7.1 Exploration du module org.tweetyproject.preferences ---");// Types d'agregationvar aggTypes = prefsAsm.GetTypes().Where(t => t.Namespace=="org.tweetyproject.preferences.aggregation").OrderBy(t => t.Name).ToList();Console.WriteLine("\nClasses d'agregation :");foreach(var t in aggTypes){string kind = t.IsAbstract?"abstract": t.IsInterface?"interface":"class"; Console.WriteLine($" [{kind}] {t.Name}");}// Types de rankingvar rankTypes = prefsAsm.GetTypes().Where(t => t.Namespace=="org.tweetyproject.preferences.ranking").OrderBy(t => t.Name).ToList();Console.WriteLine("\nClasses de ranking :");foreach(var t in rankTypes) Console.WriteLine($" {t.Name}");// Explorer les constructeurs de BordaScoringPreferenceAggregatorConsole.WriteLine("\n--- API : BordaScoringPreferenceAggregator ---");var bordaCtors = bordaType.GetConstructors();foreach(var c in bordaCtors){var parms = c.GetParameters(); Console.WriteLine($" Constructeur : ({string.Join(",", parms.Select(p => p.ParameterType.Name + "" + p.Name))})");}// Explorer les methodes publiques de l'interface PreferenceAggregatorvar aggInterface = prefsAsm.GetType("org.tweetyproject.preferences.aggregation.PreferenceAggregator");if(aggInterface !=null){ Console.WriteLine("\n--- API : PreferenceAggregator (interface) ---");foreach(var m in aggInterface.GetMethods()) Console.WriteLine($" methode : {m.ReturnType.Name} {m.Name}({string.Join(",", m.GetParameters().Select(p => p.ParameterType.Name))})");}// Enum Relationvar relType = prefsAsm.GetType("org.tweetyproject.preferences.Relation");if(relType !=null&& relType.IsEnum){ Console.WriteLine("\n--- Enum Relation ---");foreach(var val in Enum.GetValues(relType)) Console.WriteLine($" {val}");}Console.WriteLine("\nExemple conceptuel : Election");Console.WriteLine(" Scenario : 3 votants, 4 candidats (A, B, C, D)");Console.WriteLine(" Votant 1 : A > B > C > D");Console.WriteLine(" Votant 2 : B > C > D > A");Console.WriteLine(" Votant 3 : C > D > A > B");Console.WriteLine(" -> Quel candidat devrait gagner selon Borda ? Condorcet ? Copeland ?");
--- 7.1 Exploration du module org.tweetyproject.preferences ---
Classes d'agregation :
[class] BordaScoringPreferenceAggregator
[class] BordaWeightVector
[class] DynamicBordaScoringPreferenceAggregator
[class] DynamicPluralityScoringPreferenceAggregator
[abstract] DynamicPreferenceAggregator
[abstract] DynamicScoringPreferenceAggregator
[class] DynamicVetoScoringPreferenceAggregator
[class] PluralityScoringPreferenceAggregator
[abstract] PreferenceAggregator
[abstract] ScoringPreferenceAggregator
[class] SinglePeakWeightVector
[class] SingleValeWeightVector
[class] VetoScoringPreferenceAggregator
[abstract] WeightVector
Classes de ranking :
Functions
LevelingFunction
RankingFunction
--- API : BordaScoringPreferenceAggregator ---
Constructeur : (Int32 i)
--- API : PreferenceAggregator (interface) ---
methode : PreferenceOrder aggregate(List)
Exemple conceptuel : Election
Scenario : 3 votants, 4 candidats (A, B, C, D)
Votant 1 : A > B > C > D
Votant 2 : B > C > D > A
Votant 3 : C > D > A > B
-> Quel candidat devrait gagner selon Borda ? Condorcet ? Copeland ?
Interpretation : Structure du module préférences
La sortie ci-dessus revele l’architecture du module org.tweetyproject.preferences :
Classes de base (representation) : - PreferenceOrder : Ordre de préférence principal (implemente BinaryRelation) - BinaryRelation / Relation : Relations binaires generiques (enum : LESS, LESS_EQUAL) - ranking.RankingFunction / LevelingFunction : Fonctions de classement et nivellement
Entrees/Sorties : - io.POParser / io.POWriter : Lecture et ecriture d’ordres de préférence au format texte
Aggregation : - PreferenceAggregator<T> : Interface generique avec méthode aggregate(List<PreferenceOrder<T>>) - ScoringPreferenceAggregator<T> : Base abstraite pour le scoring - BordaScoringPreferenceAggregator(int size) : Implementation de Borda (ctor verifie fonctionnel) - PluralityScoringPreferenceAggregator : Règle de pluralite - VetoScoringPreferenceAggregator : Règle de veto - WeightVector / BordaWeightVector : Vecteurs de poids pour le scoring
Mise a jour : - update.Update : Mise a jour dynamique des préférences (revision)
Note IKVM : Le paramètre de type <T> est efface par IKVM 8.15 dans les metadonnees .NET. L’instanciation se fait via Activator.CreateInstance(type, new object[] { size }) et l’acces aux méthodes via dynamic.
7.2 Règles d’Agregation de Préférences
Les règles de vote transforment un profil de préférences individuelles en un résultat collectif. Les principales règles implantees ou conceptualisees dans TweetyProject :
Règles de score : - Borda : Chaque position dans un classement donne des points (n-1 pour le 1er, 0 pour le dernier) - Plurality : Seul le premier choix compte
Règles de Condorcet : - Condorcet : Le gagnant bat tous les autres en duel - Copeland : Score = victoires en duels - defaites - Kemeny-Young : Minimise la distance aux préférences individuelles
Theoreme d’Arrow : Aucune règle d’agregation ne peut satisfaire simultanement : unicite, indépendance des alternatives non pertinentes, et non-dictature (pour 3+ alternatives).
Origine. Le theoreme d’impossibilite d’Arrow est demontre par Arrow, K. J. (1951), Social Choice and Individual Values, New York: Wiley (Cowles Foundation Monograph ; 2e ed. 1963) – résultat fondateur de la théorie du choix social pour lequel Arrow recoit le prix Nobel d’economie en 1972.
// --- 7.2 Regles d'Agregation : Borda / Condorcet / Copeland ---using System;using System.Collections.Generic;using System.Linq;Console.WriteLine("--- 7.2 Regles d'Agregation de Preferences ---");// Donnees : preferences de 5 votants sur 4 candidatsvar candidates =new List<string>{"A","B","C","D"};// Chaque liste represente le classement d'un votant (du meilleur au pire)var preferences =new List<List<string>>{new(){"A","B","C","D"},// Votant 1new(){"A","C","B","D"},// Votant 2new(){"B","C","D","A"},// Votant 3new(){"C","B","D","A"},// Votant 4new(){"D","C","B","A"},// Votant 5};Console.WriteLine($"Candidats : [{string.Join(",", candidates)}]");Console.WriteLine($"Nombre de votants : {preferences.Count}");Console.WriteLine("\nPreferences individuelles :");for(int i =0; i < preferences.Count; i++) Console.WriteLine($" Votant {i + 1} : {string.Join(">", preferences[i])}");// === Regle de Borda ===Console.WriteLine("\n--- Regle de Borda ---");var bordaScores = candidates.ToDictionary(c => c, c =>0);int n = candidates.Count;foreach(var pref in preferences)for(int rank =0; rank < pref.Count; rank++) bordaScores[pref[rank]]+=(n -1- rank);Console.WriteLine("Scores Borda (points) :");foreach(var kv in bordaScores.OrderByDescending(x => x.Value)) Console.WriteLine($" {kv.Key} : {kv.Value} points");var bordaWinner = bordaScores.OrderByDescending(x => x.Value).First().Key;Console.WriteLine($"Gagnant Borda : {bordaWinner}");// === Methode de Condorcet ===Console.WriteLine("\n--- Methode de Condorcet ---");intPairwiseCount(string c1,string c2, List<List<string>> prefs){int count =0;foreach(var pref in prefs){if(pref.IndexOf(c1)< pref.IndexOf(c2)) count++;}return count;}var condorcetMatrix =new Dictionary<string, Dictionary<string,int>>();foreach(var c1 in candidates){ condorcetMatrix[c1]=new Dictionary<string,int>();foreach(var c2 in candidates)if(c1 != c2) condorcetMatrix[c1][c2]=PairwiseCount(c1, c2, preferences);}Console.WriteLine("Matrice des duels (lignes battent colonnes) :");Console.Write(" ");foreach(var c in candidates) Console.Write($"{c,2} ");Console.WriteLine();foreach(var c1 in candidates){ Console.Write($" {c1} ");foreach(var c2 in candidates){if(c1 == c2) Console.Write(" - ");else Console.Write($"{condorcetMatrix[c1][c2],2} ");} Console.WriteLine();}string condorcetWinner =null;foreach(var c in candidates){bool winsAll =true;foreach(var other in candidates){if(other == c)continue;if(condorcetMatrix[c][other]<= preferences.Count/2){ winsAll =false;break;}}if(winsAll){ condorcetWinner = c;break;}}Console.WriteLine(condorcetWinner !=null? $"Gagnant de Condorcet : {condorcetWinner} (bat tous les autres en duel)":"Pas de gagnant de Condorcet (cycle de Condorcet probable)");// === Regle de Copeland ===Console.WriteLine("\n--- Regle de Copeland ---");var copelandScores = candidates.ToDictionary(c => c, c =>0);foreach(var c1 in candidates)foreach(var c2 in candidates)if(c1 != c2){if(condorcetMatrix[c1][c2]> condorcetMatrix[c2][c1]) copelandScores[c1]++;elseif(condorcetMatrix[c1][c2]< condorcetMatrix[c2][c1]) copelandScores[c1]--;}Console.WriteLine("Scores Copeland (victoires - defaites) :");foreach(var kv in copelandScores.OrderByDescending(x => x.Value)) Console.WriteLine($" {kv.Key} : {kv.Value:+0;-0}");var copelandWinner = copelandScores.OrderByDescending(x => x.Value).First().Key;Console.WriteLine($"Gagnant Copeland : {copelandWinner}");Console.WriteLine("\n[OK] Simulation Borda + Condorcet + Copeland complete.");
--- 7.2 Regles d'Agregation de Preferences ---
Candidats : [A, B, C, D]
Nombre de votants : 5
Preferences individuelles :
Votant 1 : A > B > C > D
Votant 2 : A > C > B > D
Votant 3 : B > C > D > A
Votant 4 : C > B > D > A
Votant 5 : D > C > B > A
--- Regle de Borda ---
Scores Borda (points) :
C : 10 points
B : 9 points
A : 6 points
D : 5 points
Gagnant Borda : C
--- Methode de Condorcet ---
Matrice des duels (lignes battent colonnes) :
A B C D
A - 2 2 2
B 3 - 2 4
C 3 3 - 4
D 3 1 1 -
Gagnant de Condorcet : C (bat tous les autres en duel)
--- Regle de Copeland ---
Scores Copeland (victoires - defaites) :
C : +3
B : +1
D : -1
A : -3
Gagnant Copeland : C
[OK] Simulation Borda + Condorcet + Copeland complete.
Analyse comparative des règles de vote
Les résultats ci-dessus illustrent la coherence entre différentes méthodes d’agregation :
Convergence des résultats : - Borda : C gagne avec 10 points (consensus) - Condorcet : C est le gagnant absolu (bat tous les autres en duel) - Copeland : C a le score maximal +3 (3 victoires, 0 defaite)
Dans cet exemple, les trois règles convergent vers le même gagnant (C), ce qui renforce la legitimite du résultat.
Matrice des duels – lecture :
A B C D
C 3 3 - 4 <- C bat A (3-2), B (3-2), D (4-1)
C bat A : 3 votants preferent C a A, 2 preferent A a C
C bat B : 3 votants preferent C a B, 2 preferent B a C
C bat D : 4 votants preferent C a D, 1 prefere D a C
Cas interessants : 1. Gagnant de Condorcet : C existe ici, mais n’existe pas toujours (cycles possibles) 2. Scores Borda : C (10) > B (9) > A (6) > D (5) – l’ecart B-C est faible 3. Paradoxe potentiel : Si on ajoutait un 6e votant avec préférences stratégiques, le résultat pourrait changer
Theoreme d’Arrow : Ce résultat coherent ne contredit pas Arrow : le theoreme concerne l’impossibilite de satisfaire TOUS les critères pour TOUS les profils de préférences.
7.3 Lien avec l’Argumentation
Les préférences sont liees a l’argumentation de plusieurs facons :
Préférences sur arguments : Ordre de préférence sur la credibilite des arguments
Argumentation sociale : Les votes dans les SAF sont une forme d’agregation de préférences
Resolution de conflits : Les préférences permettent de trancher entre extensions
Dans TweetyProject, le module arg.dung (cf. Tweety-3-Dung-Csharp.ipynb) fournit le cadre d’argumentation abstraite de Dung. On charge la DLL tweety-dung.dll pour cette demonstration.
Rappel : la DLL org.tweetyproject.tweety-dung.dll couvre le module arg-dung (DungTheory, Argument, Attack, SimpleGroundedReasoner, SimplePreferredReasoner). Elle est distincte de la DLL préférences mais cohabite dans le même runtime IKVM.
// --- 7.3 Lien avec l'Argumentation : demo Dung ---using System;using System.IO;using System.Reflection;using System.Linq;Console.WriteLine("--- 7.3 Lien avec l'Argumentation ---");// Charger la DLL Dung (module arg-dung, distinct du module preferences)string dungDllPath = Path.Combine(Environment.CurrentDirectory,"org.tweetyproject.tweety-dung.dll");if(File.Exists(dungDllPath)){ Assembly dungAsm = Assembly.LoadFrom(dungDllPath); Console.WriteLine("DLL Dung chargee : "+ dungAsm.GetName().Name+" v"+ dungAsm.GetName().Version);// Types Dung (IKVM : utiliser using direct car les types sont exposes)// La DLL tweety-dung expose ses types via le namespace org.tweetyproject.arg.dung.*var dungTypes = dungAsm.GetTypes().Where(t => t.Namespace!=null&& t.Namespace.StartsWith("org.tweetyproject.arg.dung.syntax")).Select(t => t.Name).ToList(); Console.WriteLine($"Types arg.dung.syntax disponibles : {dungTypes.Count}");}else{ Console.WriteLine("ATTENTION : org.tweetyproject.tweety-dung.dll non trouvee a "+ dungDllPath); Console.WriteLine("La demo Dung ci-dessous necessite cette DLL (cf. Tweety-3-Dung-Csharp.ipynb).");}// Scenario : Preferences sur ArgumentsConsole.WriteLine("\n--- Scenario : Preferences sur Arguments ---");Console.WriteLine(@"Un debat avec 3 arguments :- A :""Le projet est rentable""(soutenu par le directeur financier)- B :""Le projet est risque""(soutenu par l'expert technique)- C :""Le risque est acceptable""(soutenu par le consultant)Relations :- A attaque B(rentabilite vs risque)- B attaque A(risque vs rentabilite)- C attaque B(minimise le risque)Question : Comment les preferences des decideurs influencent le resultat ?");// Construction du framework Dung via reflection (types IKVM)// DungTheory, Argument, Attack, SimpleGroundedReasoner, SimplePreferredReasoner// Notes : les types sont generiques-effaces mais exposent des constructeurs publics simplestry{var argType = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).FirstOrDefault(t => t.FullName=="org.tweetyproject.arg.dung.syntax.Argument");var dungType = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).FirstOrDefault(t => t.FullName=="org.tweetyproject.arg.dung.syntax.DungTheory");var attackType = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).FirstOrDefault(t => t.FullName=="org.tweetyproject.arg.dung.syntax.Attack");if(argType !=null&& dungType !=null&& attackType !=null){ dynamic theory = Activator.CreateInstance(dungType); dynamic a = Activator.CreateInstance(argType,newobject[]{"rentable"}); dynamic b = Activator.CreateInstance(argType,newobject[]{"risque"}); dynamic c = Activator.CreateInstance(argType,newobject[]{"risque_acceptable"}); theory.add(a); theory.add(b); theory.add(c); dynamic atk1 = Activator.CreateInstance(attackType,newobject[]{ a, b }); dynamic atk2 = Activator.CreateInstance(attackType,newobject[]{ b, a }); dynamic atk3 = Activator.CreateInstance(attackType,newobject[]{ c, b }); theory.add(atk1); theory.add(atk2); theory.add(atk3); Console.WriteLine("Framework : "+ theory);// Calculer les extensions via les reasonersvar groundedType = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).FirstOrDefault(t => t.FullName=="org.tweetyproject.arg.dung.reasoner.SimpleGroundedReasoner");var preferredType = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).FirstOrDefault(t => t.FullName=="org.tweetyproject.arg.dung.reasoner.SimplePreferredReasoner");if(groundedType !=null){ dynamic grReasoner = Activator.CreateInstance(groundedType); dynamic grounded = grReasoner.getModel(theory); Console.WriteLine("\nExtension Grounded : "+ grounded);}if(preferredType !=null){ dynamic prefReasoner = Activator.CreateInstance(preferredType); dynamic preferred = prefReasoner.getModels(theory); Console.WriteLine("Extensions Preferees : "+ preferred);} Console.WriteLine(@"Interpretation :- Sans preferences, l'extension grounded inclut C(risque_acceptable)- C defend indirectement A contre B- Si les decideurs preferent les arguments de l'expert technique(B), ils pourraient utiliser un framework pondere(WAF) pour ajuster les poids- Voir Tweety-7a pour l'implementation des WAF");}else{ Console.WriteLine("[INFO] Types Dung non accessibles via reflexion. Voir Tweety-3-Dung-Csharp pour la demo complete.");}}catch(Exception ex){ Console.WriteLine("Erreur demo Dung : "+ ex.GetType().Name+" : "+ ex.Message); Console.WriteLine("Voir Tweety-3-Dung-Csharp.ipynb pour la demo argumentation complete.");}
--- 7.3 Lien avec l'Argumentation ---
DLL Dung chargee : org.tweetyproject.tweety-dung v1.30.0.0
Types arg.dung.syntax disponibles : 11
--- Scenario : Preferences sur Arguments ---
Un debat avec 3 arguments :
- A : "Le projet est rentable" (soutenu par le directeur financier)
- B : "Le projet est risque" (soutenu par l'expert technique)
- C : "Le risque est acceptable" (soutenu par le consultant)
Relations :
- A attaque B (rentabilite vs risque)
- B attaque A (risque vs rentabilite)
- C attaque B (minimise le risque)
Question : Comment les preferences des decideurs influencent le resultat ?
Framework : <{ rentable, risque, risque_acceptable },[(rentable,risque), (risque_acceptable,risque), (risque,rentable)]>
Extension Grounded : {rentable,risque_acceptable}
Extensions Preferees : [{rentable,risque_acceptable}]
Interpretation :
- Sans preferences, l'extension grounded inclut C (risque_acceptable)
- C defend indirectement A contre B
- Si les decideurs preferent les arguments de l'expert technique (B),
ils pourraient utiliser un framework pondere (WAF) pour ajuster les poids
- Voir Tweety-7a pour l'implementation des WAF
Resume
Ce notebook a couvert la théorie des préférences et son lien avec l’argumentation :
Concept
Description
Implementation
Ordres de préférence
Relations binaires transitives sur alternatives
PreferenceOrder<T> (DLL IKVM)
Borda
Score par position dans le classement
Simulation C# (LINQ)
Condorcet
Gagnant qui bat tous les autres en duel
Matrice des duels
Copeland
Score = victoires - defaites en duels
Simulation C# (LINQ)
Theoreme d’Arrow
Impossibilite d’agregation parfaite
Résultat théorique
Lien argumentation
Préférences pour trancher les conflits
DungTheory (tweety-dung.dll)
Points cles
Borda favorise les candidats consensuels (bien classes par tous)
Condorcet cherche un gagnant absolu (peut ne pas exister – cycles)
Copeland est robuste quand il n’y a pas de gagnant de Condorcet
Les préférences sur arguments permettent de resoudre les conflits dans les frameworks abstracts
Conclusion de la Serie Tweety (port C# / .NET natif)
Cette serie couvre les principaux modules de TweetyProject 1.30 en C#/.NET natif via IKVM :
#
Notebook C#
Thème
Modules
2
Basic-Logics
Logique propositionnelle et FOL
logics.pl, logics.fol
3
Dung
Argumentation abstraite
arg.dung
3
ModalLogic
Logique modale
logics.ml
3
QBF
QBF
logics.qbf
3
Conditional-Logics
Logiques conditionnelles
logics.cl
4
Aspic
Argumentation structuree ASPIC+
arg.aspic
4
Belief-Revision
Revision de croyances
beliefdynamics
7b
Ranking-Probabilistic
Classement, probabilites
arg.rankings, arg.prob
9
Préférences (ce notebook)
Agregation, vote
préférences
10
MLN
Markov Logic Networks
logics.mln
Limitations connues
ADF : Necessite solveur SAT natif (non disponible via IKVM)
FOL avec egalite : Utiliser EProver pour eviter les timeouts
Generiques IKVM : Les types paramètres <T> sont effaces ; utiliser dynamic + reflexion
Concevez un système de vote pour un jury de 5 membres evaluant 4 projets.
Objectifs
Modeliser les préférences individuelles des jurys
Appliquer différentes règles de vote (Borda, Condorcet, Copeland)
Analyser les paradoxes et cycles potentiels
Proposer une méthode de resolution des conflits
Instructions
Definition : Créez une liste de 4 projets et un profil de préférence pour 5 membres du jury.
Calcul de score : Implantez la règle de Borda (chaque position donne des points decroissants).
Matrice de duels : Construisez la matrice de Condorcet pour comparer les projets deux a deux.
Detection de cycles : Identifiez si un cycle de Condorcet existe (paradoxe du vote).
Aggregation : Calculez les scores de Copeland (victoires - defaites en duels).
Questions d’analyse
Les différentes méthodes donnent-elles le même gagnant ?
Si non, quelle méthode est la plus equitable dans ce contexte ?
Comment gerer les ex aequo ?
Que se passe-t-il si un membre du jury a des préférences non transitives ?
// --- Exemple guide : Solution complete -- Systeme de vote pour un jury ---using System;using System.Collections.Generic;using System.Linq;var projets =new List<string>{"Projet_A","Projet_B","Projet_C","Projet_D"};var preferencesJury =new List<List<string>>{new(){"Projet_A","Projet_B","Projet_C","Projet_D"},// Membre 1 : expert techniquenew(){"Projet_B","Projet_C","Projet_D","Projet_A"},// Membre 2 : expert financiernew(){"Projet_C","Projet_D","Projet_A","Projet_B"},// Membre 3 : representant utilisateurnew(){"Projet_D","Projet_A","Projet_B","Projet_C"},// Membre 4 : responsable environnementalnew(){"Projet_A","Projet_C","Projet_B","Projet_D"},// Membre 5 : president du jury};// --- Fonctions de vote ---Dictionary<string,int>BordaScore(List<List<string>> prefs, List<string> cands){var scores = cands.ToDictionary(c => c, c =>0);int nn = cands.Count;foreach(var pref in prefs)for(int rank =0; rank < pref.Count; rank++) scores[pref[rank]]+=(nn -1- rank);return scores;}Dictionary<string, Dictionary<string,int>>CondorcetMatrix(List<List<string>> prefs, List<string> cands){var matrix =new Dictionary<string, Dictionary<string,int>>();foreach(var c1 in cands){ matrix[c1]=new Dictionary<string,int>();foreach(var c2 in cands)if(c2 != c1) matrix[c1][c2]=0;}foreach(var pref in prefs)for(int i =0; i < pref.Count; i++)for(int j = i +1; j < pref.Count; j++) matrix[pref[i]][pref[j]]++;return matrix;}boolDetectCycle(Dictionary<string, Dictionary<string,int>> matrix){var cands = matrix.Keys.ToList();var edges = cands.ToDictionary(c => c, c =>new List<string>());foreach(var c1 in cands)foreach(var(c2, votes)in matrix[c1])if(votes > matrix[c2].GetValueOrDefault(c1,0)) edges[c1].Add(c2);var visited =new HashSet<string>();var recStack =new HashSet<string>();boolDfs(string node){ visited.Add(node); recStack.Add(node);foreach(var neighbor in edges[node]){if(!visited.Contains(neighbor)){if(Dfs(neighbor))returntrue;}elseif(recStack.Contains(neighbor))returntrue;} recStack.Remove(node);returnfalse;}foreach(var node in cands)if(!visited.Contains(node))if(Dfs(node))returntrue;returnfalse;}// --- Affichage des preferences ---Console.WriteLine("Preferences du jury :");for(int i =0; i < preferencesJury.Count; i++) Console.WriteLine($" Membre {i + 1} : {string.Join(">", preferencesJury[i])}");// --- Calculs ---var scoresBorda =BordaScore(preferencesJury, projets);var gagnantBorda = scoresBorda.OrderByDescending(x => x.Value).First().Key;var mat =CondorcetMatrix(preferencesJury, projets);int nVotants = preferencesJury.Count;string gagnantCondorcet =null;foreach(var p in projets){bool winsAll =true;foreach(var autre in projets){if(autre == p)continue;if(mat[p].GetValueOrDefault(autre,0)<= nVotants /2){ winsAll =false;break;}}if(winsAll){ gagnantCondorcet = p;break;}}var scoresCopeland = projets.ToDictionary(p => p, p =>0);foreach(var p1 in projets)foreach(var p2 in projets)if(p1 != p2){if(mat[p1][p2]> mat[p2][p1]) scoresCopeland[p1]++;elseif(mat[p1][p2]< mat[p2][p1]) scoresCopeland[p1]--;}var gagnantCopeland = scoresCopeland.OrderByDescending(x => x.Value).First().Key;bool cycle =DetectCycle(mat);// --- Resultats ---Console.WriteLine("\nComparaison des methodes de vote :");var bordaSorted =string.Join(", ", scoresBorda.OrderByDescending(x => x.Value).Select(x => $"{x.Key}={x.Value}"));Console.WriteLine($"Borda : {gagnantBorda} (scores : {{{bordaSorted}}})");Console.WriteLine($"Condorcet : {(gagnantCondorcet ?? "Pas de gagnant(cycle detecte)")}");var copelandSorted =string.Join(", ", scoresCopeland.OrderByDescending(x => x.Value).Select(x => $"{x.Key}={x.Value:+0;-0}"));Console.WriteLine($"Copeland : {gagnantCopeland} (scores : {{{copelandSorted}}})");Console.WriteLine($"Cycle de Condorcet detecte : {cycle}");Console.WriteLine("\nMatrice des duels :");Console.Write(" ");foreach(var p in projets) Console.Write($"{p[^1],2} ");Console.WriteLine();foreach(var p1 in projets){ Console.Write($" {p1[^1]} ");foreach(var p2 in projets){if(p1 == p2) Console.Write(" - ");else Console.Write($"{mat[p1][p2],2} ");} Console.WriteLine();}Console.WriteLine("\n[OK] Exemple guide jury : Borda + Condorcet + Copeland + detection cycle.");
Preferences du jury :
Membre 1 : Projet_A > Projet_B > Projet_C > Projet_D
Membre 2 : Projet_B > Projet_C > Projet_D > Projet_A
Membre 3 : Projet_C > Projet_D > Projet_A > Projet_B
Membre 4 : Projet_D > Projet_A > Projet_B > Projet_C
Membre 5 : Projet_A > Projet_C > Projet_B > Projet_D
Comparaison des methodes de vote :
Borda : Projet_A (scores : {Projet_A=9, Projet_C=8, Projet_B=7, Projet_D=6})
Condorcet : Pas de gagnant (cycle detecte)
Copeland : Projet_A (scores : {Projet_A=+1, Projet_B=+1, Projet_C=-1, Projet_D=-1})
Cycle de Condorcet detecte : True
Matrice des duels :
A B C D
A - 4 3 2
B 1 - 3 3
C 2 2 - 4
D 3 2 1 -
[OK] Exemple guide jury : Borda + Condorcet + Copeland + detection cycle.
Reponses aux questions d’analyse
Les différentes méthodes donnent-elles le même gagnant ?
Non. Borda identifie Projet_A comme vainqueur (9 pts).
Copeland aboutit a une egalite entre Projet_A et Projet_B (+1).
Condorcet ne donne aucun vainqueur car un cycle a ete detecte (A > B > C > D > A).
Si non, quelle méthode est la plus equitable dans ce contexte ?
Dans ce scénario de cycle, la méthode de Borda semble la plus robuste car elle reflete l’intensite globale des préférences. Copeland est également une bonne alternative mais ne permet pas de trancher ici.
Comment gerer les ex aequo ?
On peut utiliser le vote du President du jury (Membre 5) comme departage. Ici, le Membre 5 prefere A a B, ce qui confirmerait A comme vainqueur.
Que se passe-t-il si un membre du jury a des préférences non transitives ?
Cela brise l’hypothese de rationalite individuelle. En pratique, cela rendrait impossible la modelisation par un ordre de préférence strict et augmenterait l’instabilite du vote collectif.
Exercice 1 : Système de vote etendu
Etendez le système de vote a 6 candidats et testez si le paradoxe de Condorcet apparait.
Indices : - Ajoutez Tokyo et Sydney aux candidats, puis comparez Condorcet vs Borda. - Generez de nouvelles préférences pour 5 votants sur ces 6 candidats. - Verifiez si le classement change selon la méthode utilisee.
Stub C.1-conforme : pas de throw new NotImplementedException() – le notebook doit s’executer end-to-end. L’etudiant remplace le stub par son implementation.
// --- Exercice 1 : Systeme de vote etendu (6 candidats) ---// TODO etudiant : ajoutez 2 candidats (Tokyo, Sydney) et testez la robustesse//// Etape 1 : ajouter Tokyo et Sydney aux candidats// Etape 2 : generer de nouvelles preferences pour 5 votants sur 6 candidats// Etape 3 : verifier si le classement change selon la methode (Borda vs Condorcet vs Copeland)// Etape 4 : detecter un cycle de Condorcet eventuel// Indice : reutiliser les fonctions BordaScore, CondorcetMatrix, DetectCycle definies ci-dessus.var candidatsEtendus =new List<string>{"Paris","Berlin","Rome","Madrid"};// TODO etudiant : ajouter Tokyo, Sydneyvar preferencesEtendues =new List<List<string>>();// TODO etudiant : 5 profils de preference sur 6 candidatsConsole.WriteLine("Exercice a completer");
Exercice a completer
Exercice 2 : Agregation Borda avec l’API native de Tweety
Les règles de vote ont ete simulees en pur C#. Tweety fournit une vraie API : PreferenceOrder et BordaScoringPreferenceAggregator. Utilisez-la.
Indices :
La DLL expose BordaScoringPreferenceAggregator(int size) – verifie en cellule 4.
La méthode aggregate(List<PreferenceOrder>) retourne un PreferenceOrder agrege.
IKVM efface les generiques : utiliser dynamic et Activator.CreateInstance.
L’enum Relation a deux valeurs : LESS et LESS_EQUAL.
Explorer l’API par reflexion : bordaType.GetMethods() pour lister les méthodes disponibles.
Note technique : la creation d’un PreferenceOrder fonctionnel requiert l’ajout de paires via addPair(f, s, relation). La méthode aggregate appelle getLevelingFunction() qui resout un problème d’optimisation (module math).
// --- Exercice 2 : Agregation Borda avec l'API native de Tweety ---// TODO etudiant : agregez des ordres de preference avec les classes Java de Tweety via IKVM//// Etape 1 : localiser les types par reflexion// var bordaType = prefsAsm.GetType("org.tweetyproject.preferences.aggregation.BordaScoringPreferenceAggregator");// var poType = prefsAsm.GetType("org.tweetyproject.preferences.PreferenceOrder");// var relType = prefsAsm.GetType("org.tweetyproject.preferences.Relation");//// Etape 2 : creer un BordaScoringPreferenceAggregator(3) via Activator.CreateInstance// Etape 3 : construire au moins 2 PreferenceOrder individuels (addPair pour chaque relation)// Etape 4 : mettre dans une java.util.ArrayList et appeler aggregate(...)// Etape 5 : afficher l'ordre collectif resultantdynamic aggregator =null;// TODO etudiant : remplacer par le BordaScoringPreferenceAggregatordynamic resultat =null;// TODO etudiant : remplacer par l'ordre collectif agregeConsole.WriteLine("Exercice a completer");
Exercice a completer
Exercice 3 : Trancher un conflit argumentatif par les préférences
La section 7.3 a montre le lien préférences <-> argumentation : quand deux arguments s’attaquent symetriquement, la sémantique grounded reste indecise. Une préférence permet de trancher.
Contexte
eco : “Privilegier l’economie” (attaque env)
env : “Privilegier l’environnement” (attaque eco)
En grounded, ni l’un ni l’autre n’est accepte. Ajoutez pref_env -> eco pour trancher.
Indices :
SimplePreferredReasoner().getModels(theory) liste les extensions preferred.
Ajouter pref_env = Argument("pref_env") puis theory.add(Attack(pref_env, eco)).
Charger la DLL Dung si ce n’est pas déjà fait (cf. cellule 13).
Stub C.1-conforme : pas de throw. L’etudiant remplace le stub par son implementation.
// --- Exercice 3 : Trancher un conflit argumentatif par les preferences ---// TODO etudiant : utilisez une preference pour resoudre un conflit symetrique//// Etape 1 : construire le DungTheory symetrique (eco <-> env) et calculer grounded (vide attendu)// Etape 2 : lister les extensions preferred ({eco} et {env})// Etape 3 : ajouter pref_env -> eco et recalculer grounded// Etape 4 (bonus) : modeliser eco > env et observer le basculement// Indice : utiliser Activator.CreateInstance pour Argument, Attack, DungTheory,// SimpleGroundedReasoner, SimplePreferredReasoner (cf. cellule 13 pour le pattern).dynamic theory =null;// TODO etudiant : remplacer par le DungTheory symetriquedynamic groundedAvant =null;// TODO etudiant : grounded avant preference (attendu : vide)dynamic groundedApres =null;// TODO etudiant : grounded apres pref_env -> ecoConsole.WriteLine("Exercice a completer");