====== Push-down Automata ====== ==== Introduction ==== Push-down automata (or PDAs) are **acceptors** of Context-Free languages. Informally, a push-down automata is equipped with a simplistic type of memory - a **stack**. Thus, a PDA is able to //push// or //pop// symbols, however it is unable to directly //index the memory// - once a symbol is popped (to reach the inner stack values) it is lost from the memory. ===== Definition ===== $def[PDA] A push-down automaton is a tuple $math[M=(K,\Sigma,\Gamma,\Delta,q_0,Z_0,F)] where: * $math[K] is a finite set of states * $math[\Sigma] is the input alphabet * $math[\Gamma] is the stack alphabet (which may contain symbols from $math[\Sigma]) * $math[\Delta\subseteq K\times \Sigma^*\times \Gamma^*\times K\times\Gamma^*] is a transition relation. If $math[(q,c,\gamma,q',\gamma')\in\Delta], then from state $math[q], if symbol $math[c] has been read, and the top of the stack corresponds to $math[\gamma], then the PDA goes to state $math[q'], and executes the stack operation $math[\gamma']: * if $math[\gamma=a] (the top of the stack is $math[a]) and $math[\gamma'=a], then the stack is left unmodified * if $math[\gamma=a] and $math[\gamma'=ba], then symbol $math[b] is pushed on the stack (over $math[a]) * if $math[\gamma=a] and $math[\gamma'=\epsilon], then symbol $math[a] is popped from the stack. * more generally, if $math[\gamma=c_1\ldots c_n] and $math[\gamma'=d_1\ldots d_m], then symbols $math[c_1] to $math[c_n] from the top of the stack are popped, and replaced by $math[d_1] to $math[d_m]. Note that some $math[c_i] may coincide to some $math[d_j]. * $math[q_0\in K] is the initial state * $math[Z_0\in\Gamma] is the //empty-stack// symbol * $math[F\subseteq K] is the set of final states $end **Remarks** - A PDA which does not modify its stack (always leaves $math[Z_0]) during its operation is an NFA - It is possible to define a //deterministic-variant// of PDAs, however, for the purposes of this lecture, they are less relevant. Such machines would be equivalent in accepting power to our PDAs. $def[Configuration] A **configuration** of a PDA is a triple $math[(q,w,\gamma)\in K\times\Sigma^*\times\Gamma^*]. $math[q] is the current state, $math[w] is the rest of the input to be processed, and $math[\gamma] is the **contents of the stack**. $end $def[Zero or more steps] The relation $math[\vdash_M], as well as $math[\vdash_M^*] over PDA configurations is defined similarly to those for NFAs and DFAs. We remark that, for a transition $math[(q,u,\gamma,q',\gamma')], we have the one-step relation $math[(q,uv,\gamma\xi)\vdash_M(q',v,\gamma'\xi)] $end $def[Acceptance] A word $math[w] is accepted by a PDA $math[M] iff $math[(q_0,w,Z_0)\vdash_M^*(p,\epsilon,Z_0)] where $math[p\in F]. $end An alternative definition is **acceptance by empty stack**: a PDA accepts a word when its stack becomes empty. This definition no longer requires the concept of final state. In this lecture, we shall **not** rely on acceptance by empty stack. ===== Examples ===== ==== PDA to accept palindromes ===== Consider $math[L=\{ ww^R \mid w\in\{a,b\}^*\}] where $math[w^R] denotes the //reversal// of word $math[w]. The PDA has two states $math[q_0,q_1] and the latter is a final (accepting) state. The input alphabet is $math[\{a,b\}], however, the stack alphabet is $math[\{X,Y,Z_0\}]. The PDA transitions are illustrated below: ^ Current state ^ Input ^ Stack top ^ Next state ^ Stack op ^ | $math[q_0] | $math[a] | $math[\epsilon] | $math[q_0] | $math[X] | | $math[q_0] | $math[b] | $math[\epsilon] | $math[q_0] | $math[Y] | | $math[q_0] | $math[a] | $math[X] | $math[q_1] | $math[\epsilon] | | $math[q_0] | $math[b] | $math[Y] | $math[q_1] | $math[\epsilon] | | $math[q_1] | $math[a] | $math[X] | $math[q_1] | $math[\epsilon] | | $math[q_1] | $math[b] | $math[Y] | $math[q_1] | $math[\epsilon] | Key ideas: * In the initial state, the PDA matches $math[\epsilon] to the top of the stack. In effect, we //do not care// what the stack contents are. Thus, the first two transitions simply add $math[a] (resp. $math[b]) on whatever values were held on the stack. * The third and fourth transitions **guess** that the middle of the string has been found (also note that our strings always have even length). If the guessing was incorrect on some execution branch, then the PDA will eventually get stuck. * We use $math[X] (resp. $math[Y]) to encode that $math[a] (resp. $math[b]) have been read - this is for illustration purposes only (as well as legibility). * In state $math[q_1], if $math[X] (resp. $math[Y]) is the current stack symbol and $math[a] (resp. $math[b]) is read, then the stack symbol is popped. Since $math[q_1] is an accepting state, once we reach the empty stack symbol and process all the input - the PDA accepts.