Préférences et Théorie du Vote en C# / .NET (port natif IKVM)

Navigation : <- Tweety-8-Agent-Dialogues | Index | Tweety-10-MLN-Csharp ->

Serie Tweety – port C#/.NET natif (EPIC #4667). Ce notebook exploite le module org.tweetyproject.preferences de TweetyProject sans JVM : la librairie Java est compilee vers un fat-jar Maven shade puis executee sur le runtime .NET via IKVM.


Objectifs pedagogiques

  1. Comprendre la representation des ordres de préférence
  2. Decouvrir les méthodes d’agregation de préférences (théorie du choix social)
  3. Explorer les règles de vote classiques (Borda, Condorcet, Copeland)
  4. Comprendre le lien entre préférences et argumentation

Prerequis

  • Runtime .NET 8.0+ avec kernel .net-csharp (cf. Tweety-3-Dung-Csharp.ipynb pour la configuration IKVM)
  • DLL compilee : dotnet-build/bin/Release/net8.0/org.tweetyproject.tweety-preferences.dll

Duree estimee : 30 minutes

Module TweetyProject couvert

  • 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);

void IkvmCopyMerge(string src, string dst)
{
    foreach (var d in Directory.GetDirectories(src, "*", SearchOption.AllDirectories))
        Directory.CreateDirectory(d.Replace(src, dst));
    foreach (var f in Directory.GetFiles(src, "*", SearchOption.AllDirectories))
    {
        var t = f.Replace(src, dst);
        Directory.CreateDirectory(Path.GetDirectoryName(t));
        File.Copy(f, t, overwrite: true);
    }
}

if (Directory.Exists(ikvmBaseAny) && Directory.Exists(ikvmArchDir))
{
    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"));
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 : " + (new FileInfo(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 instancie
var bordaType = allTypes.FirstOrDefault(t => t.Name == "BordaScoringPreferenceAggregator");
if (bordaType != null)
{
    try
    {
        dynamic testAggregator = Activator.CreateInstance(bordaType, new object[] { 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." );
DLL path : org.tweetyproject.tweety-preferences.dll
DLL size : 0.6 Mo
Assembly chargee : org.tweetyproject.tweety-preferences v1.30.0.0

=== Types exposes dans org.tweetyproject.preferences ===
  [   class] org.tweetyproject.preferences.aggregation.BordaScoringPreferenceAggregator
  [   class] org.tweetyproject.preferences.aggregation.BordaWeightVector
  [   class] org.tweetyproject.preferences.aggregation.DynamicBordaScoringPreferenceAggregator
  [   class] org.tweetyproject.preferences.aggregation.DynamicPluralityScoringPreferenceAggregator
  [interface] org.tweetyproject.preferences.aggregation.DynamicPreferenceAggregator
  [abstract] org.tweetyproject.preferences.aggregation.DynamicScoringPreferenceAggregator
  [   class] org.tweetyproject.preferences.aggregation.DynamicVetoScoringPreferenceAggregator
  [   class] org.tweetyproject.preferences.aggregation.PluralityScoringPreferenceAggregator
  [interface] org.tweetyproject.preferences.aggregation.PreferenceAggregator
  [abstract] org.tweetyproject.preferences.aggregation.ScoringPreferenceAggregator
  [   class] org.tweetyproject.preferences.aggregation.SinglePeakWeightVector
  [   class] org.tweetyproject.preferences.aggregation.SingleValeWeightVector
  [   class] org.tweetyproject.preferences.aggregation.VetoScoringPreferenceAggregator
  [interface] org.tweetyproject.preferences.aggregation.WeightVector
  [interface] org.tweetyproject.preferences.BinaryRelation
  [abstract] org.tweetyproject.preferences.BinaryRelation+__DefaultMethods
  [   class] org.tweetyproject.preferences.events.UpdateEvent
  [interface] org.tweetyproject.preferences.events.UpdateListener
  [   class] org.tweetyproject.preferences.events.UpdatePrinter
  [   class] org.tweetyproject.preferences.io.ParseException
  [   class] org.tweetyproject.preferences.io.POChanger
  [   class] org.tweetyproject.preferences.io.POParser
  [interface] org.tweetyproject.preferences.io.POParserConstants
  [   class] org.tweetyproject.preferences.io.POParserTokenManager
  [   class] org.tweetyproject.preferences.io.POWriter
  [   class] org.tweetyproject.preferences.io.SimpleCharStream
  [   class] org.tweetyproject.preferences.io.Token
  [   class] org.tweetyproject.preferences.io.TokenMgrError
  [   class] org.tweetyproject.preferences.io.UPParser
  [interface] org.tweetyproject.preferences.io.UPParserConstants
  [   class] org.tweetyproject.preferences.io.UPParserTokenManager
  [   class] org.tweetyproject.preferences.Operation
  [    enum] org.tweetyproject.preferences.Operation+__Enum
  [   class] org.tweetyproject.preferences.plugin.PreferencesPlugin
  [   class] org.tweetyproject.preferences.PreferenceOrder
  [   class] org.tweetyproject.preferences.PreferencesIntegerBugExample
  [   class] org.tweetyproject.preferences.Quadruple
  [abstract] org.tweetyproject.preferences.ranking.Functions
  [   class] org.tweetyproject.preferences.ranking.LevelingFunction
  [   class] org.tweetyproject.preferences.ranking.RankingFunction
  [   class] org.tweetyproject.preferences.Relation
  [    enum] org.tweetyproject.preferences.Relation+__Enum
  [   class] org.tweetyproject.preferences.update.Update
  [   class] org.tweetyproject.preferences.update.UpdateStream

Total : 44 types exposes (attendu : 15).

[OK] BordaScoringPreferenceAggregator(3) instancie avec succes.

IKVM runtime + Tweety preferences 1.30 charges.

Partie 7 : Préférences et Agregation

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'agregation
var 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 ranking
var 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 BordaScoringPreferenceAggregator
Console.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 PreferenceAggregator
var 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 Relation
var 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 candidats
var 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 1
    new() { "A", "C", "B", "D" },  // Votant 2
    new() { "B", "C", "D", "A" },  // Votant 3
    new() { "C", "B", "D", "A" },  // Votant 4
    new() { "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 ---");

int PairwiseCount(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]++;
            else if (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 :

  1. Préférences sur arguments : Ordre de préférence sur la credibilite des arguments
  2. Argumentation sociale : Les votes dans les SAF sont une forme d’agregation de préférences
  3. 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 Arguments
Console.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 simples
try
{
    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, new object[] { "rentable" });
        dynamic b = Activator.CreateInstance(argType, new object[] { "risque" });
        dynamic c = Activator.CreateInstance(argType, new object[] { "risque_acceptable" });

        theory.add(a); theory.add(b); theory.add(c);
        dynamic atk1 = Activator.CreateInstance(attackType, new object[] { a, b });
        dynamic atk2 = Activator.CreateInstance(attackType, new object[] { b, a });
        dynamic atk3 = Activator.CreateInstance(attackType, new object[] { c, b });
        theory.add(atk1); theory.add(atk2); theory.add(atk3);

        Console.WriteLine("Framework : " + theory);

        // Calculer les extensions via les reasoners
        var 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

  1. Borda favorise les candidats consensuels (bien classes par tous)
  2. Condorcet cherche un gagnant absolu (peut ne pas exister – cycles)
  3. Copeland est robuste quand il n’y a pas de gagnant de Condorcet
  4. 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

Navigation : <- Tweety-8-Agent-Dialogues | Index | Tweety-10-MLN-Csharp ->

Exemple guide : Système de Vote pour un Jury

Concevez un système de vote pour un jury de 5 membres evaluant 4 projets.

Objectifs

  1. Modeliser les préférences individuelles des jurys
  2. Appliquer différentes règles de vote (Borda, Condorcet, Copeland)
  3. Analyser les paradoxes et cycles potentiels
  4. Proposer une méthode de resolution des conflits

Instructions

  1. Definition : Créez une liste de 4 projets et un profil de préférence pour 5 membres du jury.
  2. Calcul de score : Implantez la règle de Borda (chaque position donne des points decroissants).
  3. Matrice de duels : Construisez la matrice de Condorcet pour comparer les projets deux a deux.
  4. Detection de cycles : Identifiez si un cycle de Condorcet existe (paradoxe du vote).
  5. 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 technique
    new() { "Projet_B", "Projet_C", "Projet_D", "Projet_A" },  // Membre 2 : expert financier
    new() { "Projet_C", "Projet_D", "Projet_A", "Projet_B" },  // Membre 3 : representant utilisateur
    new() { "Projet_D", "Projet_A", "Projet_B", "Projet_C" },  // Membre 4 : responsable environnemental
    new() { "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;
}

bool DetectCycle(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>();

    bool Dfs(string node)
    {
        visited.Add(node);
        recStack.Add(node);
        foreach (var neighbor in edges[node])
        {
            if (!visited.Contains(neighbor))
            {
                if (Dfs(neighbor)) return true;
            }
            else if (recStack.Contains(neighbor))
                return true;
        }
        recStack.Remove(node);
        return false;
    }

    foreach (var node in cands)
        if (!visited.Contains(node))
            if (Dfs(node)) return true;
    return false;
}

// --- 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]++;
            else if (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, Sydney
var preferencesEtendues = new List<List<string>>();  // TODO etudiant : 5 profils de preference sur 6 candidats

Console.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 resultant

dynamic aggregator = null;  // TODO etudiant : remplacer par le BordaScoringPreferenceAggregator
dynamic resultat = null;    // TODO etudiant : remplacer par l'ordre collectif agrege

Console.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.
  • SimpleGroundedReasoner().getModel(theory) calcule l’extension grounded (unique).
  • 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 symetrique
dynamic groundedAvant = null;      // TODO etudiant : grounded avant preference (attendu : vide)
dynamic groundedApres = null;      // TODO etudiant : grounded apres pref_env -> eco

Console.WriteLine("Exercice a completer");
Exercice a completer
Retour au sommet