r
/
fifth
1
Fork 0

3x speed increase; not quite functional

master
Joshua Potter 2015-06-06 19:07:52 -04:00
parent e931af0020
commit e64dfa9e79
11 changed files with 83 additions and 51 deletions

View File

@ -10,11 +10,10 @@ if __name__ == '__main__':
sys.path.append(os.path.abspath('src')) sys.path.append(os.path.abspath('src'))
import cam import cam
import util as u import cam_parser
import ruleset as rs
c = cam.CAM(1, 100, 2) c = cam.CAM(1, 100, 2)
p = u.CAMParser('B368/S23', c) p = cam_parser.CAMParser('B368/S23', c)
c.randomize() c.randomize()
c.start_plot(100, p.ruleset) c.start_plot(100, p.ruleset)

View File

@ -10,11 +10,10 @@ if __name__ == '__main__':
sys.path.append(os.path.abspath('src')) sys.path.append(os.path.abspath('src'))
import cam import cam
import util as u import cam_parser
import ruleset as rs
c = cam.CAM(1, 100, 2) c = cam.CAM(1, 100, 2)
p = u.CAMParser('B3/S23', c) p = cam_parser.CAMParser('B3/S23', c)
c.randomize() c.randomize()
c.start_plot(400, p.ruleset) c.start_plot(100, p.ruleset)

View File

@ -10,11 +10,10 @@ if __name__ == '__main__':
sys.path.append(os.path.abspath('src')) sys.path.append(os.path.abspath('src'))
import cam import cam
import util as u import cam_parser
import ruleset as rs
c = cam.CAM(1, 100, 2) c = cam.CAM(1, 100, 2)
p = u.CAMParser('B3/S012345678', c) p = cam_parser.CAMParser('B3/S012345678', c)
c.randomize() c.randomize()
c.start_plot(100, p.ruleset) c.start_plot(100, p.ruleset)

View File

@ -10,11 +10,10 @@ if __name__ == '__main__':
sys.path.append(os.path.abspath('src')) sys.path.append(os.path.abspath('src'))
import cam import cam
import util as u import cam_parser
import ruleset as rs
c = cam.CAM(1, 100, 2) c = cam.CAM(1, 100, 2)
p = u.CAMParser('B368/S245', c) p = cam_parser.CAMParser('B368/S245', c)
c.randomize() c.randomize()
c.start_plot(100, p.ruleset) c.start_plot(100, p.ruleset)

View File

@ -10,11 +10,10 @@ if __name__ == '__main__':
sys.path.append(os.path.abspath('src')) sys.path.append(os.path.abspath('src'))
import cam import cam
import util as u import cam_parser
import ruleset as rs
c = cam.CAM(1, 100, 2) c = cam.CAM(1, 100, 2)
p = u.CAMParser('B1357/S1357', c) p = cam_parser.CAMParser('B1357/S1357', c)
c.randomize() c.randomize()
c.start_plot(100, p.ruleset) c.start_plot(100, p.ruleset)

View File

@ -10,11 +10,10 @@ if __name__ == '__main__':
sys.path.append(os.path.abspath('src')) sys.path.append(os.path.abspath('src'))
import cam import cam
import cam_util as u import cam_parser
import ruleset as rs
c = cam.CAM(1, 100, 2) c = cam.CAM(1, 100, 2)
p = u.CAMParser('B2/S', c) p = cam_parser.CAMParser('B2/S', c)
c.randomize() c.randomize()
c.start_plot(100, p.ruleset) c.start_plot(100, p.ruleset)

View File

@ -7,6 +7,8 @@ all methods needed (i.e. supported) to interact/configure with the cellular auto
@date: June 01, 2015 @date: June 01, 2015
""" """
import plane
import time import time
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import matplotlib.animation as ani import matplotlib.animation as ani
@ -37,10 +39,12 @@ class CAM:
plane_count = max(cps, 1) plane_count = max(cps, 1)
grid_dimen = (states,) * dimen grid_dimen = (states,) * dimen
self.planes = [Plane(grid_dimen) for i in range(cps)] self.planes = [plane.Plane(grid_dimen) for i in range(cps)]
self.master = self.planes[0]
self.ticks = [(0, 1)] self.ticks = [(0, 1)]
self.total = 0 self.total = 0
def tick(self, rules, *args): def tick(self, rules, *args):
""" """
Modify all states in a given CAM "simultaneously". Modify all states in a given CAM "simultaneously".
@ -53,7 +57,8 @@ class CAM:
self.total += 1 self.total += 1
for i, j in self.ticks: for i, j in self.ticks:
if self.total % j == 0: if self.total % j == 0:
rules.applyTo(self.planes[i], *args) rules.apply_to(self.planes[i], *args)
def start_plot(self, clock, rules, *args): def start_plot(self, clock, rules, *args):
""" """
@ -68,17 +73,18 @@ class CAM:
ax.get_xaxis().set_visible(False) ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False) ax.get_yaxis().set_visible(False)
mshown = plt.matshow(self.planes[0].bits(), fig.number, cmap='Greys') mshown = plt.matshow(self.master.bits(), fig.number, cmap='Greys')
def animate(frame): def animate(frame):
self.tick(rules, *args) self.tick(rules, *args)
mshown.set_array(self.planes[0].bits()) mshown.set_array(self.master.bits())
return [mshown] return [mshown]
ani.FuncAnimation(fig, animate, interval=clock) ani.FuncAnimation(fig, animate, interval=clock)
plt.axis('off') plt.axis('off')
plt.show() plt.show()
def start_console(self, clock, rules, *args): def start_console(self, clock, rules, *args):
""" """
Initates main console loop. Initates main console loop.
@ -87,7 +93,17 @@ class CAM:
TODO: Incorporate curses, instead of just printing repeatedly. TODO: Incorporate curses, instead of just printing repeatedly.
""" """
while True: while True:
print(self.planes[0].bits()) print(self.master.bits())
time.sleep(clock / 1000) time.sleep(clock / 1000)
self.tick(rules, *args) self.tick(rules, *args)
def randomize(self):
"""
Convenience function to randomize individual planes.
"""
self.master.randomize()
for plane in self.planes[1:]:
plane.grid = self.master.grid

View File

@ -53,7 +53,7 @@ class CAMParser:
""" """
self.sfunc = None self.sfunc = None
self.offsets = c.Configuration.moore(cam.master) self.offsets = c.Configuration.moore(cam.master)
self.ruleset = r.Ruleset(rsRuleset.Method.ALWAYS_PASS) self.ruleset = r.Ruleset(r.Ruleset.Method.ALWAYS_PASS)
if re.match(CAMParser.MCELL_FORMAT, notation): if re.match(CAMParser.MCELL_FORMAT, notation):
x, y = notation.split('/') x, y = notation.split('/')
@ -98,12 +98,11 @@ class CAMParser:
Conway's Game of Life is denoted 23/3 Conway's Game of Life is denoted 23/3
""" """
x, y = list(map(int, x)), list(map(int, y)) x, y = list(map(int, x)), list(map(int, y))
def next_state(f_index, f_grid, indices, states, *args): def next_state(plane, neighborhood, *args):
total = sum(f_grid[indices]) if plane.grid.flat[neighborhood.flat_index]:
if f_grid[f_index]: return int(neighborhood.total in x)
return int(total in x)
else: else:
return int(total in y) return int(neighborhood.total in y)
return next_state return next_state

View File

@ -21,6 +21,8 @@ with the ALWAYS_PASS flag set in the given ruleset the configuration is bundled
@date: June 5th, 2015 @date: June 5th, 2015
""" """
import numpy as np
from itertools import product
from collections import namedtuple from collections import namedtuple
@ -104,8 +106,8 @@ class Configuration:
Note the center cell is excluded, so the total number of offsets are 3^N - 1. Note the center cell is excluded, so the total number of offsets are 3^N - 1.
""" """
offsets = {} offsets = {}
variants = ([-1, 0, 1],) * len(grid.shape) variants = ([-1, 0, 1],) * len(plane.shape)
for current in it.product(*variants): for current in product(*variants):
if any(current): if any(current):
offsets[current] = value offsets[current] = value
@ -124,7 +126,7 @@ class Configuration:
Note the center cell is excluded, so the total number of offsets are 2N. Note the center cell is excluded, so the total number of offsets are 2N.
""" """
offsets = [] offsets = []
variant = [0] * len(grid.shape) variant = [0] * len(plane.shape)
for i in range(len(variant)): for i in range(len(variant)):
for j in [-1, 1]: for j in [-1, 1]:
variant[i] = j variant[i] = j
@ -159,7 +161,7 @@ class Configuration:
""" """
for coor, bit in offsets.items(): for coor, bit in offsets.items():
flat_index, bit_index = plane.flatten(coor) flat_index, bit_index = plane.flatten(coor)
self.offsets.append(Offset(flat_index, bit_index, bit)) self.offsets.append(Configuration.Offset(flat_index, bit_index, bit))
def passes(self, plane, neighborhood, vfunc, *args): def passes(self, plane, neighborhood, vfunc, *args):

View File

@ -44,7 +44,7 @@ class Plane:
else: else:
self.grid = np.empty(shape[:-1], dtype=np.object) self.grid = np.empty(shape[:-1], dtype=np.object)
for i in range(self.grid.size): for i in range(self.grid.size):
self.grid.flat[i] = bitarray(self.N) self.grid.flat[i] = self.N * bitarray('0')
def __getitem__(self, index): def __getitem__(self, index):
@ -95,17 +95,21 @@ class Plane:
""" """
Sets values of grid to random values. Sets values of grid to random values.
Since numbers of the grid may be larger than numpy can handle natively (i.e. too big By default, newly initialized bitarrays are random, but in a weird way I'm not sure I
for C long types), we use the python random module instead. understand. For example, constructing bitarrays in a loop appear to set every bitarray
after the first to 0, and, if I put a print statement afterwards, all bitarrays maintain
the same value. I'm not really too interested in figuring this out, so I use the alternate
method below.
""" """
if len(self.shape) > 0: if len(self.shape) > 0:
import random as r import random as r
max_u = 2**self.N - 1 max_u = 2**self.N - 1
gen = lambda: bin(r.randrange(0, max_u))[2:]
if len(self.shape) == 1: if len(self.shape) == 1:
self.grid = r.randrange(0, max_u) self.grid = bitarray(gen().zfill(self.N))
else: else:
tmp = np.array([r.randrange(0, max_u) for i in range(len(self.grid))]) for i in range(self.grid.size):
self.grid = tmp.reshape(self.grid.shape) self.grid.flat[i] = bitarray(gen().zfill(self.N))
def flatten(self, coordinate): def flatten(self, coordinate):
@ -118,6 +122,21 @@ class Plane:
flat_index, gridprod = 0, 1 flat_index, gridprod = 0, 1
for i in reversed(range(len(coordinate[:-1]))): for i in reversed(range(len(coordinate[:-1]))):
flat_index += coordinate[i] * gridprod flat_index += coordinate[i] * gridprod
gridprod *= shape[i] gridprod *= self.shape[i]
return flat_index, coordinate[-1] return flat_index, coordinate[-1]
def bits(self):
"""
Expands out bitarray into individual bits.
This is useful for display in matplotlib for example, but does take a dimension more space.
"""
if len(self.shape) == 1:
return np.array(self.grid)
else:
tmp = np.array([list(self.grid.flat[i]) for i in range(self.grid.size)])
return np.reshape(tmp, self.shape)

View File

@ -7,8 +7,9 @@ said neighborhood that yield an "on" or "off" state on the cell a ruleset is bei
@date: May 31st, 2015 @date: May 31st, 2015
""" """
import enum import enum
import numpy as np import numpy as np
import configuration as c
from bitarray import bitarray
class Ruleset: class Ruleset:
@ -71,8 +72,8 @@ class Ruleset:
# either all bits pass or configurations are exhausted # either all bits pass or configurations are exhausted
for flat_index, value in enumerate(plane.grid.flat): for flat_index, value in enumerate(plane.grid.flat):
next_row = bitarray(self.N) next_row = bitarray(plane.N)
to_update = range(0, self.N) to_update = range(0, plane.N)
for config in self.configurations: for config in self.configurations:
next_update = [] next_update = []
@ -86,17 +87,18 @@ class Ruleset:
# no overflowing of a single column can occur. We can then find the total of the ith neighborhood by checking the # no overflowing of a single column can occur. We can then find the total of the ith neighborhood by checking the
# sum of the ith index of the summation of every 9 chunks of numbers (this is done a row at a time). # sum of the ith index of the summation of every 9 chunks of numbers (this is done a row at a time).
neighboring = [] neighboring = []
for flat_offset, bit_offset in config.offsets: for flat_offset, bit_offset, _ in config.offsets:
neighbor = plane.grid.flat[flat_index + flat_offset] neighbor = plane.grid.flat[(flat_index + flat_offset) % plane.N]
cycled = neighbor[bit_offset:] + neighbor[:bit_offset] cycled = neighbor[bit_offset:] + neighbor[:bit_offset]
neighboring.append(int(cycled.to01())) neighboring.append(int(cycled.to01()))
# Chunk into groups of 9 and sum all values # Chunk into groups of 9 and sum all values
# These summations represent the total number of active states in a given neighborhood # These summations represent the total number of active states in a given neighborhood
totals = [0] * self.N totals = [0] * plane.N
chunks = map(sum, [offset_totals[i:i+9] for i in range(0, len(neighboring), 9)]) chunks = list(map(sum, [neighboring[i:i+9] for i in range(0, len(neighboring), 9)]))
for chunk in chunks: for chunk in chunks:
totals = list(map(sum, zip(totals, chunk))) i_chunk = list(map(int, str(chunk).zfill(plane.N)))
totals = list(map(sum, zip(totals, i_chunk)))
# Determine which function should be used to test success # Determine which function should be used to test success
if self.method == Ruleset.Method.MATCH: if self.method == Ruleset.Method.MATCH:
@ -110,8 +112,8 @@ class Ruleset:
# Apply change to all successful configurations # Apply change to all successful configurations
for bit_index in to_update: for bit_index in to_update:
neighborhood = Neighborhood(flat_index, bit_index, totals[bit_index]) neighborhood = c.Neighborhood(flat_index, bit_index, totals[bit_index])
success, state = config.passes(neighborhood, vfunc, *args) success, state = config.passes(plane, neighborhood, vfunc, *args)
if success: if success:
next_row[bit_index] = state next_row[bit_index] = state
else: else: