Cornell University
BioNB 441
Cellular Automata in Matlab
Introduction
Cellular Automata (CA) are a scheme for computing using local rules and local communication. Typcally a CA is defined on a grid, with each point on the grid representing a cell with a finite number of states. A transition rule is applied to each cell simultaneously. Typical transition rules depend on the state of the cell and its (4 or 8) nearest neighbors, although other neighborhoods are used. CAs have applications in parallel computing research, physical simulations, and biological simulations. This page will consider how to write efficient matlab code to implememt CAs and look at some interesting rules.
Matlab code considerations
The following considerations will be illustrated using a Matlab program which computes Conway's life. Parts of the code are explained below.
cells contains the binary state of each cell and
the array z contains zeros, then the cat command
builds an RGB image, which is displayed by the image command.
The image command also returns a handle.
imh = image(cat(3,cells,z,z)); set(imh, 'erasemode', 'none') axis equal axis tight
z = zeros(n,n); cells = z; cells(n/2,.25*n:.75*n) = 1; cells(.25*n:.75*n,n/2) = 1;
x = 2:n-1;
y = 2:n-1;
sum(x,y) = cells(x,y-1) + cells(x,y+1) + ...
cells(x-1, y) + cells(x+1,y) + ...
cells(x-1,y-1) + cells(x-1,y+1) + ...
cells(x+1,y-1) + cells(x+1,y+1);
cells = (sum==3) | (sum==2 & cells);
%build the GUI
%define the plot button
plotbutton=uicontrol('style','pushbutton',...
'string','Run', ...
'fontsize',12, ...
'position',[100,400,50,20], ...
'callback', 'run=1;');
%define the stop button
erasebutton=uicontrol('style','pushbutton',...
'string','Stop', ...
'fontsize',12, ...
'position',[200,400,50,20], ...
'callback','freeze=1;');
%define the Quit button
quitbutton=uicontrol('style','pushbutton',...
'string','Quit', ...
'fontsize',12, ...
'position',[300,400,50,20], ...
'callback','stop=1;close;');
number = uicontrol('style','text', ...
'string','1', ...
'fontsize',12, ...
'position',[20,400,50,20]);
After the controls (and CA) are initialized, the program drops into a loop
which tests the state of the variables which are set in the callback functions
of each button. For the moment, just look at the nested structure of the while
loop and if statements. The program loops until the Quit button is pushed.
The other two buttons cause an if statement to execute when pushed.
stop= 0; %wait for a quit button push
run = 0; %wait for a draw
freeze = 0; %wait for a freeze
while (stop==0)
if (run==1)
%nearest neighbor sum
sum(x,y) = cells(x,y-1) + cells(x,y+1) + ...
cells(x-1, y) + cells(x+1,y) + ...
cells(x-1,y-1) + cells(x-1,y+1) + ...
cells(3:n,y-1) + cells(x+1,y+1);
% The CA rule
cells = (sum==3) | (sum==2 & cells);
%draw the new image
set(imh, 'cdata', cat(3,cells,z,z) )
%update the step number diaplay
stepnumber = 1 + str2num(get(number,'string'));
set(number,'string',num2str(stepnumber))
end
if (freeze==1)
run = 0;
freeze = 0;
end
drawnow %need this in the loop for controls to work
end
Examples
x = 2:n-1; y = 2:n-1; %nearest neighbor sum sum(x,y) = cells(x,y-1) + cells(x,y+1) + ... cells(x-1, y) + cells(x+1,y) + ... cells(x-1,y-1) + cells(x-1,y+1) + ... cells(3:n,y-1) + cells(x+1,y+1); % The CA rule cells = (sum==3) | (sum==2 & cells);
x = 2:n-1;
y = 2:n-1;
sum(x,y) = cells(x,y-1) + cells(x,y+1) + ...
cells(x-1, y) + cells(x+1,y) + ...
cells(x-1,y-1) + cells(x-1,y+1) + ...
cells(3:n,y-1) + cells(x+1,y+1)+...
cells(x,y);
% The CA rule
cells = ~((sum< 4) | (sum==5));
sum(2:a-1,2:b-1) = cells(2:a-1,1:b-2) + cells(2:a-1,3:b) + ...
cells(1:a-2, 2:b-1) + cells(3:a,2:b-1) + ...
cells(1:a-2,1:b-2) + cells(1:a-2,3:b) + ...
cells(3:a,1:b-2) + cells(3:a,3:b);
pick = rand(a,b);
cells = cells | ((sum>=1) & (pick>=threshold) & (visit==0)) ;
visit = (sum>=1) ;
The variables a and b are the size of the image. The inital image is determined
by graphics operations. The following statements set up axes of a fixed size,
write text into the axes, then grab the contents of the axes and put them
back into an array using the getframe function..
ax = axes('units','pixels','position',[1 1 500 400],'color','k');
text('units', 'pixels', 'position', [50,255,0],...
'string','BioNB','color','w','fontname','helvetica','fontsize',100)
text('units', 'pixels', 'position', [120,120,0],...
'string','441','color','w','fontname','helvetica','fontsize',100)
initial = getframe(gca);
[a,b,c]=size(initial.cdata);
z=zeros(a,b);
cells = double(initial.cdata(:,:,1)==255);
visit = z ;
sum = z;
After a few dozen time steps (starting with the BioNB 441 image) we get the
following image. Click on it to see a full size image.
x = [2:n-1];
y = [2:n-1];
sum(x,y) = ((cells(x,y-1)> 0)&(cells(x,y-1)< t)) + ((cells(x,y+1)> 0)&(cells(x,y+1)< t)) + ...
((cells(x-1, y)> 0)&(cells(x-1, y)< t)) + ((cells(x+1,y)> 0)&(cells(x+1,y)< t)) + ...
((cells(x-1,y-1)> 0)&(cells(x-1,y-1)< t)) + ((cells(x-1,y+1)> 0)&(cells(x-1,y+1)< t)) + ...
((cells(x+1,y-1)> 0)&(cells(x+1,y-1)< t)) + ((cells(x+1,y+1)> 0)&(cells(x+1,y+1)< t));
cells = ((cells==0) & (sum>=t1)) + ...
2*(cells==1) + ...
3*(cells==2) + ...
4*(cells==3) + ...
5*(cells==4) + ...
6*(cells==5) +...
7*(cells==6) +...
8*(cells==7) +...
9*(cells==8) +...
0*(cells==9);
An image from this CA after spiral waves develop is shown below.
sum = (veg(1:n,[n 1:n-1])==1) + (veg(1:n,[2:n 1])==1) + ...
(veg([n 1:n-1], 1:n)==1) + (veg([2:n 1],1:n)==1) ;
veg = ...
2*(veg==2) - ((veg==2) & (sum> 0 | (rand(n,n)< Plightning))) + ...
2*((veg==0) & rand(n,n)< Pgrowth) ;
Note that the toroidal connection is implemented by the ordering of subscripts.
| even | even | |
| even | cell | odd |
| odd | odd |
The rule:
| 0 | 0 | goes | 0 | 0 |
| 0 | 0 | to | 0 | 0 |
| 1 | 0 | goes | 0 | 0 |
| 0 | 0 | to | 0 | 1 |
| 1 | 0 | goes | 1 | 0 |
| 0 | 1 | to | 0 | 1 |
| 1 | 0 | goes | 0 | 1 |
| 1 | 0 | to | 0 | 1 |
| 1 | 1 | goes | 0 | 1 |
| 1 | 0 | to | 1 | 1 |
| 1 | 1 | goes | 1 | 1 |
| 1 | 1 | to | 1 | 1 |
| 1 | 0 | goes | 0 | 1 |
| 0 | 1 | to | 1 | 0 |
p=mod(i,2); %margolis neighborhood, where i is the time step
%upper left cell update
xind = [1+p:2:nx-2+p];
yind = [1+p:2:ny-2+p];
%See if exactly one diagonal is ones
%only (at most) one of the following can be true!
diag1(xind,yind) = (sand(xind,yind)==1) & (sand(xind+1,yind+1)==1) & ...
(sand(xind+1,yind)==0) & (sand(xind,yind+1)==0);
diag2(xind,yind) = (sand(xind+1,yind)==1) & (sand(xind,yind+1)==1) & ...
(sand(xind,yind)==0) & (sand(xind+1,yind+1)==0);
%The diagonals both not occupied by two particles
and12(xind,yind) = (diag1(xind,yind)==0) & (diag2(xind,yind)==0);
%One diagonal is occupied by two particles
or12(xind,yind) = diag1(xind,yind) | diag2(xind,yind);
%for every gas particle see if it near the boundary
sums(xind,yind) = gnd(xind,yind) | gnd(xind+1,yind) | ...
gnd(xind,yind+1) | gnd(xind+1,yind+1) ;
% cell layout:
% x,y x+1,y
% x,y+1 x+1,y+1
%If (no walls) and (diagonals are both not occupied)
%then there is no collision, so move opposite cell to current cell
%If (no walls) and (only one diagonal is occupied)
%then there is a collision so move ccw cell to the current cell
%If (a wall)
%then don't change the cell (causes a reflection)
sandNew(xind,yind) = ...
(and12(xind,yind) & ~sums(xind,yind) & sand(xind+1,yind+1)) + ...
(or12(xind,yind) & ~sums(xind,yind) & sand(xind,yind+1)) + ...
(sums(xind,yind) & sand(xind,yind));
sandNew(xind+1,yind) = ...
(and12(xind,yind) & ~sums(xind,yind) & sand(xind,yind+1)) + ...
(or12(xind,yind) & ~sums(xind,yind) & sand(xind,yind))+ ...
(sums(xind,yind) & sand(xind+1,yind));
sandNew(xind,yind+1) = ...
(and12(xind,yind) & ~sums(xind,yind) & sand(xind+1,yind)) + ...
(or12(xind,yind) & ~sums(xind,yind) & sand(xind+1,yind+1))+ ...
(sums(xind,yind) & sand(xind,yind+1));
sandNew(xind+1,yind+1) = ...
(and12(xind,yind) & ~sums(xind,yind) & sand(xind,yind)) + ...
(or12(xind,yind) & ~sums(xind,yind) & sand(xind+1,yind))+ ...
(sums(xind,yind) & sand(xind+1,yind+1));
sand = sandNew;
The rule:
p=mod(i,2); %margolis neighborhood
%upper left cell update
xind = [1+p:2:nx-2+p];
yind = [1+p:2:ny-2+p];
%random velocity choice
vary = rand(nx,ny)< .5 ;
vary1 = 1-vary;
%diffusion rule -- margolus neighborhood
%rotate the 4 cells to randomize velocity
sandNew(xind,yind) = ...
vary(xind,yind).*sand(xind+1,yind) + ... %cw
vary1(xind,yind).*sand(xind,yind+1) ; %ccw
sandNew(xind+1,yind) = ...
vary(xind,yind).*sand(xind+1,yind+1) + ...
vary1(xind,yind).*sand(xind,yind) ;
sandNew(xind,yind+1) = ...
vary(xind,yind).*sand(xind,yind) + ...
vary1(xind,yind).*sand(xind+1,yind+1) ;
sandNew(xind+1,yind+1) = ...
vary(xind,yind).*sand(xind,yind+1) + ...
vary1(xind,yind).*sand(xind+1,yind) ;
sand = sandNew;
%for every sand grain see if it near the fixed, sticky cluster
sum(2:nx-1,2:ny-1) = gnd(2:nx-1,1:ny-2) + gnd(2:nx-1,3:ny) + ...
gnd(1:nx-2, 2:ny-1) + gnd(3:nx,2:ny-1) + ...
gnd(1:nx-2,1:ny-2) + gnd(1:nx-2,3:ny) + ...
gnd(3:nx,1:ny-2) + gnd(3:nx,3:ny);
%add to the cluster
gnd = ((sum> 0) & (sand==1)) | gnd ;
%and eliminate the moving particle
sand(find(gnd==1)) = 0;
The following image shows the fixed cluster after many time steps.
The rule:
| 0 | 0 | goes | 0 | 0 |
| 0 | 0 | to | 0 | 0 |
| 1 | 0 | goes | 0 | 0 |
| 0 | 0 | to | 1 | 0 |
| 0 | 1 | goes | 0 | 0 |
| 0 | 0 | to | 0 | 1 |
| 1 | 0 | goes | 0 | 0 |
| 1 | 0 | to | 1 | 1 |
| 1 | 0 | goes | 0 | 0 |
| 0 | 1 | to | 1 | 1 |
| 0 | 1 | goes | 0 | 0 |
| 1 | 0 | to | 1 | 1 |
| 0 | 1 | goes | 0 | 0 |
| 0 | 1 | to | 1 | 1 |
| 1 | 1 | goes | 1 | 0 |
| 1 | 0 | to | 1 | 1 |
| 1 | 1 | goes | 0 | 1 |
| 0 | 1 | to | 1 | 1 |
The update code:
p=mod(i,2); %margolis neighborhood
sand(nx/2,ny/2) = 1; %add a grain at the top
%upper left cell update
xind = [1+p:2:nx-2+p];
yind = [1+p:2:ny-2+p];
%randomize the flow -- 10% of the time
vary = rand(nx,ny)< .9 ;
vary1 = 1-vary;
sandNew(xind,yind) = ...
gnd(xind,yind).*sand(xind,yind) + ...
(1-gnd(xind,yind)).*sand(xind,yind).*sand(xind,yind+1) .* ...
(sand(xind+1,yind+1)+(1-sand(xind+1,yind+1)).*sand(xind+1,yind));
sandNew(xind+1,yind) = ...
gnd(xind+1,yind).*sand(xind+1,yind) + ...
(1-gnd(xind+1,yind)).*sand(xind+1,yind).*sand(xind+1,yind+1) .* ...
(sand(xind,yind+1)+(1-sand(xind,yind+1)).*sand(xind,yind));
sandNew(xind,yind+1) = ...
sand(xind,yind+1) + ...
(1-sand(xind,yind+1)) .* ...
( sand(xind,yind).*(1-gnd(xind,yind)) + ...
(1-sand(xind,yind)).*sand(xind+1,yind).*(1-gnd(xind+1,yind)).*sand(xind+1,yind+1));
sandNew(xind+1,yind+1) = ...
sand(xind+1,yind+1) + ...
(1-sand(xind+1,yind+1)) .* ...
( sand(xind+1,yind).*(1-gnd(xind+1,yind)) + ...
(1-sand(xind+1,yind)).*sand(xind,yind).*(1-gnd(xind,yind)).*sand(xind,yind+1));
%scramble the sites to make it look better
temp1 = sandNew(xind,yind+1).*vary(xind,yind+1) + ...
sandNew(xind+1,yind+1).*vary1(xind,yind+1);
temp2 = sandNew(xind+1,yind+1).*vary(xind,yind+1) + ...
sandNew(xind,yind+1).*vary1(xind,yind+1);
sandNew(xind,yind+1) = temp1;
sandNew(xind+1,yind+1) = temp2;
sand = sandNew;
References
[1] Cellular Automata Machines by Tommaso Toffoli and Norman Margolus, MIT Press, 1987.
[2] Cellular Automata Modeling of Physical Systems by Bastien Chopard and Michel Droz, Cambridge University Press, 1998.