Inférence à chaînage avant avec variables¶
Solutions du code¶
Module .../moteur_avec_variables/proposition_avec_variables.py
¶
""" Fonctions utilitaires pour gérer des propositions sans ou avec variables\
dans un moteur d'inférence.
"""
def est_atomique(proposition):
""" Vérifie si la proposition courante est un atome (c'est le cas s'il\
s'agit d'un string).
:param proposition: une proposition.
:return: ``True`` si la proposition est de type string.
"""
return type(proposition) == type('')
def est_une_variable(proposition, marqueur='?'):
""" Vérifie si la proposition courante est une variable (c'est le cas s'il\
s'agit d'un atome dont la description commence par le marqueur de\
variables).
:param proposition: une proposition.
:param marqueur: marqueur de variable avec valeur par défaut : ``'?'``.
:return: ``True`` si l'argument est un atome et commence par le marqueur\
de variables.
"""
return est_atomique(proposition) and proposition[0] == marqueur
def tete(proposition):
""" Coupe la proposition courante et retourne son premier élément.
A noter que dans le cas d'une proposition atomique, la méthode soulève\
une exception.
:param proposition: une proposition.
:return: la tête de la proposition composée.
"""
if est_atomique(proposition):
raise Exception("Proposition atomique: Impossible de la segmenter.")
elif len(proposition) > 0:
return proposition[0]
else:
raise Exception("Proposition vide: Impossible de la segmenter.")
def corps(proposition):
""" Coupe la proposition courante et retourne la portion située après le\
premier élément.
A noter que dans le cas d'une proposition atomique, la méthode soulève\
une exception.
:param proposition: une proposition.
:return: le corps de la proposition composée.
"""
if est_atomique(proposition):
raise Exception("Proposition atomique: Impossible de la segmenter.")
elif len(proposition) > 0:
return proposition[1:]
else:
raise Exception("Proposition vide: Impossible de la segmenter.")
def lister_variables(proposition):
""" Retourne un ensemble (de type ``set``) contenant les variables\
mentionnées dans la proposition courante.
:param proposition: une proposition.
:return: la liste des variables apparaissant dans la proposition.
"""
variables = set()
if est_atomique(proposition):
if est_une_variable(proposition):
variables.add(proposition)
else:
for sous_prop in proposition:
variables.update(lister_variables(sous_prop))
return variables
Module .../moteur_avec_variables/regle_avec_variables.py
¶
class RegleAvecVariables:
""" Représentation d'une règle d'inférence pour le chaînage avec\
variables.
"""
def __init__(self, conditions, conclusion):
""" Construit une règle étant donné une liste de conditions et une\
conclusion.
: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.
"""
self.conditions = conditions
self.conclusion = conclusion
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, 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 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.
"""
envs = [env]
# On n'a pas besoin de tester ``cond`` car cela a été fait dans l'appel
# à ``depend_de`` qui précède l'appel à cette méthode.
conditions_a_tester = [cond1 for cond1 in self.conditions if cond1 != cond]
# Pour chaque condition dans la liste des conditions, si la liste
# des environnements n'est pas vide, on y ajoute les environnements
# qui permettent de satisfaire une des conditions.
for cond1 in conditions_a_tester:
envs_nouveaux = []
for fait in faits:
for env1 in envs:
env1 = methode.pattern_match(fait, cond1, env1)
if env1 != methode.echec:
envs_nouveaux.append(env1)
# Si au moins une condition n'est pas satisfaite, la règle ne l'est pas non plus.
if len(envs_nouveaux) == 0:
return []
envs = envs_nouveaux
return envs
def __repr__(self):
""" Représentation d'une règle sous forme de string. """
return '{} => {}'.format(str(self.conditions), str(self.conclusion))
Module .../moteur_avec_variables/chainage_avant_avec_variables.py
¶
from moteur_sans_variables.chainage import Chainage
from .filtre import Filtre
class ChainageAvantAvecVariables(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):
""" 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: les environnements servant à instancier la\
conclusion.
:return: une liste de propositions correspondant aux différentes\
instanciations de la conclusion.
"""
return [self.methode.substitue(regle.conclusion, env) for env in envs]
def chaine(self):
""" Effectue le chaînage avant sur les faits et les règles contenus\
dans la base de connaissances.
"""
queue = self.connaissances.faits[:]
self.reinitialise()
while len(queue) > 0:
fait = queue.pop(0)
if fait not in self.solutions:
self.trace.append(fait)
self.solutions.append(fait)
# Vérifie si des règles sont déclenchées par le nouveau fait.
for regle in self.connaissances.regles:
cond_envs = regle.depend_de(fait, self.methode)
for cond, env in cond_envs.items():
# Remplace l'environnement par ceux qui satisfont
# toutes les conditions de la règle et pas seulement la
# première condition.
envs = regle.satisfaite_par(self.solutions, cond, env, self.methode)
# Ajoute la conclusion de la règle instanciée pour tous
# les environnements possibles.
if len(envs) > 0:
queue.extend(self.instancie_conclusion(regle, envs))
self.trace.append(regle)
return self.solutions
Module .../moteur_avec_variables/filtre.py
¶
from .proposition_avec_variables import est_atomique, est_une_variable, tete, corps
class Filtre:
""" Classe implémentant les méthodes de filtrage de propositions avec\
variables.
"""
echec = 'échec'
def substitue(self, pattern, env):
""" Effectue des substitutions de variables par leurs valeurs dans un\
pattern.
:param pattern: une proposition dont les variables doivent être\
remplacées par des valeurs.\
Une proposition est soit un atome, soit une liste contenant des\
atomes et / ou d'autre listes.
:param dict env: un environnment, c'est-à-dire un dictionnaire de\
substitutions ``{variable : valeur}``.
:return: le pattern dont les variables ont été remplacées par leurs\
valeurs dans l'environnment.
"""
if est_atomique(pattern):
if pattern in env:
return env[pattern]
else:
return pattern
pattern_subst = ()
for sous_pattern in pattern:
sous_pattern_subst = self.substitue(sous_pattern, env)
pattern_subst = pattern_subst + (sous_pattern_subst,)
return pattern_subst
def filtre(self, datum, pattern):
""" Effectue le filtrage entre un datum et un pattern.
:param datum: une proposition sans variables.
:param pattern: une proposition pouvant contenir des variables.
:return: un environnment c'est-à-dire un dictionnaire de\
substitutions ``{variable : valeur}``, ou ``'échec'`` si le filtrage\
échoue.
"""
if len(pattern) == 0 and len(datum) == 0:
return {}
if len(pattern) == 0 or len(datum) == 0:
return Filtre.echec
if est_atomique(pattern):
if datum == pattern:
return {}
if est_une_variable(pattern):
return {pattern: datum}
return Filtre.echec
if est_atomique(datum):
return Filtre.echec
datum_tete = tete(datum)
pattern_tete = tete(pattern)
datum_reste = corps(datum)
pattern_reste = corps(pattern)
tete_env = self.filtre(datum_tete, pattern_tete)
if tete_env == Filtre.echec:
return Filtre.echec
pattern_reste = self.substitue(pattern_reste, tete_env)
reste_env = self.filtre(datum_reste, pattern_reste)
if reste_env == Filtre.echec:
return Filtre.echec
tete_env.update(reste_env)
return tete_env
def pattern_match(self, datum, pattern, env=None):
""" Effectue le filtrage en tenant compte d'un environnement initial.
:param datum: une proposition sans variables.
:param pattern: une proposition pouvant contenir des variables.
:param dict env: l'environnement initial à prendre en compte.
:return: un nouvel environnment ou ``'échec'``.
"""
if env is not None:
env = env.copy()
else:
env = {}
pattern = self.substitue(pattern, env)
resultat = self.filtre(datum, pattern)
if resultat == Filtre.echec:
return Filtre.echec
env.update(resultat)
return env
Module .../moteur_avec_variables/unificateur.py
¶
from .proposition_avec_variables import est_atomique, est_une_variable, tete, corps
class Unificateur:
""" Classe implémentant les méthodes de l'unification de propositions avec\
variables. """
echec = 'échec'
def substitue(self, pattern, env):
""" Effectue des substitutions de variables dans un pattern.
:param pattern: une proposition dont les variables doivent être\
remplacées par d'autres propositions.
:param dict env: un environnment, c'est-à-dire un dictionnaire de\
substitutions ``{variable : proposition}``.
:return: le pattern dont les variables ont été remplacées les\
propositions qui leur sont associées dans l'environnement.
"""
if est_atomique(pattern):
if pattern in env:
return self.substitue(env[pattern], env)
else:
return pattern
pattern_subst = ()
for sous_pattern in pattern:
sous_pattern_subst = self.substitue(sous_pattern, env)
pattern_subst = pattern_subst + (sous_pattern_subst, )
return pattern_subst
def unifie(self, prop1, prop2):
""" Effectue l'unification entre deux propositions.
:param prop1: une proposition pouvant contenir des variables.
:param prop2: une proposition pouvant contenir des variables.
:return: un environnment, c'est-à-dire un dictionnaire de\
substitutions ``{variable : proposition}``, ou ``'échec'`` si\
l'unification a échoué.
"""
if len(prop1) == 0 and len(prop2) == 0:
return {}
if len(prop1) == 0 or len(prop2) == 0:
return Unificateur.echec
# Une des deux propositions est un atome => on essaie de le matcher.
if est_atomique(prop1) or est_atomique(prop2):
if prop1 == prop2:
return {}
if not est_atomique(prop1):
prop1, prop2 = prop2, prop1
if est_une_variable(prop1):
if prop1 in prop2:
return Unificateur.echec
else:
return {prop1: prop2}
if est_une_variable(prop2):
return {prop2: prop1}
# Dans les autres cas, l'unification est un échec.
return Unificateur.echec
# Aucune des propositions n'est atomique : on unifie récursivement.
prop1_tete = tete(prop1)
prop2_tete = tete(prop2)
prop1_reste = corps(prop1)
prop2_reste = corps(prop2)
tete_env = self.unifie(prop1_tete, prop2_tete)
if tete_env == Unificateur.echec:
return Unificateur.echec
prop1_reste = self.substitue(prop1_reste, tete_env)
prop2_reste = self.substitue(prop2_reste, tete_env)
reste_env = self.unifie(prop1_reste, prop2_reste)
if reste_env == Unificateur.echec:
return Unificateur.echec
tete_env.update(reste_env)
return tete_env
def pattern_match(self, prop1, prop2, env=None):
""" Effectue l'unification en tenant compte d'un environnement initial.
:param prop1: une proposition pouvant contenir des variables.
:param prop2: une proposition pouvant contenir des variables.
:param dict env: l'environnement initial à prendre en compte.
:return: un nouvel environnment ou ``'échec'``.
"""
if env is not None:
prop1 = self.substitue(prop1, env)
prop2 = self.substitue(prop2, env)
env = env.copy()
else:
env = {}
resultat = self.unifie(prop1, prop2)
if resultat == Unificateur.echec:
return Unificateur.echec
env.update(resultat)
return env
Documentation du code¶
Fonctions utilitaires pour gérer des propositions sans ou avec variables dans un moteur d’inférence.
-
moteur_avec_variables.proposition_avec_variables.
corps
(proposition)¶ Coupe la proposition courante et retourne la portion située après le premier élément.
A noter que dans le cas d’une proposition atomique, la méthode soulève une exception.
Paramètres: proposition – une proposition. Retourne: le corps de la proposition composée.
-
moteur_avec_variables.proposition_avec_variables.
est_atomique
(proposition)¶ Vérifie si la proposition courante est un atome (c’est le cas s’il s’agit d’un string).
Paramètres: proposition – une proposition. Retourne: True
si la proposition est de type string.
-
moteur_avec_variables.proposition_avec_variables.
est_une_variable
(proposition, marqueur='?')¶ Vérifie si la proposition courante est une variable (c’est le cas s’il s’agit d’un atome dont la description commence par le marqueur de variables).
Paramètres: - proposition – une proposition.
- marqueur – marqueur de variable avec valeur par défaut :
'?'
.
Retourne: True
si l’argument est un atome et commence par le marqueur de variables.
-
moteur_avec_variables.proposition_avec_variables.
lister_variables
(proposition)¶ Retourne un ensemble (de type
set
) contenant les variables mentionnées dans la proposition courante.Paramètres: proposition – une proposition. Retourne: la liste des variables apparaissant dans la proposition.
-
moteur_avec_variables.proposition_avec_variables.
tete
(proposition)¶ Coupe la proposition courante et retourne son premier élément.
A noter que dans le cas d’une proposition atomique, la méthode soulève une exception.
Paramètres: proposition – une proposition. Retourne: la tête de la proposition composée.
-
class
moteur_avec_variables.regle_avec_variables.
RegleAvecVariables
(conditions, conclusion)¶ Représentation d’une règle d’inférence pour le chaînage avec variables.
-
__init__
(conditions, conclusion)¶ Construit une règle étant donné une liste de conditions et une conclusion.
Paramètres: - conditions (list) – une collection de propositions (pouvant contenir des variables) nécessaires à déclencher la règle.
- conclusion – la proposition (pouvant contenir des variables) résultant du déclenchement de la règle.
-
__repr__
()¶ Représentation d’une règle sous forme de string.
-
depend_de
(fait, methode)¶ Vérifie qu’un fait fait partie, sous réserve de substitution, des conditions de la règle.
Paramètres: - fait – un fait qui doit faire partie des conditions de déclenchement.
- methode –
Filtre
ouUnificateur
, détermine le type de pattern match à appliquer.
Retourne: 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.
-
satisfaite_par
(faits, cond, env, methode)¶ Vérifie que des faits suffisent, sous réserve de substitution, à déclencher la règle.
Paramètres: - faits (list) – une liste de faits.
- cond – la condition qui a donné lieu à
env
par le pattern match. - env (dict) – un environnement de départ déjà établi par
depend_de
. - methode –
Filtre
ouUnificateur
, détermine le type de pattern match à appliquer.
Retourne: 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.
-
-
class
moteur_avec_variables.filtre.
Filtre
¶ Classe implémentant les méthodes de filtrage de propositions avec variables.
-
filtre
(datum, pattern)¶ Effectue le filtrage entre un datum et un pattern.
Paramètres: - datum – une proposition sans variables.
- pattern – une proposition pouvant contenir des variables.
Retourne: un environnment c’est-à-dire un dictionnaire de substitutions
{variable : valeur}
, ou'échec'
si le filtrage échoue.
-
pattern_match
(datum, pattern, env=None)¶ Effectue le filtrage en tenant compte d’un environnement initial.
Paramètres: - datum – une proposition sans variables.
- pattern – une proposition pouvant contenir des variables.
- env (dict) – l’environnement initial à prendre en compte.
Retourne: un nouvel environnment ou
'échec'
.
-
substitue
(pattern, env)¶ Effectue des substitutions de variables par leurs valeurs dans un pattern.
Paramètres: - pattern – une proposition dont les variables doivent être remplacées par des valeurs. Une proposition est soit un atome, soit une liste contenant des atomes et / ou d’autre listes.
- env (dict) – un environnment, c’est-à-dire un dictionnaire de substitutions
{variable : valeur}
.
Retourne: le pattern dont les variables ont été remplacées par leurs valeurs dans l’environnment.
-
-
class
moteur_avec_variables.unificateur.
Unificateur
¶ Classe implémentant les méthodes de l’unification de propositions avec variables.
-
pattern_match
(prop1, prop2, env=None)¶ Effectue l’unification en tenant compte d’un environnement initial.
Paramètres: - prop1 – une proposition pouvant contenir des variables.
- prop2 – une proposition pouvant contenir des variables.
- env (dict) – l’environnement initial à prendre en compte.
Retourne: un nouvel environnment ou
'échec'
.
-
substitue
(pattern, env)¶ Effectue des substitutions de variables dans un pattern.
Paramètres: - pattern – une proposition dont les variables doivent être remplacées par d’autres propositions.
- env (dict) – un environnment, c’est-à-dire un dictionnaire de substitutions
{variable : proposition}
.
Retourne: le pattern dont les variables ont été remplacées les propositions qui leur sont associées dans l’environnement.
-
unifie
(prop1, prop2)¶ Effectue l’unification entre deux propositions.
Paramètres: - prop1 – une proposition pouvant contenir des variables.
- prop2 – une proposition pouvant contenir des variables.
Retourne: un environnment, c’est-à-dire un dictionnaire de substitutions
{variable : proposition}
, ou'échec'
si l’unification a échoué.
-
-
class
moteur_avec_variables.chainage_avant_avec_variables.
ChainageAvantAvecVariables
(connaissances, methode=None)¶
Bases: moteur_sans_variables.chainage.Chainage
Un moteur d’inférence à chaînage avant avec variables.
ChainageAvantAvecVariables.
__init__
(connaissances, methode=None)¶
Paramètres: methode – Filtre
ouUnificateur
, détermine le type de pattern match à appliquer.Filtre
par défaut.
ChainageAvantAvecVariables.
affiche_solutions
(indent=None)¶Affiche les solutions d’un chaînage après l’appel à
chaine
.
Paramètres: indent (str) – l’identation souhaitée au début de chaque ligne (quatre espaces par défaut).
ChainageAvantAvecVariables.
affiche_trace
(indent=None)¶Affiche la trace d’un chaînage après l’appel à
chaine
.
Paramètres: indent (str) – l’identation souhaitée au début de chaque ligne (quatre espaces par défaut).
ChainageAvantAvecVariables.
chaine
()¶Effectue le chaînage avant sur les faits et les règles contenus dans la base de connaissances.
ChainageAvantAvecVariables.
instancie_conclusion
(regle, envs)¶Instancie la conclusion d’une règle pour tous les environnements.
Paramètres:
- regle – la règle dont la conclusion doit être instanciée.
- envs (list) – les environnements servant à instancier la conclusion.
Retourne: une liste de propositions correspondant aux différentes instanciations de la conclusion.
ChainageAvantAvecVariables.
reinitialise
()¶Réinitialise le moteur.
La trace et les solutions sont à nouveau vides après l’appel à cette méthode.