Sei sulla pagina 1di 13

Cellular automata

examples using C++, Qt, python, wxpython and numpy

version: 38 author: F.Boonstra

Contents
Introduction.............................................................................................................................3 Requirements.........................................................................................................................3 Description.............................................................................................................................4 What is a cellular automaton?...........................................................................................4 Use cases..........................................................................................................................4 C++ approach........................................................................................................................5 Python approach....................................................................................................................6 What do i need?.................................................................................................................6 Naive version:................................................................................................................6 Advanced version:.........................................................................................................6 Control flow........................................................................................................................7 Data types..........................................................................................................................8

1 Introduction
While being caught by the flue I decided to play a bit with cellular automata. My first approach is to use C++ with Qt for the user interface. The second approach is using python with wxPython for the user interface. The python approach comes in two versions: a naive slow first approach in just python and a much faster solution using the numpy software. As python is a very pleasant programming language to work with, it seemed an interesting goal to see whether python would be fast enough for the task. Note that this is not a particular fancy program.

2 Requirements
The software used for the C++ appoach is: - kubuntu 10.4 - kdevelop - Qt The software for the python approach is: - kubuntu 10.4 - python 2.6 - wxpython 2.8 - numpy 1:1.3.0

3 Description
3.1 What is a cellular automaton?
From wikipedia:
A cellular automaton (pl. cellular automata, abbrev. CA) is a discrete model studied in computability theory, mathematics, physics, complexity science, theoretical biology and microstructure modeling. It consists of a regular grid of cells, each in one of a finite number of states, such as "On" and "Off" (in contrast to a coupled map lattice).

From Stephen Wolfram's Mathworld:


A cellular automaton is a collection of "colored" cells on a grid of specified shape that evolves through a number of discrete time steps according to a set of rules based on the states of neighboring cells.

3.2 Use cases


1. 2. 3. 4. 5. The user will be shown a playarea where individual cells can be set. The user should be able to select an algorithm and apply that to the playarea. The user has control on how many times the algorithm will be applied in a run. The user should be able to save and load the state of the playarea. The user should be able to reset the playarea to the initial state.

4 C++ approach
TBD

5 Python approach
5.1 Naive version 5.1.1 What do i need?
TBD

5.1.2 Control flow


TBD

5.1.3 Data types


TBD

5.2 Advanced version 5.2.1 What do i need?


Needed for some python code:
from __future__ import with_statement from struct import *

Needed for the user interface:


import wx import wx.lib.layoutf as layoutf

Needed for the algorithms / calculations:


import numpy as NU import numpy.numarray.nd_image as NI

5.3 Control flow


The main control loop:
def main(): app = wx.PySimpleApp() frame = wx.Frame(None, -1, "Cellular Automaton", size=(520,600)) caDialog(frame, (256, 256)) frame.Show(1) app.MainLoop()

The context consists of a wx.PySimpleApp and wx.Frame that will be subclassed. The subclassed Frame, where the cellular automaton will be shown, will have a 256 by 256 dimension. The overall Frame area is 520 by 600.

5.4 Data types


To represent the data for a cell we define a 32-bit datatype consisting of 4 8-bit values that will encode the color (RGBA) value for the cell. R = red, G = green, B = blue and A = alpha. A indicates the opacity of a color: 0 for full transparency, 255 for no transparency. Two utility functions are defined to create arrays from this basic datatype:
# define bytearray creation: rgbarec = NU.dtype({'r':(NU.uint8,0), 'g':(NU.uint8,1), 'b':(NU.uint8,2), 'a':(NU.uint8,3)}) def makeByteArray(shape): return NU.empty(shape, (NU.uint32, rgbarec)) def makeBWArray(shape): return NU.zeros(shape, NU.uint8)

We also define two constant cells representing a 'dead' and a 'life' cell:
# # Currently just work with 2 color values. # One for dead cells and one for life cells deadCellAR = makeByteArray((1,1)) DCR = deadCellR = deadCellAR['r'][0,0] DCG = deadCellG = deadCellAR['g'][0,0] DCB = deadCellB = deadCellAR['b'][0,0] DCA = deadCellA = deadCellAR['a'][0,0] deadCell = deadCellAR[0][0] lifeCellAR = makeByteArray((1,1)) LCR = lifeCellR = lifeCellAR['r'][0,0] LCG = lifeCellG = lifeCellAR['g'][0,0] LCB = lifeCellB = lifeCellAR['b'][0,0] LCA = lifeCellA = lifeCellAR['a'][0,0] lifeCell = lifeCellAR[0][0] = = = = 200 0 0 255

= = = =

0 200 0 255

I will use two classes:


# class that handles the dialog with the user: class caDialog(wx.Panel): def __init__(self, parent, DIM): def buildInterface(self): def executeCA(self): def applyRule(self, rule): def OnExecute(self,event): def OnRestore(self,event): def OnSave(self,event): def OnLoad(self,event): def OnPaint(self, evt): def draw(self): def redrawBitmap(self, pdc):

# class the implements the automaton rules: class caRules: def __init__(self): def parity(self): def life(self): def lwd(self): def lwd2(self): def som23_4(self): def initRules(self): def getAllRulesAsStrings(self): def getRule(self, ruleAsString): def setBuffer(self, buf): def getBuffer(self): def dumpInfo(self, buf, ker, res, label):

5.5 User interface


The user interface looks as follows:

The blue part shows the controls for selecting the pattern rule and the number of generations. Additionally the transparency (the A part of the RGBA) can be set. The buttons for executing the pattern rule, resetting the playarea, loading or saving the playarea are available. The red part shows the play area. Initially of software generated pattern is visible.

5.5.1 Ininialization
The instance of caDialog is created, as we have seen, with:
caDialog(frame, (256, 256))

The constructor does the following:


class caDialog(wx.Panel): def __init__(self, parent, dimension): wx.Panel.__init__(self, parent, -1) self.xdim = dimension[0] self.ydim = dimension[1] # The rules: self.rules = caRules() # Build the user interface: self.buildInterface() # Generate coord list: xdim = range(1,self.xdim-1) ydim = range(1,self.ydim-1) self.xydims = [(x,y) for x in xdim for y in ydim] # Using Bitmap: self.imgs = makeBitmapBuffer((self.xdim, self.ydim), DCR, DCG, DCB, DCA) # finally, use the array to create a bitmap self.imgs['i'] = wx.BitmapFromBufferRGBA(self.xdim, self.ydim, self.imgs['b']) # Init bitmap access: self.pd = wx.AlphaPixelData(self.imgs['i']) if not self.pd: raise RuntimeError("Failed to gain raw access to bitmap data.") self.pda = wx.AlphaPixelData_Accessor(self.imgs['i'], self.pd) # Init bitmap contents: self.DA = makeBitmapArray((self.xdim, self.ydim), DCR, DCG, DCB, DCA) self.LA = makeBitmapArray((self.xdim, self.ydim), LCR, LCG, LCB, LCA) initBitmap(self.imgs, (self.xdim, self.ydim)) self.rules.setBuffer((self.imgs['b'], self.imgs['s'], self.DA, self.LA)) self.Refresh() self.Update()

I will describe the blue colored functions. The caRules() creates an instance of the caRules class. This will be described in detail further on. The self.buildInterface() function builds the user interface as shown on the previous page.

10

The makeBitmapBuffer((self.xdim, self.ydim), DCR, DCG, DCB, DCA) calls the function:
def makeBitmapBuffer(size, red, green, blue, alpha): # make the array arr = makeBitmapArray(size, red, green, blue, alpha) bwa = makeBWArray(size) d = {} d['i'] = None d['b'] = arr d['s'] = bwa return d

The extra utility function makeBitmapArray is used:


def makeBitmapArray(size, red, green, blue, alpha=128): # Make an array of bytes that is DIM*DIM in size, with enough # slots for each pixel to have a RGB and A value arr = makeByteArray( size ) # initialize all pixel values to the values passed in arr['r'][:,:] = red arr['g'][:,:] = green arr['b'][:,:] = blue arr['a'][:,:] = alpha # Set the alpha for the border pixels to be fully opaque arr['a'][0, :] = wx.ALPHA_OPAQUE # first row arr['a'][size[0]-1, :] = wx.ALPHA_OPAQUE # last row arr['a'][:, 0] = wx.ALPHA_OPAQUE # first col arr['a'][:, size[1]-1] = wx.ALPHA_OPAQUE # last col return arr

The makeByteArray and makeBWArray functions have been described in the 'Data types' paragraph earlier. The array is used to create the actual bitmap. The bitmap is initialized with a pattern (see user interface earlier):
def initBitmap(img, dim): # This is for generating an image to start from: w = dim[0]; h = dim[1] for y in xrange(h): for x in xrange(w): if ((x > (w/2+0.0*w) and x < (w/2+0.1*w)) and (y > (h/2+0.0*h) and y < (h/2+0.1*h))) or ((x > (w/2+0.2*w) and x < (w/2+0.4*w)) and (y > (h/2+0.2*h) and y < (h/2+0.4*h))) or ((x > (w/2-0.4*w) and x < (w/2+0.0*w)) and (y > (h/2-0.4*h) and y < (h/2+0.0*h))): img['b'][x,y] = lifeCell img['s'][x,y] = 1

\ \ \ \ \

This will create the three green rectangular areas. The Refresh and Update functions of wx.Panel will then be called to show the dialog.

11

5.5.2 Executing a rule


When the user selects the 'Execute' button, OnExecute() in caDialog will be called:
def OnExecute(self,event): # execute the cellular automaton self.executeCA() def executeCA(self): # Get the chosen rule to apply: choice = self.patternlist.GetValue() rule = self.rules.getRule(choice) # Apply the rule as many times as asked for: for i in range(self.generations.GetValue()): self.applyRule(rule) self.draw() # draw the resulting bitmap

In the constructor of caDialog, an instance of caRules has been created. The constructor of caRules looks as follows:
class caRules: def __init__(self): self.initRules() def initRules(self): self.patternlistT = [ "parity", "life", "lwd", "lwd2", "som23_4" ] self.patternlistF = [ self.parity, self.life, self.lwd, self.lwd2,

self.som23_4 ]

In the constructor of caDialog the setBuffer() function of caRules has been used:
def setBuffer(self, buf): self.buf = buf[0] self.bufbw = buf[1] self.DA = buf[2] self.LA = buf[3] size = self.bufbw.shape xdim = range(1,size[0]-1) ydim = range(1,size[1]-1) self.xydims = [(x,y) for x in xdim for y in ydim]

Now in executeCA() the getRule() function is called:


def getRule(self, ruleAsString): k = self.patternlistT.index(ruleAsString) return self.patternlistF[k]

The returned rule is used to call applyRule() in caDialog:


def applyRule(self, rule): # Apply the rule if DOAPPLYMAP: map(rule,self.xydims) elif DOUSENU: rule() else: for xy in self.xydims: rule(xy)

12

The constants DOAPPLYMAP and DOUSENUMPY are used to experiment with different solutions. We assume here that DOAPPLYMAP is false and DOUSENUMPY is true. The argument 'rule' of applyRule is called. The actual value of rule is one of the functions in the caRules class:
[ self.parity, self.life, self.lwd, self.lwd2, self.som23_4 ]

I will look at each of them. First 'parity':


def parity(self): kernel = NU.array([[0,1,0],[1,1,1],[0,1,0]]).astype(NU.uint8) mask = (NI.convolve(self.bufbw, kernel) == 1).astype(NU.uint8) self.bufbw = mask self.buf = NU.array(self.DA, copy=1) NU.putmask(self.buf, mask, self.LA)

13

Potrebbero piacerti anche