Hey all, Jonathan here.
Today, I'm going to show an example
of how you might go about making your
very own elementary cellular automata
hereafter referred to as ECA
in NetLogo.
This exercise in particular asks us to
homebrew an asynchronous ECA
which you'll see happens quite naturally
in this program,
since NetLogo, by default,
likes to call agents in random order.
Alrighty, so,
as you saw from this unit,
ECAs being one-dimensional
are simulated row by row,
one row at a time.
First time step is simulated
on the top row.
Second time step
plays out in the second row.
Third iteration on third row,
etc., etc.
So it might behoove us
to set up the coordinate system
in such a way that
facilitates this logic.
Here, notice I set the
origin at the top,
so that the first row counts as
py coordinate of zero
and counts down minus one,
minus two, minus three, and so on
from there.
Due to starting on the top left corner,
px coordinate integers also can
be (inaudible) count
from zero, one, two three, etc.
which could also be nice
if you later decide
to expand the width of the rows.
Now for the big questions.
You want to ask:
What are the essential features
of the ECA model?
Fayal (???) or automatas are
made of cells, of course,
which in NetLogo codes,
are the patches.
The main properties of patches
in the ECAs
are the current states,
of which they have zero or one,
and neighborhood configurations,
used to determine the
cell's subsequent state.
Another important feature of the
model are the rules,
which, as you know,
is simply a list of states
telling the cells
which neighborhoods
produce which states.
As we have seen from the various
Wolfram classes,
different rules can produce
radically different behaviors
on the large scale,
while individual cells can
have their own states,
and see different neighborhoods,
the rule set itself isn't specific to
the individual cells,
but rather the model as a whole.
So, I list is as a global variable.
We also don't want to forget
the obligatory Setup and Go procedures
to make the model run and stuff,
so I'm putting that in right now.
For the Setup, it's generally
good practice to write
"clear-all",
since we want to be able to
reset these models
and replicate results independently,
without interference
from previous runs.
Particular to the way
I set up this model,
I'm also resetting
such initializing ticks,
since ticks can be useful
for keeping track
of which time step we're at.
Finally, we want the patches
to sort themselves,
to an initial random configuration
so I'm going to ask the patches
albeit with a specific qualifier,
patches with pycor equal zero,
to start with,
since, as you know,
we want cells on the top row
in the beginning.
Now these patch cells just seem to have
those states randomly set.
Recall that each cell can be in
one of two states: zero or one.
So for our current purposes,
we want a random decider variable,
with an equal chance of returning
a state of zero or one to the cells.
I'm going to call this one "draw"
just because I like the analogy
of drawing cards
although naming isn't really
consequential here.
So, let the temporary variable,
"draw", be random float one,
which is just any random quantity
between zero and one
that the computer can conceive of.
So for this random "draw",
we can say if a number picked
between zero and one
is below 0.5, which as you know,
is a fifty percent chance,
then set "state" zero,
and you want an "else" clause
in there too,
so if the "draw" number happens to be
bigger than .5,
which occurs the other fifty percent
of the time,
then set "state" to 1 instead.
Keep in mind that "state" is a variable
that's stored inside the patch,
that currently is not visible to the
human user.
So we want to mark up
through a new functional procedure
called changeColor,
which is pretty straight-forward.
So, to change color,
it might be convenient to use
another "ifelse" statement.
If the cell state is zero,
we get one color.
Otherwise, if it is a different state,
ergo one,
we get a different color
to distinguish these cells
from each other.
Beyond that, it's obviously
up to personal taste
which colors we want to use.
I'm going to say that
white cells are zeros,
that is, set pcolor to white,
if patch's state is zero.
Conversely, if the state is one,
let's make it look green instead.
I personally like using
these lighter colors for this,
mostly because it helps us
separate the rows of cells that are done
from the darker ones
that aren't done yet.
With that, we end the procedure,
and check that nothing is broken yet.
I'm going to put up some buttons,
just to test the model so far,
and see if we're on the right track.
We have a Setup button
to run our setup procedures,
and a Go button.
The Go button shouldn't
do anything just yet,
but it will eventually need to be there.
Setting up the model now.
See, so now we have about half white cells
and half green cells in the starter row,
reflecting our fifty percent chance
of the cells turning white,
and fifty percent chance
the cell is seeded green
in random sequence.
Initial conditions are re-set each time
using this procedure,
which you see,
are completely random each time,
but roughly speaking,
you'll have fifty-fifty greens and whites
on average.
Go, surprise, does nothing yet,
because I didn't put anything
out in the Go procedure, yet.
Next thing,
we want the cells to actually interact
with and respond to cells around them,
and change according to
specific rules,
regarding their neighborhood
configuration.
Right?
So far, I haven't defined what the
rule sets are.
So just to quickly see that in action,
I'm going to initialize the model for now,
with one of my favorite rule sets.
This is one of those rules with the
interesting properties of the
somewhat orderly yet chaotic behavior
characteristic of, you know,
complex structures.
With this rule set,
as a temporary place-holder,
let's specify the Go procedure,
where the CA rules come into play.
Remember, the rules dictate which state
the cells change to for each
neighborhood configuration,
which require each
patch cell to detect the
state of their two immediate neighbors,
as well as their own state.
Sticking to the top patches for now,
we're going to have each patch update
and record its neighborhood configuration.
That is, the three state combination that
includes the cell's left neighbor,
itself, and its right neighbor.
Since neighborhood is defined by a
combination of cell states,
you want to create a list,
which is just another vector.
In this vector, you want to
include as the first entry,
the binary state of the left cell.
So a little bit of NetLogo vocabulary
for you guys.
"patch-at" refers to the cell patch
in a position relative to the
current cell calling it.
The first number is the x distance
to the left, or right.
The second number, y distance,
up or down.
So patch-at -1 0
refers to the cell to the immediate left
of the current cell.
Now, add in the state of the current patch
calling this procedure
which is just "state" by default
and finally the state on the right cell,
patch-at 1 0
because now we're looking to the right.
With that, we have a cogent command block,
which I'll just close up for now.
Whoops, my bad,
So, to recap,
each cell is looking to
its left, and right,
and within itself,
for the current set of states
updating at each time step.
The cells are hence merely storing
information each time,
not actually doing anything with it,
and changing their states in accordance.
Hence, the model's apparent behavior
hasn't changed yet.
As you can see, clicking these buttons,
Obviously there are several factors here
that are required to make
the model run properly.
For example, we can't see
the manifestation
of the cell's new state
without a changeColor procedure
But more fundamentally,
the model is currently missing
an application of the rules,
when they supposedly direct
cell behavior.
In fact,
the initialist rule vector I picked out
isn't really doing anything currently.
So we need a way for the cells to refer
to those rules in the vector.
Now, there's many, many ways
to program this part,
and as I'm sure the computer scientists
among you will quickly realize,
the one I'm showing is by no means
the most efficient.
But some of you will
at least hopefully appreciate
the syntactic simplicity
of my approach here.
So far, we have the patches
looking at the neighborhood states
and keeping track of them at
each time step.
Now we want them to update their state
as a function of these
current configurations.
So we're going to have to have
our patches check these states,
and look up the appropriate corresponding
response state from the rule vector.
For instance, ask the patch if
its neighborhood configuration
was zero zero zero.
This corresponds to a
neighborhood of all white cells
on the left, center, and right cells here.
If the cell in question sees this state,
we want this cell to set the state
corresponding to item zero
from the rule set list.
This causes the focal patch to change its
state to the first item in the rule vector
While we know off-hand that
this would
currently to set the state to zero
we ultimately want the patch to respond
to any arbitrary binary rule vector
the user specifies.
That's the one above I made up.
Checking for the next possible
configuration,
zero zero one,
ergo, a white, white, green,
we want the cell to change states
to the second output state on the list,
item 1.
Similarly the third configuration
corresponds to item 2.
Fourth configuration, three,
and, you get the idea.
In an effort to save some time,
I'm just going to
copy and paste here,
to set up the remaining
six possible permutations.
Again, this is not the
most expedient algorithm.
For you code-savvy folk,
there's definitely ways,
if we use "foreach" loops,
that count up and generate any
full set of neighborhood configurations
under Wolfram's ordering scheme,
to align those configurations
with the rule vector.
We're keeping it simple right now,
so at least you have a template to
play around with.
In the meantime,
I'm just checking and filling in the
remaining possible configurations
in the cell's next state,
corresponding with the respective
items from the rule set.
And looks good.
Quick Setup and Go.
Now we see some interaction
between cells in the first row.
Notice how every time I'm doing this,
it's updating the patches
based on their prior neighborhoods,
and even without us doing the
drop down CA runs,
we're eventually going to get in the next
couple of code revisions,
one might formulate hypotheses,
about the pattern upon
casual observation.
Is there a specific set of
repetitive cycles present?
Or the emergence of more
complex tortuous meta cycles,
characteristic of chaos?
Or is it mostly random noise,
playing tricks on our brain?
However, to see that bigger picture
time evolution in one visualization,
we actually want to enable the
target cells to drop down,
so we can see this row get filled,
this row,
this one,
and so forth.
Happily for us,
NetLogo already has its own native
means of keeping track of time steps
called "tick".
As you should already be familiar with,
"tick" is a built-in function that
tallies the total number of times
its passed through the code
keeping count of the current
iteration we're at.
So this is what our coordinate scheme
from the beginning comes in handy.
Since both the ticks counter and this
p-y coordinate system start at zero,
we can literally use the number of ticks
to determine which row we want to display
the pertinent cell states.
Keep in mind,
the p-y cor values count down
negative one, negative two,
negative three.
Since downward represents the
negative direction
on the y axis,
so we want p-y cor to be the negative
of the current tick count.
For future reference,
let's refer to cells in these given rows
as the current patches.
With these current patches,
we first want to copy the
last row onto the new one,
so the cells at the current time step
can interact along the new row
without losing their previous history.
To do this,
set state for each of the current patches
the same as their former cells
directly above, with
patchAt 0 1
Now in this new row,
we can ask the cells to scan
their neighborhoods,
and update their states accordingly,
just like they were doing before,
only now in a separate row.
All right!
This is awesome.
We finally get to see a little bit of
temporal evolution in action.
In our first official run of the model,
you can see the cells moving down a row
each time step.
And slowing it down a little,
you can see they are
doing this asynchronously,
in random order,
as we intended them to
while we get fragments
of familiar clusters,
popping up at various frequencies,
the overall distribution pattern of green
is too sparse to manifest
the same complex behaviors
as the synchronous deterministic
version of this rule.
In fact,
a more thorough analysis of these
so-called clusters
might reveal that they are not actually
occurring at much higher frequency
than one would expect by mere chance,
telling us randomness, not structure,
might be the dominating factor for
this rule set
under the stochastic ECA.
But to have a true general ECA,
synchronous or not,
we obviously need to have the capacity
to change the rules,
and compare them,
within the same interface.
Furthermore,
we want to compact these long binary
based rule vectors
into the shorthand base 10
rule numbering system for the user,
with designations like
Rule 30, Rule 212,
or for what we just did
Rule 129.
So we' like the user to see these
colloquial rule numberings,
that are readily translated into rule sets
this program can directly use.
To have a rule set that always updates,
based on what the user
changes the number to,
a reporter can be really useful.
So first off we want an Arabic numeral
input from the user.
So it's probably convenient to put
a slider here
to adjust the rule number.
We know this model has a total of
256 rules.
So it's numbered from 0 to 255.
To try out another potentially interesting
rule that exhibited rich complexity
in the original ECA,
I'm going to put in Rule 90 here.
So let's get rid of the variable
in this line,
which is redundant with our new
rule set slider parameter.
And likewise, we don't need the
initial rule set and setup any more,
thus leading us back to the task of
converting base 10 numbers to binary.
So,
using digit-by-digit counting approach,
let remainder start off as the current
rule number base ten.
So in our case, the 90,
and since remainder is
actually a reserved name for a
mathematical operation in NetLogo,
I'm just going to call it remainer
Kinda makes sense I hope.
So again, let remainer be the
user specified rule number.
And then,
here's a good place to use the
foreach function.
This is where we methodically go through
the binary digits in the rule vector,
where each carry equivalent
base ten value
is from greatest to least.
So the first significant digit is
two to the seventh power,
ergo 128.
Next one is 64, 32, 16 etc
Again there's probably countless ways to
derive and generate this vector
without manually putting in
everything,
but I'm just being pedagogical-ish.
Oh yeah,
one more thing here.
Let there be a temporary variable,
RuleList.
This is just a stand-in for
a rule set variable,
which starts out as an empty list,
but collects zeros and ones,
until it has eight binary digits
and reports a complete list
as the rule set.
For each of these binary digit values,
we're going to run a comparison test.
So I'm going to say,
for remainer, ifelse remainer less than
the current binary digit,
set this digit to zero.
For example, for a rule number of 90,
the leading digit is zero
since anything with one in the front
is going to be 128 or greater
which we add to the rule list
with LPUT.
LPUT appending the value to
the latest item on the current list.
We get to use Set for these funtions
which has to do with the way
NetLogo updates lists,
since they can't edit
existing lists directly.
So if rule number is less than
current place value,
set binary digit to zero,
but otherwise, if it is equal to or
greater than this place,
as in the case of the
second significant digit,
where 90 is greater than 64,
set this digit to one.
However, we have to account for the fact
that a value of 64 is now included
in the current rule vector.
So we deduct that from 90,
leaving us with a remainder,
or in this code, remainer,
of 26.
This algorithm continues running through
each of the following significant digits
in the same manner,
running a similar comparison test,
and recording the appropriate
binary digits and subtractions.
And finally with the
8 digit list completed,
we report the final output,
allowing NetLogo to store it in our
rule set variable.
And this variable is automatically
being referenced and updated
by the program at every step.
So you don't need to set it
anywhere else.
I'll even create a monitor of the
actual rule set so you can see
what the rule vector NetLogo
is actually using,
and how instantaneous this
conversion process is.
Running Rule 90 in the asynchronous ECA
we some semblance of
emergent macro structure.
I'll leave it to you to explore the rest
of what the asynchronous ECA has to offer:
what differences it has,
what properties does it preserve
from the ordered synchronous ECA.
Of course, there a bunch of other features
you can add to this model
at the drop of a hat.
For example, we started with a default
number of patches along each row,
which is 33 here,
but we can decrease or increase this
number by teaming up the max px cor
with another slider for instance,
to change perspective,
maybe get a broader birds eye
view of the patterns.
Along those same lines,
you could also change the
maximum py cor,
and/or even make the pixels smaller.
It may be convenient to have a Go button
that runs automatically
and views evolution at different speeds.
We might not necessarily even want to have
50-50 starting conditions for the patches,
so you can adjust the
probability in the code,
or slider,
or ???est level of max,
or highest ECA model,
have an edit mode that lets you choose
the patches individually.
All right folks
Hope this demo was illuminating
and helpful
i hope you have fun with everything.
See you later.