Turing Machines

Alan Turing characterized algorithms using a primitive virtual machine. These are now called Turing Machines.

A Turing Machine (TM) has three components: tape, read-write head, and controller:

 

The tape is an unbounded array of cells. Each cell contains a 0 or 1, where 0 means "blank". The TM uses the unary system, so the number 3 would be represented by writing three consecutive 1s on the tape. In the example above the tape contains two numbers: 5 and 6.

The read/write head points at the current cell of the tape (which is initially the left-most cell). The head can read the bit stored in the cell and it can change the bit. The head can move left, right, or stay put. (It's not possible to move left from the left-most cell.)

The controller is in one of finitely many states: 0, 1, 2, ..., n, where 0 is the initial state. Certain states are designated as final states. When the TM controller enters one of these it halts.

The controller contains a TM program, which is a set of instructions of the form:

(current-state, current-bit) => (new-state, new-bit, direction)

The left side of the instruction is called the trigger, the right side is the action.

For example:

(1, 1) => (2, 0, -1)

Means if the current state is 1 and the current cell (i.e., the cell under the read-write head) contains 1, then the new state is 2, the content of the cell is changed to 0, and the head moves left one cell.

For example, here's a program for adding two numbers, arg1 and arg2:

(0, 1) => (0, 1, 1) // move right over arg1

(0, 0) => (1, 0, 1) // skip to arg2

(1, 0) => (3, 0, 0) // halt at end of arg2

(1, 1) => (2, 0, -1) // erase next bit from arg2

(2, 0) => (0, 1, 1) // scooch 1 left

(2, 1) => (4, 1, 0)

The final states are 3 = done and 4 = error.

Here's a sample run of the program where index indicates the position of the R/W head:

state = 0, index = 0, tape = 1111101111110
state = 0, index = 1, tape = 1111101111110
state = 0, index = 2, tape = 1111101111110
state = 0, index = 3, tape = 1111101111110
state = 0, index = 4, tape = 1111101111110
state = 0, index = 5, tape = 1111101111110
state = 1, index = 6, tape = 1111101111110
state = 2, index = 5, tape = 1111100111110
state = 0, index = 6, tape = 1111110111110
state = 1, index = 7, tape = 1111110111110
state = 2, index = 6, tape = 1111110011110
state = 0, index = 7, tape = 1111111011110
state = 1, index = 8, tape = 1111111011110
state = 2, index = 7, tape = 1111111001110
state = 0, index = 8, tape = 1111111101110
state = 1, index = 9, tape = 1111111101110
state = 2, index = 8, tape = 1111111100110
state = 0, index = 9, tape = 1111111110110
state = 1, index = 10, tape = 1111111110110
state = 2, index = 9, tape = 1111111110010
state = 0, index = 10, tape = 1111111111010
state = 1, index = 11, tape = 1111111111010
state = 2, index = 10, tape = 1111111111000
state = 0, index = 11, tape = 1111111111100
state = 1, index = 12, tape = 1111111111100
state = 3, index = 12, tape = 1111111111100
Halted

Enhancements

Instead of 0 and 1, the tape can contain arbitrary characters or integers less than some maximum value. These extra symbols make useful delimiters.

Instead of -1, 0, or 1, the head can move by arbitrary amounts.

There can be auxiliary tapes.

Tapes can be divided into tracks. So instead of containing an integer, a cell may contain ten integers: tracks 0 through 9.

One TM program can call another as a subroutine.

Turing Machine Emulator

Complete and test the following implementation of Turing Machines:

public class TuringMachine {
   private ArrayList<Integer> tape;
   private Map<Trigger, Action> program;
   private Set<Integer> finalStates; // run halts when state is a member
   private int state; // the current state
   private int index = 0; // the current position of the R/W head
   public void run() { ??? }
   // etc.
}

A trigger is just a pair of integers; an action is a triple of integers.

Of course you will need to supply methods for adding instructions to the program and for initializing the tape with inputs. It's also useful to provide a toString method.

Turing Machine Emulator 2.0

A more object-oriented solution is to combine the array list and index into a single instance of a Tape class:

public class TuringMachine {
   private Tape tape;
   private Map<Trigger, Action> program;
   private Set<Integer> finalStates; // run halts when state is a member
   private int state; // the current state
   public void run() { ??? }
   // etc.
}

Here's a sketch of my Tape class:

public class Tape {
   private static int BLOCK_SIZE = 100;
   private ArrayList<Character> cells;
   private int head;
  
   public Tape(String input) {
      cells = new ArrayList<Character>();
      growTape();
      for(int i = 0; i < input.length(); i++) {
         cells.set(i,  input.charAt(i));
      }
      head = 0;
   }
  
   private void growTape() {
      int excess = head - cells.size();
      if (0 <= excess) {
         for(int i = 0; i < BLOCK_SIZE + excess; i++) {
            cells.add('0');
         }
      }
   }
  
   public char read() { return cells.get(head); }
   public void write(char c) { cells.set(head, c); }
   public String toString() {
      return cells.toString() + " /head = " + head;
   }


   public void moveHead(int steps) {
      head += steps;
      if (head < 0) head = 0;
      growTape();
   }
}

Exercises

Test your emulator by implementing the following functions. (Hint: you may need to take advantage of some of the enhancements mentioned earlier.

f(n) = 2 * n

g(n) = n2

h(w) halts if w = 1n01n

Enhancements

Implementing g(n) = n2 can be tough on a basic machine. It's not hard to add a few enhancements to your machine to make it easier to program. Here are a few suggestions:

A typical instruction has the form (a, b) => (c, d, e) where a = current state, b = current symbol, c = new state, d = new symbol, and e = number of cells to move the head (e < = indicates that the head moves left, e = 0 indicates that the head doesn't move).

Suggestion 1: e doesn't need to be -1, 0, or 1. You can allow the head to move an arbitrary amount. Be careful, though. Attempting to move the head left of cell 0 should immediately halt your machine in its current state.

Suggestion 2: b and d don't need to be 0 or 1, they can be arbitrary characters. It's convenient to use weird characters to delimit or separate numbers on the tape.

Suggestion 3: A multi-tape machine has a length n array of tapes and a length n array of indices (one for each tape), where n is a constructor parameter. An instruction has the form:

(a, [b0, b1, b2, ...]) => (c, [d0, d1, d2, ...], [e0, e1, e2, ...])

tape[0] is the main input-output tape. The other tapes can be used as temporary storage for things like counters, accumulator, loop control variables, etc.