diff --git a/VERSION b/VERSION index 826e142463..9b7bc7cfe6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.1.1 +v2.1.15 \ No newline at end of file diff --git a/py/htm/encoders/BaseEncoder.py b/py/htm/encoders/BaseEncoder.py new file mode 100644 index 0000000000..99fbc24dc4 --- /dev/null +++ b/py/htm/encoders/BaseEncoder.py @@ -0,0 +1,62 @@ +# ------------------------------------------------------------------------------ +# HTM Community Edition of NuPIC +# Copyright (C) 2020, David Keeney +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Affero Public License version 3 as published by the Free +# Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Affero Public License for more details. +# +# You should have received a copy of the GNU Affero Public License along with +# this program. If not, see http://www.gnu.org/licenses. +# ------------------------------------------------------------------------------ + + +# Base class for all encoders. +# An encoder converts a value to a sparse distributed representation. +# +# Subclasses must implement method encode and Serializable interface. +# Subclasses can optionally implement method reset. +# +# There are several critical properties which all encoders must have: +# +# 1) Semantic similarity: Similar inputs should have high overlap. Overlap +# decreases smoothly as inputs become less similar. Dissimilar inputs have +# very low overlap so that the output representations are not easily confused. +# +# 2) Stability: The representation for an input does not change during the +# lifetime of the encoder. +# +# 3) Sparsity: The output SDR should have a similar sparsity for all inputs and +# have enough active bits to handle noise and subsampling. +# +# Reference: https://arxiv.org/pdf/1602.05925.pdf + + +# Members dimensions & size describe the shape of the encoded output SDR. +# For example, a 6 by 4 dimension would be (6,4,) +# size is the total number of bits in the result. +# A subclass of the BaseEncoder should be passed the dimension in the constructor. +# All subclasses must contain an encode( ) method and a reset( ) method. + +from abc import ABC, abstractmethod +import numpy as np +from htm.bindings.sdr import SDR + +class BaseEncoder(ABC): + + @abstractmethod + def __init__(dimensions): + self.dimensions = dimensions + self.size = SDR(dimensions).size + + @abstractmethod + def reset(self): + raise NotImplementedError() + + @abstractmethod + def encode(self, input, output): + raise NotImplementedError() \ No newline at end of file diff --git a/py/htm/encoders/grid_cell_encoder.py b/py/htm/encoders/grid_cell_encoder.py index f849712d2f..7c72a91108 100644 --- a/py/htm/encoders/grid_cell_encoder.py +++ b/py/htm/encoders/grid_cell_encoder.py @@ -20,8 +20,9 @@ from htm.bindings.sdr import SDR from htm.bindings.math import Random +from htm.bindings.encoder import BaseEncoder -class GridCellEncoder: +class GridCellEncoder(BaseEncoder): """ This Encoder converts a 2-D coordinate into plausible grid cell activity. The output SDR is divided into modules. Each module is a distinct groups of @@ -50,8 +51,8 @@ def __init__(self, encoder uses. This encoder produces deterministic output. The seed zero is special, seed zero is replaced with a truly random seed. """ - self.size = size - self.dimensions = (size,) + super().__init__((size,)) + self.sparsity = sparsity self.periods = tuple(sorted(float(p) for p in periods)) assert(len(self.periods) > 0) @@ -60,14 +61,14 @@ def __init__(self, assert(self.sparsity <= 1) # Assign each module a range of cells in the output SDR. - partitions = np.linspace(0, self.size, num=len(self.periods) + 1) + partitions = np.linspace(0, super().size, num=len(self.periods) + 1) partitions = list(zip(partitions, partitions[1:])) self.partitions_ = [(int(round(start)), int(round(stop))) for start, stop in partitions] # Assign each module a random offset and orientation. rng = np.random.RandomState(seed = Random(seed).getUInt32()) - self.offsets_ = rng.uniform(0, max(self.periods)*9, size=(self.size, 2)) + self.offsets_ = rng.uniform(0, max(self.periods)*9, size=(super().size, 2)) self.angles_ = [] self.rot_mats_ = [] for period in self.periods: @@ -95,10 +96,10 @@ def encode(self, location, grid_cells=None): location = list(location) assert(len(location) == 2) if grid_cells is None: - grid_cells = SDR((self.size,)) + grid_cells = SDR(super().dimensions) else: assert(isinstance(grid_cells, SDR)) - assert(grid_cells.dimensions == [self.size]) + assert(grid_cells.dimensions == [super().size]) if any(math.isnan(x) for x in location): grid_cells.zero() return grid_cells