Development of ruleset
parent
e3843a7c58
commit
f0790de0e3
|
@ -8,29 +8,29 @@ import numpy as np
|
|||
import matplotlib.pyplot as plt
|
||||
|
||||
|
||||
class Bit:
|
||||
class Cell:
|
||||
"""
|
||||
Represents a "bit" in a bitplane.
|
||||
Represents a "cell" in a CellPlane.
|
||||
|
||||
Note we keep track of the index for vectorization purposes. By maintaining each index
|
||||
and batch updating via the given index, we can much more efficiently update the entire
|
||||
bitplane.
|
||||
cell plane.
|
||||
"""
|
||||
def __init__(self, value, *index):
|
||||
self.value = value
|
||||
self.index = index
|
||||
|
||||
|
||||
class BitPlane:
|
||||
class CellPlane:
|
||||
"""
|
||||
A BitPlane represents a layer of the grids that can be placed on top of one another in a 2D CAM.
|
||||
A CellPlane represents a layer of the grids that can be placed on top of one another in a 2D CAM.
|
||||
|
||||
The use of multiple bit plane allow for more intricate states of life and death, though there
|
||||
exists only a single master bit plane that controls the others. That is, the master bit plane has
|
||||
a CAM ruleset applied to it, and the other bit planes merely copy the master, though this can
|
||||
The use of multiple cell plane allow for more intricate states of life and death, though there
|
||||
exists only a single master cell plane that controls the others. That is, the master cell plane has
|
||||
a CAM ruleset applied to it, and the other cell planes merely copy the master, though this can
|
||||
be delayed and have different color mappings.
|
||||
|
||||
For example, by setting a delay of two ticks on the second bit plane of a 2-level CAM configuration,
|
||||
For example, by setting a delay of two ticks on the second cell plane of a 2-level CAM configuration,
|
||||
one can allow for ECHOing, providing a more intuitive sense of "velocity" based on the master.
|
||||
|
||||
That is not to say one could not have multiple CAM's operating simultaneously though. We can consider
|
||||
|
@ -44,18 +44,18 @@ class BitPlane:
|
|||
"""
|
||||
The following joins indices in N-dimensions together.
|
||||
|
||||
This information is stored in a bit (with initial value False) in order for batch processing
|
||||
to be performed when actually updating values and computing whether a bit is on or off. For
|
||||
example, if exploring a 4D array, we want to be able to know which bits we need to check the
|
||||
status of, but this is relative to the current bit, whose position we do not know unless that
|
||||
information is stored with the current bit.
|
||||
This information is stored in a cell (with initial value False) in order for batch processing
|
||||
to be performed when actually updating values and computing whether a cell is on or off. For
|
||||
example, if exploring a 4D array, we want to be able to know which cells we need to check the
|
||||
status of, but this is relative to the current cell, whose position we do not know unless that
|
||||
information is stored with the current cell.
|
||||
"""
|
||||
return Bit(False, *indices)
|
||||
return Cell(False, *indices)
|
||||
|
||||
|
||||
def __init__(self, dimen):
|
||||
"""
|
||||
|
||||
"""
|
||||
self.grid = BitPlane._populate(*np.indices(dimen))
|
||||
self.grid = CellPlane._populate(*np.indices(dimen))
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
"""
|
||||
|
||||
|
||||
@author: jrpotter
|
||||
@date: May 31st, 2015
|
||||
"""
|
||||
import itertools
|
||||
|
||||
|
@ -70,25 +72,25 @@ class Neighborhood:
|
|||
self.offsets = {}
|
||||
|
||||
|
||||
def neighbors(self, bit, grid, wrap_around=True):
|
||||
def neighbors(self, cell, grid, wrap_around=True):
|
||||
"""
|
||||
Returns all bits in the given neighborhood.
|
||||
Returns all cells in the given neighborhood.
|
||||
|
||||
The returned cells are grouped with the value the cell is checked to be (a 2-tuple (Bit, value) pair).
|
||||
The returned cells are grouped with the value the cell is checked to be (a 2-tuple (Cell, value) pair).
|
||||
These are sorted based on the NeighborhoodKey comparison class defined above.
|
||||
"""
|
||||
bits = []
|
||||
cells = []
|
||||
for k in sorted(self.offsets.keys()):
|
||||
position = [sum(x) for x in zip(bit.index, k)]
|
||||
position = [sum(x) for x in zip(cell.index, k)]
|
||||
for i in range(len(position)):
|
||||
if wrap_around:
|
||||
position[i] = position[i] % grid.shape[i]
|
||||
elif i < 0 or i >= grid.shape[i]:
|
||||
break
|
||||
else:
|
||||
bits.append(grid[tuple(position)])
|
||||
cells.append((grid[tuple(position)], self.offsets[k]))
|
||||
|
||||
return bits
|
||||
return cells
|
||||
|
||||
|
||||
def extend(self, offsets, strict=False):
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
"""
|
||||
|
||||
|
||||
|
||||
@author: jrpotter
|
||||
@date: May 31st, 2015
|
||||
"""
|
||||
|
||||
class Ruleset:
|
||||
"""
|
||||
The following determines the next state of a given cell in a CAM.
|
||||
|
||||
Given a neighborhood and a tolerance level, the ruleset determines whether a given cell should be on or off after a tick.
|
||||
For example, if the tolerance level is set to 100% (i.e. neighborhoods must exactly match desired neighborhood to be on),
|
||||
then the ruleset iterates through all neighbors and verifies a match.
|
||||
|
||||
For the sake of clarity, we consider a neighborhood to actually contain the "rules" for matching, and a ruleset to be the
|
||||
application of the rules as defined in the neighborhood. We state this since the actual expected values are declared in
|
||||
a neighborhood instance's offsets member.
|
||||
"""
|
||||
|
||||
def __init__(self, grid, wrap_around=True):
|
||||
"""
|
||||
|
||||
"""
|
||||
self.grid = grid
|
||||
self.wrap_around = wrap_around
|
||||
|
||||
|
||||
def matches(self, cell, neighborhood):
|
||||
"""
|
||||
Determines that neighborhood matches expectation exactly.
|
||||
|
||||
Note this is just like the tolerate method with a tolerance of 1, but
|
||||
recoding allows for short circuiting.
|
||||
"""
|
||||
residents = neighborhood.neighbors(cell, self.grid, self.wrap_around)
|
||||
for resident in residents:
|
||||
if resident[0].value != resident[1]:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def tolerate(self, cell, neighborhood, tolerance):
|
||||
"""
|
||||
Determines that neighborhood matches expectation within tolerance.
|
||||
|
||||
We see that the percentage of actual matches are greater than or equal to the given tolerance level. If so, we
|
||||
consider this cell to be alive. Note tolerance must be a value 0 <= t <= 1.
|
||||
"""
|
||||
matches = 0
|
||||
residents = neighborhood.neighbors(cell, self.grid, self.wrap_around)
|
||||
for resident in residents:
|
||||
if resident[0].value == resident[1]:
|
||||
matches += 1
|
||||
|
||||
return (matches / len(residents)) >= tolerance
|
||||
|
||||
|
||||
def satisfies(self, cell, neighborhood, valid_func):
|
||||
"""
|
||||
Allows custom function to relay next state of given cell.
|
||||
|
||||
The passed function is supplied the list of 2-tuple elements, of which the first is a Cell and the second is
|
||||
the expected state as declared in the Neighborhood, as well as the grid and cell in question.
|
||||
"""
|
||||
residents = neighborhood.neighbors(cell, self.grid, self.wrap_around)
|
||||
|
||||
return valid_func(cell, self.grid, residents)
|
Loading…
Reference in New Issue