TD 2 - Modèles de calcul Remarques préliminaires Si ou désigne une relation binaire (de dérivation/transition suivant le contexte), on notera ou sa clôture transitive, comprendre la relation obenue en itérant la relation initiale un nombre arbitraire de fois Suivant le modèle considéré, on définira une notion de fonction calculée (de N k N ou, ce qui est équivalent à codage près, de N N) ou de langage reconnu (que l'on peut interpréter comme une fonction calculée de N {0, 1}) Machine RAM La machine RAM (pour Random-Access Machine) est un modèle formellement assez proche de l'assembleur de nos microprocesseurs Une machine RAM (sur les entiers) dispose d'un nombre infini dénombrable de registres, notés [1], [2], et pouvant contenir des entiers naturels (arbitrairement grands), et d'un programme constitué d'une séquence d'instructions parmi : UNIT r : effectue [r] := 1 ; ADD r d r 1 r 2 : effectue [r d ] := [r 1 ] + [r 2 ] ; SUB r d r 1 r 2 : effectue [r d ] := [r 1 ] [r 2 ] ; SHIFT r : effectue [r] := [r]/2 (en division entière) ; COPY_I r d r s : effectue [r d ] := [[r s ]] (attention à l'accès indirect de la source) ; I_COPY r d r s : effectue [[r d ]] := [r s ] (attention à l'accès indirect de la destination) ; JNZ r i : sauter à la i e instruction si [r] > 0 ; HALT : mettre fin au programme La machine exécute séquentiellement les instructions dans l'ordre du programme et s'arrête à la dernière instruction ou lorsqu'elle rencontre l'instruction HALT Fonction calculée Une fonction (partielle) f : N k N est calculable par machine RAM s'il existe une machine RAM telle que, pour tous v 1,, v k N : si f (v 1,, v k ) est définie, la machine s'arrête avec [1] = f (v 1,, v k ) lorsqu'elle est lancée depuis la configuration initiale dans laquelle [1] = v 1,, [k] = v k et [i] = 0 pour tout i > k ; sinon, la machine ne s'arrête pas depuis cette même configuration initiale Exercice 1 Un peu d'arithmétique Programmer une machine RAM prenant en entrée deux entiers n et i et calculant le i e bit de l'écriture binaire de n Programmer une machine RAM réalisant la multiplication de deux entiers Programmer une machine RAM calculant la factorielle de son entrée Exercice 2 Indirections Les instructions COPY_I et I_COPY permettent des accès indirects Supposons que l'on veuille stocker dans des registres les termes successifs d'une suite numérique jusqu'à un rang arbitrairement grand (par exemple donné en entrée) Justifier l'intérêt de ces indirections Qu'implique la non utilisation des indirections dans un programme? Comment pourrait-on répondre à la question précédente sans les indirections? Et si les registres étaient de taille bornée (et ne pouvaient par exemple contenir que des entiers codés sur 64 bits)? 1/5
Automate à pile(s) Un automate à pile est un automate fini qui dispose d'un espace de stockage sous la forme d'une pile L'automate effectue des transitions en fonction de la lettre courante du mot d'entrée mais également de la lettre de haut de pile À chaque transition, il dépile la lettre de haut de pile et peut empiler un nombre arbitraire de lettres à la place Formellement, un automate à pile (non déterministe) A est un 6-uplet A = (Q, Σ, Γ, δ, q 0, F ) où Q est l'ensemble fini des états, Σ l'alphabet d'entrée, Γ l'alphabet de pile qui contient un symbole spécial Z de fond de pile, δ : Q Σ {ε} Γ P(Q Γ ) la fonction de transition, q 0 l'état initial et F l'ensemble des états finaux Une configuration de A est un triplet (q, w, p) Q Σ Γ où q représente l'état courant, w le suffixe du mot d'entrée restant à lire et p le contenu actuel de la pile On définit alors la relation de transition sur les configurations : (q, aw, bp) (q, w, p p) (q, p ) δ(q, a, b) (q, w, bp) (q, w, p p) (q, p ) δ(q, ε, b) (ε-transition) On généralise naturellement cette définition à un nombre k 2 arbitraire de piles Langage reconnu Un mot w Σ est accepté par l'automate A s'il existe une séquence de transitions (q 0, w, Z) (q f, ε, p) avec q f F Cela définit le langage L(A) reconnu par l'automate Exercice 3 Des langages typiquement algébriques Programmer un automate à (une) pile qui reconnaît le langage {a n b n, n N} sur l'alphabet d'entrée Σ = {a, b} Programmer un automate à (une) pile qui reconnaît le langage des mots bien parenthésés 1 sur l'alphabet d'entrée Σ = {(, )} (qui ne contient qu'une seule forme de parenthèses) Automate à compteur(s) Un automate à k compteurs (déterministe), ou machine de Minsky, est un quintuplet A = (k, Q, δ, q 0, q f ) où k 1 est le nombre de compteurs, Q est l'ensemble fini des états, δ : Q\{q f } {0, +} k Q { 1, 0, 1} k est la fonction partielle de transition, q 0, q f Q sont les états initial et final Une configuration de A est un (k + 1)-uplet (q, n 1,, n k ) Q N k où q représente l'état courant et n 1,, n k les valeurs courantes des k compteurs Soit sgn la fonction signe définie sur N par sgn(n) = 0 si n = 0 et sgn(n) = + sinon On définit la relation de transition sur l'ensemble des configurations : (q, n 1,, n k ) (q, n 1 + d 1,, n k + d k ) si δ(q, sgn(n 1 ),, sgn(n k )) = (q, d 1,, d k ) et i, n i + d i 0 1 Un théorème de Chomsky et Schützenberger affirme que tous les langages reconnus par automate à une pile sont, à des transformations simples près, des langages de mots bien parenthésés (mais pour un nombre arbitraire de symboles de parenthèses) 2/5
Fonction calculée Une fonction (partielle) f : N k N est calculable par automate à compteurs s'il existe un automate à au moins k compteurs tel que, pour tous v 1,, v k N : si f (v 1,, v k ) est définie, l'automate atteint l'état q f avec le premier compteur à f (v 1,, v k ) lorsqu'il est lancé depuis la configuration initiale dans laquelle les k premiers compteurs contiennent respectivement v 1, v k et les autres 0 ; sinon, soit l'automate ne s'arrête pas, soit il s'arrête (c'est à dire qu'aucune transition n'est possible) dans un état différent de q f depuis cette même configuration initiale Exercice 4 Un peu d'arithmétique Encore? Programmer un automate à compteurs qui calcule l'addition, puis la multiplication de deux entiers naturels Programmer un automate à compteurs qui calcule le n e terme de la suite de Fibonacci pour n donné en entrée Système de Post Les systèmes de Post (tag systems) sont des systèmes de réécriture déterministes et séquentiels introduits par E Post dès les années 1920 Formellement, un k-système de Post est un triplet (k, Σ, p) où k 1, Σ est un alphabet fini et p : Σ Σ Le k-système transforme un mot w Σ en supprimant ses k premières lettres et en ajoutant p(w 1 ) à la fin (où w 1 est la première lettre de w) Formellement, on définit la relation de dérivation sur Σ par : w w k+1,, w p(w 1 ) si w k Cette relation n'est correctement définie que sur les mots w de taille au moins k Langage reconnu Un mot initial w est accepté par un k-système de Post si la séquence de dérivations partant de w s'arrête, c'est-à-dire que l'on atteint un mot de taille strictement plus petite que k Cela définit le langage L Σ reconnu par le système Exercice 5 Comme une lettre à la poste Programmer un système de Post qui reconnaît le langage des mots de longueur paire Programmer un système de Post qui calcule la suite de Syracuse en unaire définie par la relation de récurrence u n+1 = u n /2 si u n est pair et u n+1 = 3u n + 1 sinon Le système s'arrête dès que l'on atteint la valeur 1 Quel est le langage reconnu par ce système? 3/5
λ-calcul Le λ-calcul est un système formel (purement syntaxique) introduit par A Church dans les années 1930 Il constitue le cœur des langages de programmation fonctionnels contemporains comme OCaml ou Haskell Les termes (éléments syntaxiques) du λ-calcul sont appelés λ-termes et suivent la syntaxe récursive suivante (où x représente une variable et M un terme) : M ::= x M M λxm Ainsi, un λ-terme est soit une variable x (penser à une variable x en OCaml), soit l'application M M d'un terme M à un terme M (penser à l'application M M' d'un argument M' à une fonction M en OCaml), soit une fonction qui prend un paramètre représenté par la variable x et dont le corps est le terme M (penser à fun x -> M en OCaml) On associe à cette syntaxe un système de réécriture, noté, dont la seule règle est la β-réduction (où M[M /x] désigne le terme M dans lequel on a remplacé toutes les occurences de la variable x par le terme M ) : (λxm)m M[M /x] Ainsi l'application d'un argument M à une fonction λxm se réécrit en le corps M[M /x] de la fonction dans lequel on a remplacé les x par des M Les autres règles de réduction du λ-calcul, que l'on n'énoncera par formellement ici, ne servent qu'à permettre l'usage de la β-réduction à l'intérieur d'un terme Exercice 6 Quelques réductions Pas si β On pose I = λxx et F = λxλy(y (y I)) Réduire F (I I) F (I I) autant que possible (en ne faisant qu'une β-réduction à chaque étape) et comparez votre résultat avec celui de vos voisins Même question avec Ω = (λxx x) (λxx x) Proposer un terme dont la taille augmente à l'infini au fil des réductions Entiers de Church En λ-calcul, on représente l'entier n N par le terme suivant : [n] = λf λx(f (f ( (f x) )) = λf λx(f n x) } {{ } n Fonction calculée Une fonction (partielle) f : N k N est λ-calculable s'il existe un λ-terme T tel que T [v 1 ] [v k ] [f (v 1,, v k )] lorsque f (v 1,, v k ) est définie, la séquence des dérivations ne terminant pas sinon Exercice 7 Un peu d'arithmétique Toujours! Programmer une fonction incr telle que incr [n] [n + 1] Programmer une fonction add telle que add [n] [m] [n + m] Programmer une fonction mult telle que mult [n] [m] [n m] 4/5
Équivalence des modèles Exercice 8 En vous appuyant sur votre cours de compilation, que pensez-vous du lien entre le modèle de la machine RAM et le langage IMP vu en cours? Exercice 9 Le cycle des machines Montrer que l'on peut simuler une machine de Turing avec un automate à 2 piles déterministe On supposera pour cette simulation que le mot d'entrée lu par l'automate dispose d'un délimiteur # de fin de mot (et l'on rappelle que l'automate peut faire des ε-transitions qui ne consomment aucune lettre du mot d'entrée) Utiliser une pile pour stocker la partie du ruban située à droite de la tête de lecture et l'autre pour la partie gauche Montrer que l'on peut simuler un automate à 1 pile par un automate à 2 compteurs En déduire que l'on peut simuler un automate à 2 piles par un automate à 4 compteurs, justifier que l'on peut immédiatement réduire ce nombre à 3 compteurs Proposer enfin une méthode afin de réduire le nombre de compteurs à 2 Montrer que l'on peut simuler un automate à compteurs par une machine RAM Montrer que l'on peut simuler une machine RAM par machine de Turing On en conclurera que les quatre modèles précédents sont équivalents Exercice 10 Le cycle des systèmes de réécriture Montrer que l'on peut simuler une machine de Turing à l'aide d'un 2-système de Post Montrer que l'on peut simuler un 2-système de Post par un λ-terme du λ-calcul Justifier que l'on peut calculer une séquence de réductions d'un λ-terme sur machine de Turing On en conclurera que les trois modèles précédents sont équivalents 5/5