Traitement de l’information incertaine

Dans cette série, nous organiserons les exercices en deux parties. La première partie traitera des réseaux bayésiens, mais sans programmation. Le but est de passer en revue les principes du raisonnement probabiliste, la modélisation des liens de causalité et l’inférence dans un réseau bayésien.

Dans la deuxième partie, vous modifierez le moteur d’inférence à chaînage avant avec variables que nous avons développé dans les exercices des chapitres précédents afin de prendre en compte la possibilité d’incertitudes dans les faits et les règles.

Réseaux Bayésiens

Exercice 1: Raisonnement probabiliste

Après votre bilan de santé annuel, vous recevez par courrier les résultats de vos examens : vous avez été testé positif à une maladie grave. Les taux de faux positifs et négatifs sont de 1%, c’est-à-dire que la probabilité que le test classifie un patient sain comme étant malade ou un malade comme étant sain est de 0.01. Par bonheur, cette maladie est extrêmement rare : elle ne frappe qu’une personne sur dix mille.

À l’aide d’un raisonnement probabiliste, expliquez pourquoi la rareté de la maladie est une chance pour vous. Indication: Utilisez la règle d’inférence bayésienne, T = Test et M = Maladie constituant les événements concernés.

Exercice 2: Causalité

Les liens de causalité expriment des relations du type si~A=1, alors~B=1, c’est-à-dire, selon le formalisme de la logique propositionnelle, des règles du type A\Rightarrow B, A et B étant deux prédicats booléens. Lorsque le raisonnement est incertain, la causalité peut être établie via un raisonnement probabiliste:

(A \Rightarrow B) devient (A \Rightarrow B avec probabilité P(B|A))

L’inférence bayésienne consiste alors généralement à partir d’une observation sur B afin d’établir la probabilité selon laquelle A est vrai, c’est-à-dire que l’on remonte la chaîne de causalité. Cet exercice a pour but de vous donner une idée de la raison pour laquelle cela est généralement le cas.

Considérons une petite histoire. L’inspecteur Smith se trouve à Priory School . Il attend le détective Holmes et son ami, le docteur Watson, pour enquêter sur la disparition d’un professeur. Mais ils sont en retard. Tous deux ont la réputation d’être mauvais conducteurs et l’inspecteur Smith se demande si la route est gelée, ce qui leur causerait certainement de gros ennuis. Il téléphone à sa secrétaire, Miss Lovelace, qui l’informe que le Dr Watson a effectivement eu un accident. Voici leur conversation:

Smith — Un accident ? Bien, la route étant probablement gelée, il est probable que Holmes ait aussi eu un accident!

Lovelace — La route, gelée ? Non, certainement pas: il ne fait pas si froid et les routes ont été sablées.

Smith — Pas de chance pour Watson alors. Attendons Holmes encore dix minutes...

Le lendemain, l’épouse de Watson, Mary, lit le journal. Elle découvre l’accident de son mari et apprend que le professeur de Priory School a été sauvé et les ravisseurs arrêtés. Voici ses pensées :

Mary — Ils ont arrêté les ravisseurs? Alors, Holmes n’a probablement pas eu d’accident, donc la route n’était probablement pas gelée.

Elle lit ensuite qu’il ne faisait pas si froid et que les routes avaient été sablées. Elle se dit :

Mary — Décidément, Holmes a évité l’accident.

Modélisation du problème

Question 2.1 :

Formalisez les relations entre les événements de cette petite histoire à l’aide d’un réseau bayésien à quatre noeuds :

  • I = Route-Gelée
  • H = Accident-Holmes
  • W = Accident-Watson
  • S = Professeur-Sauvé

Question 2.2 (Inférence déductive) :

Si l’on connaît la probabilité de l’événement I, P(I):

  • Comment peut-on calculer les probabilités P(H) et P(W) des événements H et W ? De quelles informations faut-il disposer quant aux relations entre I, H et W?
  • Comment peut-on calculer la probabilité P(S) de l’événement S ? Quelles informations nous faut-il sur les relations entre I, H, W et S ?

Question 2.3 (Inférence abductive) :

Comment peut-on exprimer la probabilité de l’événement I lorsqu’on sait si l’événement W s’est produit ou pas (W = 1 ou W = 0)? Si l’on connaît P(I), quelles informations nous faut-il sur la relation entre I et W pour calculer cette probabilité?

Question 2.4 (Déduction et abduction) :

Si on connaît P(I) :

  • Comment peut-on exprimer la probabilité de l’événement H lorsqu’on sait si l’événement W s’est produit ou pas ? Quelles informations nous faut-il sur les relations entre I, W et H pour calculer cette probabilité ?
  • Comment peut-on exprimer la probabilité de H lorsqu’on sait si les événements W et S se sont produits ou pas ? Quelles informations nous faut-il sur les relations entre I, H, W et S pour calculer cette probabilité ?
  • Comment peut-on exprimer la probabilité de H lorsqu’on sait si les événements I, W et S se sont produits ou pas ? Quelles informations nous faut-il sur les relations entre I, H, W et S pour calculer cette probabilité ?

Calcul probabiliste

Ajoutons maintenant les informations sur les relations de ce réseau. Sachant qu’il est probable que la route soit gelée, et que Holmes et Watson sont de mauvais conducteurs, nous considérerons que :

P(I=1)=0.7\ P(H=1|I=1) = 0.9, P(W=1|I=1) = 0.7\ P(H=1|I=0) = 0.1, P(W=1|I=0)=0.5

Ensuite, sachant que la présence de Holmes est essentielle pour la résolution d’une affaire, nous considérerons que :

P(S=1|H=1, W = 1) = 0.1\ P(S=1|H=1, W = 0) = 0.2\ P(S=1|H=0, W = 1) = 0.8\ P(S=1|H = 0, W = 0) = 1

Question 2.5:

Complétez les tableaux des probabilités conditionnelles P(H|I) et P(W|I).

P(H|I) I=1 I=0 P(W|I) I=1 I=0
H=1 ... ... W=1 ... ...
H=0 ... ... W=0 ... ...

Question 2.6:

À partir de ces tableaux, calculez les probabilités que Holmes et Watson aient eu un accident, P(H = 1) et P(W = 1).

Question 2.7:

Dès que Miss Lovelace l’a informé que Watson a eu un accident, Smith déduit que les routes sont probablement gelées. Calculez la probabilité P(I = 1|W = 1).

Question 2.8 (Dépendance) :

Ensuite, il en déduit que Holmes a probablement eu un accident aussi. Quelle est la probabilité P(H = 1|W = 1)? Comparez-la avec P(H = 1) : H et W sont deux événements dépendants.

Question 2.9 (Indépendance conditionnelle) :

Dès que Miss Lovelace l’informe que les routes ne sont pas gelées, Smith revient sur ses conclusions. Quelle est la probabilité P(H = 1|W = 1, I = 0) ? Comparez-la avec P(H = 1 | I = 0) : H et W sont deux événements indépendants, étant donné I.

Question 2.10 :

Complétez le tableau de la probabilité conditionnelle P(S|W, H) :

P(S|W,H) H=1,W=1 H=1,W=0 H=0,W=1 H=0,W=0
S=1 ... ... ... ...
S=0 ... ... ... ...

Question 2.11 (Causes multiples) :

À partir de ce tableau, calculez la probabilité que le professeur ait été sauvé, P(S = 1).

Question 2.12 :

En découvrant l’accident de son mari et le dénouement de l’affaire de la Priory School, Mary conclut que Holmes a évité l’accident. Quelle est la probabilité P(H = 1|W = 1,S = 1) ?

Question 2.13 (Abduction avec plusieurs conséquences) :

Ensuite, elle déduit que les routes n’étaient pas gelées. Quelle est la probabilité P(I = 1|W = 1,S = 1) ?

Question 2.14 :

Finalement, elle découvre l’état des routes et déduit que Holmes n’a certainement pas eu un accident. Quelle est la probabilité P(H = 1|I = 0,W = 1,S = 1) ?

Question 2.15 (Dépendance conditionnelle) :

Et si Mary n’était pas au courant de l’accident de Watson ? Quelle est la probabilité P(H = 1|I = 0,S = 1) ? Comparez-la avec P(H = 1|I = 0, W = 1, S = 1) : H et W sont deux événements dépendants, étant donnés I et S.

Question 2.16 :

Finalement, quelle cause pourrait expliquer l’accident de Watson ? Peut-être ses pneus sont-ils trop vieux ? Modifiez le réseau en ajoutant le noeud V = Vieux-Pneus-Watson.

Question 2.17 :

Comment peut-on exprimer la probabilité de l’événement V dès lors qu’on sait si les événements I et W se sont produits ou pas ? Si l’on connaît P(V), de quelles informations faut-il disposer sur les relations entre I, W et V pour calculer cette probabilité ?

Inférence à chaînage avant avec facteurs de certitude

Dans cet exercice, nous allons modifier notre moteur d’inférence à chaînage avant de façon à intégrer la notion d’incertitude dans les faits et les règles. Les modules ci-dessous représentent le squelette du programme que nous allons développer.

Notez que le code de cette série s’appuie sur des modules développés dans les chapitres précédents. Nous ne les reproduisons pas ici, mais il est nécessaire de pouvoir les importer. Veillez à organiser vos dossiers en conséquence.

inference_facteurs_certitude.zip

Module .../moteur_avec_variables_fc/facteurs_certitude.py :

""" Fonctions utilitaires pour gérer des facteurs de certitude. """

def fc_ou(fc1, fc2):
    """ Calcule le facteur de certitude d'une disjonction de faits. """

    print('à compléter')

def fc_et(fc1, fc2):
    """ Calcule le facteur de certitude d'une conjonction de faits. """

    print('à compléter')

Module .../moteur_avec_variables_fc/regle_avec_variables_fc.py :

from .facteurs_certitude import fc_et

class RegleAvecVariables_FC:
    """ Représentation d'une règle d'inférence pour le chaînage avec variables. """

    def __init__(self, conditions, conclusion, fc=1.0):
        """ Construit une règle étant donné une liste de conditions,\
            une conclusion et un facteur de certitude associé.

            :param list conditions: une collection de propositions (pouvant\
            contenir des variables) nécessaires à déclencher la règle.
            :param conclusion: la proposition (pouvant contenir des variables)\
            résultant du déclenchement de la règle.
            :param fc: le facteur de certitude associé.
        """

        print('à compléter')

    def depend_de(self, fait, methode):
        """ Vérifie qu'un fait fait partie, sous réserve de substitution,\
            des conditions de la règle.

            :param fait: un fait qui doit faire partie des conditions de\
            déclenchement.
            :param methode: ``Filtre`` ou ``Unificateur``, détermine le type\
             de pattern match à appliquer.
            :return: un dictionnaire qui attribue un environnement à chaque\
            condition qui peut être satisfaite par le fait pasée en paramètre.\
            ``False`` si aucune condition n'est satisfaite par le fait.
        """

        envs = {}

        for condition in self.conditions:
            # Si au moins une des conditions retourne un environnement,
            # nous savons que la proposition satisfait une des conditions.
            env = methode.pattern_match(fait, condition, {})
            if env != methode.echec:
                envs[condition] = env

        return envs

    def satisfaite_par(self, faits, cond, env, env_fc, methode):
        """ Vérifie que des faits suffisent, sous réserve de substitution,\
            à déclencher la règle.

            :param list faits: une liste de faits.
            :param cond: la condition qui a donné lieu à ``env`` par le\
            pattern match.
            :param dict env: un environnement de départ déjà établi par\
            ``depend_de``.
            :param env_fc: le facteur de certitude associé à ``env``.
            :param methode: ``Filtre`` ou ``Unificateur``, détermine le type\
             de pattern match à appliquer.
            :return: une liste d'environnements qui correspondent à toutes les\
            substitutions possibles entre les conditions de la règle et les\
            propositions. On retourne une liste vide si au moins une condition\
            ne peut être satisfaite.
        """

        print('à compléter')

    def __repr__(self):
        """ Représentation d'une règle sous forme de string. """

        return '{} => {}, {}'.format(str(self.conditions),
                                     str(self.conclusion),
                                     str(self.fc))

Module .../moteur_avec_variables_fc/connaissance_fc.py :

from .facteurs_certitude import fc_ou
from moteur_avec_variables_fc.regle_avec_variables_fc import RegleAvecVariables_FC

class BaseConnaissances_FC:
    """ Une base de connaissances destinée à contenir les faits et les règles\
        d'un système de chaînage avec facteurs de certitude.
    """

    def __init__(self):
        """ Construit une base de connaissances. """

        self.faits = {}
        self.regles = []

    def ajoute_un_fait(self, fait):
        """ Ajoute un fait dans la base de connaissances.

            Un fait est un tuple composé du fait proprement dit, c'est-à-dire\
            une proposition sans variable, et optionnellement d'un scalaire\
            représentant son facteur de certitude.

            :param fait: un fait.
        """

        print('à compléter')

    def ajoute_faits(self, faits):
        """ Ajoute une liste de faits dans la base de connaissances.

            :param list faits: une liste de faits.
        """

        for fait in faits:
            self.ajoute_un_fait(fait)

    def ajoute_une_regle(self, description):
        """ Ajoute une règle dans la base de connaissances étant donné sa\
            description.

            Une règle est décrite par une liste (ou un tuple) de deux ou trois\
            éléments: une liste de conditions, une conclusion et un facteur de\
            certitude optionnel.

            Les conditions et la conclusion doivent être des propositions.

            :param description: une description de règle.
        """

        print('à compléter')

    def ajoute_regles(self, descriptions):
        """ Ajoute des règles dans la base de connaissance.

            L'argument est une liste de descriptions, chacune composée d'une\
            liste de conditions, d'une conséquence et optionnellement d'un\
            facteur de certitude.

            :param list descriptions: une liste de descriptions de règles.
        """

        for description in descriptions:
            self.ajoute_une_regle(description)

Module .../moteur_avec_variables_fc/chainage_avant_avec_variables_fc.py :

from moteur_sans_variables.chainage import Chainage
from moteur_avec_variables.filtre import Filtre

class ChainageAvantAvecVariables_FC(Chainage):
    """ Un moteur d'inférence à chaînage avant avec variables. """

    def __init__(self, connaissances, methode=None):
        """
            :param methode: ``Filtre`` ou ``Unificateur``, détermine le type de\
            pattern match à appliquer. ``Filtre`` par défaut.
        """

        Chainage.__init__(self, connaissances)

        if methode is None:
            self.methode = Filtre()
        else:
            self.methode = methode

    def instancie_conclusion(self, regle, envs_et_fcs):
        """ Instancie la conclusion d'une règle pour tous les environnements.

            :param regle: la règle dont la conclusion doit être instanciée.
            :param list envs_et_fcs: une liste de paires d'environnements et de\
            facteurs de certitude servant à instancier la conclusion de la règle.
            :return: une liste de propositions correspondant aux différentes\
            instanciations de la conclusion.
        """

        print('à compléter')

    def chaine(self):
        """ Effectue le chaînage avant sur les faits et les règles contenus\
            dans la base de connaissances.
        """

        print('à compléter')

Module .../exemple_animaux.py :

from sys import argv, exit
from moteur_avec_variables_fc.regle_avec_variables_fc import RegleAvecVariables_FC
from moteur_avec_variables_fc.connaissance_fc import BaseConnaissances_FC
from moteur_avec_variables.unificateur import Unificateur
from moteur_avec_variables.filtre import Filtre
from moteur_avec_variables_fc.chainage_avant_avec_variables_fc import ChainageAvantAvecVariables_FC

regles = [
    ([('placentaire', '?x')], ('mammifère', '?x')),
    ([('marsupial', '?x')], ('mammifère', '?x')),
    ([('monotrème', '?x')], ('mammifère', '?x')),
    ([('placentaire-1', '?x')], ('placentaire', '?x')),
    ([('genre-placentaire', '?x')], ('placentaire', '?x')),
    ([('a-des-poils', '?x'), ('a-des-bébés-formes', '?x'),
      ('température-stable', '?x')],
     ('placentaire-1', '?x'), 9.0/10.0),
    ([('a-des-poils', '?x'), ('a-des-bébés-foetaux', '?x'),
      ('température-stable', '?x')],
     ('marsupial', '?x'), 95.0/100.0),
    ([('a-des-oeufs', '?x'), ('a-des-poils', '?x'),
      ('température-stable', '?x')],
     ('monotrème', '?x')),
    ([('singe', '?x')], ('genre-placentaire', '?x')),
    ([('primate', '?x')], ('singe', '?x')),
    ([('lémurien', '?x')], ('singe', '?x')),
    ([('chimpanzé', '?x')], ('primate', '?x')),
    ([('gorille', '?x')], ('primate', '?x')),
    ([('canidé', '?x')], ('genre-placentaire', '?x')),
    ([('chien', '?x')], ('canidé', '?x')),
    ([('loup', '?x')], ('canidé', '?x')),
    ([('lycaon', '?x')], ('canidé', '?x')),
    ([('félin', '?x')], ('genre-placentaire', '?x')),
    ([('chat', '?x')], ('félin', '?x')),
    ([('lion', '?x')], ('félin', '?x')),
    ([('tigre', '?x')], ('félin', '?x')),
    ([('tigre-du-bengale', '?x')], ('tigre', '?x')),
    ([('tigre-de-l-himalaya', '?x')], ('tigre', '?x')),
    ]

if len(argv) < 2 or argv[1].lower() not in ('a', 'b'):
    print('On attend au moins un arguments: A ou B')
    exit(1)

if argv[1].lower() == 'a':
    faits_initiaux = [
        (('a-des-poils', 'blaireau'),),
        (('a-des-bébés-formes', 'blaireau'),),
        (('température-stable', 'blaireau'),),
        (('a-des-poils', 'écureuil'),),
        (('a-des-bébés-formes', 'écureuil'),),
        (('température-stable', 'écureuil'),),
        (('chimpanzé', 'cheetah'),),
        (('gorille', 'bozo'),),
        (('singe', 'babouin'),),
        (('singe', 'paresseux'),),
        (('chien', 'bill'),),
        (('loup', 'loup-1'),),
        (('lycaon', 'lycaon-1'),),
        (('chat', 'mistigri'),),
        (('lion', 'minet'),),
        (('tigre-du-bengale', 'tigre-du-bengale-1'),),
        (('a-des-poils', 'kangourou'),),
        (('a-des-bébés-foetaux', 'kangourou'),),
        (('température-stable', 'kangourou'),),
        (('a-des-oeufs', 'ornythorinque'),),
        (('a-des-oeufs', 'nouveau-spécimen'), 5.0/10.0),
        (('a-des-poils', 'ornythorinque'),),
        (('température-stable', 'nouveau-spécimen'), 9.0/10.0),
        (('température-stable', 'ornythorinque'),),
    ]
elif argv[1].lower() == 'b':
    faits_initiaux = [
        (('a-des-oeufs', 'nouveau-spécimen'), 5.0/10.0),
        (('température-stable', 'nouveau-spécimen'), 9.0/10.0),
        (('a-des-poils', 'nouveau-spécimen'), 9.0/10.0),
        (('a-des-poils', 'écureuil'),),
        (('a-des-bébé-formes', 'écureuil'),),
        (('température-stable', 'écureuil'),),
        (('a-des-poils', 'un-spécimen-qui-ressemble-à-un-écureuil'), 0.9),
        (('a-des-bébé-formes', 'un-spécimen-qui-ressemble-à-un-écureuil'), 0.7),
        (('température-stable', 'un-spécimen-qui-ressemble-à-un-écureuil'), 0.8),
    ]

bc = BaseConnaissances_FC()
bc.ajoute_faits(faits_initiaux)
bc.ajoute_regles(regles)

moteur = ChainageAvantAvecVariables_FC(connaissances=bc, methode=Filtre())
moteur.chaine()

moteur.affiche_solutions()

if len(argv) > 2 and argv[2].lower() == 'trace':
    moteur.affiche_trace()

Exercice 1 : Manipulation des facteurs de certitude

Dans cet exercice, nous aurons besoin de pouvoir combiner les facteurs de certitude de faits entrant dans une relation logique les uns avec les autres. Commencez donc par compléter la fonction fc_et de facteurs_certitude.py, qui prend comme paramètres deux facteurs de certitude devant exister conjointement et qui retourne le minimum des deux. Cette fonction sera utilisée par la méthode RegleAvecVariables_FC.satisfaite_par, que nous verrons ci-dessous, pour calculer le facteur de certitude associé à un ensemble de conditions.

Dans le même module, codez aussi une fonction fc_ou qui prend comme paramètres les facteurs de certitude de deux faits identiques par leur contenu (même proposition) et qui retourne le facteur de certitude résultant. Cette fonction sera utilisée par la méthode BaseConnaissances_FC.ajoute_un_fait pour réaliser la mise à jour des facteurs de certitude.

Exercice 2 : Le faits et les règles

Des facteurs de certitude doivent être associés aux faits et aux règles. Le langage Python ne permettant pas d’utiliser des structures au sens des struct du C, nous ferons usage de tuples de la forme (fait, fc), où fait est le fait que l’on veut définir et fc un facteur de certitude. Nous aurons ainsi par exemple :

fait = (('père', 'Jean', 'Paul'), 9/10)

D’une façon analogue, la classe RegleAvecVariables_FC implémentera une règle avec trois attributs :

  • conditions : la liste des conditions de la règle;
  • conséquence : la conséquence de la règle;
  • fc : le facteur de certitude associé à la règle.

Nous aurons ainsi par exemple :

regle = RegleAvecVariables_FC([('père', '?x', '?z'), ('père', '?z', '?y')],
                               ('grand-père', '?x', '?y'),
                               8/10)

RegleAvecVariables_FC s’inspire donc de la classe RegleAvecVariables du moteur de chaînage avant en lui ajoutant un attribut self.fc, qui contiendra la valeur du facteur de certitude associé. Le constructeur de la règle, que vous devez compléter, devra prendre trois arguments, en accord avec la définition ci-dessus. Pour simplifier la création de nouvelles règles, vous pouvez stipuler que l’argument correspondant au facteur de certitude soit optionnel, avec une valeur par défaut de 1.0.

La méthode RegleAvecVariables.satisfaite_par

La méthode depend_de de la classe RegleAvecVariables peut être reprise telle quelle. Il faut en revanche modifier la méthode satisfaite_par de sorte qu’elle prenne comme arguments :

  • La liste des faits déjà connus;
  • La condition testée avec succès par depend_de;
  • L’environnement résultant de l’appel à depend_de;
  • Le facteur de certitude associé au fait qui a déclenché la règle;
  • La classe de pattern matching (filtre ou unificateur) utilisée.

La valeur de retour doit être une liste de pairs d’environnements, accompagnés chacun par le facteur de certitude associé. Le facteur de certitude d’un nouvel environnement est donné par le minimum des facteurs de cet environnement et du fait passé en argument à l’appel de la fonction pattern_match qui a conduit à la découverte du nouvel environnement.

Prenons par exemple (par simplification, les faits et règles ne sont pas écrits selon le format interne) :

règle = ([('vole', '?x', '?y'), ('est-découvert', '?x')],
         ('en-prison', '?x', 'pour-vol-de', '?y'), 8/10)

faits = [
         (('vole', 'Jean', 'bijoux'), 9/10),
         (('vole', 'Paul', 'voiture'), 8/10),
         (('est-découvert', 'Paul'), 6/10),
         (('est-découvert', 'Jean'), 8/10))
         ]

# Fait déclencheur :
fait_declencheur = (('est-découvert', 'Jean'), 8/10)

# Paires de conditions et environnements retournées par règle.depend_de :
envs = {('est-découvert', '?x') : {'?x': 'Jean'}}

# L'essai de la règle par le moteur à chaînage avant doit provoquer l'appel
# suivant de règle.satisfaite_par :
satisfaite_par(faits, ('est-découvert', `?x'), {'?x': 'Jean'}, 8/10))
    -> [({'?x': 'Jean', '?y': 'bijoux'}, 8/10)]

La base de connaissances

La base de connaissances BaseConnaissances_FC doit aussi être modifiée en adaptant les méthodes ajoute_un_fait et ajoute_une_regle aux nouvelles définitions des faits et des règles :

  • ajoute_un_fait((('père', 'Jean', 'Paul'), 9/10)) ajoute le fait ('père', 'Jean', 'Paul') à la base de connaissances avec un facteur de certitude de 9/10;
  • ajoute_un_fait(('père', `Paul', 'Marc')) ajoute le fait ('père', 'Paul', 'Marc') à la base de connaissances avec une facteur de certitude de 1 (valeur par défaut);
  • ajoute_une_regle(([('père', '?x', '?z'), ('père', '?z', '?y')], ('grand-père', '?x', '?y'), 8/10)) ajoute une règle à la base de connaissances avec un facteur de certitude de 8/10;
  • ajoute_une_regle(([('père', '?x', '?z'), ('père', '?z', '?y')], ('grand-père', '?x', '?y'))) ajoute une règle à la base de connaissances avec un facteur de certitude de 1.

La fonction ajoute_un_fait continue de jouer le même rôle que par le passé : elle ajoute un fait à la base des faits si celui-ci n’est pas déjà connu. Cependant, elle doit être modifiée pour tenir compte des facteurs de certitude. En effet, si le fait à ajouter n’est pas nouveau (il a donc été démontré auparavant par un chemin différent), il faut mettre à jour le facteur de certitude associé pour refléter la nouvelle valeur de certitude. Utilisez la méthode fc_ou dans ce cas.

Exercice 3 : Le moteur d’inférence à chaînage avant avec variables

La méthode ChainageAvantAvecVariables.instancie_conclusion

Comme auparavant, la méthode instancie_conclusion de la classe ChainageAvantAvecVariables_FC doit instancier la conséquence d’une règle au moyen d’une liste d’environnements. Cependant, les nouveaux faits doivent maintenant se voir affecter un facteur de certitude, qui dépendra du facteur de certitude de la règle et de celui qui résulte des conditions. Elle doit donc prendre en paramètre une liste de paire d’environnements associés à leurs facteurs de certitude, et retourner les instanciations de la conclusion, munies chacune de leur facteur de certitude.

Prenez bien en compte la formule de calcul du facteur de certitude résultant de l’application d’une règle. Exemple :

règle = ([('vole', '?x', '?y'), ('est-découvert', '?x')],
         ('en-prison', '?x', 'pour-vol-de', '?y'), 8/10)

instancie_conclusion(règle,
                [({'?x' : 'Jean', '?y' : 'bijoux'}, 8/10),
                 ({'?x' : 'Paul', '?y' : 'voiture'}, 6/10)])
    -> [(('en-prison', 'Jean', 'pour-vol-de', 'bijoux'), 16/25),
       (('en-prison', 'Paul', 'pour-vol-de', 'voiture'), 12/25)]

La méthode ChainageAvantAvecVariables_FC.chaine

Les faits dont le facteur de certitude est négatif ne doivent pas être utilisés comme déclencheurs, même s’ils sont consignés dans la base des faits. Implémentez donc la méthode de chaînage avant chaine de ChainageAvantAvecVariables_FC en tenant compte des modifications apportées aux autres fonctions et en vous inspirant de l’algorithme suivant :

ChainageAvantAvecVariables_FC(faits_depart, regles)
1.  solutions <- liste vide
2.  Q <- faits_depart
3.  WHILE Q n'est pas vide DO
4.      q <- premier(Q)
5.      Q <- reste(Q)
6.      IF q n'est pas dans solutions THEN
7.          ajouter q à solutions
8.          IF le facteur de certitude fc de q est plus grand que 0 THEN
9.              FOR EACH règle r de regles DO
10.                 envs <- toute les paires de conditions et environnements issues du pattern matching réussi entre q et les conditions de r
11.                 FOR chaque condition cond et environnement env de envs DO
12.                     envs1 <-  toutes les paires environnements et fcs établis par le pattern matching des conditions restantes de r étant donné env
13.                     FOR chaque environnement env1 et facteur fc1 de envs1 DO
14.                         instances <- instanciation de la conclusion de r selon env1 et fc1
15.                         ajouter instances en queue de Q
16.                     END FOR
17.                 END FOR
18.             END FOR
19.         END IF
20.     END IF
21. END WHILE
22. RETURN solutions
END ChainageAvantAvecVariables_FC

Test du programe

Le module exemple_animaux.py contient des règles et des faits modélisant une petite partie d’un arbre de classification des mammifères. Après avoir écrit votre programme, testez-le sur le premier exemple en ajoutant l’option A. Vous pouvez afficher la trace en utilisant l’option trace.

python3 exemple_animaux.py A
python3 exemple_animaux.py A trace

Le module contient un deuxième exemple que vous pouvez exécuter avec la commande :

python3 exemple_animaux.py B
python3 exemple_animaux.py B trace