States and Transitions

Topics:


States

Definition of State of a computation:

a set of information sufficient to determine the future possible behaviors

A state also serves the role of abstracting past behaviors, in particular the behaviors which can get it to the state in question


A typical computational system:

This is obviously an over-simplification.

Objections include:

Having noted the objections, we still feel justified in declaring systems which are intended to reach a final state as "typical".


Puzzles

The notion of state can be clarified by studying various puzzles. These have state transition behavior, but typically not input and output.

e.g. Towers of Hanoi Puzzle

A state is the assignment of a set of disks to one of three spindles:

Shown here is the initial state for 4 disks:

The desired final state is:

A state in this puzzle must be "legal": A large disk cannot be placed atop a smaller one:

Illegal (not an allowable state):

In the abstract version of the puzzle, every disk must be on some spindle.

(If we were programming a graphical demo, in which the movement of disks is shown, there is a more detailed state which would include the disk's spatial position during transit.)


Transitions

A transition is a single-step change from one state to another. Call the states the pre- state and the post- state of the transition.

For computational systems and puzzles there are constraints on what constitutes a transition.

In the Towers of Hanoi:


Since a transition is a pair of states

the set of all transitions is a binary relation (set of pairs. As such, the set can be represented by a directed graph:

For the Towers of Hanoi, this graph depends on the number of disks. For two disks it looks like:

If there were only a single disk, then there would be only three states.

 

 

How many states would there be for an N-disk puzzle?

 

Is this a lot?


rex > map(pow(3), from(1));

[3, 9, 27, 81, 243, 729, 2187, 6561, 19683, 59049, 177147, 531441, 1594323, 4782969, 14348907, 43046721, 129140163, 387420489, 1162261467, 3486784401, 10460353203, 31381059609, 94143178827, 282429536481

For 10 disks there are 59049 states.

For 24 disks, 282429536481states.


The pattern of transitions as N -> infinity yields some interesting artwork.

Transition pattern for 1 disk:

Transition pattern for 2 disk:

To go from N disks to N+1 disks:

Each node in the N disk case becomes 3 nodes in the N+1 disk case. The connections are "resoldered" thus:

For example, the diagram for 3 disks:

The limiting case is a self-similar figure or fractal, in this case one resembling the Sierpinski gasket.

To see the skeleton of this pattern plotted for successive values of N, run on muddcs (under X):

rex /cs/cs60/rex/special/sierpinski.rex

 


Reachability Relation

The transition relation shows single step changes between states. The reachability relation is the transitive closure of the transition relation:

T0 ==> Tn

means that there are terms T1, T2, ...., Tn-1 (n > 0) such that

T0 => T1 => T2 => .... => Tn-1 => Tn


Deterministic vs. Non-Deterministic systems

A state-transition system is called:

 

Non-deterministic behavior in the state diagram

 

Puzzles are typically non-deterministic

(otherwise they wouldn't be puzzling)

 

Computational systems are typically deterministic (although there are definite exceptions)


Transition rules and the sol program

It is desirable to be able to represent transitions without drawing a diagram:

The number of states may be

We want a transition rule notation which will represent a large (or infinite) number of transitions at one time.

The rex rule notation is one such, for deterministic systems.

For non-deterministic systems, we can use a similar notation, but

take away the requirement that only the first applicable rule in a sequence can be used


Example: Towers of Hanoi puzzle

Represent a state as a pattern:

towers(L, M, N)

where L, M, and N are lists of disk numbers

(assume the disks are numbered

[1, 2, 3, ...., N]

e.g. initial state (for 4-disks) is

towers([1, 2, 3, 4], [ ], [ ])

final state is

towers([ ], [ ], [1, 2, 3, 4])


Represent rules as:

towers(L, M, N) => towers(L', M', N').

or as

towers(L, M, N) => Condition ? towers(L', M', N').

where L, M, N, ... are variables or patterns involve lists (with variables).

% rule 12e

towers([A | L], [], N) =>

towers( L, [A], N).

 

% rule 12n

towers([A | L], [B | M], N) => A < B ?

towers( L, [A, B | M], N).

(12e stands for spindle 1 to spindle 2 empty.)

We can represent all the transitions of the puzzle (for arbitrary N) using 12 transition rules.


The program sol

inputs such rules and a pair of initial and final states to determine if there is a solution.

Examples, including towers.sol may be found in

/cs/cs60/sol

To run these examples, tell UNIX:

sol

After the prompt

| ?-

enter

consult('towers.sol').

to load the towers example. Note that sol is different from rex:

 

To generate the solution, enter

solve.

for the a depth-first solution, or

solve_bf.

for a breadth-first solution.


Breadth-first solutions may take longer to generate, but are guaranteed to have the shortest length among all solutions for a given pair of initial and final state.

Why is this?


Other puzzle examples (try coding their rules).

See the notes for a full description.

 

 


States of an Imperative Program

We have already described how to think of a low-level functional program in terms of "states":

Now we shift to imperative prog

rams, in which the meaning is usually viewed in terms of commands, especially assignment of values to variables representing abstract memory.

We'll use the C++ syntax for assignment:

LHS = RHS;

where LHS is typically a variable and RHS is an expression.

More generally,

RHS is an expression which yields an

R-value

(a value which can be stored in a memory location),

while

LHS is an expression which yields an

L-value

(a value which is a memory location),


Example:

int sum, i, n;

n = .... input value ....;

sum = 0;

i = 0;

while( i < n )

{

sum = sum + i;

i = i + 1;

}

.... output .... = sum;

 

What are the states in such a program?


The states can be represented as an n-tuple, with a "slot" corresponding to each variable, plus a special variable ip (instruction pointer) which designates a point in the program before the next "instructions" e.g. for the above program, the values of

(ip, n, i, sum)

We can give a number to the possible values of ip for convenience. We annotate the program with those numbers:

 

int sum, i, n;

n = .... input value ....;

/* ip = 0 */

sum = 0;

/* ip = 1 */

i = 0;

while( /* ip = 2 */ i < n )

{

sum = sum + i;

/* ip = 3 */

i = i + 1;

}

/* ip = 4 */

.... output .... = sum;


Now we can state transition rules for this program:

Recall that the state is of the form

(ip, n, i, sum)

so the rules are:

(0, n, i, sum) => (1, n, i, 0);

(1, n, i, sum) => (2, n, 0, sum);

(2, n, i, sum) => i < n ? (3, n, i, sum+i);

(2, n, i, sum) => !(i < n) ? (4, n, i, sum);

(3, n, i, sum) => (2, n, i+1, sum);

 

Let's check these rules with an example, where n = 4 initially:

(ip, n, i, sum)

(0, 4, ?, ?) =>

(1, 4, ?, 0) =>

(2, 4, 0, 0) =>

(3, 4, 0, 0) =>

(2, 4, 1, 0) =>

(3, 4, 1, 1) =>

(2, 4, 2, 1) =>

(3, 4, 2, 3) =>

(2, 4, 3, 3) =>

(3, 4, 3, 6) =>

(2, 4, 4, 6) =>

(4, 4, 4, 6)

Now no rule is applicable.

The output of the program is the value of sum, which is 6.


We can make these rules into a nicer-looking functional program by replacing the ip values with separate function names:

(0, n, i, sum) => (1, n, i, 0);

(1, n, i, sum) => (2, n, 0, sum);

(2, n, i, sum) => i < n ? (3, n, i, sum+i);

(2, n, i, sum) => !(i < n) ? (4, n, i, sum);

(3, n, i, sum) => (2, n, i+1, sum);

becomes

f0(n, i, sum) => f1(n, i, 0);

f1(n, i, sum) => f2(n, 0, sum);

f2(n, i, sum) => i < n ? f3(n, i, sum+i);

f2(n, i, sum) => !(i < n) ? f4(n, i, sum);

f3(n, i, sum) => f2(n, i+1, sum);

To make this attractive for someone to use, we provide an interface function:

f(n) = f0(n, arb, arb);

where arb is any arbitrary value. We also include a way to get the answer:

f4(n, i, sum) = sum;


The above functional program can be executed:

No assignment is actually required; it is "simulated" by the way we manipulate argument values.

We call this

McCarthy's Transformation

after John McCarthy, the inventor of Lisp.

 

A corollary is that recursion is at least as fundamental as assignment, as far as computational results are concerned.


You should be able to carry out McCarthy's transformation on simple imperative programs.

You need not go through the "ip" stage.

The notes describe a flowchart viewpoint.


Turing Machines


Components of a Turing Machine (TM):

The controller, in a given state, will:


A description of, or program for, a TM is a partial function, called the transition function:

 

States x Symbols -> Symbols x Motions x States

where Motions = {left, right, none}

 

The symbol x here denotes the Cartesian product of sets.

In the present case, we are saying that the transition function of a TM is a partial function having a domain consisting of all pairs of the form

(State, Symbol)

and a co-domain consisting of all 3-tuples (triples) of the form

(Symbol, Motion, States)


Turing Machine Addition Example

A machine which adds 1 to its binary input numeral. It is assumed that the machine starts with the head to the immediate right of the least significant bit, which is on the right:

 

_ _ _ _ _ 1 0 1 1 0 1 1 1 1 1 _ _ _ _ _ _

^

The state transitions for such a machine could be specified as:

 

State Read Written Motion NewState

 

start _ _ left add1

add1 0 1 right end

add1 _ 1 right end

add1 1 0 left add1

end 0 0 right end

end 1 1 right end


Execution Example

1 0 0 1 1 1 [_] [start]

1 0 0 1 1 [1] _ [add1]

1 0 0 1 [1] 0 _ [add1]

1 0 0 [1] 0 0 _ [add1]

1 0 [0] 0 0 0 _ [add1]

1 0 1 [0] 0 0 _ [end]

1 0 1 0 [0] 0 _ [end]

1 0 1 0 0 [0] _ [end]

1 0 1 0 0 0 [_] [end]