def test_computeActivity_thresholded(self): for (name, cells, inputs, activeInputs, initialPermanence, connectedPermanence, expected) in (("Accepted", [1, 2, 3], [42, 43, 44], [42, 44], 0.55, 0.5, [2, 2, 2]), ("Rejected", [1, 2, 3], [42, 43, 44], [42, 44], 0.55, 0.6, [0, 0, 0]), ("No segments", [], [42, 43, 44], [42, 44], 0.55, 0.5, []), ("No active inputs", [1, 2, 3], [42, 43, 44], [], 0.55, 0.5, [0, 0, 0]) ): connections = SparseMatrixConnections(2048, 2048) segments = connections.createSegments(cells) connections.growSynapses(segments, inputs, initialPermanence) overlaps = connections.computeActivity(activeInputs, connectedPermanence) np.testing.assert_equal(overlaps[segments], expected, name)
def test_computeActivity(self): for (name, cells, inputs, activeInputs, initialPermanence, expected) in (("Basic test", [1, 2, 3], [42, 43, 44], [42, 44], 0.45, [2, 2, 2]), ("Small permanence", [1, 2, 3], [42, 43, 44], [42, 44], 0.01, [2, 2, 2]), ("No segments", [], [42, 43, 44], [42, 44], 0.45, []), ("No active inputs", [1, 2, 3], [42, 43, 44], [], 0.45, [0, 0, 0]) ): connections = SparseMatrixConnections(2048, 2048) segments = connections.createSegments(cells) connections.growSynapses(segments, inputs, initialPermanence) overlaps = connections.computeActivity(activeInputs) np.testing.assert_equal(overlaps[segments], expected, name)
def test_growSynapses(self): for (name, cells, growingSegments, presynapticInputs, activeInputs, initialPermanence, connectedPermanence, expected) in (("Basic test", [1, 2, 3], [0, 2], [42, 43, 44], [42, 43], 0.55, 0.5, [2, 0, 2]), ("No segments selected", [1, 2, 3], [], [42, 43, 44], [42, 43], 0.55, 0.5, [0, 0, 0]), ("No inputs selected", [1, 2, 3], [0, 2], [], [42, 43], 0.55, 0.5, [0, 0, 0]) ): connections = SparseMatrixConnections(2048, 2048) segments = connections.createSegments(cells) connections.growSynapses(segments[growingSegments], presynapticInputs, initialPermanence) overlaps = connections.computeActivity(activeInputs, connectedPermanence) np.testing.assert_equal(overlaps[segments], expected, name)
def test_mapSegmentsToSynapseCounts(self): connections = SparseMatrixConnections(2048, 2048) segments = connections.createSegments([1, 2, 3]) connections.growSynapses(segments, [42, 43, 44], 0.5) np.testing.assert_equal(connections.mapSegmentsToSynapseCounts(segments), [3, 3, 3]) segments2 = connections.createSegments([4, 5]) np.testing.assert_equal(connections.mapSegmentsToSynapseCounts(segments2), [0, 0]) np.testing.assert_equal(connections.mapSegmentsToSynapseCounts([]), [])
def test_adjustSynapses(self): for (name, cells, inputs, adjustedSegments, activeInputs, initialPermanence, activeDelta, inactiveDelta, connectedPermanence, expected) in (("Basic test", [1, 2, 3], [42, 43, 44], [0, 2], [42, 44], 0.45, 0.1, -0.1, 0.5, [2, 0, 2]), ("Reward inactive", [1, 2, 3], [42, 43, 44], [0, 2], [42, 44], 0.45, -0.1, 0.1, 0.5, [1, 0, 1]), ("No segments", [1, 2, 3], [42, 43, 44], [], [42, 44], 0.45, 0.1, -0.1, 0.5, [0, 0, 0]), ("No active synapses", [1, 2, 3], [42, 43, 44], [0, 2], [], 0.45, 0.1, -0.1, 0.5, [0, 0, 0]), ("Delta of zero", [1, 2, 3], [42, 43, 44], [0, 2], [42, 44], 0.55, 0.0, 0.0, 0.5, [3, 3, 3]) ): connections = SparseMatrixConnections(2048, 2048) segments = connections.createSegments(cells) connections.growSynapses(segments, inputs, initialPermanence) connections.adjustSynapses(segments[adjustedSegments], activeInputs, activeDelta, inactiveDelta) overlaps = connections.computeActivity(inputs, connectedPermanence) np.testing.assert_equal(overlaps[segments], expected, name)
def test_whenPermanenceFallsBelowZero(self): connections = SparseMatrixConnections(2048, 2048) segments = connections.createSegments([1, 2, 3]) connections.growSynapses(segments, [42, 43], 0.05) connections.adjustSynapses(segments, [42, 43], -0.06, 0.0) np.testing.assert_equal(connections.mapSegmentsToSynapseCounts(segments), [0, 0, 0]) connections.growSynapses(segments, [42, 43], 0.05) connections.adjustSynapses(segments, [], 0.0, -0.06) np.testing.assert_equal(connections.mapSegmentsToSynapseCounts(segments), [0, 0, 0]) connections.growSynapses(segments, [42, 43], 0.05) connections.adjustActiveSynapses(segments, [42, 43], -0.06) np.testing.assert_equal(connections.mapSegmentsToSynapseCounts(segments), [0, 0, 0]) connections.growSynapses(segments, [42, 43], 0.05) connections.adjustInactiveSynapses(segments, [], -0.06) np.testing.assert_equal(connections.mapSegmentsToSynapseCounts(segments), [0, 0, 0])
def test_clipPermanences(self): connections = SparseMatrixConnections(2048, 2048) # Destroy synapses with permanences <= 0.0 segments = connections.createSegments([1, 2, 3]) connections.growSynapses(segments, [42, 43, 44], 0.05) connections.growSynapses(segments, [45, 46], 0.1) connections.adjustInactiveSynapses(segments, [], -0.1) connections.clipPermanences(segments) np.testing.assert_equal(connections.mapSegmentsToSynapseCounts(segments), [0, 0, 0]) # Clip permanences to 1.0 connections.growSynapses(segments, [42, 43, 44], 0.95) connections.adjustInactiveSynapses(segments, [], 0.50) connections.clipPermanences(segments) np.testing.assert_equal(connections.mapSegmentsToSynapseCounts(segments), [3, 3, 3]) connections.adjustInactiveSynapses(segments, [], -0.5) overlaps1 = connections.computeActivity([42, 43, 44], 0.49) overlaps2 = connections.computeActivity([42, 43, 44], 0.51) np.testing.assert_equal(overlaps1, [3, 3, 3]) np.testing.assert_equal(overlaps2, [0, 0, 0])
def test_growSynapsesToSample_multi(self): rng = Random() for (name, cells, growingSegments, initialConnectedInputs, presynapticInputs, activeInputs, initialPermanence, connectedPermanence, sampleSizes, expected) in (("Basic test", [1, 2, 3], [0, 2], [], [42, 43, 44, 45], [42, 43, 44, 45], 0.55, 0.5, [2, 3], [2, 0, 3]), ("One already connected", [1, 2, 3], [0, 2], [42], [42, 43, 44, 45], [42, 43, 44, 45], 0.55, 0.5, [1, 2], [2, 0, 3]), ("Higher sample size than axon count", [1, 2, 3], [0, 2], [], [42, 43, 44, 45], [42, 43, 44, 45], 0.55, 0.5, [5, 10], [4, 0, 4]), ("Higher sample size than available axon count", [1, 2, 3], [0, 2], [42, 43], [42, 43, 44, 45], [42, 43, 44, 45], 0.55, 0.5, [3, 3], [4, 0, 4])): connections = SparseMatrixConnections(2048, 2048) segments = connections.createSegments(cells) connections.growSynapses(segments[growingSegments], initialConnectedInputs, initialPermanence) connections.growSynapsesToSample(segments[growingSegments], presynapticInputs, sampleSizes, initialPermanence, rng) overlaps = connections.computeActivity(activeInputs, connectedPermanence) np.testing.assert_equal(overlaps[segments], expected, name) for (name, cells, growingSegments, initialConnectedInputs, presynapticInputs, activeInputs, initialPermanence, connectedPermanence, sampleSizes) in (("Basic randomness test", [1, 2, 3], [0, 2], [], [42, 43, 44, 45], [42, 43], 0.55, 0.5, [2, 3]), ): # Activate a subset of the inputs. The resulting overlaps should # differ on various trials. firstResult = None differingResults = False for _ in range(20): connections = SparseMatrixConnections(2048, 2048) segments = connections.createSegments(cells) connections.growSynapses(segments[growingSegments], initialConnectedInputs, initialPermanence) connections.growSynapsesToSample(segments[growingSegments], presynapticInputs, sampleSizes, initialPermanence, rng) overlaps = connections.computeActivity(activeInputs, connectedPermanence) if firstResult is None: firstResult = overlaps[segments] else: differingResults = not np.array_equal( overlaps[segments], firstResult) if differingResults: break self.assertTrue(differingResults, name)
def test_growSynapsesToSample_multi(self): rng = Random() for (name, cells, growingSegments, initialConnectedInputs, presynapticInputs, activeInputs, initialPermanence, connectedPermanence, sampleSizes, expected) in (("Basic test", [1, 2, 3], [0, 2], [], [42, 43, 44, 45], [42, 43, 44, 45], 0.55, 0.5, [2, 3], [2, 0, 3]), ("One already connected", [1, 2, 3], [0, 2], [42], [42, 43, 44, 45], [42, 43, 44, 45], 0.55, 0.5, [1, 2], [2, 0, 3]), ("Higher sample size than axon count", [1, 2, 3], [0, 2], [], [42, 43, 44, 45], [42, 43, 44, 45], 0.55, 0.5, [5, 10], [4, 0, 4]), ("Higher sample size than available axon count", [1, 2, 3], [0, 2], [42, 43], [42, 43, 44, 45], [42, 43, 44, 45], 0.55, 0.5, [3, 3], [4, 0, 4]) ): connections = SparseMatrixConnections(2048, 2048) segments = connections.createSegments(cells) connections.growSynapses( segments[growingSegments], initialConnectedInputs, initialPermanence) connections.growSynapsesToSample( segments[growingSegments], presynapticInputs, sampleSizes, initialPermanence, rng) overlaps = connections.computeActivity(activeInputs, connectedPermanence) np.testing.assert_equal(overlaps[segments], expected, name) for (name, cells, growingSegments, initialConnectedInputs, presynapticInputs, activeInputs, initialPermanence, connectedPermanence, sampleSizes) in (("Basic randomness test", [1, 2, 3], [0, 2], [], [42, 43, 44, 45], [42, 43], 0.55, 0.5, [2, 3]), ): # Activate a subset of the inputs. The resulting overlaps should # differ on various trials. firstResult = None differingResults = False for _ in xrange(20): connections = SparseMatrixConnections(2048, 2048) segments = connections.createSegments(cells) connections.growSynapses( segments[growingSegments], initialConnectedInputs, initialPermanence) connections.growSynapsesToSample( segments[growingSegments], presynapticInputs, sampleSizes, initialPermanence, rng) overlaps = connections.computeActivity(activeInputs, connectedPermanence) if firstResult is None: firstResult = overlaps[segments] else: differingResults = not np.array_equal(overlaps[segments], firstResult) if differingResults: break self.assertTrue(differingResults, name)
class Thalamus(object): """ A simple discrete time thalamus. This thalamus has a 2D TRN layer and a 2D relay cell layer. L6 cells project to the dendrites of TRN cells - these connections are learned. TRN cells project to the dendrites of relay cells in a fixed fan-out pattern. A 2D feed forward input source projects to the relay cells in a fixed fan-out pattern. The output of the thalamus is the activity of each relay cell. This activity can be in one of three states: inactive, active (tonic), and active (burst). TRN cells control whether the relay cells will burst. If any dendrite on a TRN cell recognizes the current L6 pattern, it de-inactivates the T-type CA2+ channels on the dendrites of any relay cell it projects to. These relay cells are then in "burst-ready mode". Feed forward activity is in the form of a binary vector corresponding to active/spiking axons (e.g. from ganglion cells). Any relay cells that receive input from an axon will either output tonic or burst activity depending on the state of the T-type CA2+ channels on their dendrites. Relay cells that don't receive input will remain inactive, regardless of their dendritic state. Usage: 1. Train the TRN cells on a bunch of L6 patterns: learnL6Pattern() 2. De-inactivate relay cells by sending in an L6 pattern: deInactivateCells() 3. Compute feed forward activity for an input: computeFeedForwardActivity() 4. reset() 5. Goto 2 """ def __init__(self, trnCellShape=(32, 32), relayCellShape=(32, 32), inputShape=(32, 32), l6CellCount=1024, trnThreshold=10, relayThreshold=1, seed=42): """ :param trnCellShape: a 2D shape for the TRN :param relayCellShape: a 2D shape for the relay cells :param l6CellCount: number of L6 cells :param trnThreshold: dendritic threshold for TRN cells. This is the min number of active L6 cells on a dendrite for the TRN cell to recognize a pattern on that dendrite. :param relayThreshold: dendritic threshold for relay cells. This is the min number of active TRN cells on a dendrite for the relay cell to recognize a pattern on that dendrite. :param seed: Seed for the random number generator. """ self.trnCellShape = trnCellShape self.trnWidth = trnCellShape[0] self.trnHeight = trnCellShape[1] self.relayCellShape = relayCellShape self.relayWidth = relayCellShape[0] self.relayHeight = relayCellShape[1] self.l6CellCount = l6CellCount self.trnThreshold = trnThreshold self.relayThreshold = relayThreshold self.inputShape = inputShape self.seed = seed self.rng = Random(seed) self.trnActivationThreshold = 5 self.trnConnections = SparseMatrixConnections( trnCellShape[0]*trnCellShape[1], l6CellCount) self.relayConnections = SparseMatrixConnections( relayCellShape[0]*relayCellShape[1], trnCellShape[0]*trnCellShape[1]) # Initialize/reset variables that are updated with calls to compute self.reset() self._initializeTRNToRelayCellConnections() def learnL6Pattern(self, l6Pattern, cellsToLearnOn): """ Learn the given l6Pattern on TRN cell dendrites. The TRN cells to learn are given in cellsTeLearnOn. Each of these cells will learn this pattern on a single dendritic segment. :param l6Pattern: An SDR from L6. List of indices corresponding to L6 cells. :param cellsToLearnOn: Each cell index is (x,y) corresponding to the TRN cells that should learn this pattern. For each cell, create a new dendrite that stores this pattern. The SDR is stored on this dendrite """ cellIndices = [self.trnCellIndex(x) for x in cellsToLearnOn] newSegments = self.trnConnections.createSegments(cellIndices) self.trnConnections.growSynapses(newSegments, l6Pattern, 1.0) # print("Learning L6 SDR:", l6Pattern, # "new segments: ", newSegments, # "cells:", self.trnConnections.mapSegmentsToCells(newSegments)) def deInactivateCells(self, l6Input): """ Activate trnCells according to the l6Input. These in turn will impact bursting mode in relay cells that are connected to these trnCells. Given the feedForwardInput, compute which cells will be silent, tonic, or bursting. :param l6Input: :return: nothing """ # Figure out which TRN cells recognize the L6 pattern. self.trnOverlaps = self.trnConnections.computeActivity(l6Input, 0.5) self.activeTRNSegments = np.flatnonzero( self.trnOverlaps >= self.trnActivationThreshold) self.activeTRNCellIndices = self.trnConnections.mapSegmentsToCells( self.activeTRNSegments) # print("trnOverlaps:", self.trnOverlaps, # "active segments:", self.activeTRNSegments) for s, idx in zip(self.activeTRNSegments, self.activeTRNCellIndices): print(self.trnOverlaps[s], idx, self.trnIndextoCoord(idx)) # Figure out which relay cells have dendrites in de-inactivated state self.relayOverlaps = self.relayConnections.computeActivity( self.activeTRNCellIndices, 0.5 ) self.activeRelaySegments = np.flatnonzero( self.relayOverlaps >= self.relayThreshold) self.burstReadyCellIndices = self.relayConnections.mapSegmentsToCells( self.activeRelaySegments) self.burstReadyCells.reshape(-1)[self.burstReadyCellIndices] = 1 def computeFeedForwardActivity(self, feedForwardInput): """ Activate trnCells according to the l6Input. These in turn will impact bursting mode in relay cells that are connected to these trnCells. Given the feedForwardInput, compute which cells will be silent, tonic, or bursting. :param feedForwardInput: a numpy matrix of shape relayCellShape containing 0's and 1's :return: feedForwardInput is modified to contain 0, 1, or 2. A "2" indicates bursting cells. """ feedForwardInput += self.burstReadyCells * feedForwardInput def reset(self): """ Set everything back to zero """ self.trnOverlaps = [] self.activeTRNSegments = [] self.activeTRNCellIndices = [] self.relayOverlaps = [] self.activeRelaySegments = [] self.burstReadyCellIndices = [] self.burstReadyCells = np.zeros((self.relayWidth, self.relayHeight)) def trnCellIndex(self, coord): """ Map a 2D coordinate to 1D cell index. :param coord: a 2D coordinate :return: integer index """ return coord[1] * self.trnWidth + coord[0] def trnIndextoCoord(self, i): """ Map 1D cell index to a 2D coordinate :param i: integer 1D cell index :return: (x, y), a 2D coordinate """ x = i % self.trnWidth y = i / self.trnWidth return x, y def relayCellIndex(self, coord): """ Map a 2D coordinate to 1D cell index. :param coord: a 2D coordinate :return: integer index """ return coord[1] * self.relayWidth + coord[0] def relayIndextoCoord(self, i): """ Map 1D cell index to a 2D coordinate :param i: integer 1D cell index :return: (x, y), a 2D coordinate """ x = i % self.relayWidth y = i / self.relayWidth return x, y def _initializeTRNToRelayCellConnections(self): """ Initialize TRN to relay cell connectivity. For each relay cell, create a dendritic segment for each TRN cell it connects to. """ for x in range(self.relayWidth): for y in range(self.relayHeight): # Create one dendrite for each trn cell that projects to this relay cell # This dendrite contains one synapse corresponding to this TRN->relay # connection. relayCellIndex = self.relayCellIndex((x,y)) trnCells = self._preSynapticTRNCells(x, y) for trnCell in trnCells: newSegment = self.relayConnections.createSegments([relayCellIndex]) self.relayConnections.growSynapses(newSegment, [self.trnCellIndex(trnCell)], 1.0) def _preSynapticTRNCells(self, i, j): """ Given a relay cell at the given coordinate, return a list of the (x,y) coordinates of all TRN cells that project to it. :param relayCellCoordinate: :return: """ xmin = max(i - 1, 0) xmax = min(i + 2, self.trnWidth) ymin = max(j - 1, 0) ymax = min(j + 2, self.trnHeight) trnCells = [ (x, y) for x in range(xmin, xmax) for y in range(ymin, ymax) ] return trnCells