3x speed increase; not quite functional
parent
e931af0020
commit
e64dfa9e79
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
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
|
@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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
33
src/plane.py
33
src/plane.py
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in New Issue