class: center, middle # CS-3110: Formal Languages and Automata ## General Grammars ### Chapter 4.6 --- class: medium # Recall: Context-Free Grammars A formal (context-free) grammar is a four-tuple: $$ (V,\Sigma, P, S) $$ * `\(V\)` is a set of **non-terminal symbols** ("variables" in our grammar) * `\(\Sigma\)` is a set of **terminal symbols** (the actual "symbols" in our produced words, i.e. the alphabet) * `\(P\)` is a set of **production rules** of the form `\(A \rightarrow w\)` with `\(A \in V\)` and `\(w \in (V\cup \Sigma)^\ast\)` * `\(S\)` is one of the non-terminal symbols from V, and called the **start symbol** --- # Rewriting System A concrete example: $$ V = \\{E, X\\} \\\\ \Sigma = \\{a,b,\ldots, z, +, *, (, )\\} $$ $$ P = \\{ E \rightarrow X, E \rightarrow E + E\\\\ E \rightarrow E * E, E \rightarrow ( E )\\\\ X \rightarrow a, X \rightarrow b, \ldots, X \rightarrow z \\} $$ $$ S = E $$ --- # Let's look at the rules `\(P\)` is a set of **production rules** of the form `\(A \rightarrow w\)` with `\(A \in V\)` and `\(w \in (V\cup \Sigma)*\)` * On the left hand-side we have a non-terminal symbol * On the right hand-side we have a sequence of terminals and non-terminals * What if we relaxed this? --- # General Grammars * Instead of requiring a single non-terminal on the left side of a rule, we allow a *sequence* of non-terminal **and** terminal symbols * Only limitation: There has to be **at least one** non-terminal symbol * Why? So we know when we are "done" * What can we do with this? -- Let's try `\( \{a^n \cdot b^n \cdot c^n | n \in \mathbb{N} \} \)` --- # General Grammars $$ S \rightarrow S ABC\\\\ S \rightarrow X\\\\ BA \rightarrow AB\\\\ CB \rightarrow BC\\\\ CA \rightarrow AC\\\\ XA \rightarrow aX\\\\ X \rightarrow Y \\\\ YB \rightarrow bY\\\\ Y \rightarrow Z \\\\ ZC \rightarrow cZ\\\\ Z \rightarrow \varepsilon $$ --- # General Grammars $$ S \rightarrow S ABC\\\\ S \rightarrow X $$ This produces (intermediate) strings in the form: X, XABC, XABCABC, XABCABCABC, ... We have the right amount of (non-terminal!) "A", "B" and "C", but in the wrong order. And there is an X at the beginning --- # General Grammars $$ BA \rightarrow AB\\\\ CB \rightarrow BC\\\\ CA \rightarrow AC $$ These rules allow us to reorder A, B and C, (only) **into the correct order**, e.g. $$ \begin{aligned} XAB\color{red}{CA}BC \Rightarrow& XAB\color{red}{AC}BC\\\\ \Rightarrow& XA\color{red}{AB}CBC\\\\ \Rightarrow& XAAB\color{red}{BC}C \end{aligned} $$ --- # General Grammars $$ XA \rightarrow aX\\\\ X \rightarrow Y $$ These rules "move" the `X` through a sequence of `A`s, replacing each `A` with the terminal `a`, and finally replacing the `X` with a `Y`. $$ YB \rightarrow bY\\\\ Y \rightarrow Z $$ These rules do the same for `Y`, moving it through `B`s, and replacing `Y` with `Z`. --- # General Grammars $$ ZC \rightarrow cZ\\\\ Z \rightarrow \varepsilon $$ These rules, finally, are responsible for "placing" the `c`s, and removing the `Z` at the end. --- # General Grammars **Important**: While there is a rule to convert an `X` into a `Y`, we **can not** apply it before all `A`s are gone, or we will be unable to get rid of the `A`s anymore (similarly for `B`s and `C`s). The words a grammar generates are **exactly** the words we can get by applying rules **in any order** (when they are applicable) that results in no non-terminal symbols being left! --- # General Grammars * So what can we do with these grammars? * How about this language? $$ L = \\{a^n | n\:\text{is prime}\\} $$ -- This is actually possible! -- But we are not going to write the actual grammar for it :( --- # Grammars as computation * Why would grammars be able to "compute" something like prime numbers? * What did we just do for `\(\{a^n b^n c^n | n \in \mathbb{N}\}\)`? * We basically used the non-terminal symbols as "memory" * First, we "stored" the number of `a`, `b` and `c` (as the non-terminals A, B and C) * Then we rearranged them into the correct order (which is kind of like "sorting") --- # Grammars as computation * We view our current string as our "memory" * The rules tell us how we can transform/update (part of) our memory * That's basically how programming works! * But which "instructions" do we have? --- # Context-Free Grammars * In Context-Free Grammars we always had one non-terminal symbol on the left side of a rule * This means, our "instructions" were basically writing to memory locations (potentially creating more) * But we could never really combine values from multiple memory locations * In the equivalent automaton that manifested itself as not being able to access arbitrary memory --- # General Grammars * Now we allow multiple terminal and non-terminal symbols on the left side of a rule * This means we can do things like "addition" (`\(0P0 \rightarrow 0R0, 0P1 \rightarrow 1R0, 1P0 \rightarrow 1R0, 1P1 \rightarrow 0R1\)`) * We could also do "math" with terminal symbols .left-column[ $$ S \rightarrow XY\\\\ X \rightarrow BXa\\\\ Ba \rightarrow aaB\\\ BY \rightarrow Y\\\\ X \rightarrow \varepsilon, Y \rightarrow \varepsilon $$ ] --- class: center, middle # Turing Machines --- # Turing Machines * Our Pushdown Automata had a simple stack as memory * This has the advantage that it is simple * The disadvantage is that we can't access "arbitrary" memory * That was exactly why we could not recognize e.g. `\(\{a^n b^n c^n | n \in \mathbb{N}\}\)` * So let's relax that! --- # Memory * We will still stay simple * That makes it easier to prove things about our automaton * So we will have "memory", but the automaton can only access one entry at a time * It can then move left or right in our memory * You can think of this like an infinite "tape", where the automaton is a read/write head that can move along the tape --- # Turing Machines * We still keep the general structure the same: We have states and transitions between states * Instead of reading input separately, we just put it as the initial contents of our memory/tape * Transitions then depend on the symbol on the tape we are currently looking at * Each transition writes a new symbol (which may be the same one we just read), and moves left or right --- # Turing Machines
[Turing Machine in TOC](https://www.geeksforgeeks.org/turing-machine-in-toc/) --- # Turing Machines Formally, Turing Machine is a 4-tuple: `\(M = (Q, \Lambda, q_0, \delta)\)` * `\(Q\)` is a set of states, which includes a special final (halting) state `h` * `\(\Lambda\)` is the tape alphabet, which includes a special symbol for "blank" (#) * `\(q_0 \in Q\)` is the initial state * `\(\delta: (Q\setminus \{h\})\times \Lambda \mapsto \Lambda \times \{L,R\} \times Q\)` is the transition function --- class: medium # Transition Function $$ \delta: (Q\setminus \\{h\\})\times \Lambda \mapsto \Lambda \times \\{L,R\\} \times Q $$ We take: * The current state (which can not be the final state) * The symbol that is currently on the tape and produce: * A new symbol to write on the tape * A direction to move to (left or right) * The new state of the machine --- # Variations This is the formulation from the textbook. Note: * There is only one halting state; we could add more than one without any problems * The read/write head must always move left or write. We could also allow it to stay where it is without a problem * The transition function is, well, a **function**: The machine is deterministic --- # Non-Determinism * As with our other automata, we can have deterministic or non-deterministic Turing machines * Deterministic: For every state/tape symbol there is exactly one option * Non-Deterministic: There may be multiple (or no) options for a given state/tape symbol combination * Deterministic and Non-Deterministic Turing Machines can do the exact same computations --- class: medium # Turing Machines and Language A Turing Machine can compute something * Read input from the tape * Compute, and write result onto the tape We could define the same computation as a language: * Words have the form `x#y` * `x` is the input, and `y` is the output of the computation * A grammar for this language would **only** accept words that have the correct output for the given input