# Exemple guide 2 : Une meilleure politique de conflit (oracle a sources)
#
# L'oracle actuel resout les conflits fonctionnels par "premier arrive, premier
# cru" : comme ne_a(sophie, lyon) arrive avant ne_a(sophie, bordeaux), lyon gagne
# -- par chance, car le texte principal est enonce avant le vieux registre. On
# rend cette decision EXPLICITE et robuste a l'ordre : chaque triplet porte une
# etiquette de source et un score de fiabilite ; en cas de conflit fonctionnel,
# on garde le PLUS FIABLE, quel que soit l'ordre d'arrivee.
# Etape 1 : fiabilite par source
FIABILITE = {"texte_principal": 0.9, "vieux_registre": 0.4}
def oracle_avec_sources(triples_etiquetes):
"""triples_etiquetes : liste de (relation, sujet, objet, source).
Sur un conflit fonctionnel, garde le triplet de la source la plus fiable,
QUEL QUE SOIT l'ordre d'arrivee. Renvoie (acceptes, rejetes) ou chaque
element porte sa source.
"""
accepts, rejects = [], []
for (r, s, o, source) in triples_etiquetes:
# --- verifications de type (reutilisees de oracle) ---
sig = SIGNATURES.get(r)
if sig is None:
rejects.append(((r, s, o, source), "relation inconnue")); continue
if s not in TYPE_OF or o not in TYPE_OF:
rejects.append(((r, s, o, source), "entite inconnue")); continue
if (TYPE_OF[s], TYPE_OF[o]) != sig:
rejects.append(((r, s, o, source),
f"types ({TYPE_OF[s]}, {TYPE_OF[o]}) != signature {sig}")); continue
# --- conflit fonctionnel parmi les deja admis ---
conflit = None
for (rr, ss, oo, src) in accepts:
if rr != r:
continue
if (r, "sujet") in FUNCTIONAL and ss == s and oo != o:
conflit = (rr, ss, oo, src); break # ne_a(sophie, *)
if (r, "objet") in FUNCTIONAL and oo == o and ss != s:
conflit = (rr, ss, oo, src); break # dirige(*, org)
if conflit is not None:
f_new, f_old = FIABILITE[source], FIABILITE[conflit[3]]
if f_new > f_old:
# le nouveau est PLUS fiable : il ejecte l'ancien, meme arrive apres
accepts = [t for t in accepts if t != conflit]
rejects.append(((conflit[0], conflit[1], conflit[2], conflit[3]),
f"ejecte : source {conflit[3]} ({f_old}) < nouvelle {source} ({f_new})"))
accepts.append((r, s, o, source))
else:
rejects.append(((r, s, o, source),
f"rejete : source {source} ({f_new}) <= admise {conflit[3]} ({f_old})"))
continue
accepts.append((r, s, o, source))
return accepts, rejects
# Etape 2 : on etiquette le flux d'extraction. Le piege bordeaux vient du
# vieux_registre ; tout le reste, du texte_principal.
VIEUX_REGISTRE = {("ne_a", "sophie", "bordeaux")}
flux = [(r, s, o, "vieux_registre" if (r, s, o) in VIEUX_REGISTRE else "texte_principal")
for (r, s, o) in triples]
# Etape 3 : on isole le conflit ne_a(sophie, *) et on teste les DEUX ordres.
ne_lyon = ("ne_a", "sophie", "lyon", "texte_principal")
ne_bord = ("ne_a", "sophie", "bordeaux", "vieux_registre")
for nom, ordre in [("texte d'abord", [ne_lyon, ne_bord]),
("registre d'abord", [ne_bord, ne_lyon])]:
acc, rej = oracle_avec_sources(ordre)
gagnant = [o for (r, s, o, _src) in acc if r == "ne_a" and s == "sophie"]
print(f"Ordre [{nom}] : ne_a(sophie, ?) admis = {gagnant}")
print()
acc, rej = oracle_avec_sources(flux)
print(f"Oracle a sources (flux complet) : {len(acc)} acceptes, {len(rej)} rejetes")
for (r, s, o, source), raison in rej:
print(f" REJETE {r}({s}, {o}) [{source}] -> {raison}")
print()
print("Lecture : dans les DEUX ordres, lyon (texte_principal, 0.9) l'emporte sur")
print("bordeaux (vieux_registre, 0.4). La decision ne depend plus de la chance de")
print("l'enonce : elle est encodee dans la fiabilite des sources. Attention au cas")
print("d'egalite (deux sources de meme fiabilite) : le tie-break retombe sur")
print("premier-arrive (f_new > f_old est strict) -- l'exercice variation l'attaque.")