Sémantiques de Classement et Argumentation Probabiliste

Navigation: ← Tweety-7a-Extended-Frameworks | Index | Tweety-8-Agent-Dialogues →


Objectifs pédagogiques

  1. Comprendre les sémantiques de classement (ranking) des arguments
  2. Comparer différentes approches: Categorizer, Burden-Based, Discussion-Based, Tuples*, SAF-Based
  3. Découvrir l’argumentation probabiliste avec distributions sur sous-graphes
  4. Maîtriser les loteries d’argumentation et les fonctions d’utilité

Prérequis

Exécutez d’abord Tweety-01-Setup-Python.ipynb pour configurer l’environnement JVM.

Durée estimée : 40 minutes

# JPype1 doit être pré-provisionné dans l'environnement (cf Tweety-01-Setup-Python.ipynb / requirements.txt).
# Installation conditionnelle en fallback uniquement si l'import échoue.
try:
    import jpype  # noqa: F401
    jpype_ok = True
except ImportError:
    import subprocess, sys as _sys
    subprocess.check_call([_sys.executable, '-m', 'pip', 'install', 'JPype1'])
    import jpype  # noqa: F401
    jpype_ok = True
print(f"JPype1 disponible: {jpype_ok}")
import sys
print(sys.executable)
print(sys.version)
JPype1 disponible: True
<USER_PATH>\AppData\Local\Microsoft\WindowsApps\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\python.exe
3.13.14 (tags/v3.13.14:fd17997, Jun 10 2026, 13:03:48) [MSC v.1944 64 bit (AMD64)]

Initialisation de la JVM et detection des outils

JPype1 installe, nous lancons la JVM avec les JARs Tweety et detectons les outils externes disponibles. Cette configuration est requise pour toutes les sections de classement et d’argumentation probabiliste.

# --- Initialisation JVM Tweety + Outils Externes ---
print("--- Verification JVM Tweety + Outils ---")
jvm_ready = False

import jpype
import jpype.imports
import os
import pathlib
import shutil
import platform

# === Configuration COMPLETE des outils externes ===
EXTERNAL_TOOLS = {
    "CLINGO": "",
    "SPASS": "",
    "EPROVER": "",
    "SAT_SOLVER_PYTHON": "",
    "MARCO": "",
}

def get_tool_path(tool_name):
    """Retourne le chemin valide d'un outil ou None."""
    path_str = EXTERNAL_TOOLS.get(tool_name, "")
    if not path_str:
        return None
    if shutil.which(path_str):
        return path_str
    path_obj = pathlib.Path(path_str)
    if path_obj.is_file():
        return str(path_obj.resolve())
    if path_obj.is_dir():
        return str(path_obj.resolve())
    return None

# --- Auto-detection des outils ---
system = platform.system()
exe_suffix = ".exe" if system == "Windows" else ""

# 1. Clingo (ASP solver) - Tweety attend le REPERTOIRE
for cp in [shutil.which("clingo"), pathlib.Path(f"ext_tools/clingo/clingo{exe_suffix}"),
           pathlib.Path(f"../ext_tools/clingo/clingo{exe_suffix}")]:
    if cp and (isinstance(cp, str) or cp.exists()):
        parent = pathlib.Path(cp).parent if isinstance(cp, str) else cp.parent
        EXTERNAL_TOOLS["CLINGO"] = str(parent.resolve())
        break

# 2. SPASS (Modal logic prover)
for sp in [shutil.which("SPASS"), pathlib.Path(f"ext_tools/spass/SPASS{exe_suffix}"),
           pathlib.Path(f"../ext_tools/spass/SPASS{exe_suffix}")]:
    if sp and (isinstance(sp, str) or sp.exists()):
        EXTERNAL_TOOLS["SPASS"] = str(pathlib.Path(sp).resolve()) if isinstance(sp, pathlib.Path) else sp
        break

# 3. EProver (FOL theorem prover)
for ep in [shutil.which("eprover"), pathlib.Path(f"../ext_tools/EProver/eprover{exe_suffix}"),
           pathlib.Path(f"ext_tools/EProver/eprover{exe_suffix}")]:
    if ep:
        ep_path = pathlib.Path(ep) if isinstance(ep, str) else ep
        if ep_path.exists():
            EXTERNAL_TOOLS["EPROVER"] = str(ep_path.resolve())
            break

# 4. SAT Solver Python (CaDiCaL, Glucose via pySAT)
for sat in [pathlib.Path("../ext_tools/sat_solver.py"), pathlib.Path("ext_tools/sat_solver.py")]:
    if sat.exists():
        EXTERNAL_TOOLS["SAT_SOLVER_PYTHON"] = str(sat.resolve())
        break

# 5. MARCO (MUS enumerator avec Z3)
for mp in [pathlib.Path("../ext_tools/marco.py"), pathlib.Path("ext_tools/marco.py")]:
    if mp.exists():
        EXTERNAL_TOOLS["MARCO"] = str(mp.resolve())
        break

# === Initialisation JVM ===
if jpype.isJVMStarted():
    print("JVM deja en cours d'execution.")
    jvm_ready = True
else:
    jdk_portable = None
    for jdk_path in [pathlib.Path("jdk-17-portable"), pathlib.Path("../Argument_Analysis/jdk-17-portable")]:
        if jdk_path.exists():
            zulu_dirs = list(jdk_path.glob("zulu*"))
            if zulu_dirs:
                jdk_portable = zulu_dirs[0]
                os.environ["JAVA_HOME"] = str(jdk_portable.resolve())
                print(f"JDK portable: {jdk_portable.name}")
                break

    if not os.environ.get("JAVA_HOME"):
        print("ERREUR: JAVA_HOME non defini et JDK portable non trouve.")
    else:
        LIB_DIR = pathlib.Path("libs")
        if not LIB_DIR.exists():
            LIB_DIR = pathlib.Path("../Argument_Analysis/libs")

        if LIB_DIR.exists():
            jar_files = list(LIB_DIR.glob("*.jar"))
            if jar_files:
                classpath = os.pathsep.join(str(j.resolve()) for j in jar_files)
                try:
                    jpype.startJVM(classpath=[classpath])
                    print(f"JVM demarree avec {len(jar_files)} JARs.")
                    jvm_ready = True
                except Exception as e:
                    print(f"Erreur demarrage JVM: {e}")

# === Resume des outils ===
if jvm_ready:
    print("\n--- Outils disponibles ---")
    for tool, path in EXTERNAL_TOOLS.items():
        if path:
            short_path = path.split(os.sep)[-1] if len(path) > 30 else path
            print(f"  {tool}: {short_path}")
    print(f"\nJVM prete. Outils: {sum(1 for t,p in EXTERNAL_TOOLS.items() if p)}/{len(EXTERNAL_TOOLS)}")
--- Verification JVM Tweety + Outils ---
JDK portable: zulu17.50.19-ca-jdk17.0.11-win_x64
JVM demarree avec 42 JARs.

--- Outils disponibles ---
  EPROVER: eprover.exe
  SAT_SOLVER_PYTHON: sat_solver.py
  MARCO: marco.py

JVM prete. Outils: 3/5

Contexte TweetyProject

Ce notebook utilise TweetyProject, une bibliothèque Java pour l’IA symbolique et l’argumentation formelle. L’initialisation ci-dessus configure :

Composants critiques : - JVM (Java Virtual Machine) : Nécessaire pour exécuter les classes TweetyProject - Fichiers JAR : Modules pour logique, argumentation, probabilités, SAT solvers - Outils externes (jusqu’à 5, détection automatique) : CLINGO (ASP), SPASS (modal), EProver (FOL), pySAT, MARCO

Détection automatique : Le script détecte automatiquement le JDK portable (Zulu 17) et les outils dans ext_tools/. Si un outil est manquant, certaines fonctionnalités seront désactivées (avec messages d’erreur explicites).

Note technique : La sortie JVM prete. Outils: 5/5 confirme que tous les outils requis pour ce notebook sont disponibles.


Partie 5b : Classement et Probabilités

Cette section explore des méthodes d’analyse qui vont au-delà de l’acceptation binaire, en établissant des classements entre arguments ou en intégrant des probabilités dans le raisonnement argumentatif.

5.7 Sémantiques Basées sur le Classement (Ranking)

Au lieu de simplement déterminer si un argument est accepté ou rejeté, les sémantiques de classement établissent un ordre entre les arguments, du plus “fort” au moins acceptable.

Origine. Les sémantiques basées sur le classement (ranking-based semantics) constituent une ligne de recherche distincte des sémantiques d’extension de Dung : au lieu de partitionner les arguments en acceptés/rejetés, elles produisent un préordre (ranking) reflétant la force relative de chaque argument. L’étude comparative de référence est Bonzon, E., Delobelle, J., Konieczny, S. & Maudet, N. (2016), A comparative study of ranking-based semantics for abstract argumentation, AAAI 2016, pp. 914-920 — qui axiomatise et compare les principales familles (categorizer, burden-based, discussion-based, tuples*, etc.) implémentées ci-dessous.

Principales approches implémentées dans Tweety: - Categorizer: Propagation de valeurs - Burden-Based / Discussion-Based: Comptage attaquants/défenseurs - Tuples*: Matrices de valeurs (Cayrol & Lagasquie-Schiex) - Strategy-Based: Basé sur les jeux (Matt & Toni) - SAF-Based: Approche sociale - Counting / Propagation: Sémantiques itératives

Contexte historique et motivations

Les sémantiques classiques de Dung (grounded, preferred, stable) produisent des ensembles d’extensions, mais ne fournissent aucun ordre entre les arguments. Cela limite leur utilité dans plusieurs contextes :

Problèmes avec l’acceptation binaire : 1. Décisions graduelles : Comment choisir entre deux arguments acceptés ? 2. Argumentation sceptique : Que faire si aucun argument n’est accepté dans toutes les extensions ? 3. Systèmes de recommandation : Besoin de scores pour trier les options 4. Dialogues : Les agents doivent prioriser leurs mouvements

Exemple motivant : Dans un débat médical, les sémantiques classiques peuvent dire que deux traitements A et B sont “acceptés”, mais ne permettent pas de dire que A est plus robuste que B (car attaqué par moins d’arguments ou défendu par des défenseurs plus forts).

Les sémantiques de classement résolvent ce problème en calculant un ordre partiel ou total sur les arguments, ou en assignant des scores numériques reflétant leur force argumentative.


# --- 5.7 Sémantiques Basées sur le Classement (Ranking) ---
print("\n--- 5.7 Semantiques Basees sur le Classement (Ranking) ---")

if not jvm_ready:
    print("ERREUR: JVM non demarree.")
else:
    print("JVM prete. Execution des exemples de Ranking Semantics...")
    ranking_imports_ok = False
    try:
        import jpype
        from jpype.types import *

        from org.tweetyproject.arg.dung.syntax import DungTheory, Argument, Attack
        from org.tweetyproject.arg.rankings.reasoner import (
             CategorizerRankingReasoner, BurdenBasedRankingReasoner, DiscussionBasedRankingReasoner,
             TuplesRankingReasoner, StrategyBasedRankingReasoner, SAFRankingReasoner,
             CountingRankingReasoner, PropagationRankingReasoner
        )
        from org.tweetyproject.arg.rankings.util import RankingTools
        from org.tweetyproject.arg.dung.semantics import Semantics
        from org.tweetyproject.math.probability import Probability

        print("Imports pour Ranking Semantics reussis.")
        ranking_imports_ok = True

        if ranking_imports_ok:
            print("\nDefinition des AAFs exemples...")

            # Example 1 (Bonzon et al. AAAI 2016)
            aaf_ex1 = DungTheory()
            args1 = {name: Argument(name) for name in "abcde"}
            for arg in args1.values(): aaf_ex1.add(arg)
            attacks1 = [("a","e"), ("d","a"), ("e","d"), ("c","e"), ("b","c"), ("b","a")]
            for s, t in attacks1: aaf_ex1.add(Attack(args1[s], args1[t]))
            print("   - AAF Example 1 (Bonzon) defini.")

            # Example 2 (Pu et al. CoRR 2015)
            aaf_ex2 = DungTheory()
            args2 = {f"x{i}": Argument(f"x{i}") for i in range(1, 5)}
            for arg in args2.values(): aaf_ex2.add(arg)
            attacks2 = [("x2","x3"), ("x2","x1"), ("x3","x2"), ("x3","x3"), ("x4","x2")]
            for s, t in attacks2: aaf_ex2.add(Attack(args2[s], args2[t]))
            print("   - AAF Example 2 (Pu) defini.")

            # Example 5 (Delobelle Thesis 2017)
            aaf_ex5 = DungTheory()
            args5 = {name: Argument(name) for name in "abcdefghij"}
            for arg in args5.values(): aaf_ex5.add(arg)
            attacks5 = [("a","b"), ("b","c"), ("b","f"), ("d","g"), ("d","f"),
                        ("e","h"), ("e","d"), ("e","i"), ("h","g"), ("j","i")]
            for s, t in attacks5: aaf_ex5.add(Attack(args5[s], args5[t]))
            print("   - AAF Example 5 (Delobelle) defini.")

            # Example 4a (Matt & Toni JELIA 2008)
            aaf_ex4a = DungTheory()
            args4a = {name: Argument(name) for name in "abcdefg"}
            for arg in args4a.values(): aaf_ex4a.add(arg)
            attacks4a = [("b","a"),("c","a"),("d","a"),("f","a"),("e","d"),("g","f")]
            for s, t in attacks4a: aaf_ex4a.add(Attack(args4a[s], args4a[t]))
            print("   - AAF Example 4a (Matt & Toni) defini.")

            print("\n--- Application des Raisonneurs de Classement ---")

            # 1. Categorizer
            print("\n* Categorizer:")
            try:
                r_cat = CategorizerRankingReasoner()
                rank_cat1 = r_cat.getModel(aaf_ex1)
                rank_cat2 = r_cat.getModel(aaf_ex2)
                print(f"  - AAF Ex1: {RankingTools.roundRanking(rank_cat1, 2)}")
                print(f"  - AAF Ex2: {RankingTools.roundRanking(rank_cat2, 3)}")
            except Exception as e: print(f"   Erreur Categorizer: {e}")

            # 2. Burden-Based
            print("\n* Burden-Based:")
            try:
                r_bb = BurdenBasedRankingReasoner()
                rank_bb1 = r_bb.getModel(aaf_ex1)
                print(f"  - AAF Ex1: {rank_bb1}")
            except Exception as e: print(f"   Erreur BurdenBased: {e}")

            # 3. Discussion-Based
            print("\n* Discussion-Based:")
            try:
                r_db = DiscussionBasedRankingReasoner()
                rank_db1 = r_db.getModel(aaf_ex1)
                print(f"  - AAF Ex1: {rank_db1}")
            except Exception as e: print(f"   Erreur DiscussionBased: {e}")

            # 4. Tuples*
            print("\n* Tuples*:")
            try:
                r_tup = TuplesRankingReasoner()
                rank_tup5 = r_tup.getModel(aaf_ex5)
                print(f"  - AAF Ex5: {rank_tup5}")
            except Exception as e: print(f"   Erreur Tuples: {e}")

            # 5. Strategy-Based
            print("\n* Strategy-Based:")
            try:
                r_sb = StrategyBasedRankingReasoner()
                rank_sb4a = r_sb.getModel(aaf_ex4a)
                print(f"  - AAF Ex4a: {RankingTools.roundRanking(rank_sb4a, 3)}")
            except Exception as e: print(f"   Erreur StrategyBased: {e}")

            # 6. SAF-Based
            print("\n* SAF-Based (SimpleProduct):")
            try:
                r_saf = SAFRankingReasoner()
                rank_saf1 = r_saf.getModel(aaf_ex1)
                print(f"  - AAF Ex1: {RankingTools.roundRanking(rank_saf1, 2)}")
            except Exception as e: print(f"   Erreur SAFBased: {e}")

            # 7. Counting Semantics
            print("\n* Counting Semantics:")
            try:
                r_count = CountingRankingReasoner(0.98, 0.001)
                rank_count1 = r_count.getModel(aaf_ex1)
                print(f"  - AAF Ex1: {RankingTools.roundRanking(rank_count1, 2)}")
            except Exception as e: print(f"   Erreur Counting: {e}")

            # 8. Propagation
            print("\n* Propagation Semantics:")
            try:
                r_prop1 = PropagationRankingReasoner(0.75, False, PropagationRankingReasoner.PropagationSemantics.PROPAGATION1)
                rank_prop1_ex5 = r_prop1.getModel(aaf_ex5)
                print(f"  - AAF Ex5 (e=0.75): {rank_prop1_ex5}")
            except Exception as e: print(f"   Erreur Propagation: {e}")

    except ImportError as e: print(f"Erreur d'import pour Ranking Semantics : {e}.")
    except jpype.JException as e_java: print(f"Erreur Java generale Ranking: {e_java.message()}")
    except Exception as e_gen: print(f"Erreur Python inattendue Ranking: {e_gen}"); import traceback; traceback.print_exc()

--- 5.7 Semantiques Basees sur le Classement (Ranking) ---
JVM prete. Execution des exemples de Ranking Semantics...
Imports pour Ranking Semantics reussis.

Definition des AAFs exemples...
   - AAF Example 1 (Bonzon) defini.
   - AAF Example 2 (Pu) defini.
   - AAF Example 5 (Delobelle) defini.
   - AAF Example 4a (Matt & Toni) defini.

--- Application des Raisonneurs de Classement ---

* Categorizer:
  - AAF Ex1: {a=0.38, b=1.0, c=0.5, d=0.65, e=0.53}
  - AAF Ex2: {x1=0.716, x2=0.397, x3=0.521, x4=1.0}

* Burden-Based:
  - AAF Ex1: [b > d > c > e > a]

* Discussion-Based:
  - AAF Ex1: [b > d > c > e > a]

* Tuples*:
  - AAF Ex5: [j = e = a > g = f > c > h = d = b > i]

* Strategy-Based:
  - AAF Ex4a: {a=0.167, b=1.0, c=1.0, d=0.25, e=1.0, f=0.25, g=1.0}

* SAF-Based (SimpleProduct):
  - AAF Ex1: {a=0.17, b=0.48, c=0.25, d=0.33, e=0.3}

* Counting Semantics:
  - AAF Ex1: {a=0.18, b=1.0, c=0.51, d=0.68, e=0.66}

* Propagation Semantics:
  - AAF Ex5 (e=0.75): [j = e = a > c > h = d = b > f > g > i]

Interpretation des résultats

Les résultats ci-dessus montrent comment différentes sémantiques de classement produisent des ordres distincts sur le même framework :

Convergence des sémantiques (Ex1) : - Categorizer {a=0.38, b=1.0, c=0.5, d=0.65, e=0.53} et Counting {a=0.18, b=1.0, c=0.51, d=0.68, e=0.66} donnent des scores de même tendance : b est le plus fort (score 1.0), suivi de d, puis e, c et enfin a - Burden-Based et Discussion-Based convergent vers [b > d > c > e > a] — note : leur ordre place c avant e, alors que les scores numériques de Categorizer/Counting placent e legerement au-dessus de c. Les deux familles ne s’accordent donc pas sur la paire c/e

Observations cles : 1. L’argument b, non attaque dans Ex1, obtient le score maximal (1.0) dans toutes les sémantiques 2. L’argument a est le plus faible dans Ex1 (Categorizer 0.38, Counting 0.18) : il y est attaque par 2 arguments (d et b). Dans Ex4a, a est attaque par 4 arguments (b, c, d, f), d’ou son score Strategy-Based très bas (a=0.167) 3. Les sémantiques Tuples* revelent des equivalences : [j = e = a] dans Ex5 signifie que ces arguments sont incomparables 4. SAF-Based (Social) produit des scores plus faibles car elle considere l’agregation sociale des votes

Choix de sémantique : - Utiliser Categorizer pour des scores interpretables : une force d’acceptabilite dans [0, 1] (et non une probabilite) - Utiliser Burden-Based pour un ordre total simple - Utiliser Tuples* pour identifier les arguments equivalents

Exercice : Equivalences de classement (Tuples* et Counting)

L’exemple guide a compare Categorizer / Burden / Discussion. Les sémantiques Tuples* revelent des equivalences (arguments incomparables). Comparez sur un graphe symetrique.

Contexte

Debat symetrique : pour1, pour2 vs contre1, contre2. Attaques croisees.

Indices :

  • TuplesRankingReasoner().getModel(theory) puis CountingRankingReasoner(0.98, 0.001).getModel(theory)
  • Attendu (Tuples*) : pour1 ~ pour2 et contre1 ~ contre2
# --- Exercice : Equivalences de classement (Tuples* et Counting) ---
# TODO etudiant : comparez Tuples* et Counting sur un graphe symetrique
#
# Etape 1 : imports (cf section 5.7)
#   from org.tweetyproject.arg.dung.syntax import DungTheory, Argument, Attack
#   from org.tweetyproject.arg.rankings.reasoner import TuplesRankingReasoner, CountingRankingReasoner
#   from org.tweetyproject.arg.rankings.util import RankingTools
#
# Etape 2 : construire le DungTheory symetrique (pour1, pour2, contre1, contre2, 4 attaques croisees)
# Etape 3 : appliquer TuplesRankingReasoner et afficher les classes d'equivalence
# Etape 4 : appliquer CountingRankingReasoner(0.98, 0.001) et arrondir les scores
# Etape 5 (bonus) : ajouter neutre -> contre1 pour briser la symetrie et re-classer

if not jvm_ready:
    print("ERREUR: JVM non demarree.")
else:
    theory = None         # TODO etudiant : remplacer par le DungTheory symetrique
    rank_tuples = None    # TODO etudiant : remplacer par TuplesRankingReasoner().getModel(theory)
    rank_counting = None  # TODO etudiant : remplacer par CountingRankingReasoner(0.98, 0.001).getModel(theory)
    print("Exercice a completer")
Exercice a completer

5.8 Argumentation Probabiliste

L’argumentation probabiliste introduit l’incertitude dans les cadres d’argumentation:

  1. Distributions sur sous-graphes: Le graphe lui-même est incertain
  2. Acceptabilité probabiliste: Probabilité qu’un argument soit accepté
  3. Loteries d’argumentation: Décision sous incertitude avec utilités

Tweety implémente: - SubgraphProbabilityFunction: Distribution sur les sous-graphes possibles - getAcceptanceProbability(arg, semantics): Probabilité d’acceptation - Division: Partition IN/OUT/UNDEC - ArgumentationLottery, UtilityFunction: Décision et utilité attendue

Deux écoles. SubgraphProbabilityFunction relève de l’approche par constellations (Li, Oren & Norman, 2011) : l’incertitude porte sur la structure du graphe (quels arguments et attaques sont présents), et la probabilité d’acceptation d’un argument est la masse des sous-graphes dans lesquels il est accepté. Elle se distingue de l’approche épistémique (Hunter, 2013 ; Thimm, 2012), où le graphe est fixe et la probabilité exprime le degré de croyance d’un agent en chaque argument. Ce notebook illustre la première.

# --- 5.8 Argumentation Probabiliste ---
print("\n--- 5.8 Argumentation Probabiliste ---")

if not jvm_ready:
    print("ERREUR: JVM non demarree.")
else:
    print("JVM prete. Execution de l'exemple d'argumentation probabiliste...")
    prob_imports_ok = False
    try:
        import jpype
        from jpype.types import *
        from java.util import Collection, List as JavaList, Set as JavaSet

        from org.tweetyproject.arg.dung.syntax import DungTheory, Argument, Attack
        from org.tweetyproject.arg.dung.reasoner import AbstractExtensionReasoner, SimpleGroundedReasoner
        from org.tweetyproject.arg.dung.semantics import Extension, Semantics
        from org.tweetyproject.arg.dung.divisions import Division

        from org.tweetyproject.arg.prob.lotteries import SubgraphProbabilityFunction, ArgumentationLottery, UtilityFunction
        from org.tweetyproject.math.probability import Probability

        print("Imports pour Argumentation Probabiliste reussis.")
        prob_imports_ok = True

        if prob_imports_ok:
            print("\n1. Creation de l'AAF de base...")
            theory_prob = DungTheory()
            a = Argument("a"); b = Argument("b"); c = Argument("c")
            theory_prob.add(a); theory_prob.add(b); theory_prob.add(c)
            theory_prob.add(Attack(a, b)); theory_prob.add(Attack(b, a)); theory_prob.add(Attack(c, b))
            print("   AAF: ", theory_prob)

            print("\n2. Extensions Grounded (pour reference)...")
            reasoner_gr = SimpleGroundedReasoner()
            extensions_gr_coll = reasoner_gr.getModels(theory_prob)
            print("   Extensions Grounded:", extensions_gr_coll)

            print("\n3. Creation d'une fonction de probabilite uniforme sur les sous-graphes...")
            prob_function = SubgraphProbabilityFunction(theory_prob)
            print(f"   Nombre de sous-graphes possibles: {prob_function.size()}")

            print("\n4. Calcul des probabilites d'acceptation (Semantique Grounded)...")
            target_semantics = Semantics.GROUNDED_SEMANTICS
            try:
                prob_obj_a = prob_function.getAcceptanceProbability(a, target_semantics)
                prob_obj_b = prob_function.getAcceptanceProbability(b, target_semantics)
                prob_obj_c = prob_function.getAcceptanceProbability(c, target_semantics)
                val_a = prob_obj_a.doubleValue(); val_b = prob_obj_b.doubleValue(); val_c = prob_obj_c.doubleValue()
                print(f"   P(Accepte({a})) = {val_a:.4f}")
                print(f"   P(Accepte({b})) = {val_b:.4f}")
                print(f"   P(Accepte({c})) = {val_c:.4f}")

                print("\n   Calcul probabilite pour Division ({a, c}, {b})...")
                in_set = Extension(); in_set.add(a); in_set.add(c)
                out_set = Extension(); out_set.add(b)
                example_division = Division(in_set, out_set)
                prob_obj_div = prob_function.getAcceptanceProbability(example_division, target_semantics)
                val_div = prob_obj_div.doubleValue()
                print(f"   P(Division={example_division}) = {val_div:.4f}")

            except jpype.JException as e_prob_calc_java: print(f"   Erreur Java calcul probabilites: {e_prob_calc_java.message()}")
            except Exception as e_prob_calc: print(f"   Erreur calcul probabilites: {e_prob_calc}")

            print("\n5. Loteries d'Argumentation et Utilite Attendue...")
            try:
                standard_divisions_coll = Division.getStandardDivisions(theory_prob)
                print(f"   Divisions standard: {standard_divisions_coll}")

                if not standard_divisions_coll.isEmpty():
                    lottery = ArgumentationLottery(standard_divisions_coll, prob_function, target_semantics)
                    print("\n   Loterie basee sur les divisions standard:")
                    print(f"     {lottery}")

                    utility_func = UtilityFunction()
                    from java.util import ArrayList
                    standard_divisions_list = ArrayList(standard_divisions_coll)

                    Division_class = jpype.JClass("org.tweetyproject.arg.dung.divisions.Division")
                    if standard_divisions_list.size() >= 1: utility_func.put(JObject(standard_divisions_list.get(0), Division_class), 10.0)
                    if standard_divisions_list.size() >= 2: utility_func.put(JObject(standard_divisions_list.get(1), Division_class), -5.0)

                    if not utility_func.isEmpty():
                        print("\n   Fonction d'utilite exemple:")
                        print(f"     {utility_func}")
                        expected_utility = utility_func.getExpectedUtility(lottery)
                        print(f"\n   Utilite Attendue de la loterie: {expected_utility.doubleValue():.4f}")

            except jpype.JException as e_lottery_java: print(f"   Erreur Java (Loteries/Utilite): {e_lottery_java.message()}")
            except Exception as e_lottery_py: print(f"   Erreur Python (Loteries/Utilite): {e_lottery_py}")

    except ImportError as e: print(f"Erreur d'import Prob: {e}.")
    except jpype.JException as e_java: print(f"Erreur Java generale Prob: {e_java.message()}")
    except Exception as e_gen: print(f"Erreur Python inattendue Prob: {e_gen}"); import traceback; traceback.print_exc()

--- 5.8 Argumentation Probabiliste ---
JVM prete. Execution de l'exemple d'argumentation probabiliste...
Imports pour Argumentation Probabiliste reussis.

1. Creation de l'AAF de base...
   AAF:  <{ a, b, c },[(c,b), (a,b), (b,a)]>

2. Extensions Grounded (pour reference)...
   Extensions Grounded: [{a,c}]

3. Creation d'une fonction de probabilite uniforme sur les sous-graphes...
   Nombre de sous-graphes possibles: 19

4. Calcul des probabilites d'acceptation (Semantique Grounded)...
   P(Accepte(a)) = 0.5263
   P(Accepte(b)) = 0.3158
   P(Accepte(c)) = 0.6316

   Calcul probabilite pour Division ({a, c}, {b})...
   P(Division=({a,c}, {b})) = 0.3158

5. Loteries d'Argumentation et Utilite Attendue...
   Divisions standard: [({a,b}, {c}), ({c}, {a,b}), ({b}, {a,c}), ({a}, {b,c}), ({}, {a,b,c}), ({a,b,c}, {}), ({b,c}, {a}), ({a,c}, {b})]

   Loterie basee sur les divisions standard:
     [ 0.05263157894736842,({a,b}, {c}) ; 0.15789473684210525,({c}, {a,b}) ; 0.10526315789473684,({b}, {a,c}) ; 0.10526315789473684,({a}, {b,c}) ; 0.10526315789473684,({}, {a,b,c}) ; 0.05263157894736842,({a,b,c}, {}) ; 0.10526315789473684,({b,c}, {a}) ; 0.3157894736842105,({a,c}, {b}) ]

   Fonction d'utilite exemple:
     {({a,b}, {c})=10.0, ({c}, {a,b})=-5.0}

   Utilite Attendue de la loterie: -0.2632

Interpretation des résultats probabilistes

Les résultats illustrent comment l’incertitude sur le graphe d’argumentation affecte l’acceptabilite :

Distribution sur les sous-graphes : - Avec 3 arguments et 3 attaques, il y a 19 sous-graphes possibles (pas 2^3 = 8 car les attaques aussi varient) - La distribution uniforme donne une probabilite egale a chaque configuration

Probabilites d’acceptation : - P(c accepte) = 0.632 : L’argument c est le plus souvent accepte car il n’est jamais attaque - P(a accepte) = 0.526 : L’argument a est accepte dans plus de la moitie des configurations - P(b accepte) = 0.316 : L’argument b est le moins souvent accepte car il est attaque par a ET c

Loteries et utilite : - La division ({a,c}, {b}) a une probabilite de 0.316, correspondant au cas ou a et c sont IN et b est OUT - L’utilite attendue de -0.26 indique que la loterie est legerement defavorable selon la fonction d’utilite définie - Un agent rationnel peut utiliser ces valeurs pour choisir ses mouvements dans un dialogue

Application pratique : Cette approche est utile quand on ne connait pas avec certitude quels arguments ou attaques seront valides (ex: preuves a venir, temoignages incertains).

Exemple guide : Comparaison de sémantiques de classement sur un debat medical

Contexte

Modelisez un debat medical simplifie sur le choix d’un traitement :

Arguments : - t1 : “Le traitement A est efficace” (non attaque) - t2 : “Le traitement B est moins cher” - e1 : “Des effets secondaires graves sont associes a A” (attaque t1) - e2 : “Une étude recente contredit les effets secondaires” (attaque e1) - c1 : “Le cout de B est sous-estime” (attaque t2)

Objectifs

  1. Construire le DungTheory avec les 5 arguments et attaques
  2. Appliquer au moins 3 sémantiques de classement (Categorizer, Burden-Based, Discussion-Based)
  3. Comparer les classements obtenus : convergent-ils ?
  4. Repondre : quel traitement est le mieux supporte selon chaque sémantique ?
  5. (Bonus) Modifier la structure (ajouter un argument e3 qui attaque e2) et observer l’impact sur les classements

Indices :

  • Reutilisez le pattern : CategorizerRankingReasoner().getModel(theory)
  • RankingTools.roundRanking(ranking, 3) pour arrondir les scores
  • Un argument non attaque a un score de 1.0 (Categorizer)
  • Comparez les rangs relatifs, pas les valeurs absolues
# --- Exemple guide : Classement d'un debat medical ---
# Solution complete : comparaison de 3 semantiques de classement sur un debat medical.

if jvm_ready:
    from org.tweetyproject.arg.dung.syntax import DungTheory, Argument, Attack
    from org.tweetyproject.arg.rankings.reasoner import (
        CategorizerRankingReasoner, BurdenBasedRankingReasoner,
        DiscussionBasedRankingReasoner
    )
    from org.tweetyproject.arg.rankings.util import RankingTools

    # 1. Creer les arguments (traitements, effets secondaires, etudes, couts)
    # 2. Construire le framework (DungTheory) avec attaques et reinstatements
    # 3. Appliquer les semantiques de classement (Categorizer, Burden, Discussion)
    #    Indice : RankingTools.roundRanking(model, 3) pour un affichage lisible
    # 4. Analyser : quel traitement gagne selon chaque semantique ?
    # 5. (Bonus) Ajouter un argument qui attaque une etude et re-calculer

    # 1
    theory = DungTheory()
    t1 = Argument("t1")
    t2 = Argument("t2")
    e1 = Argument("e1")
    e2 = Argument("e2")
    c1 = Argument("c1")

    for arg in [t1, t2, e1, e2, c1]:
        theory.add(arg)

    # 2
    theory.add(Attack(e1, t1))
    theory.add(Attack(e2, e1))
    theory.add(Attack(c1, t2))
    print("AAF:", theory)

    # 3
    print("\nCategorizer")
    r_cat = CategorizerRankingReasoner()
    rank_cat = r_cat.getModel(theory)
    print(RankingTools.roundRanking(rank_cat, 3))

    print("\nBurden-Based")
    r_bb = BurdenBasedRankingReasoner()
    rank_bb = r_bb.getModel(theory)
    print(rank_bb)

    print("\nDiscussion-Based")
    r_db = DiscussionBasedRankingReasoner()
    rank_db = r_db.getModel(theory)
    print(rank_db)

    # 4
    print("\nAnalyse")
    print("Categorizer : t1=0.667, t2=0.500. Le traitement A (t1) est mieux classe que B (t2).")
    print("Burden-Based : [c1 = e2 > t1 > t2 = e1]. t1 est strictement au-dessus de t2.")
    print("Discussion-Based : [c1 = e2 > t1 > t2 = e1]. Meme ordre que Burden-Based.\n")
    print("Convergence : les 3 semantiques produisent le meme ordre relatif t1 > t2.")
    print("Cette convergence s'explique par la structure simple du graphe (chaine d'attaques sans cycle).")
    print("Sur des graphes avec cycles ou attaques multiples, Categorizer et Burden peuvent diverger.\n")
    print("Mecanisme : t1 est reinstaure par e2 (defenseur de t1 via e2 -> e1 -> t1).")
    print("En revanche, t2 est attaque par c1 (non attaque, score 1.0) sans aucun defenseur.")
    print("Le traitement A est donc mieux supporte que B dans les 3 semantiques.")

    # 5
    print("\nBonus : ajout de e3 attaquant e2")
    e3 = Argument("e3")
    theory.add(e3)
    theory.add(Attack(e3, e2))
    print("AAF modifie:", theory)

    print("\nCategorizer apres ajout e3:")
    rank_cat2 = r_cat.getModel(theory)
    print(RankingTools.roundRanking(rank_cat2, 3))

    print("\nBurden-Based apres ajout e3:")
    rank_bb2 = r_bb.getModel(theory)
    print(rank_bb2)

    print("\nDiscussion-Based apres ajout e3:")
    rank_db2 = r_db.getModel(theory)
    print(rank_db2)

    print("\nAnalyse du bonus")
    print("Avant e3 : Categorizer t1=0.667. Apres e3 : t1=0.600. Diminution de 0.067.")
    print("Avant e3 : Categorizer t2=0.500. Apres e3 : t2=0.500. Inchange.")
    print("e1 passe de 0.500 à 0.667 apres ajout de e3, ce qui confirme numeriquement la defense.")
    print("e3 défend e1 en attaquant l'attaquant de e1 (qui est e2).")
    print("Chaine : e3 -> e2 -> e1 -> t1. c'est une attaque de second ordre, qui annule partiellement la défense de t1 par e2")
    print("t1 reste au-dessus de t2 (0.600 > 0.500) mais l'ecart se reduit de 0.167 a 0.100.")
else:
    print("ERREUR: JVM non demarree. Exemple guide (debat medical) non execute.")
AAF: <{ e1, t1, e2, t2, c1 },[(c1,t2), (e1,t1), (e2,e1)]>

Categorizer
{e1=0.5, t1=0.667, e2=1.0, t2=0.5, c1=1.0}

Burden-Based
[c1 = e2 > t1 > t2 = e1]

Discussion-Based
[c1 = e2 > t1 > t2 = e1]

Analyse
Categorizer : t1=0.667, t2=0.500. Le traitement A (t1) est mieux classe que B (t2).
Burden-Based : [c1 = e2 > t1 > t2 = e1]. t1 est strictement au-dessus de t2.
Discussion-Based : [c1 = e2 > t1 > t2 = e1]. Meme ordre que Burden-Based.

Convergence : les 3 semantiques produisent le meme ordre relatif t1 > t2.
Cette convergence s'explique par la structure simple du graphe (chaine d'attaques sans cycle).
Sur des graphes avec cycles ou attaques multiples, Categorizer et Burden peuvent diverger.

Mecanisme : t1 est reinstaure par e2 (defenseur de t1 via e2 -> e1 -> t1).
En revanche, t2 est attaque par c1 (non attaque, score 1.0) sans aucun defenseur.
Le traitement A est donc mieux supporte que B dans les 3 semantiques.

Bonus : ajout de e3 attaquant e2
AAF modifie: <{ e1, t1, e2, t2, c1, e3 },[(c1,t2), (e1,t1), (e2,e1), (e3,e2)]>

Categorizer apres ajout e3:
{e1=0.667, t1=0.6, e2=0.5, t2=0.5, c1=1.0, e3=1.0}

Burden-Based apres ajout e3:
[e3 = c1 > e1 > t1 > t2 = e2]

Discussion-Based apres ajout e3:
[e3 = c1 > e1 > t1 > t2 = e2]

Analyse du bonus
Avant e3 : Categorizer t1=0.667. Apres e3 : t1=0.600. Diminution de 0.067.
Avant e3 : Categorizer t2=0.500. Apres e3 : t2=0.500. Inchange.
e1 passe de 0.500 à 0.667 apres ajout de e3, ce qui confirme numeriquement la defense.
e3 défend e1 en attaquant l'attaquant de e1 (qui est e2).
Chaine : e3 -> e2 -> e1 -> t1. c'est une attaque de second ordre, qui annule partiellement la défense de t1 par e2
t1 reste au-dessus de t2 (0.600 > 0.500) mais l'ecart se reduit de 0.167 a 0.100.

Exercice : Argumentation probabiliste sur un temoignage incertain

La section 5.8 a montre comment calculer la probabilite d’acceptation via SubgraphProbabilityFunction. Appliquez a un scénario judiciaire.

Contexte

  • accuse : “L’accuse est coupable”
  • alibi : “Un alibi le disculpe” (attaque accuse)
  • temoin : “Un temoin contredit l’alibi” (attaque alibi)

Indices :

  • prob_func = SubgraphProbabilityFunction(theory)
  • prob_func.getAcceptanceProbability(arg, Semantics.GROUNDED_SEMANTICS).doubleValue()
  • Attendu : temoin a la plus forte probabilite, alibi la plus faible
# --- Exercice : Argumentation probabiliste sur un temoignage incertain ---
# TODO etudiant : calculez les probabilites d'acceptation sur un scenario judiciaire
#
# Etape 1 : imports (cf section 5.8)
#   from org.tweetyproject.arg.dung.syntax import DungTheory, Argument, Attack
#   from org.tweetyproject.arg.dung.semantics import Semantics
#   from org.tweetyproject.arg.prob.lotteries import SubgraphProbabilityFunction
#
# Etape 2 : construire le DungTheory (accuse, alibi, temoin) avec alibi->accuse et temoin->alibi
# Etape 3 : creer la SubgraphProbabilityFunction
# Etape 4 : calculer P(accepte) pour chaque argument (semantique grounded)
# Etape 5 (bonus) : afficher prob_func.size()

if not jvm_ready:
    print("ERREUR: JVM non demarree.")
else:
    theory = None      # TODO etudiant : remplacer par le DungTheory construit
    prob_func = None   # TODO etudiant : remplacer par SubgraphProbabilityFunction(theory)
    print("Exercice a completer")
Exercice a completer

Parité cross-langage : Tweety-07b Python et C

Les deux notebooks utilisent TweetyProject, mais ne démontrent pas les mêmes algorithmes. Le titre commun regroupe deux approches distinctes ; il ne garantit ni les mêmes exemples ni les mêmes résultats. Le notebook Python étudie les arguments dans un graphe de Dung ; le notebook C# étudie les conditionnels probabilistes relationnels. Le recours au même écosystème Java ne transforme pas une distribution sur des sous-graphes en modèle Maximum Entropy de RP-CL.

Aspect Python (ce notebook) C# jumeau
Runtime JPype, JVM et JARs TweetyProject ; l’initialisation affiche 42 JARs et les outils externes disponibles IKVM 8.14 et tweety-rpcl.dll, sans JVM séparée ; les classes org.tweetyproject.logics.rpcl.* sont exposées
Classement CategorizerRankingReasoner, BurdenBasedRankingReasoner, DiscussionBasedRankingReasoner, Tuples*, Strategy-Based, SAF-Based, Counting et Propagation sur des DungTheory ; sorties de classements affichées Pas de calcul Categorizer ou Burden-Based exécuté ; le code historique correspondant reste en commentaire et n’est pas directement réactivable
Probabilités SubgraphProbabilityFunction sur les sous-graphes d’un DungTheory ; probabilités d’acceptation, Division, loterie et utilité attendue affichées Pas de calcul de probabilité d’acceptation par sous-graphe exécuté
Exemple médical Cinq arguments e1,t1,e2,t2,c1 et les attaques c1→t2, e1→t1, e2→e1 ; trois classements calculés avant et après l’ajout de e3 Conditionnels sur la fièvre et la grippe ; ce n’est ni le même graphe ni la même question
RP-CL Pas de RpclBeliefSet ni de requête Maximum Entropy exécutés Trois conditionnels construits dans un RpclBeliefSet ; RpclMeReasoner.query appelé avec NLCG, qui refuse les contraintes
Exercices étudiants Comparaison Tuples*/Counting, témoignage incertain et débat environnemental : stubs à compléter Chaînage médical, contraintes potentiellement incompatibles et système expert : stubs à compléter

Ce que prouvent les sorties

La cellule de classement Python exécute les raisonneurs Java sur plusieurs graphes : sur le premier exemple, Categorizer rend notamment a=0.38 et b=1.0, tandis que Burden-Based donne [b > d > c > e > a]. Ces valeurs classent des arguments ; elles ne sont pas des probabilités de conditionnels relationnels. Dans l’exemple médical guidé, le graphe initial classe t1 devant t2 ; l’ajout d’un argument attaquant e2 réduit le score Categorizer de t1 de 0.667 à 0.600. Ce second exemple reste une analyse de Dung, et non une inférence RP-CL.

La cellule probabiliste Python utilise l’approche par constellations : arguments et attaques peuvent être absents selon le sous-graphe. Elle affiche 19 sous-graphes, P(Accepte(a)) = 0.5263, P(Division=({a,c}, {b})) = 0.3158 et une utilité attendue de -0.2632 pour la loterie définie dans la cellule. Ces nombres viennent de SubgraphProbabilityFunction et de ArgumentationLottery sous la sémantique grounded, pas d’une distribution Maximum Entropy RP-CL. La distinction importe pédagogiquement : ici, l’incertitude porte sur la présence des éléments du graphe et l’acceptation d’un argument ; côté C#, une base de croyances relie des formules du premier ordre par des probabilités conditionnelles.

Côté C#, la DLL expose les classes RP-CL nécessaires à la construction : le diagnostic compte 31 types dans l’espace de noms RP-CL, au lieu de zéro comme l’affirmait l’ancienne note. Le notebook construit effectivement trois conditionnels et affiche la base ; l’appel du raisonneur ME avec le solveur NLCG configuré est tenté, mais ce solveur refuse un problème sous contraintes. Aucune distribution Maximum Entropy n’a été calculée dans ces deux notebooks. Un solveur compatible reste à identifier et tester avant de promettre ce résultat ; l’exposition des types n’établit pas à elle seule que l’inférence fonctionne.

Portée de la comparaison et provenance

Le niveau native-both du registre de jumeaux signifie que chaque côté fait son travail avec un moteur externe de référence : Python appelle les raisonneurs Tweety via JPype ; C# appelle les classes Tweety via IKVM. Il ne signifie ni correspondance cellule par cellule ni équivalence sémantique. L’ancienne note attribuait à Python RP-CL et Maximum Entropy, et attribuait à C# une absence de types IKVM : les lectures des cellules et sorties actuelles réfutent ces deux affirmations. Le diagnostic doit être relu lorsque le JAR, la DLL ou le solveur change ; il n’est pas une propriété permanente d’IKVM.

Les deux modalités restent utiles à des fins différentes. Pour comparer des classements et des probabilités d’acceptation dans l’argumentation abstraite, suivre les exemples Python ci-dessus. Pour construire des conditionnels RP-CL depuis .NET et observer la limite actuelle du solveur, consulter le notebook C#. Tweety-07a illustre une autre approche C# sur les frameworks étendus ; son architecture ne doit pas servir à inférer que les résultats des deux Tweety-07b sont interchangeables.

Voir aussi : configuration Python et JVM, frameworks étendus Python et frameworks étendus C#, argumentation abstraite, et les dialogues d’agents.


Résumé

Ce notebook a couvert deux extensions importantes à l’argumentation abstraite:

Sémantiques de Classement

Sémantique Principe Résultat
Categorizer Propagation de valeurs numériques Scores dans [0, 1]
Burden-Based Comptage attaquants/défenseurs Ordre total ou partiel
Discussion-Based Longueur des discussions gagnantes Ordre total ou partiel
Tuples* Matrices d’acceptabilité (Cayrol) Équivalences + ordre
Strategy-Based Jeux d’argumentation (Matt & Toni) Scores probabilistes
SAF-Based Votes sociaux (SimpleProduct) Scores agrégés
Counting Itérations de comptage Scores convergents
Propagation Influence des voisins Ordre avec epsilon

Argumentation Probabiliste

  • SubgraphProbabilityFunction: Distribution sur les sous-graphes possibles (les arguments et les attaques varient : 19 sous-graphes pour l’exemple a 3 arguments / 3 attaques, et non 2^n)
  • Acceptabilité probabiliste: P(arg ∈ extension | sous-graphe)
  • Division: Partition (IN, OUT) représentant un état possible
  • Loterie: Distribution sur les divisions avec utilités attendues

Points clés

  1. Les sémantiques de classement permettent de nuancer au-delà de l’acceptation binaire
  2. Le Categorizer et Counting convergent vers des valeurs numériques interprétables
  3. Burden-Based et Discussion-Based produisent des ordres comparables
  4. L’argumentation probabiliste modélise l’incertitude sur le graphe lui-même
  5. Les loteries permettent la prise de décision rationnelle sous incertitude argumentative

Prochaines étapes

Le notebook suivant explore les dialogues argumentatifs multi-agents avec le module agents.dialogues.


Navigation: ← Tweety-7a-Extended-Frameworks | Index | Tweety-8-Agent-Dialogues →

Exercice : Classement d’un debat environnemental

Contexte

Modelisez un debat sur la politique environnementale avec les arguments suivants :

Arguments : - taxe_carbone : “La taxe carbone reduit les emissions” - subvention_vert : “Les subventions vertes encouragent la transition” - emploi_industrie : “Les mesures environnementales menacent l’emploi industriel” - pollution : “La pollution actuelle reste trop elevee” - innovation_verte : “L’innovation verte reduit les couts de transition” - cout_transition : “Le cout de la transition est prohibitif”

Attaques

  • pollution -> subvention_vert
  • cout_transition -> taxe_carbone
  • innovation_verte -> cout_transition
  • emploi_industrie -> taxe_carbone
  • subvention_vert -> emploi_industrie

Objectifs

  1. Construire le DungTheory avec les 6 arguments et 5 attaques
  2. Appliquer les sémantiques Categorizer et Burden-Based
  3. Comparer : quelle politique est la mieux supportee ?
  4. Y a-t-il convergence entre les sémantiques ?

Indices :

  • Reutilisez le pattern de l’exemple guide précédent
  • RankingTools.roundRanking(model, 3) pour arrondir les scores
  • Comparez les rangs relatifs de taxe_carbone et subvention_vert
# --- Exercice : Classement d'un debat environnemental ---

# TODO etudiant : Creer un DungTheory pour un debat environnemental
# avec les arguments : taxe_carbone, subvention_vert, emploi_industrie,
# pollution, innovation_verte, cout_transition

# Etape 1 : Creer les 6 arguments
# Indice : Argument("nom") pour chaque argument

# Etape 2 : Definir les attaques
# - pollution attaque subvention_vert (la pollution rend les subventions insuffisantes)
# - cout_transition attaque taxe_carbone (la taxe est trop couteuse)
# - innovation_verte attaque cout_transition (l'innovation reduit les couts)
# - emploi_industrie attaque taxe_carbone (la taxe menace l'emploi)
# - subvention_vert attaque emploi_industrie (les subventions creeent des emplois verts)

# Etape 3 : Appliquer Categorizer et Burden-Based
# Indice : reutilisez le pattern de l'exemple guide ci-dessus

# Etape 4 : Comparer les classements
# - Quelle politique est la mieux supportee ?
# - Y a-t-il convergence entre les semantiques ?

print("Exercice a completer")
Exercice a completer
Retour au sommet