bgneal@46: """ Code example from Complexity and Computation, a book about bgneal@46: exploring complexity science with Python. Available free from bgneal@46: bgneal@46: http://greenteapress.com/complexity bgneal@46: bgneal@46: Copyright 2011 Allen B. Downey. bgneal@46: Distributed under the GNU General Public License at gnu.org/licenses/gpl.html. bgneal@46: """ bgneal@46: bgneal@46: import numpy bgneal@46: bgneal@46: class CA(object): bgneal@46: """A CA is a cellular automaton; the parameters for __init__ are: bgneal@46: bgneal@46: rule: an integer in the range 0-255 that represents the CA rule bgneal@46: using Wolfram's encoding. bgneal@46: n: the number of rows (timesteps) in the result. bgneal@46: ratio: the ratio of columns to rows. bgneal@46: """ bgneal@46: bgneal@46: def __init__(self, rule, n=100, ratio=2): bgneal@46: """Attributes: bgneal@46: table: rule dictionary that maps from triple to next state. bgneal@46: n, m: are the number of rows, columns. bgneal@46: array: the numpy array that contains the data. bgneal@46: next: the index of the next empty row. bgneal@46: """ bgneal@46: self.table = self.make_table(rule) bgneal@46: self.n = n bgneal@46: self.m = ratio*n + 1 bgneal@46: self.array = numpy.zeros((n, self.m), dtype=numpy.int8) bgneal@46: self.next = 0 bgneal@46: bgneal@46: def make_table(self, rule): bgneal@46: """Returns a table for the given CA rule. The table is a bgneal@46: dictionary that maps 3-tuples to binary values. bgneal@46: """ bgneal@46: table = {} bgneal@46: for i, bit in enumerate(binary(rule, 8)): bgneal@46: t = binary(7-i, 3) bgneal@46: table[t] = bit bgneal@46: return table bgneal@46: bgneal@46: def start_single(self): bgneal@46: """Starts with one cell in the middle of the top row.""" bgneal@46: self.array[0, self.m/2] = 1 bgneal@46: self.next += 1 bgneal@46: bgneal@46: def start_random(self): bgneal@46: """Start with random values in the top row.""" bgneal@46: self.array[0] = numpy.random.random([1,self.m]).round() bgneal@46: self.next += 1 bgneal@46: bgneal@46: def loop(self, steps=1): bgneal@46: """Executes the given number of time steps.""" bgneal@46: [self.step() for i in xrange(steps)] bgneal@46: bgneal@46: def step(self): bgneal@46: """Executes one time step by computing the next row of the array.""" bgneal@46: i = self.next bgneal@46: self.next += 1 bgneal@46: bgneal@46: a = self.array bgneal@46: t = self.table bgneal@46: for j in xrange(1,self.m-1): bgneal@46: a[i,j] = t[tuple(a[i-1, j-1:j+2])] bgneal@46: bgneal@46: def get_array(self, start=0, end=None): bgneal@46: """Gets a slice of columns from the CA, with slice indices bgneal@46: (start, end). Avoid copying if possible. bgneal@46: """ bgneal@46: if start==0 and end==None: bgneal@46: return self.array bgneal@46: else: bgneal@46: return self.array[:, start:end] bgneal@46: bgneal@46: bgneal@46: def binary(n, digits): bgneal@46: """Returns a tuple of (digits) integers representing the bgneal@46: integer (n) in binary. For example, binary(3,3) returns (0, 1, 1)""" bgneal@46: t = [] bgneal@46: for i in range(digits): bgneal@46: n, r = divmod(n, 2) bgneal@46: t.append(r) bgneal@46: bgneal@46: return tuple(reversed(t)) bgneal@46: bgneal@46: bgneal@46: def print_table(table): bgneal@46: """Prints the rule table in LaTeX format.""" bgneal@46: t = table.items() bgneal@46: t.sort(reverse=True) bgneal@46: bgneal@46: print '\\beforefig' bgneal@46: print '\\centerline{' bgneal@46: print '\\begin{tabular}{|c|c|c|c|c|c|c|c|c|}' bgneal@46: print '\\hline' bgneal@46: bgneal@46: res = ['prev'] bgneal@46: for k, v in t: bgneal@46: s = ''.join([str(x) for x in k]) bgneal@46: res.append(s) bgneal@46: print ' & '.join(res) + ' \\\\ \n\\hline' bgneal@46: bgneal@46: res = ['next'] bgneal@46: for k, v in t: bgneal@46: res.append(str(v)) bgneal@46: print ' & '.join(res) + ' \\\\ \n\\hline' bgneal@46: bgneal@46: print '\\end{tabular}}' bgneal@46: bgneal@46: