Module L17 · Partie H · Intelligence artificielle
L’IA qui raisonne : recherche, contraintes, logique, planification.
Avant l’apprentissage profond, l’IA c’était ceci : représenter un problème par des états et des règles, puis chercher une solution — un coup d’échecs, un emploi du temps, un plan d’actions pour un robot. Ces méthodes n’ont pas disparu : elles sont dans les solveurs SAT qui vérifient les processeurs, dans les planificateurs de ROS, dans AlphaGo (couplées à un réseau) et dans les « raisonneurs » des LLM. Ce module les construit.
Durée : 3 séances · Prérequis : L04 (backtracking), L05 (graphes), L09 (logique). Objectifs : jeux à deux joueurs (minimax, alpha-bêta, heuristiques, tables de transposition), satisfaction de contraintes (propagation, heuristiques, arc-consistance), SAT (DPLL, apprentissage de clauses, encodages), planification classique (STRIPS, recherche dans l’espace des états, heuristiques relaxées), programmation logique (unification, résolution).
Ce que vous saurez faire à la fin
- Écrire un joueur de puissance 4 imbattable en profondeur raisonnable.
- Résoudre sudoku, coloration, emploi du temps par CSP avec propagation.
- Encoder un problème en SAT et le résoudre avec votre propre DPLL.
- Planifier une séquence d’actions pour un robot à partir d’un but.
Références : Artificial Intelligence: A Modern Approach (Russell & Norvig) chapitres 3-11, Handbook of Satisfiability, cours CS221 (Stanford).
Fiche de cours · Définitions
Recherche et IA symbolique : définitions
Fiche de cours · Formules
Formules et complexités
| Stratégie | Complète | Optimale | Temps | Mémoire |
|---|---|---|---|---|
| BFS | oui | coûts unitaires | O(bd) | O(bd) |
| DFS | non (infini) | non | O(bm) | O(bm) |
| Approfondissement itératif | oui | coûts unitaires | O(bd) | O(bd) |
| Coût uniforme | oui | oui | O(b1+C*/ε) | idem |
| A* (h consistante) | oui | oui | exponentiel en l’erreur de h | tous les nœuds ouverts |
| Minimax / alpha-bêta | — | — | O(bm) / O(bm/2) au mieux | O(bm) |
Fiche de cours · Théorèmes et démonstrations
Démonstrations à savoir refaire (1/2)
Fiche de cours · Théorèmes et démonstrations
Démonstrations à savoir refaire (2/2)
Fiche de cours · Méthodes
Méthodes et pièges
Pièges : heuristique non admissible qui rend A* sous-optimal sans prévenir ; oublier l’ensemble fermé (explosion exponentielle sur les grilles) ; état non canonique (le même état représenté différemment n’est pas reconnu) ; profondeur fixe sans quiescence ; DFS sans détection de cycle ; encodage SAT avec des « au plus un » quadratiques sur 1 000 variables (500 000 clauses).
Fiche de cours · Exercices corrigés
Exercices corrigés
01 / Jeux
Minimax : jouer contre un adversaire parfait
L’arbre de jeu
Chaque nœud est une position, chaque arête un coup ; MAX (nous) choisit le maximum des valeurs des enfants, MIN (l’adversaire) le minimum. La valeur remonte des feuilles (positions finales). Le morpion a ~550 000 nœuds : on explore tout. Les échecs en ont 10120 : on coupe à une profondeur d et on estime les feuilles avec une fonction d’évaluation (matériel, mobilité…). Deep Blue (1997) explorait 200 millions de positions par seconde à profondeur 12-14 ; Stockfish aujourd’hui fait mieux avec une évaluation apprise (NNUE) — le mariage recherche + apprentissage.
01 / Jeux
Alpha-bêta : le même résultat, une fraction des nœuds
Pourquoi ça marche
α = la meilleure valeur que MAX est déjà sûr d’obtenir ; β = la meilleure que MIN est sûr d’obtenir. Si dans une branche MIN trouve un coup qui donne ≤ α, MAX n’y viendra jamais : inutile d’explorer le reste. Avec un bon ordre des coups (les meilleurs d’abord), alpha-bêta explore ≈ √(nœuds de minimax) : profondeur doublée à coût égal. Améliorations standard : approfondissement itératif, table de transposition (mémoïser les positions, module L04), recherche de quiescence, coups tueurs. Le puissance 4 a été résolu en 1988 (le premier joueur gagne en jouant au centre).
02 / Contraintes
CSP : variables, domaines, contraintes, propagation
Le triptyque du CSP
1) Backtracking (module L04) : affecter, vérifier, revenir. 2) Propagation : après chaque affectation, réduire les domaines des voisins (forward checking) ; plus loin, l’arc-consistance (AC-3) propage jusqu’au point fixe : chaque valeur d’une variable doit être compatible avec au moins une valeur de chaque voisine. 3) Heuristiques : MRV (variable au plus petit domaine), degré (variable la plus connectée), LCV (valeur qui contraint le moins). Les solveurs de contraintes (OR-Tools, MiniZinc) résolvent emplois du temps, ordonnancement d’usines, allocation de fréquences — des problèmes à des millions de variables.
02 / Contraintes
SAT : le problème universel, et l’algorithme DPLL
Pourquoi SAT est central
Tout problème NP se réduit à SAT (Cook-Levin, module L23) : sudoku, coloration, planification, vérification de circuits, cryptanalyse. Les solveurs modernes (MiniSat, CaDiCaL, Kissat) ajoutent à DPLL l’apprentissage de clauses (CDCL : à chaque conflit, déduire une clause qui l’explique et l’ajouter), des heuristiques de choix de variables (VSIDS), des redémarrages, et résolvent des instances à des millions de variables — Intel et AMD vérifient leurs processeurs ainsi. La transition de phase vers 4,26 clauses/variable, où les instances aléatoires deviennent brutalement difficiles, est un lien remarquable avec la physique statistique.
02 / Contraintes
Encoder un problème en SAT : le sudoku
Encoder, c’est traduire chaque règle en clauses : « au moins un », « au plus un » (paires exclues), et les indices comme clauses unitaires. La propagation unitaire de DPLL fait alors l’essentiel du travail : c’est le « raisonnement » d’un humain (« cette case ne peut plus être que 7 ») automatisé. Le même schéma encode un emploi du temps, un circuit logique ou un plan de robot.
03 / Planification
Planification classique : de l’état initial au but par des actions
Ce qu’un planificateur ajoute
La recherche aveugle explose avec le nombre d’objets (10 blocs : millions d’états). Les planificateurs (Fast Downward, LAMA) utilisent A* avec des heuristiques relaxées : résoudre le problème en ignorant les retraits (« delete relaxation ») donne une borne inférieure calculable en temps polynomial. Le langage standard est PDDL. En robotique, la planification de tâches (« prendre la tasse, ouvrir la porte… ») s’articule avec la planification de mouvement (module L21) : c’est le « task and motion planning », un domaine de recherche où les LLM commencent à servir de générateurs de plans candidats vérifiés par un planificateur symbolique.
03 / Planification
Programmation logique : unification et résolution (Prolog en 50 lignes)
Ce que Prolog a inventé
L’unification (Robinson, 1965) est l’algorithme qui fait correspondre deux motifs avec variables — c’est aussi le cœur de l’inférence de types d’OCaml (module L07) ! La résolution enchaîne les règles en arrière depuis le but. Un programme Prolog est une base de faits et de règles ; « exécuter » = prouver. La dernière requête montre la réversibilité : app écrit pour concaténer sert à découper. Les systèmes experts des années 1980, Datalog (bases de données déductives), les vérificateurs de types et les assistants de preuve descendent de là. Limites : la recherche en profondeur peut boucler, et le raisonnement sous incertitude a exigé les réseaux bayésiens (module L11) puis l’apprentissage.
04 / Synthèse
Symbolique et neuronal : deux moitiés d’une IA
| Symbolique (recherche, logique) | Neuronal (apprentissage) | |
|---|---|---|
| Forces | Exact, vérifiable, explicable, généralise hors des données, compose | Apprend de données brutes (pixels, sons), robuste au bruit, perception |
| Faiblesses | Il faut écrire les règles ; fragile face au bruit et à l’ambiguïté | Boîte noire, hallucine, raisonnement multi-étapes fragile, faim de données |
| Exemples | Solveurs SAT, planificateurs, Prolog, échecs classiques | Vision, parole, LLM, RL profond |
| Hybrides | AlphaGo (MCTS + réseaux), AlphaGeometry (LLM + moteur de déduction, médaille d’or IMO 2024), LLM + outils (Python, solveurs), vérification neuronale-symbolique, programmes synthétisés par LLM puis vérifiés | |
Le débat « symbolique contre connexionniste » a 60 ans. La position dominante aujourd’hui : la perception et l’intuition sont neuronales, le raisonnement fiable a besoin de structure symbolique, et la question de recherche est comment les combiner. Un ingénieur qui maîtrise les deux est rare et précieux.
Cours
Cours 1 — Recherche dans un espace d’états : le cadre unifié
Un problème de recherche = (état initial, fonction successeurs(état) → [(action, état′, coût)], test de but, éventuellement heuristique h). Tout ce module (jeux, CSP, planification) et le module L05 (BFS, Dijkstra, A*) sont des instances de ce cadre.
| Algorithme | Frontière | Complet ? | Optimal ? | Temps / mémoire |
|---|---|---|---|---|
| Largeur (BFS) | File | Oui | Oui (coût unitaire) | O(bd) / O(bd) |
| Profondeur (DFS) | Pile | Non (cycles, infini) | Non | O(bm) / O(bm) — mémoire linéaire |
| Approfondissement itératif | DFS bornée, borne croissante | Oui | Oui (unitaire) | O(bd) / O(bd) — le meilleur des deux |
| Coût uniforme (Dijkstra) | Tas par g | Oui | Oui | O(b1+C*/ε) |
| Gloutonne (best-first par h) | Tas par h | Non | Non | Rapide, myope |
| A* | Tas par g + h | Oui | Oui si h admissible | Optimalement efficace parmi les algorithmes utilisant h |
| IDA* | DFS bornée par f | Oui | Oui | Mémoire linéaire : taquin, Rubik’s cube |
b : facteur de branchement, d : profondeur de la solution, m : profondeur maximale. Le choix se fait sur la mémoire : BFS/A* gardent tous les états visités (10⁷ états = quelques Go) ; IDA* et l’approfondissement itératif n’en gardent qu’un chemin.
Cours
Cours 2 — Exemple travaillé : minimax avec évaluation, en profondeur bornée, sur une position d’échecs simplifiée
Sans aller jusqu’aux échecs complets, le raisonnement se voit sur un jeu à information parfaite : le Nim (des tas d’allumettes ; retirer 1 à 3 d’un tas ; qui prend la dernière gagne). Il a une solution mathématique (XOR des tas) qui sert d’oracle pour vérifier minimax.
Ce qu’il faut retenir. 1) La formulation negamax (ma valeur = − valeur adverse) évite de dupliquer le code MAX/MIN. 2) La table de transposition (lru_cache) transforme un arbre exponentiel en graphe : les mêmes positions reviennent par des ordres de coups différents. 3) Quand une théorie existe (Nim, puissance 4 résolu), elle sert d’oracle de test ; sinon, on teste par cohérence (symétries, jouer contre soi-même). 4) Aux échecs, on coupe à profondeur d et on évalue ; l’effet d’horizon (un désastre juste après la coupure) se traite par la recherche de quiescence (prolonger tant qu’il y a des prises).
Cours
Cours 3 — Modéliser un problème en CSP ou en SAT : la méthode
- Variables et domaines. Une variable par décision élémentaire (case du sudoku, créneau d’un cours, couleur d’une région), domaine fini. Astuce : préférer des domaines petits et des variables nombreuses (SAT : tout est booléen).
- Contraintes. Traduire chaque règle. Les contraintes globales (« toutes différentes », « somme = 10 ») ont des propagateurs spécialisés dans les solveurs CP ; en SAT, on les encode : « exactement un » = « au moins un » (une clause) + « au plus un » (paires, ou encodage séquentiel en O(n)).
- Symétries. Les casser (imposer un ordre) : sans cela, un solveur explore n! solutions équivalentes.
- Objectif. Un CSP n’a pas d’objectif ; pour optimiser, on ajoute une contrainte « coût ≤ K » et on cherche le plus petit K (dichotomie), ou on passe à un solveur MILP/CP-SAT.
- Choisir l’outil. Petit et pédagogique : votre backtracking. Réel : OR-Tools CP-SAT (Python, gratuit, très rapide), MiniZinc (langage de modélisation), PySAT/Z3 (SAT et SMT : SAT + arithmétique, théories).
TP guidé
TP — Solveurs industriels : OR-Tools, Z3, et votre puissance 4 en tournoi (sur PC, 3 h)
- Installer.
pip install ortools z3-solver python-sat. - Sudoku trois fois. (a) Votre DPLL sur votre encodage (module) ; (b)
pysat(Glucose) sur le même encodage : temps ; (c) OR-Tools CP-SAT avecAddAllDifferent(20 lignes). Comparez les temps sur une grille facile, une « diabolique », et une grille vide (combien de solutions ? CP-SAT peut les énumérer). - Emploi du temps réel. Modélisez l’emploi du temps d’une classe (matières, profs, salles, créneaux, contraintes de disponibilité, pas deux fois la même matière le même jour, pauses) en CP-SAT ; ajoutez un objectif (minimiser les trous). Affichez la solution en tableau. Rendez le problème infaisable et lisez la réponse du solveur.
- Z3 et la vérification. Avec Z3 (SMT), prouvez que
dichotomiene peut pas déborder : encodezm = (bas + haut) / 2en entiers 32 bits (BitVec) et demandez s’il existe bas ≤ haut < 2³¹ avec débordement ; puis avecbas + (haut − bas)/2. Z3 trouve le contre-exemple pour la première et « unsat » pour la seconde : vous venez de faire de la vérification formelle (L09). - Tournoi puissance 4. Reprenez alpha-bêta + table de transposition + approfondissement itératif (défi ★). Interface :
jouer(plateau, temps_max). Organisez un tournoi entre profondeurs / évaluations (script arbitre, 20 parties par paire, alternance du trait) ; tableau des résultats ; un classement Elo simple. - Livrable. Dépôt avec les trois solveurs de sudoku et leurs temps, le modèle d’emploi du temps, le script Z3 avec les deux verdicts, le moteur de puissance 4 et le tableau du tournoi.
Exercices
Exercices auto-corrigés — jeux et recherche
Exercice 1 — Negamax avec alpha-bêta sur le morpion
Réécrivez le morpion en negamax alpha-bêta : negamax(p, joueur, alpha, beta) renvoie la valeur du point de vue du joueur au trait. Comptez les nœuds et vérifiez : même valeur que le minimax du cours (0 pour la position vide), et beaucoup moins de nœuds.
Correction
def negamax(p, joueur, alpha=-2, beta=2):
global noeuds_nm; noeuds_nm += 1
g = gagnant(p)
if g: return 1 if g == joueur else -1
if not coups(p): return 0
autre = "O" if joueur == "X" else "X"; v = -2
for i in coups(p):
v = max(v, -negamax(jouer(p, i, joueur), autre, -beta, -alpha)); alpha = max(alpha, v)
if alpha >= beta: break
return vExercice 2 — Approfondissement itératif avec limite de temps
meilleur_coup_temps(p, joueur, budget_s) : profondeur 1, 2, 3… avec alpha-bêta borné en profondeur (évaluation 0 aux feuilles non finales), tant que le temps écoulé reste sous le budget ; renvoie le coup de la dernière profondeur complète et cette profondeur. Sur le morpion, avec 0,5 s, il doit atteindre la profondeur 9 depuis la position vide ; avec un budget minuscule, il doit quand même renvoyer un coup légal.
Correction
def meilleur_coup_temps(p, joueur, budget_s):
t0 = time.perf_counter(); meilleur, prof_ok = coups(p)[0], 0
def nm(p, j, d, alpha, beta):
g = gagnant(p)
if g: return 1 if g == j else -1
if not coups(p) or d == 0: return 0
autre = "O" if j == "X" else "X"; v = -2
for i in coups(p):
v = max(v, -nm(jouer(p, i, j), autre, d - 1, -beta, -alpha)); alpha = max(alpha, v)
if alpha >= beta: break
return v
for d in range(1, 10):
autre = "O" if joueur == "X" else "X"
c = max(coups(p), key=lambda i: -nm(jouer(p, i, joueur), autre, d - 1, -2, 2))
if time.perf_counter() - t0 > budget_s: break
meilleur, prof_ok = c, d
return meilleur, max(prof_ok, 1) if prof_ok else (meilleur, 1)Exercices
Exercices auto-corrigés — contraintes et logique
Exercice 3 — Un CSP générique avec contraintes binaires et AC-3
Écrivez resoudre_csp(variables, domaines, contraintes) où contraintes est un dictionnaire {(u, v): fonction(x, y) → bool} (ajoutez automatiquement la contrainte symétrique (v, u)). Backtracking + MRV + forward checking. Testez sur : coloration d’une carte, puis le cryptarithme « TO + GO = OUT » (contraintes binaires seulement : toutes différentes) avec vérification de l’addition en post-traitement.
Correction
def resoudre_csp(variables, domaines, contraintes):
C = dict(contraintes); C.update({(v, u): (lambda f: (lambda x, y: f(y, x)))(f) for (u, v), f in contraintes.items()})
dom = {v: set(d) for v, d in domaines.items()}; aff = {}
def rec(dom):
if len(aff) == len(variables): return dict(aff)
v = min((x for x in variables if x not in aff), key=lambda x: len(dom[x]))
for val in sorted(dom[v]):
aff[v] = val; d2 = {u: set(d) for u, d in dom.items()}; ok = True
for (a, b), f in C.items():
if a == v and b not in aff:
d2[b] = {y for y in d2[b] if f(val, y)}
if not d2[b]: ok = False; break
if ok:
r = rec(d2)
if r: return r
del aff[v]
return None
return rec(dom)
def toutes(variables, domaines, contraintes, acc):
C = dict(contraintes); C.update({(v, u): (lambda f: (lambda x, y: f(y, x)))(f) for (u, v), f in contraintes.items()})
aff = {}
def rec(dom):
if len(aff) == len(variables): acc.append(dict(aff)); return
v = min((x for x in variables if x not in aff), key=lambda x: len(dom[x]))
for val in sorted(dom[v]):
aff[v] = val; d2 = {u: set(d) for u, d in dom.items()}; ok = True
for (a, b), f in C.items():
if a == v and b not in aff:
d2[b] = {y for y in d2[b] if f(val, y)}
if not d2[b]: ok = False; break
if ok: rec(d2)
del aff[v]
rec({v: set(d) for v, d in domaines.items()})Énumérer toutes les affectations « toutes différentes » (10·9·8·…·3 = 1 814 400 avec S, M ≠ 0 : ≈ 1,45 million) prend quelques secondes ; la vraie résolution encode l’addition comme contrainte (non binaire) et propage : c’est ce que fait CP-SAT en millisecondes.
05 / Défis
Défi ★ — Puissance 4 jouable et table de transposition
Consigne
1) Ajoutez une table de transposition (dictionnaire position → valeur, profondeur) à alpha-bêta et mesurez la réduction du nombre de nœuds à profondeur 7. 2) Approfondissement itératif : profondeur 1, 2, 3… tant que le temps reste sous 1 s ; utilisez le meilleur coup de l’itération précédente en premier. 3) Faites jouer profondeur 4 contre profondeur 6 sur 10 parties (alternez qui commence) : qui gagne ?
Piste
Clé de transposition : tuple("".join(col) for col in p). Ne stocker que si la recherche n’a pas été coupée (valeur exacte), ou stocker avec un drapeau (borne inférieure / supérieure / exacte) comme les vrais moteurs. Pour la question 3, la profondeur 6 doit gagner presque toutes les parties — sauf si l’évaluation est mauvaise : c’est l’occasion de l’améliorer.
05 / Défis
Défi ★★ — Emploi du temps par CSP et par SAT
Consigne
6 cours, 3 créneaux, 2 salles ; contraintes : deux cours d’un même professeur pas au même créneau ; deux cours d’un même groupe d’élèves pas au même créneau ; un cours de TP dans la salle 2 seulement ; le cours 5 doit précéder le cours 6. 1) Modélisez et résolvez avec csp (variables = (créneau, salle) par cours ; il faudra généraliser la fonction aux contraintes binaires quelconques). 2) Encodez en SAT et résolvez avec dpll. 3) Rendez le problème insatisfiable (ajoutez une contrainte) : lequel des deux solveurs le détecte le plus vite, et pourquoi ? 4) Implémentez AC-3 et comptez les nœuds gagnés.
Piste (AC-3)
def ac3(domaines, contraintes):
"""contraintes : {(u, v): fonction(x, y) → bool}. Réduit les domaines jusqu'au point fixe."""
from collections import deque
file = deque(contraintes)
while file:
u, v = file.popleft(); ok = contraintes[(u, v)]
avant = len(domaines[u])
domaines[u] = {x for x in domaines[u] if any(ok(x, y) for y in domaines[v])}
if not domaines[u]: return False
if len(domaines[u]) < avant:
file.extend((w, u) for (w, z) in contraintes if z == u and w != v)
return True05 / Défis
Défi ★★★ — Esprit prépa : CDCL et la planification par SAT
Consigne
1) Ajoutez à DPLL l’apprentissage de clauses : gardez pour chaque affectation son niveau de décision et la clause qui l’a impliquée ; à un conflit, analysez le graphe d’implication pour produire une clause « 1-UIP » et sautez en arrière au bon niveau (backjumping). Mesurez sur 3-SAT n = 80 au seuil. 2) Planification par SAT (Kautz & Selman) : encodez le monde des blocs avec des variables indexées par le temps (fait_t, action_t), les axiomes d’état (un fait persiste sauf si une action le retire), une seule action par pas ; cherchez le plan de longueur k = 1, 2, 3… Comparez à la BFS. 3) Discutez : pourquoi la « delete relaxation » donne-t-elle une heuristique admissible pour A* ? Prouvez-le.
Piste
Pour 1) : représentez l’état du solveur par une pile d’affectations (variable, valeur, niveau, raison) ; lors d’un conflit sur la clause C, remplacez itérativement dans C le littéral affecté le plus récemment au niveau courant par sa clause raison (résolution), jusqu’à ce qu’un seul littéral du niveau courant reste : c’est la clause 1-UIP. Pour 3) : tout plan du problème original est un plan du problème relaxé (les retraits ne peuvent qu’empêcher des actions) ; donc le coût optimal relaxé minore le coût optimal réel — admissible. Le calculer exactement est NP-difficile, mais hmax et hadd s’obtiennent en temps polynomial.
06 / Vérification
Par rapport à minimax, alpha-bêta :
Deux questions supplémentaires
1. Que fait la propagation unitaire ? Si une clause n’a plus qu’un littéral non affecté, ce littéral doit être vrai ; on l’affecte et on continue jusqu’au point fixe.
2. Pourquoi encode-t-on « au plus un » par des paires exclues ? Parce que ¬(a ∧ b) = (¬a ∨ ¬b) est une clause ; n valeurs donnent n(n−1)/2 clauses — il existe des encodages plus compacts (séquentiels, en O(n)).
Référence
Les mots à retenir
| Mot | Définition |
|---|---|
| Minimax | Valeur d’une position si les deux jouent parfaitement. |
| Alpha-bêta | Minimax avec coupures ; même résultat, √ des nœuds. |
| Fonction d’évaluation | Estimation heuristique d’une position non finale. |
| Table de transposition | Mémoïsation des positions. |
| CSP | Variables, domaines, contraintes ; backtracking + propagation + heuristiques. |
| Arc-consistance (AC-3) | Chaque valeur compatible avec au moins une valeur de chaque voisine. |
| SAT / DPLL / CDCL | Satisfiabilité ; propagation unitaire + branchement ; + apprentissage de clauses. |
| STRIPS / PDDL | Actions par préconditions et effets ; langage de planification. |
| Delete relaxation | Heuristique admissible obtenue en ignorant les effets négatifs. |
| Unification / résolution | Faire correspondre des termes / enchaîner des règles (Prolog). |
| Neuro-symbolique | Combiner apprentissage et raisonnement. |
Pour continuer
Fin de la partie H : vous connaissez les deux IA
Partie I : robotique et systèmes embarqués avancés. Module suivant : les systèmes embarqués — interruptions, temps réel, RTOS, registres, C bas niveau — ce qui se passe dans le microcontrôleur de votre robot.
À faire chez soi
- Lire Russell & Norvig chapitres 5 (jeux) et 6 (CSP) ; faire les exercices de programmation.
- Sur PC : installer
python-satouz3-solveret résoudre le sudoku, puis un problème de votre choix (emploi du temps du lycée ?). - Écrire un joueur d’Othello avec alpha-bêta et le faire jouer contre des amis.