コード例 #1
0
def init():
	global numColumns, numInputs, inputDimensions, columnDimensions, connectedSynapses
	global numColumns

	inputDimensions = [32, 32]
	columnDimensions = [64, 64]
	initConnectedPct = 0.5

	# Check the matrix SM_01_32_32

	inputDimensions = numpy.array(inputDimensions, ndmin=1)
	columnDimensions = numpy.array(columnDimensions, ndmin=1)
	numColumns = columnDimensions.prod()
	numInputs = inputDimensions.prod()

	potentialPools = SparseBinaryMatrix(numInputs)
	potentialPools.resize(numColumns, numInputs)
	permances = SparseMatrix(numColumns, numInputs)

	random = NupicRandom()
	tieBreaker = 0.01*numpy.array([random.getReal64() for i in xrange(numColumns)])

	connectedSynapses = SparseBinaryMatrix(numInputs)
	connectedSynapses.resize(numColumns, numInputs)

	connectedCounts = numpy.zeros(numColumns, dtype=realDType)

	potentialPools.replaceSparseRow(0, numpy.array([0, 1], dtype='int'))
コード例 #2
0
class TemporalPooler(SpatialPooler):
  """
  This class constitutes the default implementation of the temporal pooler (TP).
  The main goal of the TP is to form stable and unique representations of an
  input data stream of cell activity from a Temporal Memory. More specifically,
  the TP forms its stable representation based on input cells that were
  correctly temporally predicted by Temporal Memory.

  If this active cell input sequence was not predicted, a competition,
  similar to that of the Spatial Pooler's, is used to select active
  TP cells. Note that while the temporal pooler functions like a
  Spatial Pooler in that it selects a sparse active set of columns,
  these columns are treated as if they only have one cell, and will be referred
  to as TP cells.

  If a TP cell is sufficiently activated by inputs that were predicted by the
  Temporal Memory, it enters into a "pooling state." A pooling cell can, for a
  limited period, maintain its active status even if it does not receive any
  bottom-up input activity. This is implemented using a timer for each cell.
  When the timer "runs out," the cell loses its pooling status. Whenever
  significant active predicted input overlaps with a TP cell's proximal
  synapses, its timer is reset.
  """

  def __init__(self,
               inputDimensions=[32,32],
               columnDimensions=[64,64],
               potentialRadius=16,
               potentialPct=0.9,
               globalInhibition=True,
               localAreaDensity=-1.0,
               numActiveColumnsPerInhArea=10.0,
               stimulusThreshold=2,
               synPermInactiveDec=0.01,
               synPermActiveInc=0.03,
               synPredictedInc=0.5,
               synPermConnected=0.3,
               minPctOverlapDutyCycle=0.001,
               dutyCyclePeriod=1000,
               boostStrength=0.0,
               useBurstingRule = False,
               usePoolingRule = True,
               poolingLife = 1000,
               poolingThreshUnpredicted = 0.0,
               initConnectedPct = 0.2,
               seed=-1,
               spVerbosity=0,
               wrapAround=True
               ):
    """
    Please see spatial_pooler.py in NuPIC for descriptions of common
    constructor parameters.

    Class-specific parameters:
    -------------------------------------

    @param synPredictedInc:
       In this model, a metabotropically active synapse is implemented as an
       active synapse whose input originates from a correctly predicted cell
       in Temporal Memory. synPredictedInc is then the amount of permanence
       added to each metabotropically active synapse in each round

    @param useBurstingRule:
      A Boolean indicating whether bursting columns in the TM will have a
      strong effect on overlap calculation

    @param usePoolingRule:
      A Boolean indicating whether inputs representing correctly predicted
      cells in the Temporal Memory will contribute to column overlap calculation

    @param poolingLife:
      The maximum number of timesteps that a pooling column will pool for
      in the absence of any predicted input.

    @param poolingThreshUnpredicted:
       A threshold, ranging from 0 to 1, on the fraction of bottom-up input that
       is unpredicted. If this threshold is exceeded, the temporal pooler will
       stop pooling across all TP cells.

    @param initConnectedPct:
      A number between 0 or 1 governing the chance, for each permanence, that
      the initial permanence value will be a value that is considered connected.
    """
    self.initialize(inputDimensions,
                    columnDimensions,
                    potentialRadius,
                    potentialPct,
                    globalInhibition,
                    localAreaDensity,
                    numActiveColumnsPerInhArea,
                    stimulusThreshold,
                    synPermInactiveDec,
                    synPermActiveInc,
                    synPredictedInc,
                    synPermConnected,
                    minPctOverlapDutyCycle,
                    dutyCyclePeriod,
                    boostStrength,
                    useBurstingRule,
                    usePoolingRule,
                    poolingLife,
                    poolingThreshUnpredicted,
                    seed,      
                    initConnectedPct,              
                    spVerbosity,
                    wrapAround)


  def initialize(self,
               inputDimensions=[32,32],
               columnDimensions=[64,64],
               potentialRadius=16,
               potentialPct=0.5,
               globalInhibition=False,
               localAreaDensity=-1.0,
               numActiveColumnsPerInhArea=10.0,
               stimulusThreshold=0,
               synPermInactiveDec=0.01,
               synPermActiveInc=0.1,
               synPredictedInc=0.1,
               synPermConnected=0.10,
               minPctOverlapDutyCycle=0.001,
               dutyCyclePeriod=1000,
               boostStrength=10.0,
               useBurstingRule=True,
               usePoolingRule=True,
               poolingLife=1000,
               poolingThreshUnpredicted=0.0,
               seed=-1,
               initConnectedPct=0.1,
               spVerbosity=0,
               wrapAround=True):

    # Verify input is valid
    inputDimensions = numpy.array(inputDimensions)
    columnDimensions = numpy.array(columnDimensions)
    numColumns = columnDimensions.prod()
    numInputs = inputDimensions.prod()

    assert(numColumns > 0)
    assert(numInputs > 0)
    assert (numActiveColumnsPerInhArea > 0 or 
           (localAreaDensity > 0 and localAreaDensity <= 0.5))

    # save arguments
    self._numInputs = int(numInputs)
    self._numColumns = int(numColumns)
    self._columnDimensions = columnDimensions
    self._inputDimensions = inputDimensions
    self._potentialRadius = int(min(potentialRadius, numInputs))
    self._potentialPct = potentialPct
    self._globalInhibition = globalInhibition
    self._numActiveColumnsPerInhArea = int(numActiveColumnsPerInhArea)
    self._localAreaDensity = localAreaDensity
    self._stimulusThreshold = stimulusThreshold
    self._synPermInactiveDec = synPermInactiveDec
    self._synPermActiveInc = synPermActiveInc
    self._synPredictedInc = synPredictedInc
    self._synPermBelowStimulusInc = synPermConnected / 10.0
    self._synPermConnected = synPermConnected
    self._minPctOverlapDutyCycles = minPctOverlapDutyCycle
    self._dutyCyclePeriod = dutyCyclePeriod
    self._boostStrength = boostStrength
    self._spVerbosity = spVerbosity
    self._wrapAround = wrapAround
    self.useBurstingRule = useBurstingRule
    self.usePoolingRule = usePoolingRule
    self._poolingLife = poolingLife
    self._initConnectedPct = initConnectedPct
    # Extra parameter settings
    self._synPermMin = 0.0
    self._synPermMax = 1.0
    self._synPermTrimThreshold = synPermActiveInc / 2.0
    assert(self._synPermTrimThreshold < self._synPermConnected)
    self._updatePeriod = 20
    self._poolingThreshUnpredicted = poolingThreshUnpredicted

    # Internal state
    self._version = 1.0
    self._iterationNum = 0
    self._iterationLearnNum = 0

    # initialize the random number generators
    self._seed(seed)
    
    # A cell will enter pooling state if it receives enough predicted inputs
    # pooling cells have priority during competition
    self._poolingActivation = numpy.zeros((self._numColumns), dtype="int32")
    self._poolingColumns = []
    # Initialize a tiny random tie breaker. This is used to determine winning
    # columns where the overlaps are identical.
    self._tieBreaker = 0.01 * numpy.array([self._random.getReal64() for i in
                                          xrange(self._numColumns)])

    # initialize connection matrix
    self.initializeConnections()

    self._overlapDutyCycles = numpy.zeros(numColumns, dtype=realDType)
    self._activeDutyCycles = numpy.zeros(numColumns, dtype=realDType)
    self._minOverlapDutyCycles = numpy.zeros(numColumns, 
                                             dtype=realDType)
    self._minActiveDutyCycles = numpy.zeros(numColumns,
                                            dtype=realDType)
    self._boostFactors = numpy.ones(numColumns, dtype=realDType)


    # The inhibition radius determines the size of a column's local 
    # neighborhood. A cortical column must overcome the overlap
    # score of columns in its neighborhood in order to become active. This
    # radius is updated every _updatePeriod iterations. It grows and shrinks
    # with the average number of connected synapses per column.
    self._inhibitionRadius = 0
    self._updateInhibitionRadius()
    
    if self._spVerbosity > 0:
      self.printParameters()

  def initializeConnections(self):
    """
    Initialize connection matrix, including:

    _permanences :      permanence of synaptic connections (sparse matrix)
    _potentialPools:    potential pool of connections for each cell
                        (sparse binary matrix)
    _connectedSynapses: connected synapses (binary sparse matrix)
    _connectedCounts:   number of connections per cell (numpy array)
    """

    numColumns = self._numColumns
    numInputs = self._numInputs

    # The SP should be setup so that stimulusThreshold is reasonably high,
    # similar to a TP's activation threshold. The pct of connected cells may
    # need to be low to avoid false positives given the monosynaptic rule 
    initConnectedPct = self._initConnectedPct

    # Store the set of all inputs that are within each column's potential pool.
    # 'potentialPools' is a matrix, whose rows represent cortical columns, and
    # whose columns represent the input bits. if potentialPools[i][j] == 1,
    # then input bit 'j' is in column 'i's potential pool. A column can only be
    # connected to inputs in its potential pool. The indices refer to a
    # flattened version of both the inputs and columns. Namely, irrespective
    # of the topology of the inputs and columns, they are treated as being a
    # one dimensional array. Since a column is typically connected to only a
    # subset of the inputs, many of the entries in the matrix are 0. Therefore
    # the the potentialPool matrix is stored using the SparseBinaryMatrix
    # class, to reduce memory footprint and compuation time of algorithms that
    # require iterating over the data strcuture.
    self._potentialPools = SparseBinaryMatrix(numInputs)
    self._potentialPools.resize(numColumns, numInputs)

    # Initialize the permanences for each column. Similar to the 
    # 'self._potentialPools', the permanences are stored in a matrix whose rows
    # represent the cortical columns, and whose columns represent the input
    # bits. if self._permanences[i][j] = 0.2, then the synapse connecting 
    # cortical column 'i' to input bit 'j'  has a permanence of 0.2. Here we 
    # also use the SparseMatrix class to reduce the memory footprint and 
    # computation time of algorithms that require iterating over the data 
    # structure. This permanence matrix is only allowed to have non-zero 
    # elements where the potential pool is non-zero.
    self._permanences = SparseMatrix(numColumns, numInputs)

    # 'self._connectedSynapses' is a similar matrix to 'self._permanences' 
    # (rows represent cortical columns, columns represent input bits) whose
    # entries represent whether the cortial column is connected to the input 
    # bit, i.e. its permanence value is greater than 'synPermConnected'. While 
    # this information is readily available from the 'self._permanence' matrix, 
    # it is stored separately for efficiency purposes.
    self._connectedSynapses = SparseBinaryMatrix(numInputs)
    self._connectedSynapses.resize(numColumns, numInputs)

    # Stores the number of connected synapses for each column. This is simply
    # a sum of each row of 'self._connectedSynapses'. again, while this 
    # information is readily available from 'self._connectedSynapses', it is
    # stored separately for efficiency purposes.
    self._connectedCounts = numpy.zeros(numColumns, dtype=realDType)


    # Initialize the set of permanence values for each column. Ensure that
    # each column is connected to enough input bits to allow it to be
    # activated.
    for i in xrange(numColumns):
      potential = self._mapPotential(i, wrapAround=self._wrapAround)
      self._potentialPools.replaceSparseRow(i, potential.nonzero()[0])
      perm = self._initPermanence(potential, initConnectedPct)
      self._updatePermanencesForColumn(perm, i, raisePerm=True)


    # TODO: The permanence initialization code below runs faster but is not
    # deterministic (doesn't use our RNG or the seed).  The speed is
    # particularly important for temporal pooling because the number if inputs =
    # number of cells in the prevous level. We should consider cleaning up the
    # code and moving it to the base spatial pooler class itself.

    # for i in xrange(numColumns):
    #   # NEW indices for inputs within _potentialRadius of the current column
    #   indices = numpy.array(range(2*self._potentialRadius+1))
    #   indices += i
    #   indices -= self._potentialRadius
    #   indices %= self._numInputs # periodic boundary conditions
    #   indices = numpy.array(list(set(indices)))
    #
    #   # NEW Select a subset of the receptive field to serve as the
    #   # potential pool
    #   sample = numpy.empty((self._inputDimensions * self._potentialPct)\
    #                         .astype('int32'),dtype=uintType)
    #   self._random.getUInt32Sample(indices.astype(uintType),sample)
    #   potential = numpy.zeros(self._numInputs)
    #   potential[sample] = 1
    #
    #   # update potentialPool
    #   self._potentialPools.replaceSparseRow(i, potential.nonzero()[0])
    #
    #   # NEW generate indicator for connected/unconnected
    #   connected = numpy.random.rand(self._numInputs) < initConnectedPct
    #
    #   # set permanence value for connected synapses to be slightly above _synPermConnected
    #   permConnected =  (self._synPermConnected + numpy.random.rand(self._numInputs) *
    #     self._synPermActiveInc / 4.0)
    #   # set permanence value for unconnected synapses below _synPermConnected
    #   permNotConnected = self._synPermConnected * numpy.random.rand(self._numInputs)
    #
    #   # update permamnce value
    #   perm = permNotConnected
    #   perm[connected] = permConnected[connected]
    #   perm[potential < 1] = 0 # permanence value for cells not in the potential
    #   # pool
    #
    #   self._updatePermanencesForColumn(perm, i, raisePerm=True)


  def reset(self):
    """
    Reset the state of the temporal pooler
    """
    self._poolingActivation = numpy.zeros((self._numColumns), dtype="int32")
    self._poolingColumns = []
    self._overlapDutyCycles = numpy.zeros(self._numColumns, dtype=realDType)
    self._activeDutyCycles = numpy.zeros(self._numColumns, dtype=realDType)
    self._minOverlapDutyCycles = numpy.zeros(self._numColumns, 
                                             dtype=realDType)
    self._minActiveDutyCycles = numpy.zeros(self._numColumns,
                                            dtype=realDType)
    self._boostFactors = numpy.ones(self._numColumns, dtype=realDType)


  def compute(self, inputVector, learn, activeArray, burstingColumns,
              predictedCells):
    """
    This is the primary public method of the class. This function takes an input
    vector and outputs the indices of the active columns.

    New parameters defined here:
    ----------------------------
    @param inputVector:         The active cells from a Temporal Memory
    @param learn:               A Boolean specifying whether learning will be
                                performed
    @param activeArray:         An array representing the active columns
                                produced by this method
    @param burstingColumns:     A numpy array with numColumns elements having
                                binary values with 1 representing a
                                currently bursting column in Temporal Memory.

    @param predictedCells: A numpy array with numInputs elements. A 1
                                    indicates that this cell switching from
                                    predicted state in the previous time step to
                                    active state in the current timestep
    """
    assert (numpy.size(inputVector) == self._numInputs)
    assert (numpy.size(predictedCells) == self._numInputs)

    self._updateBookeepingVars(learn)
    inputVector = numpy.array(inputVector, dtype=realDType)
    predictedCells = numpy.array(predictedCells, dtype=realDType)
    inputVector.reshape(-1)

    if self._spVerbosity > 3:
      print " Input bits: ", inputVector.nonzero()[0]
      print " predictedCells: ", predictedCells.nonzero()[0]

    # Phase 1: Calculate overlap scores
    # The overlap score has 4 components:
    # (1) Overlap between correctly predicted input cells and pooling TP cells
    # (2) Overlap between active input cells and all TP cells
    #     (like standard SP calculation)
    # (3) Overlap between correctly predicted input cells and all TP cells
    # (4) Overlap from bursting columns in TM and all TP cells

    # 1) Calculate pooling overlap
    if self.usePoolingRule:
      overlapsPooling = self._calculatePoolingActivity(predictedCells, learn)

      if self._spVerbosity > 4:
        print "usePoolingRule: Overlaps after step 1:"
        print "   ", overlapsPooling

    else:
      overlapsPooling = 0

    # 2) Calculate overlap between active input cells and connected synapses
    overlapsAllInput = self._calculateOverlap(inputVector)

    # 3) overlap with predicted inputs
    # NEW: Isn't this redundant with 1 and 2)? This looks at connected synapses
    # only.
    # If 1) is called with learning=False connected synapses are used and
    # it is somewhat redundant although there is a boosting factor in 1) which
    # makes 1's effect stronger. If 1) is called with learning=True it's less
    # redundant
    overlapsPredicted = self._calculateOverlap(predictedCells)

    if self._spVerbosity > 4:
      print "Overlaps with all inputs:"
      print " Number of On Bits: ", inputVector.sum()
      print "   ", overlapsAllInput

      print "Overlaps with predicted inputs:"
      print "   ", overlapsPredicted

    # 4) consider bursting columns
    if self.useBurstingRule:
      overlapsBursting = self._calculateBurstingColumns(burstingColumns)

      if self._spVerbosity > 4:
        print "Overlaps with bursting inputs:"
        print "   ", overlapsBursting
    else:
      overlapsBursting = 0

    overlaps = (overlapsPooling + overlapsPredicted +  overlapsAllInput +
               overlapsBursting)

    # Apply boosting when learning is on
    if learn:
      boostedOverlaps = self._boostFactors * overlaps
      if self._spVerbosity > 4:
        print "Overlaps after boosting:"
        print "   ", boostedOverlaps      

    else:
      boostedOverlaps = overlaps
    

    # Apply inhibition to determine the winning columns
    activeColumns = self._inhibitColumns(boostedOverlaps)    
      
    if learn:
      self._adaptSynapses(inputVector, activeColumns, predictedCells)
      self._updateDutyCycles(overlaps, activeColumns)
      self._bumpUpWeakColumns() 
      self._updateBoostFactors()
      if self._isUpdateRound():
        self._updateInhibitionRadius()
        self._updateMinDutyCycles()
    
    activeArray.fill(0)
    if activeColumns.size > 0:
      activeArray[activeColumns] = 1

    # update pooling state of cells
    activeColumnIndices = numpy.where(overlapsPredicted[activeColumns] > 0)[0]
    activeColWithPredictedInput = activeColumns[activeColumnIndices]
    
    numUnPredictedInput = float(len(burstingColumns.nonzero()[0]))
    numPredictedInput = float(len(predictedCells))
    fracUnPredicted = numUnPredictedInput / (numUnPredictedInput +
                                             numPredictedInput)

    self._updatePoolingState(activeColWithPredictedInput, fracUnPredicted)

    if self._spVerbosity > 2:
      activeColumns.sort()
      print "The following columns are finally active:"
      print "   ", activeColumns
      print "The following columns are in pooling state:"
      print "   ", self._poolingActivation.nonzero()[0]
      # print "Inputs to pooling columns"
      # print "   ",overlapsPredicted[self._poolingColumns]

    return activeColumns


  def _updatePoolingState(self, activeColWithPredictedInput, fractionUnpredicted):
    """
    This function updates the pooling state of TP cells. A cell will stop
    pooling if:
    (1) It hasn't received any predicted input in the last self._poolingLife
    steps
    or
    (2) the overall fraction of unpredicted input to the TP is above
        _poolingThreshUnpredicted
    """
    if fractionUnpredicted > self._poolingThreshUnpredicted:
      # Reset pooling activation if the fraction of unpredicted input
      # is above the threshold
      if self._spVerbosity > 3:
        print " reset pooling state for all cells"
      self._poolingActivation = numpy.zeros(self._numColumns, dtype="int32")
    else:
      # decrement activation of all pooling cells
      self._poolingActivation[self._poolingColumns] -= 1
      # reset activation of cells that are receiving predicted input
      self._poolingActivation[activeColWithPredictedInput] = self._poolingLife

    self._poolingColumns = self._poolingActivation.nonzero()[0]


  def _calculatePoolingActivity(self, predictedActiveCells, learn):
    """
    Determines each column's overlap with predicted active cell input.
    If learning, overlap is calculated between predicted active input cells and
    potential synapses. Otherwise, connected synapses are used.
    The overlap of a TP cell that was previously active and has one or more
    active synapse due to predicted active input is set to (_numInputs + 1).
    This guarantees that such a cell wins the inhibition competition.
    
    TODO: check with Jeff, what happens in biology if a cell was not previously
    active but receives metabotropic input?  Does it turn on, or does the met.
    input just extend the activity of already active cells? Does it matter
    whether the synapses are connected or not? Currently we are assuming no
    because most connected synapses become disconnected in the previous time
    step. If column i at time t is bursting and performs learning, the synapses
    from t+1 aren't active at time t, so their permanence will decrease.

    Parameters:
    ----------------------------
    predictedActiveCells: a numpy array with numInputs elements. A 1 indicates
                          that this cell switched from a predicted state in
                          the previous time step to active state in the current
                          timestep
    returns:              an array of overlap values due to predicted
                          active TM cells
    """


    overlaps = numpy.zeros(self._numColumns).astype(realDType)

    # If no pooling columns or no predicted active inputs, return all zeros
    if (sum(self._poolingActivation) == 0 or
       len(predictedActiveCells.nonzero()[0]) == 0):
      return overlaps

    if learn:
      # During learning, overlap is calculated based on potential synapses. 
      self._potentialPools.rightVecSumAtNZ_fast(predictedActiveCells, overlaps)
    else:
      # At inference stage, overlap is calculated based on connected synapses.
      self._connectedSynapses.rightVecSumAtNZ_fast(predictedActiveCells, overlaps)

    poolingColumns = self._poolingColumns

    # Only consider columns that are in pooling state
    mask = numpy.zeros(self._numColumns).astype(realDType)
    mask[poolingColumns] = 1    
    overlaps = overlaps * mask
    # Pooling TP cells that receive predicted input
    # will have their overlap boosted by a large factor so that they are likely
    # to win the inhibition competition
    boostFactorPooling = self._boostStrength * self._numInputs
    overlaps = boostFactorPooling * overlaps

    if self._spVerbosity > 3:
      print "\n============== In _calculatePoolingActivity ======"
      print "Received predicted cell inputs from following indices:"
      print "   ", predictedActiveCells.nonzero()[0]
      print "The following column indices are in pooling state:"
      print "   ", poolingColumns
      print "Overlap score of pooling columns:"
      print "   ", overlaps[poolingColumns]
      print "============== Leaving _calculatePoolingActivity  ======\n"

    return overlaps


  def _calculateBurstingColumns(self, burstingColumns):
    """
    Returns the contribution to overlap due to bursting columns. If any
    column is bursting, its overlap score is set to stimulusThreshold. This
    means it will be guaranteed to win as long as no other column is
    metabotropic or has input > stimulusThreshold. 

    Parameters:
    ----------------------------
    burstingColumns: a numpy array with numColumns elements. A 1 indicates that
                     column is currently bursting.
    """
    overlaps = burstingColumns * self._stimulusThreshold
    return overlaps


  def _adaptSynapses(self, inputVector, activeColumns, predictedActiveCells):
    """
    This is the primary learning method. It updates synapses' permanence based
    on the bottom-up input to the TP and the TP's active cells.
    For each active cell, its synapses' permanences are updated as follows:

    1. if pre-synaptic input is ON due to a correctly predicted cell,
       increase permanence by _synPredictedInc
    2. else if input is ON due to an active cell, increase permanence by
       _synPermActiveInc
    3. else input is OFF, decrease permanence by _synPermInactiveDec

    Parameters:
    ----------------------------
    inputVector:    a numpy array whose ON bits represent the active cells from
                    temporal memory
    activeColumns:  an array containing the indices of the columns that 
                    survived the inhibition step
    predictedActiveCells: a numpy array with numInputs elements. A 1 indicates
                          that this cell switched from predicted state in
                          the previous time step to active state in the current
                          timestep
    """
    inputIndices = numpy.where(inputVector > 0)[0]
    predictedIndices = numpy.where(predictedActiveCells > 0)[0]
    permChanges = numpy.zeros(self._numInputs)

    # Decrement inactive TM cell -> active TP cell connections
    permChanges.fill(-1 * self._synPermInactiveDec)

    # Increment active TM cell -> active TP cell connections
    permChanges[inputIndices] = self._synPermActiveInc

    # Increment correctly predicted TM cell -> active TP cell connections
    permChanges[predictedIndices] = self._synPredictedInc

    if self._spVerbosity > 4:
      print "\n============== _adaptSynapses ======"
      print "Active input indices:",inputIndices
      print "predicted input indices:",predictedIndices
      print "\n============== _adaptSynapses ======\n"

    for i in activeColumns:
      # Get the permanences of the synapses of TP cell i
      perm = self._permanences.getRow(i)

      # Only consider connections in column's potential pool (receptive field)
      maskPotential = numpy.where(self._potentialPools.getRow(i) > 0)[0]
      perm[maskPotential] += permChanges[maskPotential]
      self._updatePermanencesForColumn(perm, i, raisePerm=False)
   


  def printParameters(self):
    """
    Useful for debugging.
    """
    print "------------PY  TemporalPooler Parameters ------------------"
    print "numInputs                  = ", self.getNumInputs()
    print "numColumns                 = ", self.getNumColumns()
    print "columnDimensions           = ", self._columnDimensions
    print "numActiveColumnsPerInhArea = ", self.getNumActiveColumnsPerInhArea()
    print "potentialPct               = ", self.getPotentialPct()
    print "globalInhibition           = ", self.getGlobalInhibition()
    print "localAreaDensity           = ", self.getLocalAreaDensity()
    print "stimulusThreshold          = ", self.getStimulusThreshold()
    print "synPermActiveInc           = ", self.getSynPermActiveInc()
    print "synPermInactiveDec         = ", self.getSynPermInactiveDec()
    print "synPermConnected           = ", self.getSynPermConnected()
    print "minPctOverlapDutyCycle     = ", self.getMinPctOverlapDutyCycles()
    print "dutyCyclePeriod            = ", self.getDutyCyclePeriod()
    print "boostStrength              = ", self.getBoostStrength()
    print "spVerbosity                = ", self.getSpVerbosity()
    print "version                    = ", self._version


  def extractInputForTP(self, tm):  
    """
    Extract inputs for TP from the state of temporal memory
    three information are extracted
    1. correctly predicted cells
    2. all active cells
    3. bursting cells (unpredicted input)
    """

    # bursting cells in layer 4
    burstingColumns = tm.activeState["t"].sum(axis=1)
    burstingColumns[ burstingColumns < tm.cellsPerColumn ] = 0
    burstingColumns[ burstingColumns == tm.cellsPerColumn ] = 1
    # print "Bursting column indices=",burstingColumns.nonzero()[0]  
    
    # correctly predicted cells in layer 4
    correctlyPredictedCells = numpy.zeros(self._inputDimensions).astype(realDType)
    idx = (tm.predictedState["t-1"] + tm.activeState["t"]) == 2
    idx = idx.reshape(self._inputDimensions)
    correctlyPredictedCells[idx] = 1.0
    # print "Predicted->active cell indices=",correctlyPredictedCells.nonzero()[0]

    # all currently active cells in layer 4
    spInputVector = tm.learnState["t"].reshape(self._inputDimensions)
    # spInputVector = tm.activeState["t"].reshape(self._inputDimensions)
    
    return (correctlyPredictedCells, spInputVector, burstingColumns)  
コード例 #3
0
class TemporalPooler(SpatialPooler):
    """
  This class constitutes the default implementation of the temporal pooler (TP).
  The main goal of the TP is to form stable and unique representations of an
  input data stream of cell activity from a Temporal Memory. More specifically,
  the TP forms its stable representation based on input cells that were
  correctly temporally predicted by Temporal Memory.

  If this active cell input sequence was not predicted, a competition,
  similar to that of the Spatial Pooler's, is used to select active
  TP cells. Note that while the temporal pooler functions like a
  Spatial Pooler in that it selects a sparse active set of columns,
  these columns are treated as if they only have one cell, and will be referred
  to as TP cells.

  If a TP cell is sufficiently activated by inputs that were predicted by the
  Temporal Memory, it enters into a "pooling state." A pooling cell can, for a
  limited period, maintain its active status even if it does not receive any
  bottom-up input activity. This is implemented using a timer for each cell.
  When the timer "runs out," the cell loses its pooling status. Whenever
  significant active predicted input overlaps with a TP cell's proximal
  synapses, its timer is reset.
  """
    def __init__(self,
                 inputDimensions=[32, 32],
                 columnDimensions=[64, 64],
                 potentialRadius=16,
                 potentialPct=0.9,
                 globalInhibition=True,
                 localAreaDensity=-1.0,
                 numActiveColumnsPerInhArea=10.0,
                 stimulusThreshold=2,
                 synPermInactiveDec=0.01,
                 synPermActiveInc=0.03,
                 synPredictedInc=0.5,
                 synPermConnected=0.3,
                 minPctOverlapDutyCycle=0.001,
                 minPctActiveDutyCycle=0.001,
                 dutyCyclePeriod=1000,
                 maxBoost=1.0,
                 useBurstingRule=False,
                 usePoolingRule=True,
                 poolingLife=1000,
                 poolingThreshUnpredicted=0.0,
                 initConnectedPct=0.2,
                 seed=-1,
                 spVerbosity=0,
                 wrapAround=True):
        """
    Please see spatial_pooler.py in NuPIC for descriptions of common
    constructor parameters.

    Class-specific parameters:
    -------------------------------------

    @param synPredictedInc:
       In this model, a metabotropically active synapse is implemented as an
       active synapse whose input originates from a correctly predicted cell
       in Temporal Memory. synPredictedInc is then the amount of permanence
       added to each metabotropically active synapse in each round

    @param useBurstingRule:
      A Boolean indicating whether bursting columns in the TM will have a
      strong effect on overlap calculation

    @param usePoolingRule:
      A Boolean indicating whether inputs representing correctly predicted
      cells in the Temporal Memory will contribute to column overlap calculation

    @param poolingLife:
      The maximum number of timesteps that a pooling column will pool for
      in the absence of any predicted input.

    @param poolingThreshUnpredicted:
       A threshold, ranging from 0 to 1, on the fraction of bottom-up input that
       is unpredicted. If this threshold is exceeded, the temporal pooler will
       stop pooling across all TP cells.

    @param initConnectedPct:
      A number between 0 or 1 governing the chance, for each permanence, that
      the initial permanence value will be a value that is considered connected.
    """
        self.initialize(inputDimensions, columnDimensions, potentialRadius,
                        potentialPct, globalInhibition, localAreaDensity,
                        numActiveColumnsPerInhArea, stimulusThreshold,
                        synPermInactiveDec, synPermActiveInc, synPredictedInc,
                        synPermConnected, minPctOverlapDutyCycle,
                        minPctActiveDutyCycle, dutyCyclePeriod, maxBoost,
                        useBurstingRule, usePoolingRule, poolingLife,
                        poolingThreshUnpredicted, seed, initConnectedPct,
                        spVerbosity, wrapAround)

    def initialize(self,
                   inputDimensions=[32, 32],
                   columnDimensions=[64, 64],
                   potentialRadius=16,
                   potentialPct=0.5,
                   globalInhibition=False,
                   localAreaDensity=-1.0,
                   numActiveColumnsPerInhArea=10.0,
                   stimulusThreshold=0,
                   synPermInactiveDec=0.01,
                   synPermActiveInc=0.1,
                   synPredictedInc=0.1,
                   synPermConnected=0.10,
                   minPctOverlapDutyCycle=0.001,
                   minPctActiveDutyCycle=0.001,
                   dutyCyclePeriod=1000,
                   maxBoost=10.0,
                   useBurstingRule=True,
                   usePoolingRule=True,
                   poolingLife=1000,
                   poolingThreshUnpredicted=0.0,
                   seed=-1,
                   initConnectedPct=0.1,
                   spVerbosity=0,
                   wrapAround=True):

        # Verify input is valid
        inputDimensions = numpy.array(inputDimensions)
        columnDimensions = numpy.array(columnDimensions)
        numColumns = columnDimensions.prod()
        numInputs = inputDimensions.prod()

        assert (numColumns > 0)
        assert (numInputs > 0)
        assert (numActiveColumnsPerInhArea > 0
                or (localAreaDensity > 0 and localAreaDensity <= 0.5))

        # save arguments
        self._numInputs = int(numInputs)
        self._numColumns = int(numColumns)
        self._columnDimensions = columnDimensions
        self._inputDimensions = inputDimensions
        self._potentialRadius = int(min(potentialRadius, numInputs))
        self._potentialPct = potentialPct
        self._globalInhibition = globalInhibition
        self._numActiveColumnsPerInhArea = int(numActiveColumnsPerInhArea)
        self._localAreaDensity = localAreaDensity
        self._stimulusThreshold = stimulusThreshold
        self._synPermInactiveDec = synPermInactiveDec
        self._synPermActiveInc = synPermActiveInc
        self._synPredictedInc = synPredictedInc
        self._synPermBelowStimulusInc = synPermConnected / 10.0
        self._synPermConnected = synPermConnected
        self._minPctOverlapDutyCycles = minPctOverlapDutyCycle
        self._minPctActiveDutyCycles = minPctActiveDutyCycle
        self._dutyCyclePeriod = dutyCyclePeriod
        self._maxBoost = maxBoost
        self._spVerbosity = spVerbosity
        self._wrapAround = wrapAround
        self.useBurstingRule = useBurstingRule
        self.usePoolingRule = usePoolingRule
        self._poolingLife = poolingLife
        self._initConnectedPct = initConnectedPct
        # Extra parameter settings
        self._synPermMin = 0.0
        self._synPermMax = 1.0
        self._synPermTrimThreshold = synPermActiveInc / 2.0
        assert (self._synPermTrimThreshold < self._synPermConnected)
        self._updatePeriod = 20
        self._poolingThreshUnpredicted = poolingThreshUnpredicted

        # Internal state
        self._version = 1.0
        self._iterationNum = 0
        self._iterationLearnNum = 0

        # initialize the random number generators
        self._seed(seed)

        # A cell will enter pooling state if it receives enough predicted inputs
        # pooling cells have priority during competition
        self._poolingActivation = numpy.zeros((self._numColumns),
                                              dtype="int32")
        self._poolingColumns = []
        # Initialize a tiny random tie breaker. This is used to determine winning
        # columns where the overlaps are identical.
        self._tieBreaker = 0.01 * numpy.array(
            [self._random.getReal64() for i in xrange(self._numColumns)])

        # initialize connection matrix
        self.initializeConnections()

        self._overlapDutyCycles = numpy.zeros(numColumns, dtype=realDType)
        self._activeDutyCycles = numpy.zeros(numColumns, dtype=realDType)
        self._minOverlapDutyCycles = numpy.zeros(numColumns, dtype=realDType)
        self._minActiveDutyCycles = numpy.zeros(numColumns, dtype=realDType)
        self._boostFactors = numpy.ones(numColumns, dtype=realDType)

        # The inhibition radius determines the size of a column's local
        # neighborhood. A cortical column must overcome the overlap
        # score of columns in its neighborhood in order to become active. This
        # radius is updated every _updatePeriod iterations. It grows and shrinks
        # with the average number of connected synapses per column.
        self._inhibitionRadius = 0
        self._updateInhibitionRadius()

        if self._spVerbosity > 0:
            self.printParameters()

    def initializeConnections(self):
        """
    Initialize connection matrix, including:

    _permanences :      permanence of synaptic connections (sparse matrix)
    _potentialPools:    potential pool of connections for each cell
                        (sparse binary matrix)
    _connectedSynapses: connected synapses (binary sparse matrix)
    _connectedCounts:   number of connections per cell (numpy array)
    """

        numColumns = self._numColumns
        numInputs = self._numInputs

        # The SP should be setup so that stimulusThreshold is reasonably high,
        # similar to a TP's activation threshold. The pct of connected cells may
        # need to be low to avoid false positives given the monosynaptic rule
        initConnectedPct = self._initConnectedPct

        # Store the set of all inputs that are within each column's potential pool.
        # 'potentialPools' is a matrix, whose rows represent cortical columns, and
        # whose columns represent the input bits. if potentialPools[i][j] == 1,
        # then input bit 'j' is in column 'i's potential pool. A column can only be
        # connected to inputs in its potential pool. The indices refer to a
        # flattened version of both the inputs and columns. Namely, irrespective
        # of the topology of the inputs and columns, they are treated as being a
        # one dimensional array. Since a column is typically connected to only a
        # subset of the inputs, many of the entries in the matrix are 0. Therefore
        # the the potentialPool matrix is stored using the SparseBinaryMatrix
        # class, to reduce memory footprint and compuation time of algorithms that
        # require iterating over the data strcuture.
        self._potentialPools = SparseBinaryMatrix(numInputs)
        self._potentialPools.resize(numColumns, numInputs)

        # Initialize the permanences for each column. Similar to the
        # 'self._potentialPools', the permanences are stored in a matrix whose rows
        # represent the cortical columns, and whose columns represent the input
        # bits. if self._permanences[i][j] = 0.2, then the synapse connecting
        # cortical column 'i' to input bit 'j'  has a permanence of 0.2. Here we
        # also use the SparseMatrix class to reduce the memory footprint and
        # computation time of algorithms that require iterating over the data
        # structure. This permanence matrix is only allowed to have non-zero
        # elements where the potential pool is non-zero.
        self._permanences = SparseMatrix(numColumns, numInputs)

        # 'self._connectedSynapses' is a similar matrix to 'self._permanences'
        # (rows represent cortical columns, columns represent input bits) whose
        # entries represent whether the cortial column is connected to the input
        # bit, i.e. its permanence value is greater than 'synPermConnected'. While
        # this information is readily available from the 'self._permanence' matrix,
        # it is stored separately for efficiency purposes.
        self._connectedSynapses = SparseBinaryMatrix(numInputs)
        self._connectedSynapses.resize(numColumns, numInputs)

        # Stores the number of connected synapses for each column. This is simply
        # a sum of each row of 'self._connectedSynapses'. again, while this
        # information is readily available from 'self._connectedSynapses', it is
        # stored separately for efficiency purposes.
        self._connectedCounts = numpy.zeros(numColumns, dtype=realDType)

        # Initialize the set of permanence values for each column. Ensure that
        # each column is connected to enough input bits to allow it to be
        # activated.
        for i in xrange(numColumns):
            potential = self._mapPotential(i, wrapAround=self._wrapAround)
            self._potentialPools.replaceSparseRow(i, potential.nonzero()[0])
            perm = self._initPermanence(potential, initConnectedPct)
            self._updatePermanencesForColumn(perm, i, raisePerm=True)

        # TODO: The permanence initialization code below runs faster but is not
        # deterministic (doesn't use our RNG or the seed).  The speed is
        # particularly important for temporal pooling because the number if inputs =
        # number of cells in the prevous level. We should consider cleaning up the
        # code and moving it to the base spatial pooler class itself.

        # for i in xrange(numColumns):
        #   # NEW indices for inputs within _potentialRadius of the current column
        #   indices = numpy.array(range(2*self._potentialRadius+1))
        #   indices += i
        #   indices -= self._potentialRadius
        #   indices %= self._numInputs # periodic boundary conditions
        #   indices = numpy.array(list(set(indices)))
        #
        #   # NEW Select a subset of the receptive field to serve as the
        #   # potential pool
        #   sample = numpy.empty((self._inputDimensions * self._potentialPct)\
        #                         .astype('int32'),dtype=uintType)
        #   self._random.getUInt32Sample(indices.astype(uintType),sample)
        #   potential = numpy.zeros(self._numInputs)
        #   potential[sample] = 1
        #
        #   # update potentialPool
        #   self._potentialPools.replaceSparseRow(i, potential.nonzero()[0])
        #
        #   # NEW generate indicator for connected/unconnected
        #   connected = numpy.random.rand(self._numInputs) < initConnectedPct
        #
        #   # set permanence value for connected synapses to be slightly above _synPermConnected
        #   permConnected =  (self._synPermConnected + numpy.random.rand(self._numInputs) *
        #     self._synPermActiveInc / 4.0)
        #   # set permanence value for unconnected synapses below _synPermConnected
        #   permNotConnected = self._synPermConnected * numpy.random.rand(self._numInputs)
        #
        #   # update permamnce value
        #   perm = permNotConnected
        #   perm[connected] = permConnected[connected]
        #   perm[potential < 1] = 0 # permanence value for cells not in the potential
        #   # pool
        #
        #   self._updatePermanencesForColumn(perm, i, raisePerm=True)

    def reset(self):
        """
    Reset the state of the temporal pooler
    """
        self._poolingActivation = numpy.zeros((self._numColumns),
                                              dtype="int32")
        self._poolingColumns = []
        self._overlapDutyCycles = numpy.zeros(self._numColumns,
                                              dtype=realDType)
        self._activeDutyCycles = numpy.zeros(self._numColumns, dtype=realDType)
        self._minOverlapDutyCycles = numpy.zeros(self._numColumns,
                                                 dtype=realDType)
        self._minActiveDutyCycles = numpy.zeros(self._numColumns,
                                                dtype=realDType)
        self._boostFactors = numpy.ones(self._numColumns, dtype=realDType)

    def compute(self, inputVector, learn, activeArray, burstingColumns,
                predictedCells):
        """
    This is the primary public method of the class. This function takes an input
    vector and outputs the indices of the active columns.

    New parameters defined here:
    ----------------------------
    @param inputVector:         The active cells from a Temporal Memory
    @param learn:               A Boolean specifying whether learning will be
                                performed
    @param activeArray:         An array representing the active columns
                                produced by this method
    @param burstingColumns:     A numpy array with numColumns elements having
                                binary values with 1 representing a
                                currently bursting column in Temporal Memory.

    @param predictedCells: A numpy array with numInputs elements. A 1
                                    indicates that this cell switching from
                                    predicted state in the previous time step to
                                    active state in the current timestep
    """
        assert (numpy.size(inputVector) == self._numInputs)
        assert (numpy.size(predictedCells) == self._numInputs)

        self._updateBookeepingVars(learn)
        inputVector = numpy.array(inputVector, dtype=realDType)
        predictedCells = numpy.array(predictedCells, dtype=realDType)
        inputVector.reshape(-1)

        if self._spVerbosity > 3:
            print " Input bits: ", inputVector.nonzero()[0]
            print " predictedCells: ", predictedCells.nonzero()[0]

        # Phase 1: Calculate overlap scores
        # The overlap score has 4 components:
        # (1) Overlap between correctly predicted input cells and pooling TP cells
        # (2) Overlap between active input cells and all TP cells
        #     (like standard SP calculation)
        # (3) Overlap between correctly predicted input cells and all TP cells
        # (4) Overlap from bursting columns in TM and all TP cells

        # 1) Calculate pooling overlap
        if self.usePoolingRule:
            overlapsPooling = self._calculatePoolingActivity(
                predictedCells, learn)

            if self._spVerbosity > 4:
                print "usePoolingRule: Overlaps after step 1:"
                print "   ", overlapsPooling

        else:
            overlapsPooling = 0

        # 2) Calculate overlap between active input cells and connected synapses
        overlapsAllInput = self._calculateOverlap(inputVector)

        # 3) overlap with predicted inputs
        # NEW: Isn't this redundant with 1 and 2)? This looks at connected synapses
        # only.
        # If 1) is called with learning=False connected synapses are used and
        # it is somewhat redundant although there is a boosting factor in 1) which
        # makes 1's effect stronger. If 1) is called with learning=True it's less
        # redundant
        overlapsPredicted = self._calculateOverlap(predictedCells)

        if self._spVerbosity > 4:
            print "Overlaps with all inputs:"
            print " Number of On Bits: ", inputVector.sum()
            print "   ", overlapsAllInput

            print "Overlaps with predicted inputs:"
            print "   ", overlapsPredicted

        # 4) consider bursting columns
        if self.useBurstingRule:
            overlapsBursting = self._calculateBurstingColumns(burstingColumns)

            if self._spVerbosity > 4:
                print "Overlaps with bursting inputs:"
                print "   ", overlapsBursting
        else:
            overlapsBursting = 0

        overlaps = (overlapsPooling + overlapsPredicted + overlapsAllInput +
                    overlapsBursting)

        # Apply boosting when learning is on
        if learn:
            boostedOverlaps = self._boostFactors * overlaps
            if self._spVerbosity > 4:
                print "Overlaps after boosting:"
                print "   ", boostedOverlaps

        else:
            boostedOverlaps = overlaps

        # Apply inhibition to determine the winning columns
        activeColumns = self._inhibitColumns(boostedOverlaps)

        if learn:
            self._adaptSynapses(inputVector, activeColumns, predictedCells)
            self._updateDutyCycles(overlaps, activeColumns)
            self._bumpUpWeakColumns()
            self._updateBoostFactors()
            if self._isUpdateRound():
                self._updateInhibitionRadius()
                self._updateMinDutyCycles()

        activeArray.fill(0)
        if activeColumns.size > 0:
            activeArray[activeColumns] = 1

        # update pooling state of cells
        activeColWithPredictedInput = activeColumns[numpy.where(\
                                    overlapsPredicted[activeColumns]>0)[0]]

        numUnPredictedInput = float(len(burstingColumns.nonzero()[0]))
        numPredictedInput = float(len(predictedCells))
        fracUnPredicted = numUnPredictedInput / (numUnPredictedInput +
                                                 numPredictedInput)

        self._updatePoolingState(activeColWithPredictedInput, fracUnPredicted)

        if self._spVerbosity > 2:
            activeColumns.sort()
            print "The following columns are finally active:"
            print "   ", activeColumns
            print "The following columns are in pooling state:"
            print "   ", self._poolingActivation.nonzero()[0]
            # print "Inputs to pooling columns"
            # print "   ",overlapsPredicted[self._poolingColumns]

        return activeColumns

    def _updatePoolingState(self, activeColWithPredictedInput,
                            fractionUnpredicted):
        """
    This function updates the pooling state of TP cells. A cell will stop
    pooling if:
    (1) It hasn't received any predicted input in the last self._poolingLife
    steps
    or
    (2) the overall fraction of unpredicted input to the TP is above
        _poolingThreshUnpredicted
    """
        if fractionUnpredicted > self._poolingThreshUnpredicted:
            # Reset pooling activation if the fraction of unpredicted input
            # is above the threshold
            if self._spVerbosity > 3:
                print " reset pooling state for all cells"
            self._poolingActivation = numpy.zeros(self._numColumns,
                                                  dtype="int32")
        else:
            # decrement activation of all pooling cells
            self._poolingActivation[self._poolingColumns] -= 1
            # reset activation of cells that are receiving predicted input
            self._poolingActivation[
                activeColWithPredictedInput] = self._poolingLife

        self._poolingColumns = self._poolingActivation.nonzero()[0]

    def _calculatePoolingActivity(self, predictedActiveCells, learn):
        """
    Determines each column's overlap with predicted active cell input.
    If learning, overlap is calculated between predicted active input cells and
    potential synapses. Otherwise, connected synapses are used.
    The overlap of a TP cell that was previously active and has one or more
    active synapse due to predicted active input is set to (_numInputs + 1).
    This guarantees that such a cell wins the inhibition competition.
    
    TODO: check with Jeff, what happens in biology if a cell was not previously
    active but receives metabotropic input?  Does it turn on, or does the met.
    input just extend the activity of already active cells? Does it matter
    whether the synapses are connected or not? Currently we are assuming no
    because most connected synapses become disconnected in the previous time
    step. If column i at time t is bursting and performs learning, the synapses
    from t+1 aren't active at time t, so their permanence will decrease.

    Parameters:
    ----------------------------
    predictedActiveCells: a numpy array with numInputs elements. A 1 indicates
                          that this cell switched from a predicted state in
                          the previous time step to active state in the current
                          timestep
    returns:              an array of overlap values due to predicted
                          active TM cells
    """

        overlaps = numpy.zeros(self._numColumns).astype(realDType)

        # If no pooling columns or no predicted active inputs, return all zeros
        if (sum(self._poolingActivation) == 0
                or len(predictedActiveCells.nonzero()[0]) == 0):
            return overlaps

        if learn:
            # During learning, overlap is calculated based on potential synapses.
            self._potentialPools.rightVecSumAtNZ_fast(predictedActiveCells,
                                                      overlaps)
        else:
            # At inference stage, overlap is calculated based on connected synapses.
            self._connectedSynapses.rightVecSumAtNZ_fast(
                predictedActiveCells, overlaps)

        poolingColumns = self._poolingColumns

        # Only consider columns that are in pooling state
        mask = numpy.zeros(self._numColumns).astype(realDType)
        mask[poolingColumns] = 1
        overlaps = overlaps * mask
        # Pooling TP cells that receive predicted input
        # will have their overlap boosted by a large factor so that they are likely
        # to win the inhibition competition
        boostFactorPooling = self._maxBoost * self._numInputs
        overlaps = boostFactorPooling * overlaps

        if self._spVerbosity > 3:
            print "\n============== In _calculatePoolingActivity ======"
            print "Received predicted cell inputs from following indices:"
            print "   ", predictedActiveCells.nonzero()[0]
            print "The following column indices are in pooling state:"
            print "   ", poolingColumns
            print "Overlap score of pooling columns:"
            print "   ", overlaps[poolingColumns]
            print "============== Leaving _calculatePoolingActivity  ======\n"

        return overlaps

    def _calculateBurstingColumns(self, burstingColumns):
        """
    Returns the contribution to overlap due to bursting columns. If any
    column is bursting, its overlap score is set to stimulusThreshold. This
    means it will be guaranteed to win as long as no other column is
    metabotropic or has input > stimulusThreshold. 

    Parameters:
    ----------------------------
    burstingColumns: a numpy array with numColumns elements. A 1 indicates that
                     column is currently bursting.
    """
        overlaps = burstingColumns * self._stimulusThreshold
        return overlaps

    def _adaptSynapses(self, inputVector, activeColumns, predictedActiveCells):
        """
    This is the primary learning method. It updates synapses' permanence based
    on the bottom-up input to the TP and the TP's active cells.
    For each active cell, its synapses' permanences are updated as follows:

    1. if pre-synaptic input is ON due to a correctly predicted cell,
       increase permanence by _synPredictedInc
    2. else if input is ON due to an active cell, increase permanence by
       _synPermActiveInc
    3. else input is OFF, decrease permanence by _synPermInactiveDec

    Parameters:
    ----------------------------
    inputVector:    a numpy array whose ON bits represent the active cells from
                    temporal memory
    activeColumns:  an array containing the indices of the columns that 
                    survived the inhibition step
    predictedActiveCells: a numpy array with numInputs elements. A 1 indicates
                          that this cell switched from predicted state in
                          the previous time step to active state in the current
                          timestep
    """
        inputIndices = numpy.where(inputVector > 0)[0]
        predictedIndices = numpy.where(predictedActiveCells > 0)[0]
        permChanges = numpy.zeros(self._numInputs)

        # Decrement inactive TM cell -> active TP cell connections
        permChanges.fill(-1 * self._synPermInactiveDec)

        # Increment active TM cell -> active TP cell connections
        permChanges[inputIndices] = self._synPermActiveInc

        # Increment correctly predicted TM cell -> active TP cell connections
        permChanges[predictedIndices] = self._synPredictedInc

        if self._spVerbosity > 4:
            print "\n============== _adaptSynapses ======"
            print "Active input indices:", inputIndices
            print "predicted input indices:", predictedIndices
            print "\n============== _adaptSynapses ======\n"

        for i in activeColumns:
            # Get the permanences of the synapses of TP cell i
            perm = self._permanences.getRow(i)

            # Only consider connections in column's potential pool (receptive field)
            maskPotential = numpy.where(self._potentialPools.getRow(i) > 0)[0]
            perm[maskPotential] += permChanges[maskPotential]
            self._updatePermanencesForColumn(perm, i, raisePerm=False)

    def printParameters(self):
        """
    Useful for debugging.
    """
        print "------------PY  TemporalPooler Parameters ------------------"
        print "numInputs                  = ", self.getNumInputs()
        print "numColumns                 = ", self.getNumColumns()
        print "columnDimensions           = ", self._columnDimensions
        print "numActiveColumnsPerInhArea = ", self.getNumActiveColumnsPerInhArea(
        )
        print "potentialPct               = ", self.getPotentialPct()
        print "globalInhibition           = ", self.getGlobalInhibition()
        print "localAreaDensity           = ", self.getLocalAreaDensity()
        print "stimulusThreshold          = ", self.getStimulusThreshold()
        print "synPermActiveInc           = ", self.getSynPermActiveInc()
        print "synPermInactiveDec         = ", self.getSynPermInactiveDec()
        print "synPermConnected           = ", self.getSynPermConnected()
        print "minPctOverlapDutyCycle     = ", self.getMinPctOverlapDutyCycles(
        )
        print "minPctActiveDutyCycle      = ", self.getMinPctActiveDutyCycles()
        print "dutyCyclePeriod            = ", self.getDutyCyclePeriod()
        print "maxBoost                   = ", self.getMaxBoost()
        print "spVerbosity                = ", self.getSpVerbosity()
        print "version                    = ", self._version

    def extractInputForTP(self, tm):
        """
    Extract inputs for TP from the state of temporal memory
    three information are extracted
    1. correctly predicted cells
    2. all active cells
    3. bursting cells (unpredicted input)
    """

        # bursting cells in layer 4
        burstingColumns = tm.activeState["t"].sum(axis=1)
        burstingColumns[burstingColumns < tm.cellsPerColumn] = 0
        burstingColumns[burstingColumns == tm.cellsPerColumn] = 1
        # print "Bursting column indices=",burstingColumns.nonzero()[0]

        # correctly predicted cells in layer 4
        correctlyPredictedCells = numpy.zeros(
            self._inputDimensions).astype(realDType)
        idx = (tm.predictedState["t-1"] + tm.activeState["t"]) == 2
        idx = idx.reshape(self._inputDimensions)
        correctlyPredictedCells[idx] = 1.0
        # print "Predicted->active cell indices=",correctlyPredictedCells.nonzero()[0]

        # all currently active cells in layer 4
        spInputVector = tm.learnState["t"].reshape(self._inputDimensions)
        # spInputVector = tm.activeState["t"].reshape(self._inputDimensions)

        return (correctlyPredictedCells, spInputVector, burstingColumns)
コード例 #4
0
ファイル: TP_New.py プロジェクト: chagge/nupic.research
class SPTP(SpatialPooler):
  """
  This class implements the new temporal pooler. It tries to form stable and unique
  representations for sequences that are correctly predicted. If the input is not 
  predicted, it used similar competition rule as the spatial pooler to select active
  cells
  """

  def __init__(self,
               inputDimensions=[32,32],
               columnDimensions=[64,64],
               cellsPerColumn = 8,
               potentialRadius=16,
               potentialPct=0.5,
               globalInhibition=False,
               localAreaDensity=-1.0,
               numActiveColumnsPerInhArea=10.0,
               stimulusThreshold=0,
               synPermInactiveDec=0.01,
               synPermActiveInc=0.03,
               synPermActiveInactiveDec = 0,
               synPredictedInc=0.5,
               synPermConnected=0.10,
               minPctOverlapDutyCycle=0.001,
               minPctActiveDutyCycle=0.001,
               dutyCyclePeriod=1000,
               maxBoost=10.0,
               useBurstingRule = True,
               usePoolingRule = True,
               poolingLife = 1000,
               poolingThreshUnpredicted = 0.0,
               initConnectedPct = 0.1,
               seed=-1,
               spVerbosity=0
               ):
    """
    Parameters:
    ----------------------------
    inputDimensions:      A list representing the dimensions of the input
                          vector. Format is [height, width, depth, ...], where
                          each value represents the size of the dimension. For a
                          topology of one dimesion with 100 inputs use 100, or
                          [100]. For a two dimensional topology of 10x5 use
                          [10,5]. 
    columnDimensions:     A list representing the dimensions of the columns in
                          the region. Format is [height, width, depth, ...],
                          where each value represents the size of the dimension.
                          For a topology of one dimesion with 2000 columns use
                          2000, or [2000]. For a three dimensional topology of
                          32x64x16 use [32, 64, 16]. 
    potentialRadius:      This parameter deteremines the extent of the input 
                          that each column can potentially be connected to. 
                          This can be thought of as the input bits that
                          are visible to each column, or a 'receptiveField' of 
                          the field of vision. A large enough value will result 
                          in the 'global coverage', meaning that each column 
                          can potentially be connected to every input bit. This 
                          parameter defines a square (or hyper square) area: a 
                          column will have a max square potential pool with 
                          sides of length 2 * potentialRadius + 1. 
    potentialPct:         The percent of the inputs, within a column's
                          potential radius, that a column can be connected to. 
                          If set to 1, the column will be connected to every 
                          input within its potential radius. This parameter is 
                          used to give each column a unique potential pool when 
                          a large potentialRadius causes overlap between the 
                          columns. At initialization time we choose 
                          ((2*potentialRadius + 1)^(# inputDimensions) * 
                          potentialPct) input bits to comprise the column's
                          potential pool.
    globalInhibition:     If true, then during inhibition phase the winning 
                          columns are selected as the most active columns from 
                          the region as a whole. Otherwise, the winning columns 
                          are selected with resepct to their local 
                          neighborhoods. using global inhibition boosts 
                          performance x60.
    localAreaDensity:     The desired density of active columns within a local
                          inhibition area (the size of which is set by the
                          internally calculated inhibitionRadius, which is in
                          turn determined from the average size of the 
                          connected potential pools of all columns). The 
                          inhibition logic will insure that at most N columns 
                          remain ON within a local inhibition area, where N = 
                          localAreaDensity * (total number of columns in 
                          inhibition area).
    numActivePerInhArea:  An alternate way to control the density of the active
                          columns. If numActivePerInhArea is specified then
                          localAreaDensity must less than 0, and vice versa. 
                          When using numActivePerInhArea, the inhibition logic 
                          will insure that at most 'numActivePerInhArea' 
                          columns remain ON within a local inhibition area (the 
                          size of which is set by the internally calculated
                          inhibitionRadius, which is in turn determined from 
                          the average size of the connected receptive fields of 
                          all columns). When using this method, as columns 
                          learn and grow their effective receptive fields, the
                          inhibitionRadius will grow, and hence the net density
                          of the active columns will *decrease*. This is in
                          contrast to the localAreaDensity method, which keeps
                          the density of active columns the same regardless of
                          the size of their receptive fields.
    stimulusThreshold:    This is a number specifying the minimum number of
                          synapses that must be on in order for a columns to
                          turn ON. The purpose of this is to prevent noise 
                          input from activating columns. Specified as a percent 
                          of a fully grown synapse.
    synPermInactiveDec:   The amount by which an inactive synapse is 
                          decremented in each round. Specified as a percent of 
                          a fully grown synapse.
    synPermActiveInc:     The amount by which an active synapse is incremented 
                          in each round. Specified as a percent of a
                          fully grown synapse.
    synPermActiveInactiveDec: For inactive columns, synapses connected to input
                          bits that are on are decreased by
                          synPermActiveInactiveDec.
    synPredictedInc:      The amount by which a metabotropically active synapse
                          is incremented in each round. These are active
                          synapses originating from a previously predicted cell.
                          Specified as a percent of a fully grown synapse.
    synPermConnected:     The default connected threshold. Any synapse whose
                          permanence value is above the connected threshold is
                          a "connected synapse", meaning it can contribute to
                          the cell's firing.
    minPctOverlapDutyCycle: A number between 0 and 1.0, used to set a floor on
                          how often a column should have at least
                          stimulusThreshold active inputs. Periodically, each
                          column looks at the overlap duty cycle of
                          all other column within its inhibition radius and 
                          sets its own internal minimal acceptable duty cycle 
                          to: minPctDutyCycleBeforeInh * max(other columns' 
                          duty cycles).
                          On each iteration, any column whose overlap duty 
                          cycle falls below this computed value will  get
                          all of its permanence values boosted up by
                          synPermActiveInc. Raising all permanences in response
                          to a sub-par duty cycle before  inhibition allows a
                          cell to search for new inputs when either its
                          previously learned inputs are no longer ever active,
                          or when the vast majority of them have been 
                          "hijacked" by other columns.
    minPctActiveDutyCycle: A number between 0 and 1.0, used to set a floor on
                          how often a column should be activate.
                          Periodically, each column looks at the activity duty 
                          cycle of all other columns within its inhibition 
                          radius and sets its own internal minimal acceptable 
                          duty cycle to:
                            minPctDutyCycleAfterInh *
                            max(other columns' duty cycles).
                          On each iteration, any column whose duty cycle after
                          inhibition falls below this computed value will get
                          its internal boost factor increased.
    dutyCyclePeriod:      The period used to calculate duty cycles. Higher
                          values make it take longer to respond to changes in
                          boost or synPerConnectedCell. Shorter values make it
                          more unstable and likely to oscillate.
    maxBoost:            The maximum overlap boost factor. Each column's
                          overlap gets multiplied by a boost factor
                          before it gets considered for inhibition.
                          The actual boost factor for a column is number 
                          between 1.0 and maxBoost. A boost factor of 1.0 is 
                          used if the duty cycle is >= minOverlapDutyCycle, 
                          maxBoost is used if the duty cycle is 0, and any duty 
                          cycle in between is linearly extrapolated from these 
                          2 endpoints.
    seed:                 Seed for our own pseudo-random number generator.
    spVerbosity:          spVerbosity level: 0, 1, 2, or 3
    useBurstingRule:      A bool value indicating whether to use bursting rule
    usePoolingRule:       A bool value indicating whether to use pooling rule
    poolingLife:          A pooling cell will stop pooling if it hasn't received
                          any predicted input in poolingLife steps
    poolingThreshUnpredicted: A number between 0 and 1. The temporal pooler will
                          stop pooling if the fraction of unpredicted input exceeds
                          this threshold.
    initConnectedPct:     A number between 0 and 1, indicating fraction of the inputs
                          that are initially connected.
    """
    self.initialize(inputDimensions,
                    columnDimensions,
                    cellsPerColumn,
                    potentialRadius,
                    potentialPct,
                    globalInhibition,
                    localAreaDensity,
                    numActiveColumnsPerInhArea,
                    stimulusThreshold,
                    synPermInactiveDec,
                    synPermActiveInc,
                    synPermActiveInactiveDec,
                    synPredictedInc,
                    synPermConnected,
                    minPctOverlapDutyCycle,
                    minPctActiveDutyCycle,
                    dutyCyclePeriod,
                    maxBoost,
                    useBurstingRule,
                    usePoolingRule,
                    poolingLife,
                    poolingThreshUnpredicted,
                    seed,      
                    initConnectedPct,              
                    spVerbosity)


  def initialize(self,
               inputDimensions=[32,32],
               columnDimensions=[64,64],
               cellsPerColumn = 8,
               potentialRadius=16,
               potentialPct=0.5,
               globalInhibition=False,
               localAreaDensity=-1.0,
               numActiveColumnsPerInhArea=10.0,
               stimulusThreshold=0,
               synPermInactiveDec=0.01,
               synPermActiveInc=0.1,
               synPermActiveInactiveDec =0,
               synPredictedInc=0.1,
               synPermConnected=0.10,
               minPctOverlapDutyCycle=0.001,
               minPctActiveDutyCycle=0.001,
               dutyCyclePeriod=1000,
               maxBoost=10.0,
               useBurstingRule=True,
               usePoolingRule=True,
               poolingLife=1000,
               poolingThreshUnpredicted=0.0,
               seed=-1,
               initConnectedPct=0.1,
               spVerbosity=0):

    # Verify input is valid
    inputDimensions = numpy.array(inputDimensions)
    columnDimensions = numpy.array(columnDimensions)
    numColumns = columnDimensions.prod()
    numInputs = inputDimensions.prod()

    assert(numColumns > 0)
    assert(numInputs > 0)
    assert (numActiveColumnsPerInhArea > 0 or 
           (localAreaDensity > 0 and localAreaDensity <= 0.5))

    # save arguments
    self._numInputs = int(numInputs)
    self._numColumns = int(numColumns)
    self._columnDimensions = columnDimensions
    self._inputDimensions = inputDimensions
    self._potentialRadius = int(min(potentialRadius, numInputs))
    self._potentialPct = potentialPct
    self._globalInhibition = globalInhibition
    self._numActiveColumnsPerInhArea = int(numActiveColumnsPerInhArea)
    self._localAreaDensity = localAreaDensity
    self._stimulusThreshold = stimulusThreshold
    self._synPermInactiveDec = synPermInactiveDec
    self._synPermActiveInc = synPermActiveInc
    self._synPredictedInc = synPredictedInc
    self._synPermBelowStimulusInc = synPermConnected / 10.0
    self._synPermConnected = synPermConnected
    self._minPctOverlapDutyCycles = minPctOverlapDutyCycle
    self._minPctActiveDutyCycles = minPctActiveDutyCycle
    self._dutyCyclePeriod = dutyCyclePeriod
    self._maxBoost = maxBoost
    self._spVerbosity = spVerbosity
    self._synPermActiveInactiveDec = synPermActiveInactiveDec
    self.useBurstingRule = useBurstingRule
    self.usePoolingRule = usePoolingRule
    self._poolingLife = poolingLife
    self._initConnectedPct = initConnectedPct
    # Extra parameter settings
    self._synPermMin = 0.0
    self._synPermMax = 1.0
    self._synPermTrimThreshold = synPermActiveInc / 2.0
    assert(self._synPermTrimThreshold < self._synPermConnected)
    self._updatePeriod = 20
    self._poolingThreshUnpredicted = poolingThreshUnpredicted

    # Internal state
    self._version = 1.0
    self._iterationNum = 0
    self._iterationLearnNum = 0

    # initialize the random number generators
    self._seed(seed)
    
    # A cell will enter pooling state if it receives enough predicted inputs
    # pooling cells have priority during competition
    self._poolingState = numpy.zeros((self._numColumns), dtype='int32')
    self._poolingColumns = []
    # Initialize a tiny random tie breaker. This is used to determine winning
    # columns where the overlaps are identical.
    self._tieBreaker = 0.01*numpy.array([self._random.getReal64() for i in 
                                        xrange(self._numColumns)])

    # initialize connection matrix
    self.initializeConnections()


    self._overlapDutyCycles = numpy.zeros(numColumns, dtype=realDType)
    self._activeDutyCycles = numpy.zeros(numColumns, dtype=realDType)
    self._minOverlapDutyCycles = numpy.zeros(numColumns, 
                                             dtype=realDType)
    self._minActiveDutyCycles = numpy.zeros(numColumns,
                                            dtype=realDType)
    self._boostFactors = numpy.ones(numColumns, dtype=realDType)

    # The inhibition radius determines the size of a column's local 
    # neighborhood. of a column. A cortical column must overcome the overlap 
    # score of columns in its neighborhood in order to become active. This
    # radius is updated every updatePeriod iterations. It grows and shrinks
    # with the average number of connected synapses per column.
    self._inhibitionRadius = 0
    self._updateInhibitionRadius()
    
    if self._spVerbosity > 0:
      self.printParameters()

  def initializeConnections(self):

    '''
    Initialize connection matrix, including:

    _permanences : permanence of synaptic connections (sparse matrix)
    _potentialPools: potential pool of connections for each cell
                    (sparse binary matrix)
    _connectedSynapses: connected synapses (binary sparse matrix)
    _connectedCounts: number of connections per cell (numpy array)
    _permanenceDecCache: a cache for permanence decremant. 
    ''' 

    numColumns = self._numColumns
    numInputs = self._numInputs

    # The SP should be setup so that stimulusThreshold is reasonably high,
    # similar to a TP's activation threshold. The pct of connected cells may
    # need to be low to avoid false positives given the monosynaptic rule 
    initConnectedPct = self._initConnectedPct

    # Store the set of all inputs that are within each column's potential pool.
    # 'potentialPools' is a matrix, whose rows represent cortical columns, and 
    # whose columns represent the input bits. if potentialPools[i][j] == 1,
    # then input bit 'j' is in column 'i's potential pool. A column can only be 
    # connected to inputs in its potential pool. The indices refer to a 
    # flattened version of both the inputs and columns. Namely, irrespective
    # of the topology of the inputs and columns, they are treated as being a 
    # one dimensional array. Since a column is typically connected to only a 
    # subset of the inputs, many of the entries in the matrix are 0. Therefore 
    # the the potentialPool matrix is stored using the SparseBinaryMatrix 
    # class, to reduce memory footprint and compuation time of algorithms that 
    # require iterating over the data strcuture.
    self._potentialPools = SparseBinaryMatrix(numInputs)
    self._potentialPools.resize(numColumns, numInputs)

    # Initialize the permanences for each column. Similar to the 
    # 'self._potentialPools', the permanences are stored in a matrix whose rows
    # represent the cortial columns, and whose columns represent the input 
    # bits. if self._permanences[i][j] = 0.2, then the synapse connecting 
    # cortical column 'i' to input bit 'j'  has a permanence of 0.2. Here we 
    # also use the SparseMatrix class to reduce the memory footprint and 
    # computation time of algorithms that require iterating over the data 
    # structure. This permanence matrix is only allowed to have non-zero 
    # elements where the potential pool is non-zero.
    self._permanences = SparseMatrix(numColumns, numInputs)

    # A cache for permanence decrements of (Active->Inactive Type)
    # Permanence decrements won't be initiated until the next time
    # a cell fire
    self._permanenceDecCache = SparseMatrix(numColumns, numInputs)


    # 'self._connectedSynapses' is a similar matrix to 'self._permanences' 
    # (rows represent cortial columns, columns represent input bits) whose 
    # entries represent whether the cortial column is connected to the input 
    # bit, i.e. its permanence value is greater than 'synPermConnected'. While 
    # this information is readily available from the 'self._permanence' matrix, 
    # it is stored separately for efficiency purposes.
    self._connectedSynapses = SparseBinaryMatrix(numInputs)
    self._connectedSynapses.resize(numColumns, numInputs)

    # Stores the number of connected synapses for each column. This is simply
    # a sum of each row of 'self._connectedSynapses'. again, while this 
    # information is readily available from 'self._connectedSynapses', it is
    # stored separately for efficiency purposes.
    self._connectedCounts = numpy.zeros(numColumns, dtype=realDType)

    # Initialize the set of permanence values for each columns. Ensure that 
    # each column is connected to enough input bits to allow it to be 
    # activated

    for i in xrange(numColumns):
      # indice for inputs within _potentialRadius of the current column
      indices = numpy.array(range(2*self._potentialRadius+1))
      indices += i
      indices -= self._potentialRadius      
      indices %= self._numInputs # periodic boundary conditions
      indices = numpy.array(list(set(indices)))          

      # Select a subset of the receptive field to serve as the
      # the potential pool
      sample = numpy.empty((self._inputDimensions * self._potentialPct)\
                            .astype('int32'),dtype=uintType)
      self._random.getUInt32Sample(indices.astype(uintType),sample)
      potential = numpy.zeros(self._numInputs)  
      potential[sample] = 1        

      # update potentialPool
      self._potentialPools.replaceSparseRow(i, potential.nonzero()[0])

      # number of potential connections
      numPotential = numpy.nonzero(potential)[0].size
      # generate indicator for connected/unconnected
      connected = numpy.random.rand(self._numInputs) < initConnectedPct

      # set permanence value for connected synapses to be slightly above _synPermConnected
      permConnected =  (self._synPermConnected + numpy.random.rand(self._numInputs) * 
        self._synPermActiveInc / 4.0)
      # set permamnce value for unconnected synapses below _synPermConnected
      permNotConnected = self._synPermConnected * numpy.random.rand(self._numInputs)

      # update permamnce value
      perm = permNotConnected
      perm[connected] = permConnected[connected]
      perm[potential < 1] = 0 # permanace value for cells not in the potential pool

      self._updatePermanencesForColumn(perm, i, raisePerm=True) 



  def reset(self):
    """
    Reset the status of temporal pooler
    """
    self._poolingState = numpy.zeros((self._numColumns), dtype='int32')
    self._poolingColumns = []
    self._overlapDutyCycles = numpy.zeros(self._numColumns, dtype=realDType)
    self._activeDutyCycles = numpy.zeros(self._numColumns, dtype=realDType)
    self._minOverlapDutyCycles = numpy.zeros(self._numColumns, 
                                             dtype=realDType)
    self._minActiveDutyCycles = numpy.zeros(self._numColumns,
                                            dtype=realDType)
    self._boostFactors = numpy.ones(self._numColumns, dtype=realDType)

  def compute(self, inputVector, learn, activeArray, burstingColumns,
              predictedCells):
    """
    This is the primary public method of the SpatialPooler class. This 
    function takes a input vector and outputs the indices of the active columns.
    If 'learn' is set to True, this method also updates the permanences of the
    columns.

    Parameters:
    ----------------------------
    inputVector:    a numpy array of 0's and 1's thata comprises the input to 
                    the spatial pooler. The array will be treated as a one
                    dimensional array, therefore the dimensions of the array
                    do not have to much the exact dimensions specified in the 
                    class constructor. In fact, even a list would suffice. 
                    The number of input bits in the vector must, however, 
                    match the number of bits specified by the call to the 
                    constructor. Therefore there must be a '0' or '1' in the
                    array for every input bit.
    learn:          a boolean value indicating whether learning should be 
                    performed. Learning entails updating the  permanence 
                    values of the synapses, and hence modifying the 'state' 
                    of the model. Setting learning to 'off' freezes the SP
                    and has many uses. For example, you might want to feed in
                    various inputs and examine the resulting SDR's.
    activeArray:    an array whose size is equal to the number of columns. 
                    Before the function returns this array will be populated 
                    with 1's at the indices of the active columns, and 0's 
                    everywhere else.
    burstingColumns: a numpy array with numColumns elements. A 1 indicates that
                    column is currently bursting.
    predictedCells: a numpy array with numInputs elements. A 1 indicates that
                    this cell switching from predicted state in the previous
                    time step to active state in the current timestep
    """
    assert (numpy.size(inputVector) == self._numInputs)
    assert (numpy.size(predictedCells) == self._numInputs)

    self._updateBookeepingVars(learn)
    inputVector = numpy.array(inputVector, dtype=realDType)
    predictedCells = numpy.array(predictedCells, dtype=realDType)
    inputVector.reshape(-1)

    if self._spVerbosity > 3:
      print " Input bits: ", inputVector.nonzero()[0]
      print " predictedCells: ", predictedCells.nonzero()[0]

    # Phase 1: Calculate overlap scores
    # The overlap score has 4 components
    # (1) Overlap with correctly predicted inputs for pooling cells 
    # (2) Overlap with correctly predicted inputs for all cells 
    # (3) Overlap with all inputs
    # (4) Overlap with cells in the bursting column

    # 1) Check pooling rule.
    if self.usePoolingRule:
      overlapsPooling = self._calculatePoolingActivity(predictedCells, learn)

      if self._spVerbosity > 4:
        print "Use Pooling Rule: Overlaps after step a:"
        print "   ", overlapsPooling

    else:
      overlapsPooling = 0
  
    # 2) Calculate overlap between input and connected synapses
    overlapsAllInput = self._calculateOverlap(inputVector)

    # 3) overlap with predicted inputs
    overlapsPredicted = self._calculateOverlap(predictedCells)      

    if self._spVerbosity > 4:
      print "Overlaps with all inputs:"
      print " Number of On Bits: ", inputVector.sum()
      print "   ", overlapsAllInput

      print "Overlaps with predicted inputs:"
      print "   ", overlapsPredicted

    # 4) consider bursting columns
    if self.useBurstingRule:
      overlapsBursting = self._calculateBurstingColumns(burstingColumns)

      if self._spVerbosity > 4:
        print "Overlaps with bursting inputs:"
        print "   ", overlapsBursting
    else:
      overlapsBursting = 0

    
    overlaps = overlapsPooling + overlapsPredicted + \
              overlapsAllInput + overlapsBursting

    # Apply boosting when learning is on
    if learn:
      boostedOverlaps = self._boostFactors * overlaps
      if self._spVerbosity > 4:
        print "Overlaps after boosting:"
        print "   ", boostedOverlaps      

    else:
      boostedOverlaps = overlaps
    

    # Apply inhibition to determine the winning columns
    activeColumns = self._inhibitColumns(boostedOverlaps)    
      
    if learn:
      self._adaptSynapses(inputVector, activeColumns, predictedCells)
      self._updateDutyCycles(overlaps, activeColumns)
      self._bumpUpWeakColumns() 
      self._updateBoostFactors()
      if self._isUpdateRound():
        self._updateInhibitionRadius()
        self._updateMinDutyCycles()
    
    activeArray.fill(0)
    if activeColumns.size > 0:
      activeArray[activeColumns] = 1

    # update pooling state of cells
    activeColWithPredictedInput = activeColumns[numpy.where(\
                                overlapsPredicted[activeColumns]>0)[0]]
    
    numUnPredictedInput = float(len(burstingColumns.nonzero()[0]))
    numPredictedInput = float(len(predictedCells))
    fracUnPredicted = numUnPredictedInput/(numUnPredictedInput + numPredictedInput)

    self._updatePoolingState(activeColWithPredictedInput, fracUnPredicted)

    if self._spVerbosity > 2:
      activeColumns.sort()
      print "The following columns are finally active:"
      print "   ",activeColumns
      print "The following columns are in pooling state:"
      print "   ",self._poolingState.nonzero()[0]
      # print "Inputs to pooling columns"
      # print "   ",overlapsPredicted[self._poolingColumns]


  def _updatePoolingState(self, activeColWithPredictedInput, fracUnPredicted):
    """
    This function update pooling state of cells

    A cell will stop pooling if 
    (1) it hasn't received any predicted input in the last self._poolingLife steps
    or
    (2) the fraction of unpredicted input is above poolingThreshUnpredicted
    """
    

    if fracUnPredicted>self._poolingThreshUnpredicted:
      # reset pooling state if the fraction of unpredicted input 
      # is above the threshold
      if self._spVerbosity > 3:
        print " reset pooling state for all cells"
      self._poolingState = numpy.zeros((self._numColumns), dtype='int32')
    else:
      # decremant life of all pooling cells
      self._poolingState[self._poolingColumns] -= 1
      # reset life of cells that are receiving predicted input
      self._poolingState[activeColWithPredictedInput] = self._poolingLife

    self._poolingColumns = self._poolingState.nonzero()[0]


  def _calculatePoolingActivity(self, predictedCells, learn):
    """
    This function determines each column's overlap with metabotropically
    activated inputs. Overlap is calculated based on potential synapses. The
    overlap of a column that was previously active and has even one active
    metabotropic synapses is set to _numInputs+1 so they are guaranteed to win
    during inhibition.
    
    TODO: check with Jeff, what happens in biology if a cell was not previously
    active but receives metabotropic input?  Does it turn on, or does the met.
    input just extend the activity of already active cells? Does it matter
    whether the synapses are connected or not? Currently we are assuming no
    because most connected synapses become disconnected in the previous time
    step. If column i at time t is bursting and performs learning, the synapses
    from t+1 aren't active at time t, so their permanence will decrease.

    Parameters:
    ----------------------------
    predictedCells: a numpy array with numInputs elements. A 1 indicates that
                    this cell switching from predicted state in the previous
                    time step to active state in the current timestep  
    """


    overlaps = numpy.zeros(self._numColumns).astype(realDType)

    # no predicted inputs or no cell in pooling state, return zero
    if sum(self._poolingState)==0 or len(predictedCells.nonzero()[0])==0:
      return overlaps


    # self._connectedSynapses.rightVecSumAtNZ_fast(predictedCells, overlaps)      
    if learn:
      # During learning, overlap is calculated based on potential synapses. 
      self._potentialPools.rightVecSumAtNZ_fast(predictedCells, overlaps)
    else:
      # At inference stage, overlap is calculated based on connected synapses.
      self._connectedSynapses.rightVecSumAtNZ_fast(predictedCells, overlaps)      

    poolingColumns = self._poolingColumns

    # # only consider columns that are in pooling state
    mask = numpy.zeros(self._numColumns).astype(realDType)
    mask[poolingColumns] = 1    
    overlaps = overlaps * mask
    # columns that are in polling state and receives predicted input
    # will be boosted by a large factor
    boostFactorPooling = self._maxBoost*self._numInputs
    overlaps = boostFactorPooling * overlaps

    if self._spVerbosity > 3:
      print "\n============== Entering _calculateMetabotropicActivity ======"
      print "Received metabotropic inputs from following indices:"
      print "   ",predictedCells.nonzero()[0]
      print "The following column indices are in pooling state:"
      print "   ",poolingColumns
      print "Overlap score of pooling columns:"
      print "   ",overlaps[poolingColumns]
      print "============== Leaving _calculateMetabotropicActivity ======\n"

    return overlaps
  
  def _calculateBurstingColumns(self, burstingColumns):
    """
    Return the contribution to overlap due to bursting columns. If any column is
    bursting, its overlap score is set to stimulusThreshold. This
    means it will be guaranteed to win as long as no other column is
    metabotropic or has input > stimulusThreshold. 

    Parameters:
    ----------------------------
    burstingColumns: a numpy array with numColumns elements. A 1 indicates that
                    column is currently bursting.
    """
    overlaps = burstingColumns * self._stimulusThreshold
    return overlaps


  def _adaptSynapses(self, inputVector, activeColumns, predictedCells):
    """
    The primary method in charge of learning. Adapts the permanence values of 
    the synapses based on the input vector, and the chosen columns after 
    inhibition round. 
    The following rules applies to synapse adaptation:

    For active cells:

    1. synapses connected to input bits that are on are increased by synPermActiveInc
    2. synapses connected to input bits that are off are decreased by synPermInactiveDec  
    3. synapses connected to inputs bits that are on due to predicted inputs
    are increased by synPredictedInc. 

    For inactive cells:
    4. synapses connected to input bits that are on are decreased by synPermActiveInactiveDec


    Parameters:
    ----------------------------
    inputVector:    a numpy array of 0's and 1's thata comprises the input to 
                    the spatial pooler. There exists an entry in the array 
                    for every input bit.
    activeColumns:  an array containing the indices of the columns that 
                    survived inhibition.
    predictedCells: a numpy array with numInputs elements. A 1 indicates that
                    this cell switching from predicted state in the previous
                    time step to active state in the current timestep
    """
    inputIndices = numpy.where(inputVector > 0)[0]
    predictedIndices = numpy.where(predictedCells > 0)[0]
    permChanges = numpy.zeros(self._numInputs)

    # decrement Inactive -> active connections
    permChanges.fill(-1 * self._synPermInactiveDec)
    # increment active -> active connections
    permChanges[inputIndices] = self._synPermActiveInc
    # increment correctly predicted cells -> active connections
    permChanges[predictedIndices] = self._synPredictedInc

    if self._spVerbosity > 4:
      print "\n============== _adaptSynapses ======"
      print "Active input indices:",inputIndices
      print "predicted input indices:",predictedIndices
      # print "permChanges:"
      # print formatRow(permChanges, "%9.4f", 20)
      print "\n============== _adaptSynapses ======\n"

    for i in activeColumns:
      # input connections to column i
      perm = self._permanences.getRow(i)

      # decremant cached (active->inactive connections)
      permChangesBinary = self._permanenceDecCache.getRow(i)
      perm = numpy.where(permChangesBinary>0, perm-self._synPermActiveInactiveDec, perm)
      self._permanenceDecCache.setRowToZero(i)

      # only consider connections in potential pool
      maskPotential = numpy.where(self._potentialPools.getRow(i) > 0)[0]
      perm[maskPotential] += permChanges[maskPotential]
      self._updatePermanencesForColumn(perm, i, raisePerm=False)

    # decrement active -> inactive connections
    if self._synPermActiveInactiveDec > 0:

      for i in inputIndices: 
        # go through all active inputs
        if self._spVerbosity > 5:
          print "Active Input: ", i
          print "Current Connection: ", self._connectedSynapses.getRow(i)
                          
        # push permanance decremant to cache
        permChangesBinary = numpy.zeros(self._numColumns)
        permChangesBinary.fill(1) 
        permChangesBinary[activeColumns] = 0        
        self._permanenceDecCache.setColFromDense(i, permChangesBinary)     


  def printParameters(self):
    """
    Useful for debugging.
    """
    print "------------PY  SPTP Parameters ------------------"
    print "numInputs                  = ", self.getNumInputs()
    print "numColumns                 = ", self.getNumColumns()
    print "columnDimensions           = ", self._columnDimensions
    print "numActiveColumnsPerInhArea = ", self.getNumActiveColumnsPerInhArea()
    print "potentialPct               = ", self.getPotentialPct()
    print "globalInhibition           = ", self.getGlobalInhibition()
    print "localAreaDensity           = ", self.getLocalAreaDensity()
    print "stimulusThreshold          = ", self.getStimulusThreshold()
    print "synPermActiveInc           = ", self.getSynPermActiveInc()
    print "synPermInactiveDec         = ", self.getSynPermInactiveDec()
    print "synPermConnected           = ", self.getSynPermConnected()
    print "minPctOverlapDutyCycle     = ", self.getMinPctOverlapDutyCycles()
    print "minPctActiveDutyCycle      = ", self.getMinPctActiveDutyCycles()
    print "dutyCyclePeriod            = ", self.getDutyCyclePeriod()
    print "maxBoost                   = ", self.getMaxBoost()
    print "spVerbosity                = ", self.getSpVerbosity()
    print "version                    = ", self._version


  def extractInputForTP(self, tm):  
    """
    Extract inputs for TP from the state of temporal memory
    three information are extracted
    1. correctly predicted cells
    2. all active cells
    3. bursting cells (unpredicted input)
    """

    # bursting cells in layer 4
    burstingColumns = tm.activeState['t'].sum(axis=1)
    burstingColumns[ burstingColumns < tm.cellsPerColumn ] = 0
    burstingColumns[ burstingColumns == tm.cellsPerColumn ] = 1
    # print "Bursting column indices=",burstingColumns.nonzero()[0]  
    
    # correctly predicted cells in layer 4
    correctlyPredictedCells = numpy.zeros(self._inputDimensions).astype(realDType)
    idx = (tm.predictedState['t-1'] + tm.activeState['t']) == 2
    idx = idx.reshape(self._inputDimensions)
    correctlyPredictedCells[idx] = 1.0
    # print "Predicted->active cell indices=",correctlyPredictedCells.nonzero()[0]

    # all currently active cells in layer 4
    spInputVector = tm.learnState['t'].reshape(self._inputDimensions)  
    # spInputVector = tm.activeState['t'].reshape(self._inputDimensions)      
    
    return (correctlyPredictedCells, spInputVector, burstingColumns)  
コード例 #5
0
class ColumnPooler(object):
  """
  This class constitutes a temporary implementation for a cross-column pooler.
  The implementation goal of this class is to prove basic properties before
  creating a cleaner implementation.
  """

  def __init__(self,
               inputWidth,
               numActiveColumnsPerInhArea=40,
               synPermProximalInc=0.1,
               synPermProximalDec=0.001,
               initialProximalPermanence=0.6,
               columnDimensions=(2048,),
               activationThreshold=13,
               minThreshold=10,
               initialPermanence=0.41,
               connectedPermanence=0.50,
               maxNewSynapseCount=20,
               permanenceIncrement=0.10,
               permanenceDecrement=0.10,
               predictedSegmentDecrement=0.0,
               maxSegmentsPerCell=255,
               maxSynapsesPerSegment=255,
               seed=42):
    """
    This classes uses an ExtendedTemporalMemory internally to keep track of
    distal segments. Please see ExtendedTemporalMemory for descriptions of
    constructor parameters not defined below.

    Parameters:
    ----------------------------
    @param  inputWidth (int)
            The number of proximal inputs into this layer

    @param  numActiveColumnsPerInhArea (int)
            Target number of active cells

    @param  synPermProximalInc (float)
            Permanence increment for proximal synapses

    @param  synPermProximalDec (float)
            Permanence decrement for proximal synapses

    @param  initialProximalPermanence (float)
            Initial permanence value for proximal segments

    """

    self.inputWidth = inputWidth
    self.numActiveColumnsPerInhArea = numActiveColumnsPerInhArea
    self.synPermProximalInc = synPermProximalInc
    self.synPermProximalDec = synPermProximalDec
    self.initialProximalPermanence = initialProximalPermanence
    self.connectedPermanence = connectedPermanence
    self.maxNewSynapseCount = maxNewSynapseCount
    self.minThreshold = minThreshold
    self.activeCells = set()
    self._random = Random(seed)

    # Create our own instance of extended temporal memory to handle distal
    # segments.
    self.tm = createModel(
                      modelName="extendedCPP",
                      columnDimensions=columnDimensions,
                      cellsPerColumn=1,
                      activationThreshold=activationThreshold,
                      initialPermanence=initialPermanence,
                      connectedPermanence=connectedPermanence,
                      minThreshold=minThreshold,
                      maxNewSynapseCount=maxNewSynapseCount,
                      permanenceIncrement=permanenceIncrement,
                      permanenceDecrement=permanenceDecrement,
                      predictedSegmentDecrement=predictedSegmentDecrement,
                      maxSegmentsPerCell=maxSegmentsPerCell,
                      maxSynapsesPerSegment=maxSynapsesPerSegment,
                      seed=seed,
                      learnOnOneCell=False,
    )

    # These sparse matrices will hold the synapses for each proximal segment.
    #
    # proximalPermanences - SparseMatrix with permanence values
    # proximalConnections - SparseBinaryMatrix of connected synapses

    self.proximalPermanences = SparseMatrix(self.numberOfColumns(),
                                               inputWidth)
    self.proximalConnections = SparseBinaryMatrix(inputWidth)
    self.proximalConnections.resize(self.numberOfColumns(), inputWidth)



  def compute(self,
              feedforwardInput=None,
              activeExternalCells=None,
              learn=True):
    """
    Parameters:
    ----------------------------
    @param  feedforwardInput     (set)
            Indices of active input bits

    @param  activeExternalCells  (set)
            Indices of active cells that will form connections to distal
            segments.

    @param learn                    (bool)
            If True, we are learning a new object
    """
    if activeExternalCells is None:
      activeExternalCells = set()

    if learn:
      self._computeLearningMode(feedforwardInput=feedforwardInput,
                               lateralInput=activeExternalCells)

    else:
      self._computeInferenceMode(feedforwardInput=feedforwardInput,
                                 lateralInput=activeExternalCells)


  def _computeLearningMode(self, feedforwardInput, lateralInput):
    """
    Learning mode: we are learning a new object. If there is no prior
    activity, we randomly activate 2% of cells and create connections to
    incoming input. If there was prior activity, we maintain it.

    These cells will represent the object and learn distal connections to
    lateral cortical columns.

    Parameters:
    ----------------------------
    @param  feedforwardInput (set)
            Indices of active input bits

    @param  lateralInput (set)
            Indices of active cells from neighboring columns.
    """
    # If there are no previously active cells, select random subset of cells
    if len(self.activeCells) == 0:
      self.activeCells = set(self._random.shuffle(
            numpy.array(range(self.numberOfCells()),
                        dtype="uint32"))[0:self.numActiveColumnsPerInhArea])

    # else we maintain previous activity, nothing to do.

    # Those cells that remain active will learn on their proximal and distal
    # dendrites as long as there is some input.  If there are no
    # cells active, no learning happens.  This only happens in the very
    # beginning if there has been no bottom up activity at all.
    if len(self.activeCells) > 0:

      # Learn on proximal dendrite if appropriate
      if len(feedforwardInput) > 0:
        self._learnProximal(feedforwardInput, self.activeCells,
                            self.maxNewSynapseCount, self.proximalPermanences,
                            self.proximalConnections,
                            self.initialProximalPermanence,
                            self.synPermProximalInc, self.synPermProximalDec,
                            self.connectedPermanence)

      # Learn on distal dendrites if appropriate
      self.tm.compute(activeColumns=self.activeCells,
                      activeExternalCells=lateralInput,
                      formInternalConnections=False,
                      learn=True)


  def _computeInferenceMode(self, feedforwardInput, lateralInput):
    """
    Inference mode: if there is some feedforward activity, perform
    spatial pooling on it to recognize previously known objects. If there
    is no feedforward activity, maintain previous activity.

    Parameters:
    ----------------------------
    @param  feedforwardInput (set)
            Indices of active input bits

    @param  lateralInput (list of lists)
            A list of list of active cells from neighboring columns.
            len(lateralInput) == number of connected neighboring cortical
            columns.

    """
    # Figure out which cells are active due to feedforward proximal inputs
    # In order to form unions, we keep all cells that are over threshold
    inputVector = numpy.zeros(self.numberOfInputs(), dtype=realDType)
    inputVector[list(feedforwardInput)] = 1
    overlaps = numpy.zeros(self.numberOfColumns(), dtype=realDType)
    self.proximalConnections.rightVecSumAtNZ_fast(inputVector.astype(realDType),
                                                 overlaps)
    overlaps[overlaps < self.minThreshold] = 0
    bottomUpActivity =  set(overlaps.nonzero()[0])

    # If there is insufficient current bottom up activity, we incorporate all
    # previous activity. We set their overlaps so they are sure to win.
    if len(bottomUpActivity) < self.numActiveColumnsPerInhArea:
      bottomUpActivity = bottomUpActivity.union(self.activeCells)
      maxOverlap = overlaps.max()
      overlaps[self.getActiveCells()] = maxOverlap+1

    # Narrow down list of active cells based on lateral activity
    self.activeCells = self._winnersBasedOnLateralActivity(
      bottomUpActivity,
      self.getPredictiveCells(),
      overlaps,
      self.numActiveColumnsPerInhArea
    )

    # Update predictive cells for next time step
    self.tm.compute(activeColumns=self.activeCells,
                    activeExternalCells=lateralInput,
                    formInternalConnections=False,
                    learn=False)


  def numberOfInputs(self):
    """
    Returns the number of inputs into this layer
    """
    return self.inputWidth


  def numberOfColumns(self):
    """
    Returns the number of columns in this layer.
    @return (int) Number of columns
    """
    return self.tm.numberOfColumns()


  def numberOfCells(self):
    """
    Returns the number of cells in this layer.
    @return (int) Number of cells
    """
    return self.tm.numberOfCells()


  def getActiveCells(self):
    """
    Returns the indices of the active cells.
    @return (set) Indices of active cells.
    """
    return self.getCellIndices(self.activeCells)


  @classmethod
  def getCellIndices(cls, cells):
    return [cls.getCellIndex(c) for c in cells]


  @staticmethod
  def getCellIndex(cell):
    return cell


  def numberOfConnectedSynapses(self, cells=None):
    """
    Returns the number of proximal connected synapses on these cells.

    Parameters:
    ----------------------------
    @param  cells (set or list)
            Indices of the cells. If None return count for all cells.
    """
    if cells is None:
      cells = xrange(self.numberOfCells())
    n = 0
    for cell in cells:
      n += self.proximalConnections.nNonZerosOnRow(cell)
    return n


  def numberOfSynapses(self, cells=None):
    """
    Returns the number of proximal synapses with permanence>0 on these cells.

    Parameters:
    ----------------------------
    @param  cells (set or list)
            Indices of the cells. If None return count for all cells.
    """
    if cells is None:
      cells = xrange(self.numberOfCells())
    n = 0
    for cell in cells:
      n += self.proximalPermanences.nNonZerosOnRow(cell)
    return n


  def numberOfDistalSegments(self, cells):
    """
    Returns the total number of distal segments for these cells.

    Parameters:
    ----------------------------
    @param  cells (set or list)
            Indices of the cells
    """
    n = 0
    for cell in cells:
      n += len(self.tm.connections.segmentsForCell(cell))
    return n


  def numberOfDistalSynapses(self, cells):
    """
    Returns the total number of distal synapses for these cells.

    Parameters:
    ----------------------------
    @param  cells (set or list)
            Indices of the cells
    """
    n = 0
    for cell in cells:
      segments = self.tm.connections.segmentsForCell(cell)
      for segment in segments:
        n += len(self.tm.connections.synapsesForSegment(segment))
    return n


  def reset(self):
    """
    Reset internal states. When learning this signifies we are to learn a
    unique new object.
    """
    self.activeCells = set()
    self.tm.reset()


  def getPredictiveCells(self):
    """
    Get the set of distally predictive cells as a set.

    @return (list) A list containing indices of the current distally predicted
    cells.
    """
    return self.tm.getPredictiveCells()


  def getPredictedActiveCells(self):
    """
    Get the set of cells that were predicted previously then became active

    @return (set) A set containing indices.
    """
    return self.tm.predictedActiveCellsIndices()


  def getConnections(self):
    """
    Get the Connections structure associated with our TM. Beware of using
    this as it is implementation specific and may change.

    @return (object) A Connections object
    """
    return self.tm.connections


  def _learnProximal(self,
             activeInputs, activeCells, maxNewSynapseCount, proximalPermanences,
             proximalConnections, initialPermanence, synPermProximalInc,
             synPermProximalDec, connectedPermanence):
    """
    Learn on proximal dendrites of active cells.  Updates proximalPermanences
    """
    for cell in activeCells:
      cellPermanencesDense = proximalPermanences.getRow(cell)
      cellNonZeroIndices, _ = proximalPermanences.rowNonZeros(cell)
      cellNonZeroIndices = list(cellNonZeroIndices)

      # Get new and existing connections for this segment
      newInputs, existingInputs = self._pickProximalInputsToLearnOn(
        maxNewSynapseCount, activeInputs, cellNonZeroIndices
      )

      # Adjust existing connections appropriately
      # First we decrement all existing permanences
      if len(cellNonZeroIndices) > 0:
        cellPermanencesDense[cellNonZeroIndices] -= synPermProximalDec

      # Then we add inc + dec to existing active synapses
      if len(existingInputs) > 0:
        cellPermanencesDense[existingInputs] += synPermProximalInc + synPermProximalDec

      # Add new connections
      if len(newInputs) > 0:
        cellPermanencesDense[newInputs] += initialPermanence

      # Update proximalPermanences and proximalConnections
      proximalPermanences.setRowFromDense(cell, cellPermanencesDense)
      newConnected = numpy.where(cellPermanencesDense >= connectedPermanence)[0]
      proximalConnections.replaceSparseRow(cell, newConnected)



  def _pickProximalInputsToLearnOn(self, newSynapseCount, activeInputs,
                                  cellNonZeros):
    """
    Pick inputs to form proximal connections to a particular cell. We just
    randomly subsample from activeInputs, regardless of whether they are already
    connected.

    We return a list of up to newSynapseCount input indices from activeInputs
    that are valid new connections for this cell. We also return a list
    containing all inputs in activeInputs that are already connected to this
    cell.

    Parameters:
    ----------------------------
    @param newSynapseCount  (int)        Number of inputs to pick
    @param cell             (int)        Cell index
    @param activeInputs     (set)        Indices of active inputs
    @param cellNonZeros     (list)       Indices of inputs input this cell with
                                         non-zero permanences.

    @return (list, list) Indices of new inputs to connect to, inputs already
                         connected
    """
    candidates = []
    alreadyConnected = []

    # Collect inputs that already have synapses and list of new candidate inputs
    for inputIdx in activeInputs:
      if inputIdx in cellNonZeros:
        alreadyConnected += [inputIdx]
      else:
        candidates += [inputIdx]

    # Select min(newSynapseCount, len(candidates)) new inputs to connect to
    if newSynapseCount >= len(candidates):
      return candidates, alreadyConnected

    else:
      # Pick newSynapseCount cells randomly
      # TODO: we could maybe implement this more efficiently with shuffle.
      inputs = []
      for _ in range(newSynapseCount):
        i = self._random.getUInt32(len(candidates))
        inputs += [candidates[i]]
        candidates.remove(candidates[i])

      return inputs, alreadyConnected


  def _winnersBasedOnLateralActivity(self,
                                     activeCells,
                                     predictiveCells,
                                     overlaps,
                                     targetActiveCells):
    """
    Given the set of cells active due to feedforward input, narrow down the
    list of active cells based on predictions due to previous lateralInput.

    Parameters:
    ----------------------------
    @param    activeCells           (set)
              Indices of cells activated by bottom-up input.

    @param    predictiveCells       (set)
              Indices of cells that are laterally predicted.

    @param    overlaps              (numpy array)
              Bottom up overlap scores for each proximal segment. This is used
              to select additional cells if the narrowed down list contains less
              than targetActiveCells.

    @param    targetActiveCells     (int)
              The number of active cells we want to have active.

    @return (set) List of new winner cell indices

    """
    # No TM accessors that return set so access internal member directly
    predictedActiveCells = activeCells.intersection(predictiveCells)

    # If predicted cells don't intersect at all with active cells, we go with
    # bottom up input. In these cases we can stick with existing active cells
    # and skip the overlap sorting
    if len(predictedActiveCells) == 0:
      predictedActiveCells = activeCells

    # We want to keep all cells that were predicted and currently active due to
    # feedforward input. This set could be larger than our target number of
    # active cells due to unions, which is ok. However if there are insufficient
    # cells active after this intersection, we fill in with those currently
    # active cells that have highest overlap.
    elif len(predictedActiveCells) < targetActiveCells:
      # Don't want to consider cells already chosen
      overlaps[list(predictedActiveCells)] = 0

      # Add in the desired number of cells with highest activity
      numActive = targetActiveCells - len(predictedActiveCells)
      winnerIndices = numpy.argsort(overlaps, kind='mergesort')
      sortedWinnerIndices = winnerIndices[-numActive:][::-1]
      predictedActiveCells = predictedActiveCells.union(set(sortedWinnerIndices))

    return predictedActiveCells
コード例 #6
0
class ColumnPooler(object):
  """
  This class constitutes a temporary implementation for a cross-column pooler.
  The implementation goal of this class is to prove basic properties before
  creating a cleaner implementation.
  """

  def __init__(self,
               inputWidth,
               lateralInputWidth,
               numActiveColumnsPerInhArea=40,
               synPermProximalInc=0.1,
               synPermProximalDec=0.001,
               initialProximalPermanence=0.6,
               columnDimensions=(2048,),
               minThresholdProximal=10,
               activationThresholdDistal=13,
               minThresholdDistal=10,
               initialPermanence=0.41,
               connectedPermanence=0.50,
               maxNewProximalSynapseCount=20,
               maxNewDistalSynapseCount=20,
               permanenceIncrement=0.10,
               permanenceDecrement=0.10,
               predictedSegmentDecrement=0.0,
               maxSegmentsPerCell=255,
               maxSynapsesPerProximalSegment=255,
               maxSynapsesPerDistalSegment=255,
               seed=42):
    """
    This classes uses an ExtendedTemporalMemory internally to keep track of
    distal segments. Please see ExtendedTemporalMemory for descriptions of
    constructor parameters not defined below.

    Parameters:
    ----------------------------
    @param  inputWidth (int)
            The number of proximal inputs into this layer

    @param  lateralInputWidth (int)
            The number of lateral inputs into this layer

    @param  numActiveColumnsPerInhArea (int)
            Target number of active cells

    @param  synPermProximalInc (float)
            Permanence increment for proximal synapses

    @param  synPermProximalDec (float)
            Permanence decrement for proximal synapses

    @param  initialProximalPermanence (float)
            Initial permanence value for proximal segments

    """

    self.inputWidth = inputWidth
    self.lateralInputWidth = lateralInputWidth
    self.numActiveColumnsPerInhArea = numActiveColumnsPerInhArea
    self.synPermProximalInc = synPermProximalInc
    self.synPermProximalDec = synPermProximalDec
    self.initialProximalPermanence = initialProximalPermanence
    self.connectedPermanence = connectedPermanence
    self.maxNewProximalSynapseCount = maxNewProximalSynapseCount
    self.maxNewDistalSynapseCount = maxNewDistalSynapseCount
    self.minThresholdProximal = minThresholdProximal
    self.minThresholdDistal = minThresholdDistal
    self.maxSynapsesPerProximalSegment = maxSynapsesPerProximalSegment
    self.activeCells = set()
    self._random = Random(seed)

    # Create our own instance of extended temporal memory to handle distal
    # segments.
    self.tm = createModel(
                      modelName="etm_cpp",
                      columnDimensions=columnDimensions,
                      basalInputDimensions=(lateralInputWidth,),
                      apicalInputDimensions=(),
                      cellsPerColumn=1,
                      activationThreshold=activationThresholdDistal,
                      initialPermanence=initialPermanence,
                      connectedPermanence=connectedPermanence,
                      minThreshold=minThresholdDistal,
                      maxNewSynapseCount=maxNewDistalSynapseCount,
                      permanenceIncrement=permanenceIncrement,
                      permanenceDecrement=permanenceDecrement,
                      predictedSegmentDecrement=predictedSegmentDecrement,
                      formInternalBasalConnections=False,
                      learnOnOneCell=False,
                      maxSegmentsPerCell=maxSegmentsPerCell,
                      maxSynapsesPerSegment=maxSynapsesPerDistalSegment,
                      seed=seed,
    )

    # These sparse matrices will hold the synapses for each proximal segment.
    #
    # proximalPermanences - SparseMatrix with permanence values
    # proximalConnections - SparseBinaryMatrix of connected synapses

    self.proximalPermanences = SparseMatrix(self.numberOfColumns(),
                                               inputWidth)
    self.proximalConnections = SparseBinaryMatrix(inputWidth)
    self.proximalConnections.resize(self.numberOfColumns(), inputWidth)


  def depolarizeCells(self, activeExternalCells, learn=True):
    """
    Parameters:
    ----------------------------
    @param  activeExternalCells  (set)
            Indices of active cells that will form connections to distal
            segments.

    @param  learn (bool)
            If true, distal segment activations will be recorded. This
            information is used during segment cleanup.

    """
    self.tm.depolarizeCells(activeCellsExternalBasal=activeExternalCells,
                            learn=learn)


  def activateCells(self,
                    feedforwardInput=(),
                    reinforceCandidatesExternal=(),
                    growthCandidatesExternal=(),
                    learn=True):
    """

    @param  feedforwardInput (set)
            Indices of active input bits

    @param  reinforceCandidatesExternal (set)
            Indices of active cells that will reinforce synapses to distal
            segments.

    @param  growthCandidatesExternal  (set)
            Indices of active cells that will grow synapses to distal segments.

    @param learn                    (bool)
            If True, we are learning a new object
    """
    if learn:
      self._activateCellsLearningMode(feedforwardInput,
                                      reinforceCandidatesExternal,
                                      growthCandidatesExternal)
    else:
      self._activateCellsInferenceMode(feedforwardInput)


  def compute(self, feedforwardInput=(), lateralInput=(), learn=True):
    """
    Runs one time step of the column pooler algorithm.

    This method assumes:

     - Lateral input should trigger predictions for this time step, i.e. for
       this feedforward input.
     - During learning, all lateral input is eligible for growth and
       reinforcement.

    If these are bad assumptions, use depolarizeCells and activateCells
    directly.

    @param  feedforwardInput (set)
            Indices of active feedforward input bits

    @param  lateralInput  (set)
            Indices of active lateral input bits

    @param learn                    (bool)
            If True, we are learning a new object
    """
    self.depolarizeCells(lateralInput, learn)
    self.activateCells(feedforwardInput, lateralInput, lateralInput, learn)


  def _activateCellsLearningMode(self,
                                 feedforwardInput,
                                 reinforceCandidatesExternal,
                                 growthCandidatesExternal):
    """
    Learning mode: we are learning a new object. If there is no prior
    activity, we randomly activate 2% of cells and create connections to
    incoming input. If there was prior activity, we maintain it.

    These cells will represent the object and learn distal connections to
    lateral cortical columns.

    Parameters:
    ----------------------------
    @param  feedforwardInput (set)
            Indices of active input bits

    @param  lateralInput (set)
            Indices of active cells from neighboring columns.
    """
    # If there are no previously active cells, select random subset of cells
    if len(self.activeCells) == 0:
      self.activeCells = set(self._random.shuffle(
            numpy.array(range(self.numberOfCells()),
                        dtype="uint32"))[0:self.numActiveColumnsPerInhArea])

    # else we maintain previous activity, nothing to do.

    # Those cells that remain active will learn on their proximal and distal
    # dendrites as long as there is some input.  If there are no
    # cells active, no learning happens.  This only happens in the very
    # beginning if there has been no bottom up activity at all.
    if len(self.activeCells) > 0:

      # Learn on proximal dendrite if appropriate
      if len(feedforwardInput) > 0:
        self._learnProximal(feedforwardInput, self.activeCells,
                            self.maxNewProximalSynapseCount,
                            self.proximalPermanences,
                            self.proximalConnections,
                            self.initialProximalPermanence,
                            self.synPermProximalInc, self.synPermProximalDec,
                            self.connectedPermanence)

      # Learn on distal dendrites if appropriate
      self.tm.activateCells(
        activeColumns=sorted(self.activeCells),
        reinforceCandidatesExternalBasal=sorted(reinforceCandidatesExternal),
        growthCandidatesExternalBasal=sorted(growthCandidatesExternal),
        learn=True)


  def _activateCellsInferenceMode(self, feedforwardInput):
    """
    Inference mode: if there is some feedforward activity, perform
    spatial pooling on it to recognize previously known objects. If there
    is no feedforward activity, maintain previous activity.

    Parameters:
    ----------------------------
    @param  feedforwardInput (set)
            Indices of active input bits
    """
    # Figure out which cells are active due to feedforward proximal inputs
    # In order to form unions, we keep all cells that are over threshold
    inputVector = numpy.zeros(self.numberOfInputs(), dtype=realDType)
    inputVector[list(feedforwardInput)] = 1
    overlaps = numpy.zeros(self.numberOfColumns(), dtype=realDType)
    self.proximalConnections.rightVecSumAtNZ_fast(inputVector.astype(realDType),
                                                 overlaps)
    overlaps[overlaps < self.minThresholdProximal] = 0
    bottomUpActivity =  set(overlaps.nonzero()[0])

    # If there is insufficient current bottom up activity, we incorporate all
    # previous activity. We set their overlaps so they are sure to win.
    if len(bottomUpActivity) < self.numActiveColumnsPerInhArea:
      bottomUpActivity = bottomUpActivity.union(self.activeCells)
      maxOverlap = overlaps.max()
      overlaps[self.getActiveCells()] = maxOverlap+1

    # Narrow down list of active cells based on lateral activity
    self.activeCells = self._winnersBasedOnLateralActivity(
      bottomUpActivity,
      self.getPredictiveCells(),
      overlaps,
      self.numActiveColumnsPerInhArea
    )

    # Update the active cells in the TM. Without learning and without internal
    # basal connections, this has no effect on column pooler output.
    self.tm.activateCells(activeColumns=sorted(self.activeCells),
                          learn=False)


  def numberOfInputs(self):
    """
    Returns the number of inputs into this layer
    """
    return self.inputWidth


  def numberOfColumns(self):
    """
    Returns the number of columns in this layer.
    @return (int) Number of columns
    """
    return self.tm.numberOfColumns()


  def numberOfCells(self):
    """
    Returns the number of cells in this layer.
    @return (int) Number of cells
    """
    return self.tm.numberOfCells()


  def getActiveCells(self):
    """
    Returns the indices of the active cells.
    @return (list) Indices of active cells.
    """
    return list(self.activeCells)


  def numberOfConnectedSynapses(self, cells=None):
    """
    Returns the number of proximal connected synapses on these cells.

    Parameters:
    ----------------------------
    @param  cells (set or list)
            Indices of the cells. If None return count for all cells.
    """
    if cells is None:
      cells = xrange(self.numberOfCells())
    n = 0
    for cell in cells:
      n += self.proximalConnections.nNonZerosOnRow(cell)
    return n


  def numberOfSynapses(self, cells=None):
    """
    Returns the number of proximal synapses with permanence>0 on these cells.

    Parameters:
    ----------------------------
    @param  cells (set or list)
            Indices of the cells. If None return count for all cells.
    """
    if cells is None:
      cells = xrange(self.numberOfCells())
    n = 0
    for cell in cells:
      n += self.proximalPermanences.nNonZerosOnRow(cell)
    return n


  def numberOfDistalSegments(self, cells):
    """
    Returns the total number of distal segments for these cells.

    Parameters:
    ----------------------------
    @param  cells (set or list)
            Indices of the cells
    """
    n = 0
    for cell in cells:
      n += self.tm.basalConnections.numSegments(cell)
    return n


  def numberOfDistalSynapses(self, cells):
    """
    Returns the total number of distal synapses for these cells.

    Parameters:
    ----------------------------
    @param  cells (set or list)
            Indices of the cells
    """
    n = 0
    for cell in cells:
      segments = self.tm.basalConnections.segmentsForCell(cell)
      for segment in segments:
        n += self.tm.basalConnections.numSynapses(segment)
    return n


  def reset(self):
    """
    Reset internal states. When learning this signifies we are to learn a
    unique new object.
    """
    self.activeCells = set()
    self.tm.reset()


  def getPredictiveCells(self):
    """
    Get the set of distally predictive cells as a set.

    @return (list) A list containing indices of the current distally predicted
    cells.
    """
    return self.tm.getPredictiveCells()


  def getPredictedActiveCells(self):
    """
    Get the set of cells that were predicted previously then became active

    @return (set) A set containing indices.
    """
    return self.tm.predictedActiveCellsIndices()


  def getConnections(self):
    """
    Get the Connections structure associated with our TM. Beware of using
    this as it is implementation specific and may change.

    @return (object) A Connections object
    """
    return self.tm.basalConnections


  def _learnProximal(self,
             activeInputs, activeCells, maxNewSynapseCount, proximalPermanences,
             proximalConnections, initialPermanence, synPermProximalInc,
             synPermProximalDec, connectedPermanence):
    """
    Learn on proximal dendrites of active cells.  Updates proximalPermanences
    """
    for cell in activeCells:
      cellPermanencesDense = proximalPermanences.getRow(cell)
      cellNonZeroIndices, _ = proximalPermanences.rowNonZeros(cell)
      cellNonZeroIndices = set(cellNonZeroIndices)

      # Find the synapses that should be reinforced, punished, and grown.
      reinforce = list(activeInputs & cellNonZeroIndices)
      punish = list(cellNonZeroIndices - activeInputs)
      growthCandidates = activeInputs - cellNonZeroIndices
      newSynapseCount = min(len(growthCandidates), maxNewSynapseCount)
      grow = _sample(growthCandidates, newSynapseCount, self._random)

      # Make the changes.
      cellPermanencesDense[punish] -= synPermProximalDec
      cellPermanencesDense[reinforce] += synPermProximalInc
      cellPermanencesDense[grow] = initialPermanence

      # Update proximalPermanences and proximalConnections.
      proximalPermanences.setRowFromDense(cell, cellPermanencesDense)
      newConnected = numpy.where(cellPermanencesDense >= connectedPermanence)[0]
      proximalConnections.replaceSparseRow(cell, newConnected)


  def _winnersBasedOnLateralActivity(self,
                                     activeCells,
                                     predictiveCells,
                                     overlaps,
                                     targetActiveCells):
    """
    Given the set of cells active due to feedforward input, narrow down the
    list of active cells based on predictions due to previous lateralInput.

    Parameters:
    ----------------------------
    @param    activeCells           (set)
              Indices of cells activated by bottom-up input.

    @param    predictiveCells       (set)
              Indices of cells that are laterally predicted.

    @param    overlaps              (numpy array)
              Bottom up overlap scores for each proximal segment. This is used
              to select additional cells if the narrowed down list contains less
              than targetActiveCells.

    @param    targetActiveCells     (int)
              The number of active cells we want to have active.
    @return (set) List of new winner cell indices
    """
    # No TM accessors that return set so access internal member directly
    predictedActiveCells = activeCells.intersection(predictiveCells)

    # If predicted cells don't intersect at all with active cells, we go with
    # bottom up input. In these cases we can stick with existing active cells
    # and skip the overlap sorting
    if len(predictedActiveCells) == 0:
      predictedActiveCells = activeCells

    # We want to keep all cells that were predicted and currently active due to
    # feedforward input. This set could be larger than our target number of
    # active cells due to unions, which is ok. However if there are insufficient
    # cells active after this intersection, we fill in with those currently
    # active cells that have highest overlap.
    elif len(predictedActiveCells) < targetActiveCells:
      # Don't want to consider cells already chosen
      overlaps[list(predictedActiveCells)] = 0

      # Add in the desired number of cells with highest activity
      numActive = targetActiveCells - len(predictedActiveCells)
      winnerIndices = numpy.argsort(overlaps, kind='mergesort')
      sortedWinnerIndices = winnerIndices[-numActive:][::-1]
      predictedActiveCells = predictedActiveCells.union(set(sortedWinnerIndices))

    return predictedActiveCells
コード例 #7
0
class ColumnPooler(ExtendedTemporalMemory):
  """
  This class constitutes a temporary implementation for a cross-column pooler.
  The implementation goal of this class is to prove basic properties before
  creating a cleaner implementation.
  """

  def __init__(self,
               inputWidth,
               numActiveColumnsPerInhArea=40,
               synPermProximalInc=0.1,
               synPermProximalDec=0.001,
               initialProximalPermanence=0.6,
               **kwargs):
    """
    Please see ExtendedTemporalMemory for descriptions of common constructor
    parameters.

    Parameters:
    ----------------------------
    @param  inputWidth (int)
            The number of proximal inputs into this layer

    @param  numActiveColumnsPerInhArea (int)
            Number of active cells

    @param  synPermProximalInc (float)
            Permanence increment for proximal synapses

    @param  synPermProximalDec (float)
            Permanence decrement for proximal synapses

    @param  initialProximalPermanence (float)
            Initial permanence value for proximal segments
    """

    # Override: we only support one cell per column for now
    kwargs['cellsPerColumn'] = 1
    super(ColumnPooler, self).__init__(**kwargs)

    self.inputWidth = inputWidth
    self.numActiveColumnsPerInhArea = numActiveColumnsPerInhArea
    self.synPermProximalInc = synPermProximalInc
    self.synPermProximalDec = synPermProximalDec
    self.initialProximalPermanence = initialProximalPermanence
    self.previousOverlaps = None

    # These sparse matrices will hold the synapses for each proximal segment.
    #
    # proximalPermanences - SparseMatrix with permanence values
    # proximalConnections - SparseBinaryMatrix of connected synapses


    self.proximalPermanences = SparseMatrix(self.numberOfColumns(),
                                               inputWidth)
    self.proximalConnections = SparseBinaryMatrix(inputWidth)
    self.proximalConnections.resize(self.numberOfColumns(), inputWidth)


  def compute(self,
              feedforwardInput=None,
              activeExternalCells=None,
              activeApicalCells=None,
              formInternalConnections=True,
              learn=True):
    """

    Parameters:
    ----------------------------
    @param  feedforwardInput     (set)
            Indices of active input bits

    @param  activeExternalCells  (set)
            Indices of active cells that will form connections to distal
            segments.

    @param  activeApicalCells (set)
            Indices of active cells that will form connections to apical
            segments.

    @param  formInternalConnections (bool)
            If True, cells will form

    @param learn                If True, we are learning a new object

    """
    if activeExternalCells is None:
      activeExternalCells = set()

    if activeApicalCells is None:
      activeApicalCells = set()

    if learn:
      self._computeLearningMode(feedforwardInput=feedforwardInput,
                               lateralInput=activeExternalCells)

    else:
      self._computeInferenceMode(feedforwardInput=feedforwardInput,
                                 lateralInput=activeExternalCells)


  def _computeLearningMode(self, feedforwardInput, lateralInput):
    """
    Learning mode: we are learning a new object. If there is no prior
    activity, we randomly activate 2% of cells and create connections to
    incoming input. If there was prior activity, we maintain it.

    These cells will represent the object and learn distal connections to
    lateral cortical columns.

    Parameters:
    ----------------------------
    @param  feedforwardInput (set)
            Indices of active input bits

    @param  lateralInput (list of lists)
            A list of list of active cells from neighboring columns.
            len(lateralInput) == number of connected neighboring cortical
            columns.

    """
    # If there are no previously active cells, select random subset of cells
    if len(self.activeCells) == 0:
      self.activeCells = set(self._random.shuffle(
            numpy.array(range(self.numberOfCells()),
                        dtype="uint32"))[0:self.numActiveColumnsPerInhArea])

    # else we maintain previous activity, nothing to do.

    # Incorporate distal segment activity and update list of active cells
    self.activeCells = self._winnersBasedOnLateralActivity(
      self.activeCells, lateralInput, self.minThreshold
    )

    # Those cells that remain active will learn on their proximal and distal
    # dendrites as long as there is some input.  If there are no
    # cells active, no learning happens.
    if len(self.activeCells) > 0:

      # Learn on proximal dendrite if appropriate
      if len(feedforwardInput) > 0:
        self._learnProximal(feedforwardInput, self.activeCells,
                            self.maxNewSynapseCount, self.proximalPermanences,
                            self.proximalConnections,
                            self.initialProximalPermanence,
                            self.synPermProximalInc, self.synPermProximalDec,
                            self.connectedPermanence)

      # Learn on distal dendrites if appropriate
      if len(lateralInput) > 0:
        self._learnDistal(lateralInput, self.activeCells)


  def _computeInferenceMode(self, feedforwardInput, lateralInput):
    """
    Inference mode: if there is some feedforward activity, perform
    spatial pooling on it to recognize previously known objects. If there
    is no feedforward activity, maintain previous activity.

    Parameters:
    ----------------------------
    @param  feedforwardInput (set)
            Indices of active input bits

    @param  lateralInput (list of lists)
            A list of list of active cells from neighboring columns.
            len(lateralInput) == number of connected neighboring cortical
            columns.

    """
    # Figure out which cells are active due to feedforward proximal inputs
    inputVector = numpy.zeros(self.numberOfInputs(), dtype=realDType)
    inputVector[list(feedforwardInput)] = 1
    overlaps = numpy.zeros(self.numberOfColumns(), dtype=realDType)
    self.proximalConnections.rightVecSumAtNZ_fast(inputVector.astype(realDType),
                                                 overlaps)
    overlaps[overlaps < self.minThreshold] = 0

    # If there isn't enough bottom up activity, do nothing and maintain previous
    # activity.
    if overlaps.sum() > 0.0:

      # In order to form unions, we keep all cells that are over threshold
      self.activeCells = set(overlaps.nonzero()[0])


  def numberOfInputs(self):
    """
    Returns the number of inputs into this layer
    """
    return self.inputWidth


  def numberOfConnectedSynapses(self, cells):
    """
    Returns the number of connected synapses on these cells.

    Parameters:
    ----------------------------
    @param  cells (set or list)
            Indices of the cells
    """
    n = 0
    for cell in cells:
      n += self.proximalConnections.nNonZerosOnRow(cell)
    return n


  def numberOfSynapses(self, cells):
    """
    Returns the number of synapses with permanence>0 on these cells.

    Parameters:
    ----------------------------
    @param  cells (set or list)
            Indices of the cells
    """
    n = 0
    for cell in cells:
      n += self.proximalPermanences.nNonZerosOnRow(cell)
    return n


  def _learnProximal(self,
             activeInputs, activeCells, maxNewSynapseCount, proximalPermanences,
             proximalConnections, initialPermanence, synPermProximalInc,
             synPermProximalDec, connectedPermanence):
    """
    Learn on proximal dendrites of active cells.  Updates proximalPermanences
    """
    for cell in activeCells:
      cellPermanencesDense = proximalPermanences.getRow(cell)
      cellNonZeroIndices, _ = proximalPermanences.rowNonZeros(cell)
      cellNonZeroIndices = list(cellNonZeroIndices)

      # Get new and existing connections for this segment
      newInputs, existingInputs = self._pickProximalInputsToLearnOn(
        maxNewSynapseCount, activeInputs, cellNonZeroIndices
      )

      # Adjust existing connections appropriately
      # First we decrement all existing permanences
      if len(cellNonZeroIndices) > 0:
        cellPermanencesDense[cellNonZeroIndices] -= synPermProximalDec

      # Then we add inc + dec to existing active synapses
      if len(existingInputs) > 0:
        cellPermanencesDense[existingInputs] += synPermProximalInc + synPermProximalDec

      # Add new connections
      if len(newInputs) > 0:
        cellPermanencesDense[newInputs] += initialPermanence

      # Update proximalPermanences and proximalConnections
      proximalPermanences.setRowFromDense(cell, cellPermanencesDense)
      newConnected = numpy.where(cellPermanencesDense >= connectedPermanence)[0]
      proximalConnections.replaceSparseRow(cell, newConnected)

      cellNonZeroIndices, _ = proximalPermanences.rowNonZeros(cell)


  def _winnersBasedOnLateralActivity(self,
                                     activeCells,
                                     lateralInput,
                                     minThreshold):
    """
    Incorporate effect of lateral activity, if any, and update the set of
    winners.

    UNIMPLEMENTED

    @return (set) list of new winner cell indices
    """
    if len(lateralInput) == 0:
      return activeCells

    sortedWinnerIndices = activeCells

    # Figure out distal input into active cells

    # TODO: Reconcile and select the cells with sufficient bottom up activity
    # plus maximal lateral activity

    # Calculate winners using stable sort algorithm (mergesort)
    # for compatibility with C++
    # if overlaps.max() >= minThreshold:
    #   winnerIndices = numpy.argsort(overlaps, kind='mergesort')
    #   sortedWinnerIndices = winnerIndices[
    #                         -self.numActiveColumnsPerInhArea:][::-1]
    #   sortedWinnerIndices = set(sortedWinnerIndices)

    return sortedWinnerIndices


  def _pickProximalInputsToLearnOn(self, newSynapseCount, activeInputs,
                                  cellNonZeros):
    """
    Pick inputs to form proximal connections to. We just randomly subsample
    from activeInputs, regardless of whether they are already connected.

    We return a list of up to newSynapseCount input indices from activeInputs
    that are valid new connections for this cell. We also return a list
    containing all inputs in activeInputs that are already connected to this
    cell.

    Parameters:
    ----------------------------
    @param newSynapseCount  (int)        Number of inputs to pick
    @param cell             (int)        Cell index
    @param activeInputs     (set)        Indices of active inputs
    @param proximalPermanences (sparse)  The matrix of proximal connections

    @return (list, list) Indices of new inputs to connect to, inputs already
                         connected
    """
    candidates = []
    alreadyConnected = []

    # Collect inputs that already have synapses and list of new candidate inputs
    for inputIdx in activeInputs:
      if inputIdx in cellNonZeros:
        alreadyConnected += [inputIdx]
      else:
        candidates += [inputIdx]

    # Select min(newSynapseCount, len(candidates)) new inputs to connect to
    if newSynapseCount >= len(candidates):
      return candidates, alreadyConnected

    else:
      # Pick newSynapseCount cells randomly
      # TODO: we could maybe implement this more efficiently with shuffle.
      inputs = []
      for _ in range(newSynapseCount):
        i = self._random.getUInt32(len(candidates))
        inputs += [candidates[i]]
        candidates.remove(candidates[i])

      # print "number of new candidates:",len(inputs)

      return inputs, alreadyConnected


  def _learnDistal(self, lateralInput, activeCells):
    pass