1. La révolution algorithmique silencieuse de l’informatique
Ouvrez n’importe quel manuel d’informatique théorique et vous y trouverez cet avertissement solennel : « Le problème de satisfaisabilité booléenne (SAT) est NP-complet. Sa complexité temporelle dans le pire des cas croissant exponentiellement en $O(2^n)$ avec le nombre de variables $n$, les grandes instances sont fondamentalement intraitables. »
Sur le plan purement combinatoire, le constat semble sans appel. Une formule portant sur $n = 100$ variables booléennes admet $2^{100} \approx 1,26 \times 10^{30}$ affectations de vérité possibles. Même si un supercalculateur inspectait $10^{12}$ affectations par seconde depuis le Big Bang (il y a 13,8 milliards d’années), il aurait exploré moins de $10^{-8}$ de l’espace de recherche aujourd’hui. De fait, vers 1990, les meilleurs solveurs SAT butaient sur des formules d’à peine $100$ variables et $400$ clauses.
Pourtant, au XXIᵉ siècle, la pratique industrielle dément cette intuition des millions de fois par jour. Les solveurs SAT et SMT modernes — tels que MiniSat, Glucose, Lingeling, CaDiCaL, Kissat et Z3 (Microsoft Research) — résolvent quotidiennement des instances de vérification formelle de microprocesseurs, d’optimisation de compilateurs et de gestion de paquets contenant $10\,000\,000$ ($10^7$) variables et $50\,000\,000$ ($5 \times 10^7$) clauses en quelques secondes.
Dans le volume 4B (section 7.2.2.2) de The Art of Computer Programming publié en 2015, Donald E. Knuth a consacré plus de 300 pages aux seuls solveurs SAT, saluant les avancées de la fin des années 1990 et du début des années 2000 comme l’une des plus grandes révolutions algorithmiques de l’histoire de l’informatique. Autrefois, réduire un problème à SAT revenait à prouver son insolubilité pratique ; aujourd’hui, traduire un problème combinatoire en SAT est la stratégie royale pour le résoudre instantanément.
2. Forme normale conjonctive (CNF), transformation de Tseitin et théorème de Cook-Levin (1971)
2.1 Forme normale conjonctive (CNF) et encodage linéaire de Tseitin
Un solveur SAT prend en entrée une formule propositionnelle $\Phi$ sur $n$ variables booléennes $V = \{x_1, \dots, x_n\}$ à valeurs dans $\{0, 1\}$. Un littéral $\ell \in \{x_v, \neg x_v\}$ est une variable ou sa négation ; une clause $C_i = \bigvee_{j=1}^{k_i} \ell_{i,j}$ est une disjonction (OU logique, $\lor$) de littéraux ; et une formule en forme normale conjonctive (CNF) est une conjonction (ET logique, $\land$) de $m$ clauses :
$$\Phi(x_1, \dots, x_n) = \bigwedge_{i=1}^m C_i = \bigwedge_{i=1}^m \left(\bigvee_{j=1}^{k_i} \ell_{i,j}\right)$$Si l’on convertissait naïvement un circuit booléen arbitraire en CNF par application répétée de la distributivité $A \lor (B \land C) \equiv (A \lor B) \land (A \lor C)$, la taille de la formule exploserait exponentiellement. En 1968, Grigori Tseitin a résolu ce problème grâce à la transformation de Tseitin : on introduit une variable auxiliaire $y_g$ pour la sortie de chaque porte logique $g$ du circuit et l’on encode l’équivalence locale $y_g \leftrightarrow \text{porte}(a, b)$ en seulement 3 ou 4 clauses. Par exemple, une porte ET $y_g \leftrightarrow (a \land b)$ se traduit par exactement trois clauses :
$$\left(\neg y_g \lor a\right) \;\land\; \left(\neg y_g \lor b\right) \;\land\; \left(y_g \lor \neg a \lor \neg b\right)$$Tout circuit comportant $|G|$ portes est ainsi converti en temps et en espace strictement linéaires $O(|G|)$ en une formule 3-CNF équisatisfaisable.
2.2 Le théorème de Cook-Levin et la falaise de complexité entre 2-SAT et 3-SAT
En 1971, Stephen Cook et Leonid Levin ont démontré indépendamment le théorème de Cook-Levin : tout problème de décision appartenant à la classe $\text{NP}$ peut être réduit en temps polynomial à SAT en encodant le tableau de calcul d’une machine de Turing non déterministe sur $p(n)$ étapes (états $S_{t,q}$, positions de tête $H_{t,i}$, symboles du ruban $T_{t,i,\sigma}$) sous forme de clauses CNF.
Lorsque chaque clause contient au plus $k$ littéraux ($k$-SAT), une falaise abrupte sépare $k = 2$ et $k = 3$ :
- 2-SAT se résout en temps linéaire $O(n + m)$ : une clause binaire $(\ell_1 \lor \ell_2)$ équivaut à deux implications dirigées déterministes $(\neg \ell_1 \Rightarrow \ell_2) \land (\neg \ell_2 \Rightarrow \ell_1)$. Dans le graphe d’implication à $2n$ sommets, l’algorithme de Tarjan identifie les composantes fortement connexes (CFC / SCC) en temps $O(n+m)$ : la formule est satisfaisable si et seulement si aucune variable $x_v$ n’appartient à la même CFC que $\neg x_v$.
- 3-SAT est NP-complet : une clause ternaire $(\ell_1 \lor \ell_2 \lor \ell_3)$ s’écrit $(\neg \ell_1 \land \neg \ell_2) \Rightarrow \ell_3$, une hyperarête nécessitant deux prémisses simultanées. Sous l’hypothèse du temps exponentiel (ETH), aucun algorithme déterministe ne peut résoudre 3-SAT en temps sous-exponentiel $2^{o(n)}$ dans le pire cas.
3. L’algorithme DPLL classique (1962) et le piège du retour sur trace chronologique
L’algorithme DPLL (Davis, Putnam, Logemann, Loveland, 1962) repose sur la recherche en profondeur couplée à la propagation unitaire (Unit Propagation / Boolean Constraint Propagation, BCP) :
Propagation unitaire (BCP) : Si, sous l’affectation partielle courante, tous les littéraux d’une clause $C = (\ell_1 \lor \dots \lor \ell_{k-1} \lor \ell_k)$ sauf $\ell_k$ valent
False($0$) et que $\ell_k$ est non affecté, $C$ est une clause unitaire. Le solveur force immédiatement $\ell_k = \text{True}$ ($1$), déclenchant une cascade domino d’implications jusqu’à saturation ou jusqu’à ce qu’une clause devienne entièrement fausse (Conflit).
Pourquoi DPLL plafonnait-il autour de 100 variables ? À cause du retour sur trace chronologique (Chronological Backtracking). Si le solveur fixe $x_1 = 0$ au niveau de décision $d = 1$, effectue 48 choix totalement indépendants $x_2, \dots, x_{49}$ aux niveaux $d = 2 \dots 49$, puis choisit $x_{50} = 1$ au niveau $d = 50$, provoquant un conflit local uniquement dû à $x_1 = 0$ et $x_{50}$ via les clauses $(x_1 \lor \neg x_{50} \lor \pm x_{51})$ et $(x_1 \lor x_{50} \lor \pm x_{52})$, DPLL est incapable d’identifier que la faute remonte au niveau 1. Il recule d’un seul cran au niveau $d = 49$, inverse l’innocente variable $x_{49}$ et répète exactement le même échec à travers $2^{48} \approx 2,81 \times 10^{14}$ sous-arbres inutiles (piétinement / thrashing) !
4. La révolution CDCL : graphe d’implication, résolution 1-UIP et saut arrière
Introduit dans GRASP (Marques-Silva & Sakallah, 1996) et porté à maturité dans Chaff (Moskewicz et al., 2001), l’apprentissage de clauses dirigé par les conflits (CDCL) remplace l’aveuglement chronologique par le diagnostic causal.
flowchart TD
Start["Entrée : Formule CNF Φ (Niveau de décision d = 0)"] --> BCP["Propagation des contraintes booléennes (BCP)<br/>Propagation unitaire rapide via Deux Littéraux Surveillés (2WL)"]
BCP --> CheckConflict{"Clause de conflit κ<br/>falsifiée ?"}
CheckConflict -- "Aucun conflit" --> CheckAllAssigned{"Toutes les variables<br/>affectées ?"}
CheckAllAssigned -- "Oui" --> SAT["SATISFAISABLE (SAT)<br/>Renvoyer le modèle σ"]
CheckAllAssigned -- "Non" --> Branch["Branchement (Decision) : d ← d + 1<br/>Choisir la variable avec activité VSIDS maximale<br/>Affecter la polarité via Phase Saving"]
Branch --> BCP
CheckConflict -- "Oui (Conflit !)" --> CheckLevelZero{"Au niveau racine<br/>d == 0 ?"}
CheckLevelZero -- "Oui" --> UNSAT["INSATISFAISABLE (UNSAT)<br/>Émettre le certificat de preuve DRAT"]
CheckLevelZero -- "Non (d > 0)" --> Analyze["Analyse de conflit (Résolution 1-UIP)<br/>Remonter le graphe d'implication depuis κ<br/>Résoudre jusqu'à n'avoir qu'un seul littéral du niveau d (First UIP)"]
Analyze --> Learn["Apprendre la clause & incrémenter VSIDS<br/>Ajouter C_learned à la base (calculer LBD)<br/>Augmenter le score VSIDS des variables impliquées"]
Learn --> Backjump["Saut arrière non chronologique (Backjump)<br/>Sauter directement au niveau d'assertion d_back (2e plus haut niveau dans C_learned)<br/>C_learned devient immédiatement unitaire dans BCP !"]
Backjump --> BCP
4.1 Graphe d’implication, résolution booléenne et premier point d’implication unique (1-UIP)
Chaque littéral affecté $\ell@d$ conserve son niveau de décision $d$ et sa clause antécédente (raison) $\text{reason}(\ell)$. Lorsqu’une clause $\kappa$ est falsifiée, CDCL initialise $C \leftarrow \kappa$ et remonte la pile d’affectation (Trail) au niveau courant $d_{\text{curr}}$ en appliquant itérativement la règle de résolution booléenne :
$$\frac{(A \lor x) \qquad (\neg x \lor B)}{(A \lor B)} = \text{Res}(C_1, C_2, x)$$Dans le graphe d’implication, un sommet $u@d_{\text{curr}}$ est un point d’implication unique (Unique Implication Point, UIP) si tout chemin dirigé allant du littéral de décision $\ell_{\text{dec}}@d_{\text{curr}}$ au sommet de conflit $\Lambda$ passe obligatoirement par $u$ (dominateur). Le UIP le plus proche du conflit est appelé premier UIP (First UIP / 1-UIP). Algébriquement, la résolution arrière atteint le 1-UIP à l’instant précis où la clause de travail $C$ ne contient plus qu’un seul littéral appartenant au niveau de décision courant $d_{\text{curr}}$ !
Dans notre exemple avec $x_1 = 0@1, x_2 = 0@2, x_3 = 1@3$, la décision $x_4 = 1@5$ et les clauses $\omega_1 = (x_1 \lor \neg x_4 \lor x_5)$, $\omega_2 = (\neg x_4 \lor \neg x_5 \lor x_6)$, $\omega_3 = (x_2 \lor \neg x_6 \lor x_7)$, $\omega_4 = (\neg x_3 \lor \neg x_6 \lor x_8)$ menant au conflit $\kappa = (\neg x_7 \lor \neg x_8)$, deux étapes de résolution de $\kappa$ avec $\omega_4$ (pivot $x_8$) puis $\omega_3$ (pivot $x_7$) isolent immédiatement la clause apprise 1-UIP :
$$C_{\text{learned}} = \left(x_2@2 \lor \neg x_3@3 \lor \neg x_6@5\right)$$4.2 Saut arrière non chronologique (Backjumping) et niveau d’assertion
Les niveaux présents dans $C_{\text{learned}}$ étant $\{5, 3, 2\}$, le solveur saute directement au deuxième niveau de décision le plus élevé (niveau d’assertion) $d_{\text{back}} = 3$ ! Au niveau $3$, $x_2@2$ et $\neg x_3@3$ sont toujours faux tandis que $\neg x_6$ redevient libre : $C_{\text{learned}}$ agit instantanément comme une clause unitaire asserteuse et force $\neg x_6 = 1@3$ ($x_6 = 0@3$) via BCP !
5. Miracles d’ingénierie : deux littéraux surveillés (2WL), VSIDS et LBD
- Deux littéraux surveillés (Two-Watched Literals, 2WL, Chaff 2001) : Puisque $90\%$ du temps CPU est consacré à BCP, 2WL s’appuie sur l’invariant suivant : tant qu’une clause comporte au moins deux littéraux non falsifiés (libres ou vrais), elle ne peut devenir ni unitaire ni conflictuelle. Le solveur ne surveille donc que deux littéraux $w_1, w_2$ par clause. Lors d’un saut arrière (Backjump), la désaffectation des variables préserve automatiquement cet invariant : le retour arrière ne nécessite aucune mise à jour de pointeurs ($O(1)$, coût zéro) !
- VSIDS (Variable State Independent Decaying Sum) & Phase Saving : Chaque variable impliquée dans la résolution d’un conflit reçoit un bonus d’activité $+\Delta$, tandis que $\Delta \leftarrow \Delta / \gamma$ ($\gamma = 0.95$) croît après chaque conflit. Cette moyenne mobile exponentielle concentre le branchement sur le goulot d’étranglement actif.
- LBD (Literal Block Distance / Clauses « Glue ») & Redémarrages rapides : Glucose (Audemard & Simon, 2009) mesure la qualité d’une clause apprise par le nombre $\text{LBD}(C)$ de niveaux de décision distincts qu’elle contient. Les clauses avec $\text{LBD} = 2$ (clauses de colle / Glue Clauses) lient directement l’UIP courant à un unique bloc de décision passé et sont conservées indéfiniment.
| Dimension architecturale | DPLL classique (1962) | CDCL 1re génération (GRASP / Chaff 2001) | CDCL de pointe (Glucose / CaDiCaL / Kissat) |
|---|---|---|---|
| Structure BCP | Compteurs de littéraux sur chaque clause | Deux littéraux surveillés (2WL) (coût de backtrack $O(1)$ nul) | 2WL avec littéraux bloquants en ligne + listes binaires dédiées |
| Gestion des conflits | Retour chronologique ($d-1$, thrashing en $2^k$) | Saut arrière non chronologique au niveau d’assertion $d_{\text{back}}$ | Hybride Backjumping + retour chronologique local (2018) |
| Apprentissage | Aucun (arbre sans mémoire) | Résolution 1-UIP + minimisation récursive des clauses | 1-UIP + auto-subsomption à la volée, vivification et élimination BVE |
| Heuristique de choix | Ordre statique / DLIS | VSIDS (score d’activité à décroissance exponentielle) | Alternance EVSIDS + VMTF + Phase Saving & recherche locale WalkSAT |
| Gestion de la base | N/A | Suppression périodique par taille et activité | Architecture à 3 niveaux basée sur LBD (clauses Glue $\text{LBD} \le 2$ permanentes) |
6. Implémentation Python complète d’un moteur CDCL
Voici une implémentation Python autonome et complète d’un solveur CDCL intégrant BCP, graphe d’implication, résolution 1-UIP, saut arrière non chronologique et VSIDS :
| |
7. Physique statistique et transition de phase du $k$-SAT aléatoire ($\alpha_c \approx 4,267$)
Pourquoi les solveurs CDCL écrasent-ils des instances industrielles à $10^7$ variables mais échouent-ils sur des formules 3-SAT aléatoires de $700$ variables ? Pour une formule 3-SAT aléatoire à $n$ variables et $m = \alpha n$ clauses (densité de contraintes $\alpha = m / n$), la méthode du premier moment donne l’espérance du nombre de solutions $Z$ :
$$\mathbb{E}[Z] = 2^n \left(1 - 2^{-3}\right)^m = \left(2 \cdot \left(\frac{7}{8}\right)^\alpha\right)^n \implies \alpha_{\text{recuit}} = \frac{\ln 2}{\ln(8/7)} \approx 5,191$$Grâce à la méthode de la cavité et à la brisure de symétrie des répliques à une étape (1RSB) issue de la physique des verres de spin, Mézard, Parisi et Zecchina (2002) ont établi le seuil critique exact du 3-SAT aléatoire :
$$\alpha_c \approx 4,267$$
flowchart TD
subgraph PhaseTransition["Transition de phase du 3-SAT aléatoire selon la densité α = m/n"]
S1["1. Phase liquide sous-contrainte (α < 3,86 : Easy-SAT)<br/>• Les solutions forment un unique amas géant connexe sur {0,1}^n<br/>• Résolution rapide en temps polynomial"]
S2["2. Phase vitreuse de fragmentation et gel (3,86 < α < 4,267 : Hard-SAT)<br/>• À α_d ≈ 3,86 (transition 1RSB), l'espace des solutions éclate en un nombre exponentiel d'îlots isolés !<br/>• À α_f ≈ 4,254, les variables se figent rigidement dans chaque amas (Freezing)"]
S3["3. Seuil critique de satisfaisabilité (α_c ≈ 4,267 : Pic de dureté exponentiel)<br/>• La probabilité de SAT chute brutalement de 1 à 0<br/>• Toute réfutation par résolution exige une taille exponentielle 2^Ω(n) (Chvátal–Szemerédi)"]
S4["4. Phase sur-contrainte (α > 4,267 : Easy-UNSAT)<br/>• La forte densité de clauses déclenche des contradictions à faible profondeur<br/>• Le temps de résolution diminue quand α augmente"]
end
S1 -->|"α_d ≈ 3,86 (Fragmentation)"| S2
S2 -->|"α_c ≈ 4,267 (Transition UNSAT)"| S3
S3 -->|"α > 4,267 (Conflits rapides)"| S4
Au point critique $\alpha_c \approx 4,267$, l’hypergraphe aléatoire est un graphe expanseur dépourvu de goulots d’étranglement locaux, ce qui impose à toute preuve par résolution une longueur exponentielle $2^{\Omega(n)}$ (Chvátal & Szemerédi, 1988). Les instances industrielles réelles possèdent au contraire une structure cachée :
- Modularité et structure communautaire (Ansótegui et al. 2012) : les circuits et logiciels sont composés de modules faiblement couplés par un petit nombre de variables d’interface, que la résolution 1-UIP identifie immédiatement comme dominateurs pour générer des clauses Glue ($\text{LBD}=2$).
- Petits ensembles « Backdoor » (Williams, Gomes, Selman 2003) : il suffit souvent d’affecter quelques dizaines de variables clés $B \subset V$ ($|B| \ll n$) pour que le reste de la formule à 10 millions de variables s’effondre intégralement par propagation unitaire !
8. Triomphes industriels et preuve mathématique de 200 téraoctets
- Mathématiques assistées par ordinateur et preuve DRAT de 200 To (Heule, Kullmann, Marek 2016) : Peut-on bicolorer $\{1, \dots, N\}$ sans créer de triplet pythagoricien $a^2 + b^2 = c^2$ monochrome ? Par Cube-and-Conquer, Heule et al. ont prouvé sur 800 cœurs que $N = 7824$ est satisfaisable (SAT) mais que $N = 7825$ est insatisfaisable (UNSAT), produisant un certificat de preuve formelle DRAT de 200 téraoctets vérifié bit à bit ! Ils ont ensuite résolu le nombre de Schur $S(5) = 160$ (2018) et la conjecture de Keller en dimension 7 (2020).
- Vérification formelle EDA (CEC et BMC) : depuis le bogue FDIV du Pentium d’Intel (1994), chaque processeur est vérifié en construisant un circuit Miter (XOR entre spécification RTL et circuit synthétisé) soumis à un solveur SAT.
- Gestionnaires de paquets (Debian apt, Rust Cargo, Dart PubGrub, uv) : l’algorithme PubGrub exécute directement CDCL sur les contraintes de versions et traduit le graphe de résolution 1-UIP en explications claires lors des conflits.
- De SAT à SMT ($\text{DPLL}(T)$ : Z3 et cvc5) : en couplant CDCL avec des solveurs de théories (arithmétique linéaire, vecteurs de bits, tableaux), les solveurs SMT propulsent l’exécution symbolique et le moteur de sécurité cloud AWS Zelkova.
