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: Truesi 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: Truesi 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 –
FiltreouUnificateur, 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.
Falsesi 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 à
envpar le pattern match. - env (dict) – un environnement de départ déjà établi par
depend_de. - methode –
FiltreouUnificateur, 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 – FiltreouUnificateur, détermine le type de pattern match à appliquer.Filtrepar 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.