In [1]:
%run -i turing.py
init()


/home/david/anaconda3/lib/python3.5/site-packages/IPython/html.py:14: ShimWarning: The `IPython.html` package has been deprecated. You should import from `notebook` instead. `IPython.html.widgets` has moved to `ipywidgets`.
  "`IPython.html.widgets` has moved to `ipywidgets`.", ShimWarning)

Turing machine computation

Tape

We will represent the tape as a list of tape symbols and we will represent tape symbols as Python strings. The string ' ' represents the blank symbol. The string '|>' represents the start symbol, which indicates the beginning of the tape.

States

We will also encode states as Python strings. The string 'start' represents that start state. The strings 'accept', 'reject', and 'halt' represent final states of the machine, that indicate acceptance, rejection, and halting, respectively.

Simulation

The following function simulates a given Turing machine for a given number of steps on a given input


In [2]:
def run(transitions, input, steps):
    """simulate Turing machine for the given number of steps and the given input"""

    # convert input from string to list of symbols
    # we use '|>' as a symbol to indicate the beginning of the tape
    input = ['|>'] + list(input) + [' ']

    # sanitize transitions for 'accept' and 'reject' states and for symbol '|>'
    transitions = sanitize_transitions(transitions)

    # create initial configuration
    c = Configuration(state='start', head=1, tape=input)

    for i in range(0, steps):
        # read tape content under head
        current = c.state
        read = c.tape[c.head]

        # lookup transition based on state and read symbol
        next, write, move = transitions(current, read)

        # update configuration
        c.state = next
        c.tape[c.head] = write
        c.head += move
        if c.head >= len(c.tape):
            c.tape += [' ']

    # return final configuration
    return c

The following function checks that the transition functions satisfies some simple syntactic requirements (don't move to the left of the start symbol, don't remove or add start symbols, don't change state after accepting, rejecting, or halting.)


In [3]:
def check_transitions(transitions, states, alphabet):

    transitions = sanitize_transitions(transitions)

    for current in states:
        for read in alphabet:
            next, write, move = transitions(current, read)

            # we either stay in place or move one position
            # to the left or right
            assert(move in [-1,0,1])

            # if we read the begin symbol,
            if read == '|>':
                # we need to write it back
                assert(write == '|>')
                # we need to move to the right
                assert(move == 1)
            else:
                # we cannot write the begin symbol
                assert(write != '|>')

            # if we are in one of the final states
            if current in ['accept', 'reject', 'halt']:
                # we cannot change to a different state
                assert(next == current)

    print("transition checks passed")

Examples

Copy machine

The following Turing machine copies its input, i.e., it computes the function $f(x)=xx$. The actual implementation uses different versions of the '0' and '1' symbol (called '0-read', '0-write' and '1-read', '1-write') in the two copies of the string $x$. We could replace those by regular '0' and '1' symbols by sweeping once more over the tape before the end of the computation.


In [4]:
def transitions_copy(current, read):
    if read == '|>':
        return 'start', read, 1
    elif current == 'start':
        if 'write' not in read:
            return read + '-write', read + '-read', 1
        else:
            return 'accept', read, 1
    elif 'write' in current:
        if read != ' ':
            return current, read, 1
        else:
            return 'rewind', current, -1
    elif current == 'rewind':
        if 'read' not in read:
            return current, read, -1
        else:
            return 'start', read, 1

Here is the full transitions function table of the machine:


In [5]:
transitions_table(transitions_copy, 
                  ['start', '0-write', '1-write', 'rewind'],
                  ['0', '1', '0-read', '1-read', '0-write', '1-write'])


transition checks passed
Out[5]:
current read next write move
0 start 0 0-write 0-read 1
1 start 1 1-write 1-read 1
2 start 0-read 0-read-write 0-read-read 1
3 start 1-read 1-read-write 1-read-read 1
4 start 0-write accept 0-write 1
5 start 1-write accept 1-write 1
6 0-write 0 0-write 0 1
7 0-write 1 0-write 1 1
8 0-write 0-read 0-write 0-read 1
9 0-write 1-read 0-write 1-read 1
10 0-write 0-write 0-write 0-write 1
11 0-write 1-write 0-write 1-write 1
12 1-write 0 1-write 0 1
13 1-write 1 1-write 1 1
14 1-write 0-read 1-write 0-read 1
15 1-write 1-read 1-write 1-read 1
16 1-write 0-write 1-write 0-write 1
17 1-write 1-write 1-write 1-write 1
18 rewind 0 rewind 0 -1
19 rewind 1 rewind 1 -1
20 rewind 0-read start 0-read 1
21 rewind 1-read start 1-read 1
22 rewind 0-write rewind 0-write -1
23 rewind 1-write rewind 1-write -1

Here is an interactive simulation of the copy Turing machine (requires that ipython notebook is run locally). You can either click on the simulate button to view the computation during a given range of steps or you can drag the current step slider to view the configuration of the machine at a particular step. (If you click on the current step slider, you can also change it using the arrow keys.)


In [6]:
simulate(transitions_copy, input='10011', unary=False)


start|>10011 

Power-of-2 machine

The following Turing machine determines if the input is the unary encoding of a power of 2. Furthermore, given any string $1^n$, it outputs a string of the form $\{0,1\}^n2^i$, where $i$ is the largest number such that $2^i$ divides $n$.


In [7]:
def transitions_power(current,read):
    if read == '|>':
        return 'start', read, 1;
    elif current == 'rewind':
        return current, read, -1
    elif read == 'x':
        return current, read, 1  
    elif current == 'start':
        if read != '1':
            return 'reject', read, 1
        else: 
            return 'start-even', read, 1
    elif 'even' in current and read == '1':
        return 'odd', 'x', 1
    elif current == 'odd' and read == '1':
        return 'even', read, 1
    elif current == 'odd':
        if read == ' ':
            return 'rewind', '2', -1
        else:
            return current, read, 1
    elif current == 'start-even' and read != '1':
        return 'accept', read, -1
    elif current == 'even' and read != '1':
        return 'reject', read, -1

Here is the full transition function table of the Turing machine:


In [8]:
transitions_table(transitions_power, 
                  ['start', 'start-even', 'even', 'odd', 'rewind'], 
                  ['0', '1', 'x', ' ', '|>'])


transition checks passed
Out[8]:
current read next write move
0 start 0 reject 0 1
1 start 1 start-even 1 1
2 start x start x 1
3 start reject 1
4 start |> start |> 1
5 start-even 0 accept 0 -1
6 start-even 1 odd x 1
7 start-even x start-even x 1
8 start-even accept -1
9 start-even |> start |> 1
10 even 0 reject 0 -1
11 even 1 odd x 1
12 even x even x 1
13 even reject -1
14 even |> start |> 1
15 odd 0 odd 0 1
16 odd 1 even 1 1
17 odd x odd x 1
18 odd rewind 2 -1
19 odd |> start |> 1
20 rewind 0 rewind 0 -1
21 rewind 1 rewind 1 -1
22 rewind x rewind x -1
23 rewind rewind -1
24 rewind |> start |> 1

Here is an interactive simulation of the power Turing machine (requires that ipython notebook is run locally). You can either click on the simulate button to view the computation during a given range of steps or you can drag the current step slider to view the configuration of the machine at a particular step. (If you click on the current step slider, you can also change it using the arrow keys.)


In [9]:
simulate(transitions_power, input_unary=16, step_to=200, unary=True)


start|>1111111111111111 

In [ ]: