Turing machines


In [1]:
from tock import *

Creating TMs

A Turing machine (TM) can be created with TuringMachine() or by reading from a file. Although Sipser never actually writes the transition function of a TM as a table, it's a perfectly reasonable thing to do. The machine in Example 3.7 would be:


In [2]:
m = read_csv("examples/sipser-3-7.csv")
m.is_pushdown(), m.is_deterministic()


Out[2]:
(False, True)

In [3]:
to_table(m)


Out[3]:
0 x
>q1 q2,␣,R qreject,x,R qreject,␣,R
q2 q3,x,R q2,x,R qaccept,␣,R
q3 q4,0,R q3,x,R q5,␣,L
q4 q3,x,R q4,x,R qreject,␣,R
q5 q5,0,L q5,x,L q2,␣,R
@qaccept

As always, the first column lists the possible states, and the start state is marked with a > and the accept state is marked with a @.

The first row lists possible tape symbols. Use _ for the blank symbol.

In the cells, we write the destination state, the written symbol, and the move direction.

Here's the state transition diagram, which might or might not be more intuitive:


In [4]:
to_graph(m)


%3 _START 5 q1 _START->5 0 q2 0->0 x → x,R 2 q3 0->2 0 → x,R 6 qaccept 0->6 ␣ → ␣,R 1 qreject 2->2 x → x,R 3 q4 2->3 0 → 0,R 4 q5 2->4 ␣ → ␣,L 3->1 ␣ → ␣,R 3->2 0 → x,R 3->3 x → x,R 4->0 ␣ → ␣,R 4->4 0 → 0,L x → x,L 5->0 0 → ␣,R 5->1 x → x,R ␣ → ␣,R

Sipser allows a shorthand in which there are multiple symbols to the left of the arrow. For example, the transitions from q5 to itself could be written 0,x -> L for "if the current symbol is either 0 or x, move left". We don't allow this; instead, use two transitions. He also allows you to write 0 -> L for "move left and don't write anything". Again, we don't allow this; instead, use 0 -> 0,L.

Running TMs

This machine recognizes the language $\{\texttt{0}^{2^n} \mid n \geq 0\}$. Here are some example runs:


In [8]:
run(m, "0 0").only_path()


Out[8]:
q1[0] 0
q2␣ [0]
q3␣ x [␣]
q5␣ [x] ␣
q5[␣] x ␣
q2␣ [x] ␣
q2␣ x [␣]
qaccept␣ x ␣ [␣]

This run ended in the accept state, so the machine accepted the string.


In [9]:
run(m, "0 0 0").only_path()


Out[9]:
q1[0] 0 0
q2␣ [0] 0
q3␣ x [0]
q4␣ x 0 [␣]
qreject␣ x 0 ␣ [␣]

This run ended in the reject state, so the machine rejected the string.

It's possible, of course, for a run of a Turing machine to go on forever, so the run function will give up after a certain number of steps. You can control that limit using the steps option:


In [10]:
run(m, "0 0 0 0 0 0 0 0", steps=5)


%3 _START 0 q1,[0] 0 0 0 0 0 0 0 _START->0 2 q2,␣ [0] 0 0 0 0 0 0 0->2 1 q4,␣ x 0 [0] 0 0 0 0 4 q3,␣ x 0 x [0] 0 0 0 1->4 3 q3,␣ x [0] 0 0 0 0 0 2->3 3->1 5 q4,␣ x 0 x 0 [0] 0 0 4->5 _DOTS_5 5->_DOTS_5

The dotted edge indicates that the run continues but is not shown. In this case, you don't know whether the machine would eventually accept the string.