Skip to content
Handbook navigation

Chapter 1 · Python

Python: The Dream of Thinking Machines

Run this example

Edit and run the python example

Change the source and run it here. Use the output panel to compare your result with the expected highlights below.

Unlock execution

Output

Run the code to see output here.

Example 1 - Tracing a Turing Machine by Hand, Then Breaking It

Scenario

You have the six-rule adder from Figure 1.4 and two inputs to try: 1001 (nine) and 1111 (fifteen). Before running any code, predict each output and step count, then trace the machine with pencil and paper. Finally, remove one rule and see what a missing rule does to a machine that otherwise works.

The rule table, written as (state, symbol read) → (write, move, next state):

(right, 0)     -> (0, R, right)
(right, 1)     -> (1, R, right)
(right, blank) -> (blank, L, carry)
(carry, 1)     -> (0, L, carry)
(carry, 0)     -> (1, stay, done)
(carry, blank) -> (1, stay, done)

Step 1: Predict from the formula

For an input of n digits ending in k ones, the machine takes n + k + 2 steps.

1001:  n = 4, k = 1  ->  4 + 1 + 2 = 7 steps, output 1010 (ten)
1111:  n = 4, k = 4  ->  4 + 4 + 2 = 10 steps, output 10000 (sixteen)

Step 2: Trace 1001

The head is shown in brackets.

StepStateReadsWritesMovesTape afterwards
0right[1] 0 0 1
1right11R1 [0] 0 1
2right00R1 0 [0] 1
3right00R1 0 0 [1]
4right11R1 0 0 1 [_]
5rightblankblankL1 0 0 [1] _
6carry10L1 0 [0] 0 _
7carry01stay1 0 [1] 0 _

Seven steps, output 1010, as predicted. The chapter's program prints the same row count for 1001 in its summary table.


Step 3: Trace 1111 and watch the overflow

The first five steps walk right and turn around. Steps 6 to 9 turn each 1 into 0, moving left each time, until the head sits on the blank cell to the left of the number. Step 10 applies the rule (carry, blank) → (1, stay, done) and writes the new leading digit: 10000. The number got longer by one digit, exactly as 15 + 1 = 16 needs a fifth binary digit.


Step 4: Remove a rule

Delete the rule for (carry, blank) and run 1111 again. Everything proceeds as before until step 10, when the machine is in state carry reading a blank and finds no rule. A Turing machine with no applicable rule simply stops, leaving 0000 on the tape: a wrong answer produced without complaint. In the Python program the same situation raises a KeyError, which is the better failure because it is loud.


Lesson

A rule table is a complete specification only if it covers every (state, symbol) pair the machine can reach. The inputs that exercise a rarely used rule, here numbers that are all ones, are the ones a small test set is most likely to miss. Enumerating reachable (state, symbol) pairs is cheap for a machine this size, which is why the chapter recommends exhaustive checking wherever the space of cases is small enough to allow it.

Example 2 - Enumerate or Sample? Three Components, Three Answers

Scenario

A claims-processing team is adding three automated components and must tell its risk committee what evidence backs each one.

  1. An eligibility rule: a claim goes to fast-track only if the policy is active, the amount is under the fast-track limit, and the claimant has no open fraud flag. Three yes/no facts.
  2. A validator for a 32-bit policy-number field, which must accept every well-formed number and reject every malformed one.
  3. An AI agent that drafts and runs small scripts to reconcile claim records against the payments ledger.

The committee's question is the same for all three: "How do you know it never does the wrong thing?"


Diagnosing It

ComponentCan it express arbitrary behaviour?Inputs to coverEvidence you can offer
Eligibility ruleNo: a fixed Boolean formula2³ = 8Every case, in one unit test
Policy-number validatorNo: a fixed function of 32 bits2³² = 4,294,967,296Every case, by brute force on one machine
Reconciliation agentYes: it writes programsunboundedWithheld permissions plus a sampled failure-rate bound

The first two are finite. Checking all eight combinations of the eligibility rule is a table test; checking all 4.3 billion policy numbers against a reference implementation is an overnight job at most. For both, the committee can be told that the behaviour has been verified on every possible input.

The agent is different. The scripts it writes are programs, and Rice's theorem (1953) says no analyser can decide, for every program, a property such as "never modifies a paid claim". The evidence therefore has two parts.


Part 1: Make the worst outcomes impossible

Give the agent read-only credentials on the payments ledger and write access only to a staging table of proposed corrections. A human approves corrections before they reach production. Now "the agent never changes a paid claim" is true by construction, whatever scripts it writes, because the permission does not exist.


Part 2: Bound the rest by sampling

What remains is the rate at which the agent proposes wrong corrections. The team runs the agent on held-out reconciliation cases and counts failures. With zero failures in n independent runs, the highest failure rate still consistent with the data at 95% confidence is 1 − 0.05^(1/n), roughly 3/n:

clean runs    failure rate not yet ruled out (95%)
      115      2.57%
      300      0.99%
      500      0.60%
    3,000      0.10%

If the committee wants the wrong-proposal rate below 1%, the team needs about 300 consecutive clean runs; below 0.1%, about 3,000. A single failure changes the arithmetic: after one failure in n runs the 95% bound is about 4.7/n rather than 3/n, so 300 runs with one failure support only about 1.6%. A failure that repeats across similar cases also means the runs were not independent, so the team reviews every failure it finds.


The Answer to the Committee

For the rule and the validator: verified on every input. For the agent: it cannot change a paid claim because it has no permission to, a human approves every proposed correction, and 3,000 clean runs on held-out cases put its wrong-proposal rate below 0.1% at 95% confidence. The first two answers are proofs. The third is a bound plus a boundary, and it is the strongest honest answer available for a component that writes code.


Lesson

Decide the kind of evidence before arguing about its amount. Where the inputs are few enough to enumerate, enumerate. Where the component can do arbitrary things, remove the dangerous permissions first and then sample, and quote the result as a bound with its confidence, never as "it passed all our tests".

Runnable Example - Four Thinking Machines in One Program

One short program runs four of the chapter's machines: Turing's binary adder, McCulloch and Pitts's threshold units, a few rules in the style of Weizenbaum's ELIZA, and the arithmetic of what testing can certify. It uses only the Python standard library, needs no network, and finishes in well under a second. Its Java twin, in the Java tab, produces output identical to the last byte.

Expected output:

=== Part 1: A Turing machine that adds one (rule table of 6 entries) ===
step  state  read  write  move  next    tape after the step
   0  right                             [1] 0  1  1
   1  right     1      1     R  right    1 [0] 1  1
   2  right     0      0     R  right    1  0 [1] 1
   3  right     1      1     R  right    1  0  1 [1]
   4  right     1      1     R  right    1  0  1  1 [_]
   5  right     _      _     L  carry    1  0  1 [1] _
   6  carry     1      0     L  carry    1  0 [1] 0  _
   7  carry     1      0     L  carry    1 [0] 0  0  _
   8  carry     0      1     -  done     1 [1] 0  0  _

input        value  output        value  steps  n + k + 2
1011            11  1100             12      8          8
111              7  1000              8      8          8
0                0  1                 1      3          3
1001             9  1010             10      7          7
1111111111    1023  10000000000    1024     22         22
(n = input length, k = trailing 1s: walk right, turn, carry, write.)

=== Part 2: Threshold units (McCulloch and Pitts, 1943) ===
unit     w1  w2  theta   (0,0) (0,1) (1,0) (1,1)
AND       1   1    2.0      0     0     0     1
OR        1   1    1.0      0     1     1     1
NAND     -1  -1   -1.5      1     1     1     0
NOT x1   -1   0   -0.5      1     1     0     0
Searched 1,323 settings (weights -3..3, threshold -6.5..6.5 in steps of 0.5).
Two-input functions computed by at least one setting: 14 of 16
Settings that compute XOR: 0   XNOR: 0
XOR from two layers: hidden OR and NAND, then AND
  x=(0,0)  OR=0  NAND=1  XOR=0
  x=(0,1)  OR=1  NAND=1  XOR=1
  x=(1,0)  OR=1  NAND=1  XOR=1
  x=(1,1)  OR=1  NAND=0  XOR=0

=== Part 3: Four ELIZA-style rules ===
> Men are all alike.
  IN WHAT WAY
> They're always bugging us about something or other.
  CAN YOU THINK OF A SPECIFIC EXAMPLE
> Well, my boyfriend made me come here.
  YOUR BOYFRIEND MADE YOU COME HERE
> He says I'm depressed much of the time.
  I AM SORRY TO HEAR YOU ARE DEPRESSED
> My gloop made me frimble.
  YOUR GLOOP MADE YOU FRIMBLE
> I am deeply sick of purple arithmetic.
  I AM SORRY TO HEAR YOU ARE SICK
> The quarterly figures look fine.
  PLEASE GO ON

=== Part 4: What testing can certify ===
Inputs to check if every case is enumerated:
  three true/false facts                                    8
  one 32-bit integer                            4,294,967,296
  100 tokens from a 128,256-token vocabulary         10^510.8
Failure-free runs needed to rule out a failure rate above the target:
target   95% confidence   99% confidence   rule of three (3/p)
10%                  29               44                    30
1%                  299              459                   300
0.1%              2,995            4,603                 3,000
0.01%            29,956           46,050                30,000
Upper bound on the failure rate after zero failures (95% confidence):
    115 clean runs  tau-bench retail test tasks     2.57%
    500 clean runs  SWE-bench Verified tasks        0.60%
  3,000 clean runs  an in-house regression suite    0.10%

What to notice:

  • Part 1. The whole program is a six-entry dictionary from (state, symbol) to (write, move, next state). The loop never looks further than the cell under the head. The step count follows n + k + 2 for every input, including 1111111111, whose ten carries ripple all the way to a new leading digit.
  • Part 2. The search tries all 1,323 combinations of integer weights from −3 to 3 and thresholds from −6.5 to 6.5. Fourteen of the sixteen two-input truth tables appear; XOR and XNOR never do. The two-layer network computes XOR by combining OR and NAND, the construction that multi-layer networks generalise.
  • Part 3. Four rules reproduce the opening of Weizenbaum's published transcript. The same rules turn "My gloop made me frimble" into a confident question-shaped echo and read "sick of" as illness. The rules match word patterns; nothing in them represents what a gloop, a boyfriend or depression is.
  • Part 4. Enumeration is decisive up to billions of cases and hopeless for free text: the 100-token count is a 511-digit number. The sampling table shows why the rule of three is a good approximation at 95% confidence, and the last block shows how weak a clean pass on a public benchmark is as evidence of a low failure rate. The 3,000-run suite is an illustrative size, not a real benchmark.

Try this:

  • Delete the ("carry", BLANK) rule and run the machine on 111. Predict what happens before you run it; the answer is the machine-level version of an unhandled case.
  • Add a rule set that subtracts one instead of adding one. Which states do you need, and what should happen on input 0?
  • Change the threshold search to real-valued thresholds on a finer grid. Does XOR ever appear? Use the chapter's two-line argument to predict the answer first.
  • Give the ELIZA rules a new keyword, because, with the reassembly IS THAT THE REAL REASON, and find a sentence where it gives an absurd reply.
  • Set the confidence to 0.99 in upper_bound and recompute the benchmark rows.
Python — The Dream of Thinking Machines | KnowledgeOS