📚 The CoCalc Library - books, templates and other resources
License: OTHER
Cellular automata
Code examples from Think Complexity, 2nd edition.
Copyright 2016 Allen Downey, MIT License
Zero-dimensional CA
Here's a simple implementation of the 0-D CA I mentioned in the book, with one cell.
To get the state of the cell in the next time step, we increment the current state mod 2.
Filling in the rest of the array.
So the behavior of this CA is simple: it blinks.
One-dimensional CA
Just as we used a 1-D array to show the state of a single cell over time, we'll use a 2-D array to show the state of a 1-D CA over time, with one column per cell and one row per timestep.
To plot the array I use plt.imshow
Here's what it looks like after we initialize the first row.
And here's the function that fills in the next row. The rule for this CA is to take the sum of a cell and its two neighbors mod 2.
Here's the second row.
And here's what it looks like with the rest of the cells filled in.
For a simple set of rules, the behavior is more interesting than you might expect.
Exercise: Modify this code to increase the number of rows and columns and see what this CA does after more time steps.
Cross correlation
We can update the CA more quickly using "cross correlation". The cross correlation of an array, a
, with a window, w
, is a new array, c
, where element k
is:
In Python, we can compute element k
like this:
To see how this works, I'll create an array:
And a window:
With this window, each element of c
is the sum of three neighbors in the array:
The following function computes the elements of c
for all values of k
where the window can overlap with the array:
This operation is useful in many domains, so libraries like NumPy usually provide an implementation. Here's the version from NumPy.
With mode='valid'
, the NumPy version does the same thing as mine: it only computes the elements of c
where the window overlaps with the array. A drawback of this mode is that the result is smaller than array
.
And alternative is mode='same'
, which makes the result the same size as array
by extending array with zeros on both sides. Here's the result:
Exercise: Write a version of correlate
that returns the same result as np.correlate
with mode='same'.
Update with correlate
Now we can use np.correlate
to update the array. I'll start again with an array that contains one column for each cell and one row for each time step, and I'll initialize the first row with a single "on" cell in the middle:
Now here's a version of step
that uses np.correlate
And the result is the same.
CA Tables
What we have so far is good enough for a CA that only depends on the total number of "on" cells, but for more general CAs, we need a table that maps from the configuration of the neighborhood to the future state of the center cell.
The following function makes the table by interpreting the Rule number in binary.
Here's what it looks like as an array:
If we correlate the row with the window [4, 2, 1]
, it treats each neighborhood as a binary number between 000 and 111.
Now we can use the result from np.correlate
as an index into the table; the result is the next row of the array.
We can wrap up that code in a function:
And test it again.
How did I know that Rule 150 is the same as the previous CA? I wrote out the table and converted it to binary.
The Cell1D object
Cell1D
encapsulates the code from the previous section.
The following function makes and draws a CA.
Here's an example that runs a Rule 50 CA for 10 steps.
Another example:
And one more example showing recursive structure.
Rule 30 generates a sequence of bits that is indistinguishable from random:
And Rule 110 is Turing complete!
Here's a longer run that has some spaceships.
Exercises
Exercise: This exercise asks you to experiment with Rule 110 and see how many spaceships you can find.
Read the Wikipedia page about Rule 110, which describes its background pattern and spaceships.
Create a Rule 110 CA with an initial condition that yields the stable background pattern. Note that the CA class provides
start_string
, which allow you to initialize the state of the array using a string of1
s and0
s.Modify the initial condition by adding different patterns in the center of the row and see which ones yield spaceships. You might want to enumerate all possible patterns of bits, for some reasonable value of . For each spaceship, can you find the period and rate of translation? What is the biggest spaceship you can find?
What happens when spaceships collide?
Exercise: The goal of this exercise is to implement a Turing machine.
Read about Turing machines at http://en.wikipedia.org/wiki/Turing_machine.
Write a class called
Turing
that implements a Turing machine. For the action table, use the rules for a 3-state busy beaver.Write a
draw
method that plots the state of the tape and the position and state of the head. For one example of what that might look like, see http://mathworld.wolfram.com/TuringMachine.html.
Exercise: This exercise asks you to implement and test several PRNGs. For testing, you will need to install DieHarder
, which you can download from https://www.phy.duke.edu/~rgb/General/dieharder.php, or it might be available as a package for your operating system.
Write a program that implements one of the linear congruential generators described at http://en.wikipedia.org/wiki/Linear_congruential_generator}. Test it using
DieHarder
.Read the documentation of Python's
random
module. What PRNG does it use? Test it.Implement a Rule 30 CA with a few hundred cells, run it for as many time steps as you can in a reasonable amount of time, and output the center column as a sequence of bits. Test it.