From f0790de0e3ddd8b478d1c94e39397a435fb655e3 Mon Sep 17 00:00:00 2001 From: jrpotter Date: Sun, 31 May 2015 18:13:14 -0400 Subject: [PATCH] Development of ruleset --- src/{bitplane.py => cell_plane.py} | 32 +++++++------- src/neighborhood.py | 16 ++++--- src/ruleset.py | 70 ++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 23 deletions(-) rename src/{bitplane.py => cell_plane.py} (52%) create mode 100644 src/ruleset.py diff --git a/src/bitplane.py b/src/cell_plane.py similarity index 52% rename from src/bitplane.py rename to src/cell_plane.py index c08f5c1..5c80157 100644 --- a/src/bitplane.py +++ b/src/cell_plane.py @@ -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)) diff --git a/src/neighborhood.py b/src/neighborhood.py index 873374b..f94b27a 100644 --- a/src/neighborhood.py +++ b/src/neighborhood.py @@ -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): diff --git a/src/ruleset.py b/src/ruleset.py new file mode 100644 index 0000000..e0f5f75 --- /dev/null +++ b/src/ruleset.py @@ -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)