Turing Machine#

In this example, we show how we can simulate a Turing machine purely within the dataflow graph language of OptFlow. In such a way we prove that the dataflow graph language in OptFlow is Turing-complete. That is, from the perspective of computability theory, it is computationally universal to simulate the computational aspects of any possible real-world computer.

In the following code, we define a Turing machine with two states: 0 (init) and 1 (final) and an alphabet size of 2 (0 and 1). Its transition function is

state, tape_read -> next_state, tape_write, tape_move

which means “If the machine is in state state and tape_read is read by the head, a tape_write will be written, the state will change to next_state and the head will be moved one field to the position indicated by tape_move.” Check this page for a detailed introduction.

While state, tape_read, tape_write and tape_move is given in this example, we want to optimize next_state so that the sum of number on the tape can be maximized.

import optflow as flow
import numpy as np

tape_size, max_step, transition_table_size = 5, 5, 3
tape = flow.Constant(np.array([0, 1, 0, 0, 2]))

# transition table
state = flow.Constant(np.array([0, 0, 0]))
tape_read = flow.Constant(np.array([0, 1, 2]))
next_state = flow.Variable(flow.categorical, size=2, shape=(transition_table_size, ))
tape_write = flow.Constant(np.array([1, 0, 2]))
tape_move = flow.Constant(np.array([1, 1, 1]))

s = flow.Constant(0)    # current state
p = flow.Constant(0)    # read/write head position
obj = flow.Constant(0)

with flow.ForLoop(max_step) as t:
    with flow.ForLoop(transition_table_size) as i:  # iterate the transition table
        # if the current state s and the number of tape on the head position match the current table element
        with flow.IfCond((state[i] == s) & (tape_read[i] == tape[p])):
            s = next_state[i]                       # change the state
            tape[p] = tape_write[i]                 # write the tape at the head position
            with flow.IfCond(tape_move[i] == 0):    # move the read/write head: 0 -> left, 1 -> right
                p = flow.maximum(p - 1, 0)
            with flow.IfCond(tape_move[i] == 1):
                p = flow.minimum(p + 1, tape_size - 1)
    with flow.IfCond(s == 1):   # if the Turing machine reached the final state
        flow.break_loop()       # halt

prob = flow.Problem(objective=flow.reduce_sum(tape), constraints=[], sense=flow.maximize)
prob.solve(solver=flow.search)
print(next_state.optimized_value)
prob.solve(solver=flow.metaheuristics)
print(next_state.optimized_value)

[Run Online Demo] (password: )

The result is as follows

Search started
Step 2, found a feasible solution with obj 5.000000
Search finished in 9 steps, optimal objective = 5.000000
Time elapsed: 0.12 s
[0 0 0]

indicating that the Turing machine should keep staying at the init state 0 to maximize the sum of the number on its tape.