3x speed increase; not quite functional
parent
e931af0020
commit
e64dfa9e79
|
@ -10,11 +10,10 @@ if __name__ == '__main__':
|
|||
sys.path.append(os.path.abspath('src'))
|
||||
|
||||
import cam
|
||||
import util as u
|
||||
import ruleset as rs
|
||||
import cam_parser
|
||||
|
||||
c = cam.CAM(1, 100, 2)
|
||||
p = u.CAMParser('B368/S23', c)
|
||||
p = cam_parser.CAMParser('B368/S23', c)
|
||||
|
||||
c.randomize()
|
||||
c.start_plot(100, p.ruleset)
|
||||
|
|
|
@ -10,11 +10,10 @@ if __name__ == '__main__':
|
|||
sys.path.append(os.path.abspath('src'))
|
||||
|
||||
import cam
|
||||
import util as u
|
||||
import ruleset as rs
|
||||
import cam_parser
|
||||
|
||||
c = cam.CAM(1, 100, 2)
|
||||
p = u.CAMParser('B3/S23', c)
|
||||
p = cam_parser.CAMParser('B3/S23', c)
|
||||
|
||||
c.randomize()
|
||||
c.start_plot(400, p.ruleset)
|
||||
c.start_plot(100, p.ruleset)
|
||||
|
|
|
@ -10,11 +10,10 @@ if __name__ == '__main__':
|
|||
sys.path.append(os.path.abspath('src'))
|
||||
|
||||
import cam
|
||||
import util as u
|
||||
import ruleset as rs
|
||||
import cam_parser
|
||||
|
||||
c = cam.CAM(1, 100, 2)
|
||||
p = u.CAMParser('B3/S012345678', c)
|
||||
p = cam_parser.CAMParser('B3/S012345678', c)
|
||||
|
||||
c.randomize()
|
||||
c.start_plot(100, p.ruleset)
|
||||
|
|
|
@ -10,11 +10,10 @@ if __name__ == '__main__':
|
|||
sys.path.append(os.path.abspath('src'))
|
||||
|
||||
import cam
|
||||
import util as u
|
||||
import ruleset as rs
|
||||
import cam_parser
|
||||
|
||||
c = cam.CAM(1, 100, 2)
|
||||
p = u.CAMParser('B368/S245', c)
|
||||
p = cam_parser.CAMParser('B368/S245', c)
|
||||
|
||||
c.randomize()
|
||||
c.start_plot(100, p.ruleset)
|
||||
|
|
|
@ -10,11 +10,10 @@ if __name__ == '__main__':
|
|||
sys.path.append(os.path.abspath('src'))
|
||||
|
||||
import cam
|
||||
import util as u
|
||||
import ruleset as rs
|
||||
import cam_parser
|
||||
|
||||
c = cam.CAM(1, 100, 2)
|
||||
p = u.CAMParser('B1357/S1357', c)
|
||||
p = cam_parser.CAMParser('B1357/S1357', c)
|
||||
|
||||
c.randomize()
|
||||
c.start_plot(100, p.ruleset)
|
||||
|
|
|
@ -10,11 +10,10 @@ if __name__ == '__main__':
|
|||
sys.path.append(os.path.abspath('src'))
|
||||
|
||||
import cam
|
||||
import cam_util as u
|
||||
import ruleset as rs
|
||||
import cam_parser
|
||||
|
||||
c = cam.CAM(1, 100, 2)
|
||||
p = u.CAMParser('B2/S', c)
|
||||
p = cam_parser.CAMParser('B2/S', c)
|
||||
|
||||
c.randomize()
|
||||
c.start_plot(100, p.ruleset)
|
||||
|
|
26
src/cam.py
26
src/cam.py
|
@ -7,6 +7,8 @@ all methods needed (i.e. supported) to interact/configure with the cellular auto
|
|||
|
||||
@date: June 01, 2015
|
||||
"""
|
||||
import plane
|
||||
|
||||
import time
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.animation as ani
|
||||
|
@ -37,10 +39,12 @@ class CAM:
|
|||
plane_count = max(cps, 1)
|
||||
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.total = 0
|
||||
|
||||
|
||||
def tick(self, rules, *args):
|
||||
"""
|
||||
Modify all states in a given CAM "simultaneously".
|
||||
|
@ -53,7 +57,8 @@ class CAM:
|
|||
self.total += 1
|
||||
for i, j in self.ticks:
|
||||
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):
|
||||
"""
|
||||
|
@ -68,17 +73,18 @@ class CAM:
|
|||
ax.get_xaxis().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):
|
||||
self.tick(rules, *args)
|
||||
mshown.set_array(self.planes[0].bits())
|
||||
mshown.set_array(self.master.bits())
|
||||
return [mshown]
|
||||
|
||||
ani.FuncAnimation(fig, animate, interval=clock)
|
||||
plt.axis('off')
|
||||
plt.show()
|
||||
|
||||
|
||||
def start_console(self, clock, rules, *args):
|
||||
"""
|
||||
Initates main console loop.
|
||||
|
@ -87,7 +93,17 @@ class CAM:
|
|||
TODO: Incorporate curses, instead of just printing repeatedly.
|
||||
"""
|
||||
while True:
|
||||
print(self.planes[0].bits())
|
||||
print(self.master.bits())
|
||||
time.sleep(clock / 1000)
|
||||
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
|
||||
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ class CAMParser:
|
|||
"""
|
||||
self.sfunc = None
|
||||
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):
|
||||
x, y = notation.split('/')
|
||||
|
@ -98,12 +98,11 @@ class CAMParser:
|
|||
Conway's Game of Life is denoted 23/3
|
||||
"""
|
||||
x, y = list(map(int, x)), list(map(int, y))
|
||||
def next_state(f_index, f_grid, indices, states, *args):
|
||||
total = sum(f_grid[indices])
|
||||
if f_grid[f_index]:
|
||||
return int(total in x)
|
||||
def next_state(plane, neighborhood, *args):
|
||||
if plane.grid.flat[neighborhood.flat_index]:
|
||||
return int(neighborhood.total in x)
|
||||
else:
|
||||
return int(total in y)
|
||||
return int(neighborhood.total in y)
|
||||
|
||||
return next_state
|
||||
|
|
@ -21,6 +21,8 @@ with the ALWAYS_PASS flag set in the given ruleset the configuration is bundled
|
|||
|
||||
@date: June 5th, 2015
|
||||
"""
|
||||
import numpy as np
|
||||
from itertools import product
|
||||
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.
|
||||
"""
|
||||
offsets = {}
|
||||
variants = ([-1, 0, 1],) * len(grid.shape)
|
||||
for current in it.product(*variants):
|
||||
variants = ([-1, 0, 1],) * len(plane.shape)
|
||||
for current in product(*variants):
|
||||
if any(current):
|
||||
offsets[current] = value
|
||||
|
||||
|
@ -124,7 +126,7 @@ class Configuration:
|
|||
Note the center cell is excluded, so the total number of offsets are 2N.
|
||||
"""
|
||||
offsets = []
|
||||
variant = [0] * len(grid.shape)
|
||||
variant = [0] * len(plane.shape)
|
||||
for i in range(len(variant)):
|
||||
for j in [-1, 1]:
|
||||
variant[i] = j
|
||||
|
@ -159,7 +161,7 @@ class Configuration:
|
|||
"""
|
||||
for coor, bit in offsets.items():
|
||||
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):
|
||||
|
|
33
src/plane.py
33
src/plane.py
|
@ -44,7 +44,7 @@ class Plane:
|
|||
else:
|
||||
self.grid = np.empty(shape[:-1], dtype=np.object)
|
||||
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):
|
||||
|
@ -95,17 +95,21 @@ class Plane:
|
|||
"""
|
||||
Sets values of grid to random values.
|
||||
|
||||
Since numbers of the grid may be larger than numpy can handle natively (i.e. too big
|
||||
for C long types), we use the python random module instead.
|
||||
By default, newly initialized bitarrays are random, but in a weird way I'm not sure I
|
||||
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:
|
||||
import random as r
|
||||
max_u = 2**self.N - 1
|
||||
gen = lambda: bin(r.randrange(0, max_u))[2:]
|
||||
if len(self.shape) == 1:
|
||||
self.grid = r.randrange(0, max_u)
|
||||
self.grid = bitarray(gen().zfill(self.N))
|
||||
else:
|
||||
tmp = np.array([r.randrange(0, max_u) for i in range(len(self.grid))])
|
||||
self.grid = tmp.reshape(self.grid.shape)
|
||||
for i in range(self.grid.size):
|
||||
self.grid.flat[i] = bitarray(gen().zfill(self.N))
|
||||
|
||||
|
||||
def flatten(self, coordinate):
|
||||
|
@ -118,6 +122,21 @@ class Plane:
|
|||
flat_index, gridprod = 0, 1
|
||||
for i in reversed(range(len(coordinate[:-1]))):
|
||||
flat_index += coordinate[i] * gridprod
|
||||
gridprod *= shape[i]
|
||||
gridprod *= self.shape[i]
|
||||
|
||||
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)
|
||||
|
||||
|
||||
|
|
|
@ -7,8 +7,9 @@ said neighborhood that yield an "on" or "off" state on the cell a ruleset is bei
|
|||
@date: May 31st, 2015
|
||||
"""
|
||||
import enum
|
||||
|
||||
import numpy as np
|
||||
import configuration as c
|
||||
from bitarray import bitarray
|
||||
|
||||
|
||||
class Ruleset:
|
||||
|
@ -71,8 +72,8 @@ class Ruleset:
|
|||
# either all bits pass or configurations are exhausted
|
||||
for flat_index, value in enumerate(plane.grid.flat):
|
||||
|
||||
next_row = bitarray(self.N)
|
||||
to_update = range(0, self.N)
|
||||
next_row = bitarray(plane.N)
|
||||
to_update = range(0, plane.N)
|
||||
for config in self.configurations:
|
||||
|
||||
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
|
||||
# sum of the ith index of the summation of every 9 chunks of numbers (this is done a row at a time).
|
||||
neighboring = []
|
||||
for flat_offset, bit_offset in config.offsets:
|
||||
neighbor = plane.grid.flat[flat_index + flat_offset]
|
||||
for flat_offset, bit_offset, _ in config.offsets:
|
||||
neighbor = plane.grid.flat[(flat_index + flat_offset) % plane.N]
|
||||
cycled = neighbor[bit_offset:] + neighbor[:bit_offset]
|
||||
neighboring.append(int(cycled.to01()))
|
||||
|
||||
# Chunk into groups of 9 and sum all values
|
||||
# These summations represent the total number of active states in a given neighborhood
|
||||
totals = [0] * self.N
|
||||
chunks = map(sum, [offset_totals[i:i+9] for i in range(0, len(neighboring), 9)])
|
||||
totals = [0] * plane.N
|
||||
chunks = list(map(sum, [neighboring[i:i+9] for i in range(0, len(neighboring), 9)]))
|
||||
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
|
||||
if self.method == Ruleset.Method.MATCH:
|
||||
|
@ -110,8 +112,8 @@ class Ruleset:
|
|||
|
||||
# Apply change to all successful configurations
|
||||
for bit_index in to_update:
|
||||
neighborhood = Neighborhood(flat_index, bit_index, totals[bit_index])
|
||||
success, state = config.passes(neighborhood, vfunc, *args)
|
||||
neighborhood = c.Neighborhood(flat_index, bit_index, totals[bit_index])
|
||||
success, state = config.passes(plane, neighborhood, vfunc, *args)
|
||||
if success:
|
||||
next_row[bit_index] = state
|
||||
else:
|
||||
|
|
Loading…
Reference in New Issue