Beispiel #1
0
    def grow(self, rows, cols):
        """
    Grows the histogram to have rows rows and cols columns.
    Must not have been initialized before, or already have the same
    number of columns.
    If rows is smaller than the current number of rows,
    does not shrink.
    Also updates the sizes of the row and column sums.

    :param rows: Integer number of rows.
    :param cols: Integer number of columns.
    """
        if not self.hist_:
            self.hist_ = SparseMatrix(rows, cols)
            self.rowSums_ = numpy.zeros(rows, dtype=dtype)
            self.colSums_ = numpy.zeros(cols, dtype=dtype)
            self.hack_ = None
        else:
            oldRows = self.hist_.nRows()
            oldCols = self.hist_.nCols()
            nextRows = max(oldRows, rows)
            nextCols = max(oldCols, cols)
            if (oldRows < nextRows) or (oldCols < nextCols):
                self.hist_.resize(nextRows, nextCols)
                if oldRows < nextRows:
                    oldSums = self.rowSums_
                    self.rowSums_ = numpy.zeros(nextRows, dtype=dtype)
                    self.rowSums_[0:len(oldSums)] = oldSums
                    self.hack_ = None
                if oldCols < nextCols:
                    oldSums = self.colSums_
                    self.colSums_ = numpy.zeros(nextCols, dtype=dtype)
                    self.colSums_[0:len(oldSums)] = oldSums
                    self.hack_ = None
def countBitFrequenciesForTerms(client, tokens):
  # Accumulate counts by inplace-adding sparse matrices
  counts = SparseMatrix()
  width = RETINA_SIZES[client.retina]["width"]
  height = RETINA_SIZES[client.retina]["height"]
  counts.resize(1, width*height)

  # Pre-allocate buffer sparse matrix
  sparseBitmap = SparseMatrix()
  sparseBitmap.resize(1, width*height)

  for term in tokens:
    term = term.strip()

    try:
      bitmap = client.getBitmap(term)["fingerprint"]["positions"]
    except Exception as err:
      print "Skipping '{}', reason: {}".format(term, str(err))
      continue

    if not bitmap:
      print "Skipping '{}', reason: empty".format(term)
      continue

    sparseBitmap.setRowFromSparse(0, bitmap, [1]*len(bitmap))
    counts += sparseBitmap

  return counts
Beispiel #3
0
 def clean_outcpd(self):
     """Hack to act like clean_outcpd on zeta1.TopLevelNode.
 Take the max element in each to column, set it to 1, and set all the
 other elements to 0.
 Only called by inferRowMaxProd() and only needed if an updateRow()
 has been called since the last clean_outcpd().
 """
     m = self.hist_.toDense()
     for j in range(m.shape[1]):  # For each column.
         cmax = m[:, j].max()
         if cmax:
             m[:, j] = numpy.array(m[:, j] == cmax, dtype=dtype)
     self.hack_ = SparseMatrix(0, self.hist_.nCols())
     for i in range(m.shape[0]):
         self.hack_.addRow(m[i, :])
Beispiel #4
0
  def grow(self, rows, cols):
    """
    Grows the histogram to have rows rows and cols columns.
    Must not have been initialized before, or already have the same
    number of columns.
    If rows is smaller than the current number of rows,
    does not shrink.
    Also updates the sizes of the row and column sums.

    :param rows: Integer number of rows.
    :param cols: Integer number of columns.
    """
    if not self.hist_:
      self.hist_ = SparseMatrix(rows, cols)
      self.rowSums_ = numpy.zeros(rows, dtype=dtype)
      self.colSums_ = numpy.zeros(cols, dtype=dtype)
      self.hack_ = None
    else:
      oldRows = self.hist_.nRows()
      oldCols = self.hist_.nCols()
      nextRows = max(oldRows, rows)
      nextCols = max(oldCols, cols)
      if (oldRows < nextRows) or (oldCols < nextCols):
        self.hist_.resize(nextRows, nextCols)
        if oldRows < nextRows:
          oldSums = self.rowSums_
          self.rowSums_ = numpy.zeros(nextRows, dtype=dtype)
          self.rowSums_[0:len(oldSums)] = oldSums
          self.hack_ = None
        if oldCols < nextCols:
          oldSums = self.colSums_
          self.colSums_ = numpy.zeros(nextCols, dtype=dtype)
          self.colSums_[0:len(oldSums)] = oldSums
          self.hack_ = None
Beispiel #5
0
class ForwardModel(object):
  """
  Simple forward model. Every cell has a set of synapses. The cell fires when
  its number of active synapses reaches a threshold.
  """

  def __init__(self, cellCount, inputSize, threshold):
    self.permanences = SparseMatrix(cellCount, inputSize)
    self.threshold = threshold
    self.activeCells = np.empty(0, dtype='uint32')

  def associate(self, activeCells, activeInput):
    self.activeCells = activeCells
    self.permanences.setZerosOnOuter(
      self.activeCells, activeInput, 1.0)

  def infer(self, activeInput):
    overlaps = self.permanences.rightVecSumAtNZSparse(activeInput)
    self.activeCells = np.where(overlaps >= self.threshold)[0]
    self.activeCells.sort()
Beispiel #6
0
 def clean_outcpd(self):
   """Hack to act like clean_outcpd on zeta1.TopLevelNode.
   Take the max element in each to column, set it to 1, and set all the
   other elements to 0.
   Only called by inferRowMaxProd() and only needed if an updateRow()
   has been called since the last clean_outcpd().
   """
   m = self.hist_.toDense()
   for j in xrange(m.shape[1]): # For each column.
     cmax = m[:,j].max()
     if cmax:
       m[:,j] = numpy.array(m[:,j] == cmax, dtype=dtype)
   self.hack_ = SparseMatrix(0, self.hist_.nCols())
   for i in xrange(m.shape[0]):
     self.hack_.addRow(m[i,:])
    def __init__(
            self,
            inputWidth,
            lateralInputWidths=(),
            cellCount=4096,
            sdrSize=40,

            # Proximal
            synPermProximalInc=0.1,
            synPermProximalDec=0.001,
            initialProximalPermanence=0.6,
            sampleSizeProximal=20,
            minThresholdProximal=10,
            connectedPermanenceProximal=0.50,

            # Distal
            synPermDistalInc=0.1,
            synPermDistalDec=0.001,
            initialDistalPermanence=0.6,
            sampleSizeDistal=20,
            activationThresholdDistal=13,
            connectedPermanenceDistal=0.50,
            distalSegmentInhibitionFactor=1.001,
            seed=42):
        """
    Parameters:
    ----------------------------
    @param  inputWidth (int)
            The number of bits in the feedforward input

    @param  lateralInputWidths (list of ints)
            The number of bits in each lateral input

    @param  sdrSize (int)
            The number of active cells in an object SDR

    @param  synPermProximalInc (float)
            Permanence increment for proximal synapses

    @param  synPermProximalDec (float)
            Permanence decrement for proximal synapses

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

    @param  sampleSizeProximal (int)
            Number of proximal synapses a cell should grow to each feedforward
            pattern, or -1 to connect to every active bit

    @param  minThresholdProximal (int)
            Number of active synapses required for a cell to have feedforward
            support

    @param  connectedPermanenceProximal (float)
            Permanence required for a proximal synapse to be connected

    @param  synPermDistalInc (float)
            Permanence increment for distal synapses

    @param  synPermDistalDec (float)
            Permanence decrement for distal synapses

    @param  sampleSizeDistal (int)
            Number of distal synapses a cell should grow to each lateral
            pattern, or -1 to connect to every active bit

    @param  initialDistalPermanence (float)
            Initial permanence value for distal synapses

    @param  activationThresholdDistal (int)
            Number of active synapses required to activate a distal segment

    @param  connectedPermanenceDistal (float)
            Permanence required for a distal synapse to be connected

    @param  distalSegmentInhibitionFactor (float)
            The minimum ratio of active dendrite segment counts that will lead
            to inhibition. For example, with value 1.5, cells with 2 active
            segments will be inhibited by cells with 3 active segments, but
            cells with 3 active segments will not be inhibited by cells with 4.

    @param  seed (int)
            Random number generator seed
    """

        assert distalSegmentInhibitionFactor > 0.0

        self.inputWidth = inputWidth
        self.cellCount = cellCount
        self.sdrSize = sdrSize
        self.synPermProximalInc = synPermProximalInc
        self.synPermProximalDec = synPermProximalDec
        self.initialProximalPermanence = initialProximalPermanence
        self.connectedPermanenceProximal = connectedPermanenceProximal
        self.sampleSizeProximal = sampleSizeProximal
        self.minThresholdProximal = minThresholdProximal
        self.synPermDistalInc = synPermDistalInc
        self.synPermDistalDec = synPermDistalDec
        self.initialDistalPermanence = initialDistalPermanence
        self.connectedPermanenceDistal = connectedPermanenceDistal
        self.sampleSizeDistal = sampleSizeDistal
        self.activationThresholdDistal = activationThresholdDistal
        self.distalSegmentInhibitionFactor = distalSegmentInhibitionFactor

        self.activeCells = numpy.empty(0, dtype="uint32")
        self._random = Random(seed)

        # These sparse matrices will hold the synapses for each segment.
        # Each row represents one segment on a cell, so each cell potentially has
        # 1 proximal segment and 1+len(lateralInputWidths) distal segments.
        self.proximalPermanences = SparseMatrix(cellCount, inputWidth)
        self.internalDistalPermanences = SparseMatrix(cellCount, cellCount)
        self.distalPermanences = tuple(
            SparseMatrix(cellCount, n) for n in lateralInputWidths)
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,
            lateralInputWidths=(),
            cellCount=4096,
            sdrSize=40,

            # Proximal
            synPermProximalInc=0.1,
            synPermProximalDec=0.001,
            initialProximalPermanence=0.6,
            sampleSizeProximal=20,
            minThresholdProximal=10,
            connectedPermanenceProximal=0.50,

            # Distal
            synPermDistalInc=0.1,
            synPermDistalDec=0.001,
            initialDistalPermanence=0.6,
            sampleSizeDistal=20,
            activationThresholdDistal=13,
            connectedPermanenceDistal=0.50,
            distalSegmentInhibitionFactor=1.001,
            seed=42):
        """
    Parameters:
    ----------------------------
    @param  inputWidth (int)
            The number of bits in the feedforward input

    @param  lateralInputWidths (list of ints)
            The number of bits in each lateral input

    @param  sdrSize (int)
            The number of active cells in an object SDR

    @param  synPermProximalInc (float)
            Permanence increment for proximal synapses

    @param  synPermProximalDec (float)
            Permanence decrement for proximal synapses

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

    @param  sampleSizeProximal (int)
            Number of proximal synapses a cell should grow to each feedforward
            pattern, or -1 to connect to every active bit

    @param  minThresholdProximal (int)
            Number of active synapses required for a cell to have feedforward
            support

    @param  connectedPermanenceProximal (float)
            Permanence required for a proximal synapse to be connected

    @param  synPermDistalInc (float)
            Permanence increment for distal synapses

    @param  synPermDistalDec (float)
            Permanence decrement for distal synapses

    @param  sampleSizeDistal (int)
            Number of distal synapses a cell should grow to each lateral
            pattern, or -1 to connect to every active bit

    @param  initialDistalPermanence (float)
            Initial permanence value for distal synapses

    @param  activationThresholdDistal (int)
            Number of active synapses required to activate a distal segment

    @param  connectedPermanenceDistal (float)
            Permanence required for a distal synapse to be connected

    @param  distalSegmentInhibitionFactor (float)
            The minimum ratio of active dendrite segment counts that will lead
            to inhibition. For example, with value 1.5, cells with 2 active
            segments will be inhibited by cells with 3 active segments, but
            cells with 3 active segments will not be inhibited by cells with 4.

    @param  seed (int)
            Random number generator seed
    """

        assert distalSegmentInhibitionFactor > 0.0

        self.inputWidth = inputWidth
        self.cellCount = cellCount
        self.sdrSize = sdrSize
        self.synPermProximalInc = synPermProximalInc
        self.synPermProximalDec = synPermProximalDec
        self.initialProximalPermanence = initialProximalPermanence
        self.connectedPermanenceProximal = connectedPermanenceProximal
        self.sampleSizeProximal = sampleSizeProximal
        self.minThresholdProximal = minThresholdProximal
        self.synPermDistalInc = synPermDistalInc
        self.synPermDistalDec = synPermDistalDec
        self.initialDistalPermanence = initialDistalPermanence
        self.connectedPermanenceDistal = connectedPermanenceDistal
        self.sampleSizeDistal = sampleSizeDistal
        self.activationThresholdDistal = activationThresholdDistal
        self.distalSegmentInhibitionFactor = distalSegmentInhibitionFactor

        self.activeCells = numpy.empty(0, dtype="uint32")
        self._random = Random(seed)

        # These sparse matrices will hold the synapses for each segment.
        # Each row represents one segment on a cell, so each cell potentially has
        # 1 proximal segment and 1+len(lateralInputWidths) distal segments.
        self.proximalPermanences = SparseMatrix(cellCount, inputWidth)
        self.internalDistalPermanences = SparseMatrix(cellCount, cellCount)
        self.distalPermanences = tuple(
            SparseMatrix(cellCount, n) for n in lateralInputWidths)

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

    @param  feedforwardInput (sequence)
            Sorted indices of active feedforward input bits

    @param  lateralInputs (list of sequences)
            For each lateral layer, a list of sorted indices of active lateral
            input bits

    @param  feedforwardGrowthCandidates (sequence or None)
            Sorted indices of feedforward input bits that active cells may grow
            new synapses to. If None, the entire feedforwardInput is used.

    @param  learn (bool)
            If True, we are learning a new object
    """

        if feedforwardGrowthCandidates is None:
            feedforwardGrowthCandidates = feedforwardInput

        if learn:
            self._computeLearningMode(feedforwardInput, lateralInputs,
                                      feedforwardGrowthCandidates)
        else:
            self._computeInferenceMode(feedforwardInput, lateralInputs)

    def _computeLearningMode(self, feedforwardInput, lateralInputs,
                             feedforwardGrowthCandidates):
        """
    Learning mode: we are learning a new object. If there is no prior
    activity, we randomly activate 'sdrSize' 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 each
    other and to lateral cortical columns.

    Parameters:
    ----------------------------
    @param  feedforwardInput (sequence)
            Sorted indices of active feedforward input bits

    @param  lateralInputs (list of sequences)
            For each lateral layer, a list of sorted indices of active lateral
            input bits

    @param  feedforwardGrowthCandidates (sequence or None)
            Sorted indices of feedforward input bits that the active cells may
            grow new synapses to
    """

        prevActiveCells = self.activeCells

        # If there are no previously active cells, select random subset of cells.
        # Else we maintain previous activity.
        if len(self.activeCells) == 0:
            self.activeCells = _sampleRange(self._random,
                                            0,
                                            self.numberOfCells(),
                                            step=1,
                                            k=self.sdrSize)
            self.activeCells.sort()

        if len(feedforwardInput) > 0:
            # Proximal learning
            self._learn(self.proximalPermanences, self._random,
                        self.activeCells, feedforwardInput,
                        feedforwardGrowthCandidates, self.sampleSizeProximal,
                        self.initialProximalPermanence,
                        self.synPermProximalInc, self.synPermProximalDec,
                        self.connectedPermanenceProximal)

            # Internal distal learning
            if len(prevActiveCells) > 0:
                self._learn(self.internalDistalPermanences, self._random,
                            self.activeCells, prevActiveCells, prevActiveCells,
                            self.sampleSizeDistal,
                            self.initialDistalPermanence,
                            self.synPermDistalInc, self.synPermDistalDec,
                            self.connectedPermanenceDistal)

            # External distal learning
            for i, lateralInput in enumerate(lateralInputs):
                self._learn(self.distalPermanences[i], self._random,
                            self.activeCells, lateralInput, lateralInput,
                            self.sampleSizeDistal,
                            self.initialDistalPermanence,
                            self.synPermDistalInc, self.synPermDistalDec,
                            self.connectedPermanenceDistal)

    def _computeInferenceMode(self, feedforwardInput, lateralInputs):
        """
    Inference mode: if there is some feedforward activity, perform
    spatial pooling on it to recognize previously known objects, then use
    lateral activity to activate a subset of the cells with feedforward
    support. If there is no feedforward activity, use lateral activity to
    activate a subset of the previous active cells.

    Parameters:
    ----------------------------
    @param  feedforwardInput (sequence)
            Sorted indices of active feedforward input bits

    @param  lateralInputs (list of sequences)
            For each lateral layer, a list of sorted indices of active lateral
            input bits
    """

        prevActiveCells = self.activeCells

        # Calculate the feedforward supported cells
        overlaps = self.proximalPermanences.rightVecSumAtNZGteThresholdSparse(
            feedforwardInput, self.connectedPermanenceProximal)
        feedforwardSupportedCells = numpy.where(
            overlaps >= self.minThresholdProximal)[0]

        # Calculate the number of active segments on each cell
        numActiveSegmentsByCell = numpy.zeros(self.cellCount, dtype="int")
        overlaps = self.internalDistalPermanences.rightVecSumAtNZGteThresholdSparse(
            prevActiveCells, self.connectedPermanenceDistal)
        numActiveSegmentsByCell[
            overlaps >= self.activationThresholdDistal] += 1
        for i, lateralInput in enumerate(lateralInputs):
            overlaps = self.distalPermanences[
                i].rightVecSumAtNZGteThresholdSparse(
                    lateralInput, self.connectedPermanenceDistal)
            numActiveSegmentsByCell[
                overlaps >= self.activationThresholdDistal] += 1

        # Activate some of the feedforward supported cells
        minNumActiveCells = self.sdrSize / 2
        chosenCells = self._chooseCells(feedforwardSupportedCells,
                                        minNumActiveCells,
                                        numActiveSegmentsByCell)

        # If necessary, activate some of the previously active cells
        if len(chosenCells) < minNumActiveCells:
            remaining = numpy.setdiff1d(prevActiveCells,
                                        feedforwardSupportedCells)
            remaining = remaining[numActiveSegmentsByCell[remaining] > 0]

            chosenCells = numpy.append(
                chosenCells,
                self._chooseCells(remaining,
                                  minNumActiveCells - len(chosenCells),
                                  numActiveSegmentsByCell))

        chosenCells.sort()
        self.activeCells = numpy.asarray(chosenCells, dtype="uint32")

    def _chooseCells(self, candidates, n, numActiveSegmentsByCell):
        """
    Choose cells to activate, using their active segment counts to determine
    inhibition.

    Count backwards through the active segment counts. For each count, find all
    of the cells that this count is unable to inhibit. Activate these cells.
    If there aren't at least n active cells, repeat with the next lowest
    segment count.

    Parameters:
    ----------------------------
    @param  candidates (sequence)
            List of cells to consider activating

    @param  n (int)
            Minimum number of cells to activate, if possible

    @param  numActiveSegmentsByCell (associative)
            A mapping from cells to number of active segments.
            This can be any data structure that associates an index with a
            value. (list, dict, numpy array)

    @return (list) Cells to activate
    """

        if n <= 0:
            return numpy.empty(0)

        # Keep a list of numpy arrays.
        allChosenCells = []
        numChosen = 0

        # Walk the active segment counts in decreasing order.
        activeSegmentCounts = numpy.unique(
            numActiveSegmentsByCell[candidates])[::-1]

        for activeSegmentCount in activeSegmentCounts:

            if activeSegmentCount == 0:
                allChosenCells.append(candidates)
                numChosen += candidates.size
                break
            else:
                # Activate all cells that are not inhibited by this segment count.
                # A cell is inhibited if another cell has at least
                # 'distalSegmentInhibitionFactor' times as many active segments.
                boundary = float(
                    activeSegmentCount) / self.distalSegmentInhibitionFactor

                includeMask = numActiveSegmentsByCell[candidates] > boundary
                chosenCells = candidates[includeMask]

                numChosen += chosenCells.size
                allChosenCells.append(chosenCells)

                if numChosen >= n:
                    break
                else:
                    candidates = candidates[~includeMask]

        if len(allChosenCells) > 0:
            return numpy.concatenate(allChosenCells)
        else:
            return numpy.empty(0)

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

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

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

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

    Parameters:
    ----------------------------
    @param  cells (iterable)
            Indices of the cells. If None return count for all cells.
    """
        if cells is None:
            cells = xrange(self.numberOfCells())

        return _countWhereGreaterEqualInRows(self.proximalPermanences, cells,
                                             self.connectedPermanenceProximal)

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

    Parameters:
    ----------------------------
    @param  cells (iterable)
            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=None):
        """
    Returns the total number of distal segments for these cells.

    A segment "exists" if its row in the matrix has any permanence values > 0.

    Parameters:
    ----------------------------
    @param  cells (iterable)
            Indices of the cells
    """
        if cells is None:
            cells = xrange(self.numberOfCells())

        n = 0

        for cell in cells:
            if self.internalDistalPermanences.nNonZerosOnRow(cell) > 0:
                n += 1

            for permanences in self.distalPermanences:
                if permanences.nNonZerosOnRow(cell) > 0:
                    n += 1

        return n

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

    Parameters:
    ----------------------------
    @param  cells (iterable)
            Indices of the cells. If None return count for all cells.
    """
        if cells is None:
            cells = xrange(self.numberOfCells())

        n = _countWhereGreaterEqualInRows(self.internalDistalPermanences,
                                          cells,
                                          self.connectedPermanenceDistal)

        for permanences in self.distalPermanences:
            n += _countWhereGreaterEqualInRows(permanences, cells,
                                               self.connectedPermanenceDistal)

        return n

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

    Parameters:
    ----------------------------
    @param  cells (iterable)
            Indices of the cells
    """
        if cells is None:
            cells = xrange(self.numberOfCells())
        n = 0
        for cell in cells:
            n += self.internalDistalPermanences.nNonZerosOnRow(cell)

            for permanences in self.distalPermanences:
                n += permanences.nNonZerosOnRow(cell)
        return n

    def reset(self):
        """
    Reset internal states. When learning this signifies we are to learn a
    unique new object.
    """
        self.activeCells = numpy.empty(0, dtype="uint32")

    @staticmethod
    def _learn(  # mutated args
            permanences,
            rng,

            # activity
            activeCells,
            activeInput,
            growthCandidateInput,

            # configuration
            sampleSize,
            initialPermanence,
            permanenceIncrement,
            permanenceDecrement,
            connectedPermanence):
        """
    For each active cell, reinforce active synapses, punish inactive synapses,
    and grow new synapses to a subset of the active input bits that the cell
    isn't already connected to.

    Parameters:
    ----------------------------
    @param  permanences (SparseMatrix)
            Matrix of permanences, with cells as rows and inputs as columns

    @param  rng (Random)
            Random number generator

    @param  activeCells (sorted sequence)
            Sorted list of the cells that are learning

    @param  activeInput (sorted sequence)
            Sorted list of active bits in the input

    @param  growthCandidateInput (sorted sequence)
            Sorted list of active bits in the input that the activeCells may
            grow new synapses to

    For remaining parameters, see the __init__ docstring.
    """

        permanences.incrementNonZerosOnOuter(activeCells, activeInput,
                                             permanenceIncrement)
        permanences.incrementNonZerosOnRowsExcludingCols(
            activeCells, activeInput, -permanenceDecrement)
        permanences.clipRowsBelowAndAbove(activeCells, 0.0, 1.0)
        if sampleSize == -1:
            permanences.setZerosOnOuter(activeCells, activeInput,
                                        initialPermanence)
        else:
            existingSynapseCounts = permanences.nNonZerosPerRowOnCols(
                activeCells, activeInput)

            maxNewByCell = numpy.empty(len(activeCells), dtype="int32")
            numpy.subtract(sampleSize, existingSynapseCounts, out=maxNewByCell)

            permanences.setRandomZerosOnOuter(activeCells,
                                              growthCandidateInput,
                                              maxNewByCell, initialPermanence,
                                              rng)
    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)
Beispiel #10
0
  def __init__(self,
               inputWidth,
               lateralInputWidths=(),
               cellCount=4096,
               sdrSize=40,

               # Proximal
               synPermProximalInc=0.1,
               synPermProximalDec=0.001,
               initialProximalPermanence=0.6,
               sampleSizeProximal=20,
               minThresholdProximal=10,
               connectedPermanenceProximal=0.50,

               # Distal
               synPermDistalInc=0.1,
               synPermDistalDec=0.001,
               initialDistalPermanence=0.6,
               sampleSizeDistal=20,
               activationThresholdDistal=13,
               connectedPermanenceDistal=0.50,
               distalSegmentInhibitionFactor=1.5,

               seed=42):
    """
    Parameters:
    ----------------------------
    @param  inputWidth (int)
            The number of bits in the feedforward input

    @param  lateralInputWidths (list of ints)
            The number of bits in each lateral input

    @param  sdrSize (int)
            The number of active cells in an object SDR

    @param  synPermProximalInc (float)
            Permanence increment for proximal synapses

    @param  synPermProximalDec (float)
            Permanence decrement for proximal synapses

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

    @param  sampleSizeProximal (int)
            Number of proximal synapses a cell should grow to each feedforward
            pattern, or -1 to connect to every active bit

    @param  minThresholdProximal (int)
            Number of active synapses required for a cell to have feedforward
            support

    @param  connectedPermanenceProximal (float)
            Permanence required for a proximal synapse to be connected

    @param  synPermDistalInc (float)
            Permanence increment for distal synapses

    @param  synPermDistalDec (float)
            Permanence decrement for distal synapses

    @param  sampleSizeDistal (int)
            Number of distal synapses a cell should grow to each lateral
            pattern, or -1 to connect to every active bit

    @param  initialDistalPermanence (float)
            Initial permanence value for distal synapses

    @param  activationThresholdDistal (int)
            Number of active synapses required to activate a distal segment

    @param  connectedPermanenceDistal (float)
            Permanence required for a distal synapse to be connected

    @param  distalSegmentInhibitionFactor (float)
            The minimum ratio of active dendrite segment counts that will lead
            to inhibition. For example, with value 1.5, cells with 2 active
            segments will be inhibited by cells with 3 active segments, but
            cells with 3 active segments will not be inhibited by cells with 4.

    @param  seed (int)
            Random number generator seed
    """

    self.inputWidth = inputWidth
    self.cellCount = cellCount
    self.sdrSize = sdrSize
    self.synPermProximalInc = synPermProximalInc
    self.synPermProximalDec = synPermProximalDec
    self.initialProximalPermanence = initialProximalPermanence
    self.connectedPermanenceProximal = connectedPermanenceProximal
    self.sampleSizeProximal = sampleSizeProximal
    self.minThresholdProximal = minThresholdProximal
    self.synPermDistalInc = synPermDistalInc
    self.synPermDistalDec = synPermDistalDec
    self.initialDistalPermanence = initialDistalPermanence
    self.connectedPermanenceDistal = connectedPermanenceDistal
    self.sampleSizeDistal = sampleSizeDistal
    self.activationThresholdDistal = activationThresholdDistal
    self.distalSegmentInhibitionFactor = distalSegmentInhibitionFactor

    self.activeCells = ()
    self._random = Random(seed)

    # These sparse matrices will hold the synapses for each segment.
    # Each row represents one segment on a cell, so each cell potentially has
    # 1 proximal segment and 1+len(lateralInputWidths) distal segments.
    self.proximalPermanences = SparseMatrix(cellCount, inputWidth)
    self.internalDistalPermanences = SparseMatrix(cellCount, cellCount)
    self.distalPermanences = tuple(SparseMatrix(cellCount, n)
                                   for n in lateralInputWidths)
Beispiel #11
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,
               lateralInputWidths=(),
               cellCount=4096,
               sdrSize=40,

               # Proximal
               synPermProximalInc=0.1,
               synPermProximalDec=0.001,
               initialProximalPermanence=0.6,
               sampleSizeProximal=20,
               minThresholdProximal=10,
               connectedPermanenceProximal=0.50,

               # Distal
               synPermDistalInc=0.1,
               synPermDistalDec=0.001,
               initialDistalPermanence=0.6,
               sampleSizeDistal=20,
               activationThresholdDistal=13,
               connectedPermanenceDistal=0.50,
               distalSegmentInhibitionFactor=1.5,

               seed=42):
    """
    Parameters:
    ----------------------------
    @param  inputWidth (int)
            The number of bits in the feedforward input

    @param  lateralInputWidths (list of ints)
            The number of bits in each lateral input

    @param  sdrSize (int)
            The number of active cells in an object SDR

    @param  synPermProximalInc (float)
            Permanence increment for proximal synapses

    @param  synPermProximalDec (float)
            Permanence decrement for proximal synapses

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

    @param  sampleSizeProximal (int)
            Number of proximal synapses a cell should grow to each feedforward
            pattern, or -1 to connect to every active bit

    @param  minThresholdProximal (int)
            Number of active synapses required for a cell to have feedforward
            support

    @param  connectedPermanenceProximal (float)
            Permanence required for a proximal synapse to be connected

    @param  synPermDistalInc (float)
            Permanence increment for distal synapses

    @param  synPermDistalDec (float)
            Permanence decrement for distal synapses

    @param  sampleSizeDistal (int)
            Number of distal synapses a cell should grow to each lateral
            pattern, or -1 to connect to every active bit

    @param  initialDistalPermanence (float)
            Initial permanence value for distal synapses

    @param  activationThresholdDistal (int)
            Number of active synapses required to activate a distal segment

    @param  connectedPermanenceDistal (float)
            Permanence required for a distal synapse to be connected

    @param  distalSegmentInhibitionFactor (float)
            The minimum ratio of active dendrite segment counts that will lead
            to inhibition. For example, with value 1.5, cells with 2 active
            segments will be inhibited by cells with 3 active segments, but
            cells with 3 active segments will not be inhibited by cells with 4.

    @param  seed (int)
            Random number generator seed
    """

    self.inputWidth = inputWidth
    self.cellCount = cellCount
    self.sdrSize = sdrSize
    self.synPermProximalInc = synPermProximalInc
    self.synPermProximalDec = synPermProximalDec
    self.initialProximalPermanence = initialProximalPermanence
    self.connectedPermanenceProximal = connectedPermanenceProximal
    self.sampleSizeProximal = sampleSizeProximal
    self.minThresholdProximal = minThresholdProximal
    self.synPermDistalInc = synPermDistalInc
    self.synPermDistalDec = synPermDistalDec
    self.initialDistalPermanence = initialDistalPermanence
    self.connectedPermanenceDistal = connectedPermanenceDistal
    self.sampleSizeDistal = sampleSizeDistal
    self.activationThresholdDistal = activationThresholdDistal
    self.distalSegmentInhibitionFactor = distalSegmentInhibitionFactor

    self.activeCells = ()
    self._random = Random(seed)

    # These sparse matrices will hold the synapses for each segment.
    # Each row represents one segment on a cell, so each cell potentially has
    # 1 proximal segment and 1+len(lateralInputWidths) distal segments.
    self.proximalPermanences = SparseMatrix(cellCount, inputWidth)
    self.internalDistalPermanences = SparseMatrix(cellCount, cellCount)
    self.distalPermanences = tuple(SparseMatrix(cellCount, n)
                                   for n in lateralInputWidths)


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

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

    @param  lateralInputs (list of iterables)
            Sets of indices of active lateral input bits, one per lateral layer

    @param  learn (bool)
            If True, we are learning a new object
    """

    if learn:
      self._computeLearningMode(feedforwardInput, lateralInputs)
    else:
      self._computeInferenceMode(feedforwardInput, lateralInputs)


  def _computeLearningMode(self, feedforwardInput, lateralInputs):
    """
    Learning mode: we are learning a new object. If there is no prior
    activity, we randomly activate 'sdrSize' 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 each
    other and to lateral cortical columns.

    Parameters:
    ----------------------------
    @param  feedforwardInput (iterable)
            List of indices of active feedforward input bits

    @param  lateralInputs (list of iterables)
            Lists of indices of active lateral input bits, one per lateral layer
    """

    prevActiveCells = self.activeCells

    # If there are no previously active cells, select random subset of cells.
    # Else we maintain previous activity.
    if len(self.activeCells) == 0:
      self.activeCells = sorted(_sampleRange(self._random,
                                             0, self.numberOfCells(),
                                             step=1, k=self.sdrSize))

    if len(feedforwardInput) > 0:
      # Proximal learning
      self._learn(self.proximalPermanences, self._random,
                  self.activeCells, sorted(feedforwardInput),
                  self.sampleSizeProximal, self.initialProximalPermanence,
                  self.synPermProximalInc, self.synPermProximalDec,
                  self.connectedPermanenceProximal)

      # Internal distal learning
      if len(prevActiveCells) > 0:
        self._learn(self.internalDistalPermanences, self._random,
                    self.activeCells, prevActiveCells,
                    self.sampleSizeDistal, self.initialDistalPermanence,
                    self.synPermDistalInc, self.synPermDistalDec,
                    self.connectedPermanenceDistal)

      # External distal learning
      for i, lateralInput in enumerate(lateralInputs):
        self._learn(self.distalPermanences[i], self._random,
                    self.activeCells, sorted(lateralInput),
                    self.sampleSizeDistal, self.initialDistalPermanence,
                    self.synPermDistalInc, self.synPermDistalDec,
                    self.connectedPermanenceDistal)


  def _computeInferenceMode(self, feedforwardInput, lateralInputs):
    """
    Inference mode: if there is some feedforward activity, perform
    spatial pooling on it to recognize previously known objects, then use
    lateral activity to activate a subset of the cells with feedforward
    support. If there is no feedforward activity, use lateral activity to
    activate a subset of the previous active cells.

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

    @param  lateralInputs (list of iterables)
            Sets of indices of active lateral input bits, one per lateral layer
    """

    prevActiveCells = self.activeCells

    # Calculate the feedforward supported cells
    overlaps = self.proximalPermanences.rightVecSumAtNZGteThresholdSparse(
      list(feedforwardInput), self.connectedPermanenceProximal)
    feedforwardSupportedCells = set(
      numpy.where(overlaps >= self.minThresholdProximal)[0])

    # Calculate the number of active segments on each cell
    numActiveSegmentsByCell = numpy.zeros(self.cellCount, dtype="int")
    overlaps = self.internalDistalPermanences.rightVecSumAtNZGteThresholdSparse(
      list(prevActiveCells), self.connectedPermanenceDistal)
    numActiveSegmentsByCell[overlaps >= self.activationThresholdDistal] += 1
    for i, lateralInput in enumerate(lateralInputs):
      overlaps = self.distalPermanences[i].rightVecSumAtNZGteThresholdSparse(
        list(lateralInput), self.connectedPermanenceDistal)
      numActiveSegmentsByCell[overlaps >= self.activationThresholdDistal] += 1

    # Activate some of the feedforward supported cells
    minNumActiveCells = self.sdrSize / 2
    chosenCells = self._chooseCells(feedforwardSupportedCells,
                                    minNumActiveCells, numActiveSegmentsByCell)

    # If necessary, activate some of the previously active cells
    if len(chosenCells) < minNumActiveCells:
      remainingCandidates = [cell for cell in prevActiveCells
                             if cell not in feedforwardSupportedCells
                             if numActiveSegmentsByCell[cell] > 0]
      chosenCells.extend(self._chooseCells(remainingCandidates,
                                           minNumActiveCells - len(chosenCells),
                                           numActiveSegmentsByCell))

    self.activeCells = sorted(chosenCells)


  def _chooseCells(self, candidates, n, numActiveSegmentsByCell):
    """
    Choose cells to activate, using their active segment counts to determine
    inhibition.

    Count backwards through the active segment counts. For each count, find all
    of the cells that this count is unable to inhibit. Activate these cells.
    If there aren't at least n active cells, repeat with the next lowest
    segment count.

    Parameters:
    ----------------------------
    @param  candidates (iterable)
            List of cells to consider activating

    @param  n (int)
            Minimum number of cells to activate, if possible

    @param  numActiveSegmentsByCell (associative)
            A mapping from cells to number of active segments.
            This can be any data structure that associates an index with a
            value. (list, dict, numpy array)

    @return (list) Cells to activate
    """

    orderedCandidates = sorted(candidates,
                               key=numActiveSegmentsByCell.__getitem__,
                               reverse=True)
    activeSegmentCounts = sorted(set(numActiveSegmentsByCell[cell]
                                     for cell in candidates),
                                 reverse=True)

    chosenCells = []
    i = 0

    for activeSegmentCount in activeSegmentCounts:
      if len(chosenCells) >= n or i >= len(orderedCandidates):
        break

      if activeSegmentCount == 0:
        chosenCells.extend(orderedCandidates[i:])
        break

      # If one cell has 'distalSegmentInhibitionFactor' * the number of active
      # segments of another cell, the latter cell is inhibited.
      boundary = float(activeSegmentCount) / self.distalSegmentInhibitionFactor

      while (i < len(orderedCandidates) and
             numActiveSegmentsByCell[orderedCandidates[i]] > boundary):
        chosenCells.append(orderedCandidates[i])
        i += 1

    return chosenCells


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


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


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


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

    Parameters:
    ----------------------------
    @param  cells (iterable)
            Indices of the cells. If None return count for all cells.
    """
    if cells is None:
      cells = xrange(self.numberOfCells())

    return _countWhereGreaterEqualInRows(self.proximalPermanences, cells,
                                         self.connectedPermanenceProximal)


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

    Parameters:
    ----------------------------
    @param  cells (iterable)
            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=None):
    """
    Returns the total number of distal segments for these cells.

    A segment "exists" if its row in the matrix has any permanence values > 0.

    Parameters:
    ----------------------------
    @param  cells (iterable)
            Indices of the cells
    """
    if cells is None:
      cells = xrange(self.numberOfCells())

    n = 0

    for cell in cells:
      if self.internalDistalPermanences.nNonZerosOnRow(cell) > 0:
        n += 1

      for permanences in self.distalPermanences:
        if permanences.nNonZerosOnRow(cell) > 0:
          n += 1

    return n


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

    Parameters:
    ----------------------------
    @param  cells (iterable)
            Indices of the cells. If None return count for all cells.
    """
    if cells is None:
      cells = xrange(self.numberOfCells())

    n = _countWhereGreaterEqualInRows(self.internalDistalPermanences, cells,
                                      self.connectedPermanenceDistal)

    for permanences in self.distalPermanences:
      n += _countWhereGreaterEqualInRows(permanences, cells,
                                         self.connectedPermanenceDistal)

    return n


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

    Parameters:
    ----------------------------
    @param  cells (iterable)
            Indices of the cells
    """
    if cells is None:
      cells = xrange(self.numberOfCells())
    n = 0
    for cell in cells:
      n += self.internalDistalPermanences.nNonZerosOnRow(cell)

      for permanences in self.distalPermanences:
        n += permanences.nNonZerosOnRow(cell)
    return n


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


  @staticmethod
  def _learn(# mutated args
             permanences, rng,

             # activity
             activeCells, activeInput,

             # configuration
             sampleSize, initialPermanence, permanenceIncrement,
             permanenceDecrement, connectedPermanence):
    """
    For each active cell, reinforce active synapses, punish inactive synapses,
    and grow new synapses to a subset of the active input bits that the cell
    isn't already connected to.

    Parameters:
    ----------------------------
    @param  permanences (SparseMatrix)
            Matrix of permanences, with cells as rows and inputs as columns

    @param  rng (Random)
            Random number generator

    @param  activeCells (sorted sequence)
            Sorted list of the cells that are learning

    @param  activeInput (sorted sequence)
            Sorted list of active bits in the input

    For remaining parameters, see the __init__ docstring.
    """

    permanences.incrementNonZerosOnOuter(
      activeCells, activeInput, permanenceIncrement)
    permanences.incrementNonZerosOnRowsExcludingCols(
      activeCells, activeInput, -permanenceDecrement)
    permanences.clipRowsBelowAndAbove(
      activeCells, 0.0, 1.0)
    if sampleSize == -1:
      permanences.setZerosOnOuter(
        activeCells, activeInput, initialPermanence)
    else:
      permanences.increaseRowNonZeroCountsOnOuterTo(
        activeCells, activeInput, sampleSize, initialPermanence, rng)
  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 getUnionEncodingFromTokens(self, tokens):
    """
    Create a single, sparsified bitmap from a union of bitmaps for given tokens

    @param  tokens  (sequence) A sequence of tokens
    @return         (sequence) Bitmap
    """
    # Accumulate counts by inplace-adding sparse matrices
    counts = SparseMatrix()
    counts.resize(1, self.width*self.height)

    # Pre-allocate buffer sparse matrix
    sparseBitmap = SparseMatrix()
    sparseBitmap.resize(1, self.width*self.height)

    for t in tokens:
      bitmap = self._getWordBitmap(t)
      sparseBitmap.setRowFromSparse(0, bitmap, [1]*len(bitmap))
      counts += sparseBitmap

    numNonZeros = counts.nNonZerosOnRow(0)

    # Add some jitter to aid in tie-breaking during sort
    jitterValues = tuple((random.random()/100)+0.1
                         for _ in xrange(numNonZeros))
    sparseBitmap.setRowFromSparse(0, counts.nonZeroCols(), jitterValues)
    counts += sparseBitmap

    maxSparsity = int(self.unionSparsity * self.n)
    w = min(numNonZeros, maxSparsity)

    # Return top w most popular positions
    return  tuple(x[1] for x in counts.getNonZerosSorted())[:w]
  def __init__(self,
               inputWidth,
               lateralInputWidths=(),
               cellCount=4096,
               sdrSize=40,
               onlineLearning = False,
               maxSdrSize = None,
               minSdrSize = None,

               # Proximal
               synPermProximalInc=0.1,
               synPermProximalDec=0.001,
               initialProximalPermanence=0.6,
               sampleSizeProximal=20,
               minThresholdProximal=10,
               connectedPermanenceProximal=0.50,
               predictedInhibitionThreshold=20,

               # Distal
               synPermDistalInc=0.1,
               synPermDistalDec=0.001,
               initialDistalPermanence=0.6,
               sampleSizeDistal=20,
               activationThresholdDistal=13,
               connectedPermanenceDistal=0.50,
               distalSegmentInhibitionFactor=0.999,
               inertiaFactor=1.,

               seed=42):
    """
    Parameters:
    ----------------------------
    @param  inputWidth (int)
            The number of bits in the feedforward input

    @param  lateralInputWidths (list of ints)
            The number of bits in each lateral input

    @param  sdrSize (int)
            The number of active cells in an object SDR

    @param  onlineLearning (Bool)
            Whether or not the column pooler should learn in online mode.

    @param  maxSdrSize (int)
            The maximum SDR size for learning.  If the column pooler has more
            than this many cells active, it will refuse to learn.  This serves
            to stop the pooler from learning when it is uncertain of what object
            it is sensing.

    @param  minSdrSize (int)
            The minimum SDR size for learning.  If the column pooler has fewer
            than this many active cells, it will create a new representation
            and learn that instead.  This serves to create separate
            representations for different objects and sequences.

            If online learning is enabled, this parameter should be at least
            inertiaFactor*sdrSize.  Otherwise, two different objects may be
            incorrectly inferred to be the same, as SDRs may still be active
            enough to learn even after inertial decay.

    @param  synPermProximalInc (float)
            Permanence increment for proximal synapses

    @param  synPermProximalDec (float)
            Permanence decrement for proximal synapses

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

    @param  sampleSizeProximal (int)
            Number of proximal synapses a cell should grow to each feedforward
            pattern, or -1 to connect to every active bit

    @param  minThresholdProximal (int)
            Number of active synapses required for a cell to have feedforward
            support

    @param  connectedPermanenceProximal (float)
            Permanence required for a proximal synapse to be connected

    @param  predictedInhibitionThreshold (int)
            How much predicted input must be present for inhibitory behavior
            to be triggered.  Only has effects if onlineLearning is true.

    @param  synPermDistalInc (float)
            Permanence increment for distal synapses

    @param  synPermDistalDec (float)
            Permanence decrement for distal synapses

    @param  sampleSizeDistal (int)
            Number of distal synapses a cell should grow to each lateral
            pattern, or -1 to connect to every active bit

    @param  initialDistalPermanence (float)
            Initial permanence value for distal synapses

    @param  activationThresholdDistal (int)
            Number of active synapses required to activate a distal segment

    @param  connectedPermanenceDistal (float)
            Permanence required for a distal synapse to be connected

    @param  distalSegmentInhibitionFactor (float)
            The proportion of the highest number of active lateral segments
            necessary for a cell not to be inhibited (must be <1).

    @param  inertiaFactor (float)
            The proportion of previously active cells that remain
            active in the next timestep due to inertia (in the absence of
            inhibition).  If onlineLearning is enabled, should be at most
            1 - learningTolerance, or representations may incorrectly become
            mixed.

    @param  seed (int)
            Random number generator seed
    """

    assert distalSegmentInhibitionFactor > 0.0
    assert distalSegmentInhibitionFactor < 1.0
    assert maxSdrSize is None or maxSdrSize >= sdrSize
    assert minSdrSize is None or minSdrSize <= sdrSize

    self.inputWidth = inputWidth
    self.cellCount = cellCount
    self.sdrSize = sdrSize
    self.onlineLearning = onlineLearning
    if maxSdrSize is None:
      self.maxSdrSize = sdrSize
    else:
      self.maxSdrSize = maxSdrSize
    if minSdrSize is None:
      self.minSdrSize = sdrSize
    else:
      self.minSdrSize = minSdrSize
    self.synPermProximalInc = synPermProximalInc
    self.synPermProximalDec = synPermProximalDec
    self.initialProximalPermanence = initialProximalPermanence
    self.connectedPermanenceProximal = connectedPermanenceProximal
    self.sampleSizeProximal = sampleSizeProximal
    self.minThresholdProximal = minThresholdProximal
    self.predictedInhibitionThreshold = predictedInhibitionThreshold
    self.synPermDistalInc = synPermDistalInc
    self.synPermDistalDec = synPermDistalDec
    self.initialDistalPermanence = initialDistalPermanence
    self.connectedPermanenceDistal = connectedPermanenceDistal
    self.sampleSizeDistal = sampleSizeDistal
    self.activationThresholdDistal = activationThresholdDistal
    self.distalSegmentInhibitionFactor = distalSegmentInhibitionFactor
    self.inertiaFactor = inertiaFactor

    self.activeCells = numpy.empty(0, dtype="uint32")
    self._random = Random(seed)

    # These sparse matrices will hold the synapses for each segment.
    # Each row represents one segment on a cell, so each cell potentially has
    # 1 proximal segment and 1+len(lateralInputWidths) distal segments.
    self.proximalPermanences = SparseMatrix(cellCount, inputWidth)
    self.internalDistalPermanences = SparseMatrix(cellCount, cellCount)
    self.distalPermanences = tuple(SparseMatrix(cellCount, n)
                                   for n in lateralInputWidths)

    self.useInertia=True
Beispiel #15
0
 def __init__(self, cellCount, inputSize, threshold):
   self.permanences = SparseMatrix(cellCount, inputSize)
   self.threshold = threshold
   self.activeCells = np.empty(0, dtype='uint32')
Beispiel #16
0
class ConditionalProbabilityTable2D(object):
  """
  Holds frequencies in a 2D grid of bins.
  Binning is not performed automatically by this class.
  Bin updates must be done one row at a time.
  Based on nupic::SparseMatrix which is a compressed sparse row matrix.
  Number of columns cannot be changed once set.
  Number of rows may be increased.
  Also maintains the row and column sumProp distributions.
  
  Constructor constructs a new empty histogram with no rows or columns. 
  
  :param rowHint: if specified, ncols must be specified (though not vice versa)
  :param ncols: if speicified, number of columns cannot be changed thereafter.
  """
  def __init__(self, rowHint=None, ncols=None):
    self.hist_ = None
    self.rowSums_ = None
    self.colSums_ = None
    if ncols:
      if not rowHint: rowHint = 1
      assert dtype
      self.grow(rowHint, ncols)
    else: assert not rowHint
    self.hack_ = None

  def numRows(self):
    """Gets the number of rows in the histogram.
    
    :returns: Integer number of rows.
    """
    if self.hist_: return self.hist_.nRows()
    else: return 0

  def numColumns(self):
    """
    :return: (int) number of columns 
    """
    if self.hist_: return self.hist_.nCols()
    else: return 0

  def grow(self, rows, cols):
    """
    Grows the histogram to have rows rows and cols columns.
    Must not have been initialized before, or already have the same
    number of columns.
    If rows is smaller than the current number of rows,
    does not shrink.
    Also updates the sizes of the row and column sums.

    :param rows: Integer number of rows.
    :param cols: Integer number of columns.
    """
    if not self.hist_:
      self.hist_ = SparseMatrix(rows, cols)
      self.rowSums_ = numpy.zeros(rows, dtype=dtype)
      self.colSums_ = numpy.zeros(cols, dtype=dtype)
      self.hack_ = None
    else:
      oldRows = self.hist_.nRows()
      oldCols = self.hist_.nCols()
      nextRows = max(oldRows, rows)
      nextCols = max(oldCols, cols)
      if (oldRows < nextRows) or (oldCols < nextCols):
        self.hist_.resize(nextRows, nextCols)
        if oldRows < nextRows:
          oldSums = self.rowSums_
          self.rowSums_ = numpy.zeros(nextRows, dtype=dtype)
          self.rowSums_[0:len(oldSums)] = oldSums
          self.hack_ = None
        if oldCols < nextCols:
          oldSums = self.colSums_
          self.colSums_ = numpy.zeros(nextCols, dtype=dtype)
          self.colSums_[0:len(oldSums)] = oldSums
          self.hack_ = None

  def updateRow(self, row, distribution):
    """
    Add distribution to row row.
    Distribution should be an array of probabilities or counts.

    :param row:   Integer index of the row to add to.
                  May be larger than the current number of rows, in which case
                  the histogram grows.
    :param distribution: Array of length equal to the number of columns.
    """
    self.grow(row+1, len(distribution))
    self.hist_.axby(row, 1, 1, distribution)
    self.rowSums_[row] += distribution.sum()
    self.colSums_ += distribution
    self.hack_ = None # Clear out the cached inference.

  def inferRow(self, distribution):
    """
    Computes the sumProp probability of each row given the input probability
    of each column. Normalizes the distribution in each column on the fly.

    The semantics are as follows: If the distribution is P(col|e) where e is
    the evidence is col is the column, and the CPD represents P(row|col), then
    this calculates sum(P(col|e) P(row|col)) = P(row|e).

    :param distribution: Array of length equal to the number of columns.
    :returns: array of length equal to the number of rows.
    """
    # normalize over colSums_ because P(row|col) = P(col,row)/P(col)
    return self.hist_ * (distribution / self.colSums_)

  def inferRowEvidence(self, distribution):
    """
    Computes the probability of evidence given each row from the probability
    of evidence given each column.  Essentially, this just means that it sums
    probabilities over (normalized) rows.  Normalizes the distribution over
    each row on the fly.

    The semantics are as follows:  If the distribution is P(e|col) where e is
    evidence and col is the column, and the CPD is of P(col|row), then this
    calculates sum(P(e|col) P(col|row)) = P(e|row).

    :param distribution: Array of length equal to the number of columns.
    :returns: array of length equal to the number of rows.
    """
    # normalize over rowSums_ because P(col|row) = P(col,row)/P(row).
    return (self.hist_ * distribution) / self.rowSums_

  def inferRowMaxProd(self, distribution):
    return self.hist_.vecMaxProd(distribution)

  def inferRowCompat(self, distribution):
    """
    Equivalent to the category inference of zeta1.TopLevel.
    Computes the max_prod (maximum component of a component-wise multiply)
    between the rows of the histogram and the incoming distribution.
    May be slow if the result of clean_outcpd() is not valid.

    :param distribution: Array of length equal to the number of columns.
    :returns: array of length equal to the number of rows.
    """
    if self.hack_ is None:
      self.clean_outcpd()
    return self.hack_.vecMaxProd(distribution)

  def clean_outcpd(self):
    """Hack to act like clean_outcpd on zeta1.TopLevelNode.
    Take the max element in each to column, set it to 1, and set all the
    other elements to 0.
    Only called by inferRowMaxProd() and only needed if an updateRow()
    has been called since the last clean_outcpd().
    """
    m = self.hist_.toDense()
    for j in xrange(m.shape[1]): # For each column.
      cmax = m[:,j].max()
      if cmax:
        m[:,j] = numpy.array(m[:,j] == cmax, dtype=dtype)
    self.hack_ = SparseMatrix(0, self.hist_.nCols())
    for i in xrange(m.shape[0]):
      self.hack_.addRow(m[i,:])
Beispiel #17
0
class ConditionalProbabilityTable2D(object):
    """
  Holds frequencies in a 2D grid of bins.
  Binning is not performed automatically by this class.
  Bin updates must be done one row at a time.
  Based on nupic::SparseMatrix which is a compressed sparse row matrix.
  Number of columns cannot be changed once set.
  Number of rows may be increased.
  Also maintains the row and column sumProp distributions.
  
  Constructor constructs a new empty histogram with no rows or columns. 
  
  :param rowHint: if specified, ncols must be specified (though not vice versa)
  :param ncols: if speicified, number of columns cannot be changed thereafter.
  """
    def __init__(self, rowHint=None, ncols=None):
        self.hist_ = None
        self.rowSums_ = None
        self.colSums_ = None
        if ncols:
            if not rowHint: rowHint = 1
            assert dtype
            self.grow(rowHint, ncols)
        else:
            assert not rowHint
        self.hack_ = None

    def numRows(self):
        """Gets the number of rows in the histogram.
    
    :returns: Integer number of rows.
    """
        if self.hist_: return self.hist_.nRows()
        else: return 0

    def numColumns(self):
        """
    :return: (int) number of columns 
    """
        if self.hist_: return self.hist_.nCols()
        else: return 0

    def grow(self, rows, cols):
        """
    Grows the histogram to have rows rows and cols columns.
    Must not have been initialized before, or already have the same
    number of columns.
    If rows is smaller than the current number of rows,
    does not shrink.
    Also updates the sizes of the row and column sums.

    :param rows: Integer number of rows.
    :param cols: Integer number of columns.
    """
        if not self.hist_:
            self.hist_ = SparseMatrix(rows, cols)
            self.rowSums_ = numpy.zeros(rows, dtype=dtype)
            self.colSums_ = numpy.zeros(cols, dtype=dtype)
            self.hack_ = None
        else:
            oldRows = self.hist_.nRows()
            oldCols = self.hist_.nCols()
            nextRows = max(oldRows, rows)
            nextCols = max(oldCols, cols)
            if (oldRows < nextRows) or (oldCols < nextCols):
                self.hist_.resize(nextRows, nextCols)
                if oldRows < nextRows:
                    oldSums = self.rowSums_
                    self.rowSums_ = numpy.zeros(nextRows, dtype=dtype)
                    self.rowSums_[0:len(oldSums)] = oldSums
                    self.hack_ = None
                if oldCols < nextCols:
                    oldSums = self.colSums_
                    self.colSums_ = numpy.zeros(nextCols, dtype=dtype)
                    self.colSums_[0:len(oldSums)] = oldSums
                    self.hack_ = None

    def updateRow(self, row, distribution):
        """
    Add distribution to row row.
    Distribution should be an array of probabilities or counts.

    :param row:   Integer index of the row to add to.
                  May be larger than the current number of rows, in which case
                  the histogram grows.
    :param distribution: Array of length equal to the number of columns.
    """
        self.grow(row + 1, len(distribution))
        self.hist_.axby(row, 1, 1, distribution)
        self.rowSums_[row] += distribution.sum()
        self.colSums_ += distribution
        self.hack_ = None  # Clear out the cached inference.

    def inferRow(self, distribution):
        """
    Computes the sumProp probability of each row given the input probability
    of each column. Normalizes the distribution in each column on the fly.

    The semantics are as follows: If the distribution is P(col|e) where e is
    the evidence is col is the column, and the CPD represents P(row|col), then
    this calculates sum(P(col|e) P(row|col)) = P(row|e).

    :param distribution: Array of length equal to the number of columns.
    :returns: array of length equal to the number of rows.
    """
        # normalize over colSums_ because P(row|col) = P(col,row)/P(col)
        return self.hist_ * (distribution / self.colSums_)

    def inferRowEvidence(self, distribution):
        """
    Computes the probability of evidence given each row from the probability
    of evidence given each column.  Essentially, this just means that it sums
    probabilities over (normalized) rows.  Normalizes the distribution over
    each row on the fly.

    The semantics are as follows:  If the distribution is P(e|col) where e is
    evidence and col is the column, and the CPD is of P(col|row), then this
    calculates sum(P(e|col) P(col|row)) = P(e|row).

    :param distribution: Array of length equal to the number of columns.
    :returns: array of length equal to the number of rows.
    """
        # normalize over rowSums_ because P(col|row) = P(col,row)/P(row).
        return (self.hist_ * distribution) / self.rowSums_

    def inferRowMaxProd(self, distribution):
        return self.hist_.vecMaxProd(distribution)

    def inferRowCompat(self, distribution):
        """
    Equivalent to the category inference of zeta1.TopLevel.
    Computes the max_prod (maximum component of a component-wise multiply)
    between the rows of the histogram and the incoming distribution.
    May be slow if the result of clean_outcpd() is not valid.

    :param distribution: Array of length equal to the number of columns.
    :returns: array of length equal to the number of rows.
    """
        if self.hack_ is None:
            self.clean_outcpd()
        return self.hack_.vecMaxProd(distribution)

    def clean_outcpd(self):
        """Hack to act like clean_outcpd on zeta1.TopLevelNode.
    Take the max element in each to column, set it to 1, and set all the
    other elements to 0.
    Only called by inferRowMaxProd() and only needed if an updateRow()
    has been called since the last clean_outcpd().
    """
        m = self.hist_.toDense()
        for j in range(m.shape[1]):  # For each column.
            cmax = m[:, j].max()
            if cmax:
                m[:, j] = numpy.array(m[:, j] == cmax, dtype=dtype)
        self.hack_ = SparseMatrix(0, self.hist_.nCols())
        for i in range(m.shape[0]):
            self.hack_.addRow(m[i, :])
Beispiel #18
0
    def getUnionEncodingFromTokens(self, tokens):
        """
    Create a single, sparsified bitmap from a union of bitmaps for given tokens

    @param  tokens  (sequence) A sequence of tokens
    @return         (sequence) Bitmap
    """
        # Accumulate counts by inplace-adding sparse matrices
        counts = SparseMatrix()
        counts.resize(1, self.width * self.height)

        # Pre-allocate buffer sparse matrix
        sparseBitmap = SparseMatrix()
        sparseBitmap.resize(1, self.width * self.height)

        for t in tokens:
            bitmap = self._getWordBitmap(t)
            sparseBitmap.setRowFromSparse(0, bitmap, [1] * len(bitmap))
            counts += sparseBitmap

        numNonZeros = counts.nNonZerosOnRow(0)

        # Add some jitter to aid in tie-breaking during sort
        jitterValues = tuple(
            (random.random() / 100) + 0.1 for _ in xrange(numNonZeros))
        sparseBitmap.setRowFromSparse(0, counts.nonZeroCols(), jitterValues)
        counts += sparseBitmap

        maxSparsity = int(self.unionSparsity * self.n)
        w = min(numNonZeros, maxSparsity)

        # Return top w most popular positions
        return tuple(x[1] for x in counts.getNonZerosSorted())[:w]
def countBitFrequenciesForTerms(client, lines,
                                acceptanceProbability = 0.1,
                                usePlaceholderEncoding = True,
                                percentSparsity = 0.0102):
  # Accumulate counts by inplace-adding sparse matrices
  skippedWords = {}
  counts = SparseMatrix()
  width = RETINA_SIZES[client.retina]["width"]
  height = RETINA_SIZES[client.retina]["height"]
  counts.resize(1, width*height)

  # Pre-allocate buffer sparse matrix
  sparseBitmap = SparseMatrix()
  sparseBitmap.resize(1, width*height)

  # Accumulate counts for each bit for each word
  numWords=0
  numLines=0
  for line in lines:
    tokens = TextPreprocess().tokenize(line)
    for term in tokens:

      p = random.uniform(0,1)
      if p <= acceptanceProbability:
        if usePlaceholderEncoding:
          random.seed(term)
          bitmap = random.sample(xrange(width*height),
                                 int(width*height*percentSparsity))
          bitmap.sort()
          random.seed(p)
        else:
          try:
            bitmap = client.getBitmap(term)["fingerprint"]["positions"]
          except Exception as err:
            print "Skipping '{}', reason: {}".format(term, str(err))
            continue

          if not bitmap:
            skippedWords[term] = skippedWords.get(term,0)+1
            # print "Skipping '{}', reason: empty".format(term)
            continue

        sparseBitmap.setRowFromSparse(0, bitmap, [1]*len(bitmap))
        counts += sparseBitmap
        numWords += 1

    numLines += 1
    if numLines%1000==0:
      print "...processed=",numLines,"lines and",numWords,"words"

  # Compute normalized version of counts as a separate matrix
  frequencies = SparseMatrix()
  frequencies.resize(1, width*height)
  frequencies.copy(counts)
  frequencies.divide(float(numWords))

  # Wrap up by printing some statistics and then saving the normalized version
  print "Processed",numLines,"lines"
  printFrequencyStatistics(counts, frequencies, numWords, width*height)

  frequencyFilename = "bit_frequencies_"+client.retina+".pkl"
  print "Saving frequency matrix in",frequencyFilename
  with open(frequencyFilename, "wb") as frequencyPickleFile:
    pickle.dump(frequencies, frequencyPickleFile)

  print "These words were skipped N times because of empty bitmap result"
  print skippedWords

  return counts
def countRandomBitFrequencies(numTerms = 100000, percentSparsity = 0.01):
  """Create a uniformly random counts matrix through sampling."""
  # Accumulate counts by inplace-adding sparse matrices
  counts = SparseMatrix()
  size = 128*128
  counts.resize(1, size)

  # Pre-allocate buffer sparse matrix
  sparseBitmap = SparseMatrix()
  sparseBitmap.resize(1, size)

  random.seed(42)

  # Accumulate counts for each bit for each word
  numWords=0
  for term in xrange(numTerms):
    bitmap = random.sample(xrange(size), int(size*percentSparsity))
    bitmap.sort()

    sparseBitmap.setRowFromSparse(0, bitmap, [1]*len(bitmap))
    counts += sparseBitmap
    numWords += 1

  # Compute normalized version of counts as a separate matrix
  frequencies = SparseMatrix()
  frequencies.resize(1, size)
  frequencies.copy(counts)
  frequencies.divide(float(numWords))

  # Wrap up by printing some statistics and then saving the normalized version
  printFrequencyStatistics(counts, frequencies, numWords, size)

  frequencyFilename = "bit_frequencies_random.pkl"
  print "Saving frequency matrix in",frequencyFilename
  with open(frequencyFilename, "wb") as frequencyPickleFile:
    pickle.dump(frequencies, frequencyPickleFile)

  return counts
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,
               lateralInputWidths=(),
               cellCount=4096,
               sdrSize=40,
               onlineLearning = False,
               maxSdrSize = None,
               minSdrSize = None,

               # Proximal
               synPermProximalInc=0.1,
               synPermProximalDec=0.001,
               initialProximalPermanence=0.6,
               sampleSizeProximal=20,
               minThresholdProximal=10,
               connectedPermanenceProximal=0.50,
               predictedInhibitionThreshold=20,

               # Distal
               synPermDistalInc=0.1,
               synPermDistalDec=0.001,
               initialDistalPermanence=0.6,
               sampleSizeDistal=20,
               activationThresholdDistal=13,
               connectedPermanenceDistal=0.50,
               inertiaFactor=1.,

               seed=42):
    """
    Parameters:
    ----------------------------
    @param  inputWidth (int)
            The number of bits in the feedforward input

    @param  lateralInputWidths (list of ints)
            The number of bits in each lateral input

    @param  sdrSize (int)
            The number of active cells in an object SDR

    @param  onlineLearning (Bool)
            Whether or not the column pooler should learn in online mode.

    @param  maxSdrSize (int)
            The maximum SDR size for learning.  If the column pooler has more
            than this many cells active, it will refuse to learn.  This serves
            to stop the pooler from learning when it is uncertain of what object
            it is sensing.

    @param  minSdrSize (int)
            The minimum SDR size for learning.  If the column pooler has fewer
            than this many active cells, it will create a new representation
            and learn that instead.  This serves to create separate
            representations for different objects and sequences.

            If online learning is enabled, this parameter should be at least
            inertiaFactor*sdrSize.  Otherwise, two different objects may be
            incorrectly inferred to be the same, as SDRs may still be active
            enough to learn even after inertial decay.

    @param  synPermProximalInc (float)
            Permanence increment for proximal synapses

    @param  synPermProximalDec (float)
            Permanence decrement for proximal synapses

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

    @param  sampleSizeProximal (int)
            Number of proximal synapses a cell should grow to each feedforward
            pattern, or -1 to connect to every active bit

    @param  minThresholdProximal (int)
            Number of active synapses required for a cell to have feedforward
            support

    @param  connectedPermanenceProximal (float)
            Permanence required for a proximal synapse to be connected

    @param  predictedInhibitionThreshold (int)
            How much predicted input must be present for inhibitory behavior
            to be triggered.  Only has effects if onlineLearning is true.

    @param  synPermDistalInc (float)
            Permanence increment for distal synapses

    @param  synPermDistalDec (float)
            Permanence decrement for distal synapses

    @param  sampleSizeDistal (int)
            Number of distal synapses a cell should grow to each lateral
            pattern, or -1 to connect to every active bit

    @param  initialDistalPermanence (float)
            Initial permanence value for distal synapses

    @param  activationThresholdDistal (int)
            Number of active synapses required to activate a distal segment

    @param  connectedPermanenceDistal (float)
            Permanence required for a distal synapse to be connected

    @param  inertiaFactor (float)
            The proportion of previously active cells that remain
            active in the next timestep due to inertia (in the absence of
            inhibition).  If onlineLearning is enabled, should be at most
            1 - learningTolerance, or representations may incorrectly become
            mixed.

    @param  seed (int)
            Random number generator seed
    """

    assert maxSdrSize is None or maxSdrSize >= sdrSize
    assert minSdrSize is None or minSdrSize <= sdrSize

    self.inputWidth = inputWidth
    self.cellCount = cellCount
    self.sdrSize = sdrSize
    self.onlineLearning = onlineLearning
    if maxSdrSize is None:
      self.maxSdrSize = sdrSize
    else:
      self.maxSdrSize = maxSdrSize
    if minSdrSize is None:
      self.minSdrSize = sdrSize
    else:
      self.minSdrSize = minSdrSize
    self.synPermProximalInc = synPermProximalInc
    self.synPermProximalDec = synPermProximalDec
    self.initialProximalPermanence = initialProximalPermanence
    self.connectedPermanenceProximal = connectedPermanenceProximal
    self.sampleSizeProximal = sampleSizeProximal
    self.minThresholdProximal = minThresholdProximal
    self.predictedInhibitionThreshold = predictedInhibitionThreshold
    self.synPermDistalInc = synPermDistalInc
    self.synPermDistalDec = synPermDistalDec
    self.initialDistalPermanence = initialDistalPermanence
    self.connectedPermanenceDistal = connectedPermanenceDistal
    self.sampleSizeDistal = sampleSizeDistal
    self.activationThresholdDistal = activationThresholdDistal
    self.inertiaFactor = inertiaFactor

    self.activeCells = numpy.empty(0, dtype="uint32")
    self._random = Random(seed)

    # These sparse matrices will hold the synapses for each segment.
    # Each row represents one segment on a cell, so each cell potentially has
    # 1 proximal segment and 1+len(lateralInputWidths) distal segments.
    self.proximalPermanences = SparseMatrix(cellCount, inputWidth)
    self.internalDistalPermanences = SparseMatrix(cellCount, cellCount)
    self.distalPermanences = tuple(SparseMatrix(cellCount, n)
                                   for n in lateralInputWidths)

    self.useInertia=True


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

    @param  feedforwardInput (sequence)
            Sorted indices of active feedforward input bits

    @param  lateralInputs (list of sequences)
            For each lateral layer, a list of sorted indices of active lateral
            input bits

    @param  feedforwardGrowthCandidates (sequence or None)
            Sorted indices of feedforward input bits that active cells may grow
            new synapses to. If None, the entire feedforwardInput is used.

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

    @param predictedInput (sequence)
           Sorted indices of predicted cells in the TM layer.
    """

    if feedforwardGrowthCandidates is None:
      feedforwardGrowthCandidates = feedforwardInput

    # inference step
    if not learn:
      self._computeInferenceMode(feedforwardInput, lateralInputs)

    # learning step
    elif not self.onlineLearning:
      self._computeLearningMode(feedforwardInput, lateralInputs,
                                feedforwardGrowthCandidates)
    # online learning step
    else:
      if (predictedInput is not None and
          len(predictedInput) > self.predictedInhibitionThreshold):
        predictedActiveInput = numpy.intersect1d(feedforwardInput,
                                                 predictedInput)
        predictedGrowthCandidates = numpy.intersect1d(
            feedforwardGrowthCandidates, predictedInput)
        self._computeInferenceMode(predictedActiveInput, lateralInputs)
        self._computeLearningMode(predictedActiveInput, lateralInputs,
                                  feedforwardGrowthCandidates)
      elif not self.minSdrSize <= len(self.activeCells) <= self.maxSdrSize:
        # If the pooler doesn't have a single representation, try to infer one,
        # before actually attempting to learn.
        self._computeInferenceMode(feedforwardInput, lateralInputs)
        self._computeLearningMode(feedforwardInput, lateralInputs,
                                  feedforwardGrowthCandidates)
      else:
        # If there isn't predicted input and we have a single SDR,
        # we are extending that representation and should just learn.
        self._computeLearningMode(feedforwardInput, lateralInputs,
                                  feedforwardGrowthCandidates)


  def _computeLearningMode(self, feedforwardInput, lateralInputs,
                                 feedforwardGrowthCandidates):
    """
    Learning mode: we are learning a new object in an online fashion. If there
    is no prior activity, we randomly activate 'sdrSize' cells and create
    connections to incoming input. If there was prior activity, we maintain it.
    If we have a union, we simply do not learn at all.

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

    Parameters:
    ----------------------------
    @param  feedforwardInput (sequence)
            Sorted indices of active feedforward input bits

    @param  lateralInputs (list of sequences)
            For each lateral layer, a list of sorted indices of active lateral
            input bits

    @param  feedforwardGrowthCandidates (sequence or None)
            Sorted indices of feedforward input bits that the active cells may
            grow new synapses to.  This is assumed to be the predicted active
            cells of the input layer.
    """
    prevActiveCells = self.activeCells

    # If there are not enough previously active cells, then we are no longer on
    # a familiar object.  Either our representation decayed due to the passage
    # of time (i.e. we moved somewhere else) or we were mistaken.  Either way,
    # create a new SDR and learn on it.
    # This case is the only way different object representations are created.
    # enforce the active cells in the output layer
    if len(self.activeCells) < self.minSdrSize:
      self.activeCells = _sampleRange(self._random,
                                      0, self.numberOfCells(),
                                      step=1, k=self.sdrSize)
      self.activeCells.sort()

    # If we have a union of cells active, don't learn.  This primarily affects
    # online learning.
    if len(self.activeCells) > self.maxSdrSize:
      return

    # Finally, now that we have decided which cells we should be learning on, do
    # the actual learning.
    if len(feedforwardInput) > 0:
      self._learn(self.proximalPermanences, self._random,
                  self.activeCells, feedforwardInput,
                  feedforwardGrowthCandidates, self.sampleSizeProximal,
                  self.initialProximalPermanence, self.synPermProximalInc,
                  self.synPermProximalDec, self.connectedPermanenceProximal)

      # External distal learning
      for i, lateralInput in enumerate(lateralInputs):
        self._learn(self.distalPermanences[i], self._random,
                    self.activeCells, lateralInput, lateralInput,
                    self.sampleSizeDistal, self.initialDistalPermanence,
                    self.synPermDistalInc, self.synPermDistalDec,
                    self.connectedPermanenceDistal)

      # Internal distal learning
      self._learn(self.internalDistalPermanences, self._random,
                  self.activeCells, prevActiveCells, prevActiveCells,
                  self.sampleSizeDistal, self.initialDistalPermanence,
                  self.synPermDistalInc, self.synPermDistalDec,
                  self.connectedPermanenceDistal)


  def _computeInferenceMode(self, feedforwardInput, lateralInputs):
    """
    Inference mode: if there is some feedforward activity, perform
    spatial pooling on it to recognize previously known objects, then use
    lateral activity to activate a subset of the cells with feedforward
    support. If there is no feedforward activity, use lateral activity to
    activate a subset of the previous active cells.

    Parameters:
    ----------------------------
    @param  feedforwardInput (sequence)
            Sorted indices of active feedforward input bits

    @param  lateralInputs (list of sequences)
            For each lateral layer, a list of sorted indices of active lateral
            input bits
    """

    prevActiveCells = self.activeCells

    # Calculate the feedforward supported cells
    overlaps = self.proximalPermanences.rightVecSumAtNZGteThresholdSparse(
      feedforwardInput, self.connectedPermanenceProximal)
    feedforwardSupportedCells = numpy.where(
      overlaps >= self.minThresholdProximal)[0]

    # Calculate the number of active segments on each cell
    numActiveSegmentsByCell = numpy.zeros(self.cellCount, dtype="int")
    overlaps = self.internalDistalPermanences.rightVecSumAtNZGteThresholdSparse(
      prevActiveCells, self.connectedPermanenceDistal)
    numActiveSegmentsByCell[overlaps >= self.activationThresholdDistal] += 1
    for i, lateralInput in enumerate(lateralInputs):
      overlaps = self.distalPermanences[i].rightVecSumAtNZGteThresholdSparse(
        lateralInput, self.connectedPermanenceDistal)
      numActiveSegmentsByCell[overlaps >= self.activationThresholdDistal] += 1

    chosenCells = []

    # First, activate the FF-supported cells that have the highest number of
    # lateral active segments (as long as it's not 0)
    if len(feedforwardSupportedCells) == 0:
      pass
    else:
      numActiveSegsForFFSuppCells = numActiveSegmentsByCell[
        feedforwardSupportedCells]

      # This loop will select the FF-supported AND laterally-active cells, in
      # order of descending lateral activation, until we exceed the sdrSize
      # quorum - but will exclude cells with 0 lateral active segments.
      ttop = numpy.max(numActiveSegsForFFSuppCells)
      while ttop > 0 and len(chosenCells) < self.sdrSize:
        chosenCells = numpy.union1d(chosenCells,
                    feedforwardSupportedCells[numActiveSegsForFFSuppCells >= ttop])
        ttop -= 1

    # If we haven't filled the sdrSize quorum, add in inertial cells.
    if len(chosenCells) < self.sdrSize:
      if self.useInertia:
        prevCells = numpy.setdiff1d(prevActiveCells, chosenCells)
        inertialCap = int(len(prevCells) * self.inertiaFactor)
        if inertialCap > 0:
          numActiveSegsForPrevCells = numActiveSegmentsByCell[prevCells]
          # We sort the previously-active cells by number of active lateral
          # segments (this really helps).  We then activate them in order of
          # descending lateral activation.
          sortIndices = numpy.argsort(numActiveSegsForPrevCells)[::-1]
          prevCells = prevCells[sortIndices]
          numActiveSegsForPrevCells = numActiveSegsForPrevCells[sortIndices]

          # We use inertiaFactor to limit the number of previously-active cells
          # which can become active, forcing decay even if we are below quota.
          prevCells = prevCells[:inertialCap]
          numActiveSegsForPrevCells = numActiveSegsForPrevCells[:inertialCap]

          # Activate groups of previously active cells by order of their lateral
          # support until we either meet quota or run out of cells.
          ttop = numpy.max(numActiveSegsForPrevCells)
          while ttop >= 0 and len(chosenCells) < self.sdrSize:
            chosenCells = numpy.union1d(chosenCells,
                        prevCells[numActiveSegsForPrevCells >= ttop])
            ttop -= 1

    # If we haven't filled the sdrSize quorum, add cells that have feedforward
    # support and no lateral support.
    discrepancy = self.sdrSize - len(chosenCells)
    if discrepancy > 0:
      remFFcells = numpy.setdiff1d(feedforwardSupportedCells, chosenCells)

      # Inhibit cells proportionally to the number of cells that have already
      # been chosen. If ~0 have been chosen activate ~all of the feedforward
      # supported cells. If ~sdrSize have been chosen, activate very few of
      # the feedforward supported cells.

      # Use the discrepancy:sdrSize ratio to determine the number of cells to
      # activate.
      n = (len(remFFcells) * discrepancy) // self.sdrSize
      # Activate at least 'discrepancy' cells.
      n = max(n, discrepancy)
      # If there aren't 'n' available, activate all of the available cells.
      n = min(n, len(remFFcells))

      if len(remFFcells) > n:
        selected = _sample(self._random, remFFcells, n)
        chosenCells = numpy.append(chosenCells, selected)
      else:
        chosenCells = numpy.append(chosenCells, remFFcells)

    chosenCells.sort()
    self.activeCells = numpy.asarray(chosenCells, dtype="uint32")


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


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


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


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

    Parameters:
    ----------------------------
    @param  cells (iterable)
            Indices of the cells. If None return count for all cells.
    """
    if cells is None:
      cells = xrange(self.numberOfCells())

    return _countWhereGreaterEqualInRows(self.proximalPermanences, cells,
                                         self.connectedPermanenceProximal)


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

    Parameters:
    ----------------------------
    @param  cells (iterable)
            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=None):
    """
    Returns the total number of distal segments for these cells.

    A segment "exists" if its row in the matrix has any permanence values > 0.

    Parameters:
    ----------------------------
    @param  cells (iterable)
            Indices of the cells
    """
    if cells is None:
      cells = xrange(self.numberOfCells())

    n = 0

    for cell in cells:
      if self.internalDistalPermanences.nNonZerosOnRow(cell) > 0:
        n += 1

      for permanences in self.distalPermanences:
        if permanences.nNonZerosOnRow(cell) > 0:
          n += 1

    return n


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

    Parameters:
    ----------------------------
    @param  cells (iterable)
            Indices of the cells. If None return count for all cells.
    """
    if cells is None:
      cells = xrange(self.numberOfCells())

    n = _countWhereGreaterEqualInRows(self.internalDistalPermanences, cells,
                                      self.connectedPermanenceDistal)

    for permanences in self.distalPermanences:
      n += _countWhereGreaterEqualInRows(permanences, cells,
                                         self.connectedPermanenceDistal)

    return n


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

    Parameters:
    ----------------------------
    @param  cells (iterable)
            Indices of the cells
    """
    if cells is None:
      cells = xrange(self.numberOfCells())
    n = 0
    for cell in cells:
      n += self.internalDistalPermanences.nNonZerosOnRow(cell)

      for permanences in self.distalPermanences:
        n += permanences.nNonZerosOnRow(cell)
    return n


  def reset(self):
    """
    Reset internal states. When learning this signifies we are to learn a
    unique new object.
    """
    self.activeCells = numpy.empty(0, dtype="uint32")

  def getUseInertia(self):
    """
    Get whether we actually use inertia  (i.e. a fraction of the
    previously active cells remain active at the next time step unless
    inhibited by cells with both feedforward and lateral support).
    @return (Bool) Whether inertia is used.
    """
    return self.useInertia

  def setUseInertia(self, useInertia):
    """
    Sets whether we actually use inertia (i.e. a fraction of the
    previously active cells remain active at the next time step unless
    inhibited by cells with both feedforward and lateral support).
    @param useInertia (Bool) Whether inertia is used.
    """
    self.useInertia = useInertia

  @staticmethod
  def _learn(# mutated args
             permanences, rng,

             # activity
             activeCells, activeInput, growthCandidateInput,

             # configuration
             sampleSize, initialPermanence, permanenceIncrement,
             permanenceDecrement, connectedPermanence):
    """
    For each active cell, reinforce active synapses, punish inactive synapses,
    and grow new synapses to a subset of the active input bits that the cell
    isn't already connected to.

    Parameters:
    ----------------------------
    @param  permanences (SparseMatrix)
            Matrix of permanences, with cells as rows and inputs as columns

    @param  rng (Random)
            Random number generator

    @param  activeCells (sorted sequence)
            Sorted list of the cells that are learning

    @param  activeInput (sorted sequence)
            Sorted list of active bits in the input

    @param  growthCandidateInput (sorted sequence)
            Sorted list of active bits in the input that the activeCells may
            grow new synapses to

    For remaining parameters, see the __init__ docstring.
    """

    permanences.incrementNonZerosOnOuter(
      activeCells, activeInput, permanenceIncrement)
    permanences.incrementNonZerosOnRowsExcludingCols(
      activeCells, activeInput, -permanenceDecrement)
    permanences.clipRowsBelowAndAbove(
      activeCells, 0.0, 1.0)
    if sampleSize == -1:
      permanences.setZerosOnOuter(
        activeCells, activeInput, initialPermanence)
    else:
      existingSynapseCounts = permanences.nNonZerosPerRowOnCols(
        activeCells, activeInput)

      maxNewByCell = numpy.empty(len(activeCells), dtype="int32")
      numpy.subtract(sampleSize, existingSynapseCounts, out=maxNewByCell)

      permanences.setRandomZerosOnOuter(
        activeCells, growthCandidateInput, maxNewByCell, initialPermanence, rng)
  def __init__(self,
               inputWidth,
               lateralInputWidths=(),
               cellCount=4096,
               sdrSize=40,
               onlineLearning = False,
               maxSdrSize = None,
               minSdrSize = None,

               # Proximal
               synPermProximalInc=0.1,
               synPermProximalDec=0.001,
               initialProximalPermanence=0.6,
               sampleSizeProximal=20,
               minThresholdProximal=10,
               connectedPermanenceProximal=0.50,
               predictedInhibitionThreshold=20,

               # Distal
               synPermDistalInc=0.1,
               synPermDistalDec=0.001,
               initialDistalPermanence=0.6,
               sampleSizeDistal=20,
               activationThresholdDistal=13,
               connectedPermanenceDistal=0.50,
               inertiaFactor=1.,

               seed=42):
    """
    Parameters:
    ----------------------------
    @param  inputWidth (int)
            The number of bits in the feedforward input

    @param  lateralInputWidths (list of ints)
            The number of bits in each lateral input

    @param  sdrSize (int)
            The number of active cells in an object SDR

    @param  onlineLearning (Bool)
            Whether or not the column pooler should learn in online mode.

    @param  maxSdrSize (int)
            The maximum SDR size for learning.  If the column pooler has more
            than this many cells active, it will refuse to learn.  This serves
            to stop the pooler from learning when it is uncertain of what object
            it is sensing.

    @param  minSdrSize (int)
            The minimum SDR size for learning.  If the column pooler has fewer
            than this many active cells, it will create a new representation
            and learn that instead.  This serves to create separate
            representations for different objects and sequences.

            If online learning is enabled, this parameter should be at least
            inertiaFactor*sdrSize.  Otherwise, two different objects may be
            incorrectly inferred to be the same, as SDRs may still be active
            enough to learn even after inertial decay.

    @param  synPermProximalInc (float)
            Permanence increment for proximal synapses

    @param  synPermProximalDec (float)
            Permanence decrement for proximal synapses

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

    @param  sampleSizeProximal (int)
            Number of proximal synapses a cell should grow to each feedforward
            pattern, or -1 to connect to every active bit

    @param  minThresholdProximal (int)
            Number of active synapses required for a cell to have feedforward
            support

    @param  connectedPermanenceProximal (float)
            Permanence required for a proximal synapse to be connected

    @param  predictedInhibitionThreshold (int)
            How much predicted input must be present for inhibitory behavior
            to be triggered.  Only has effects if onlineLearning is true.

    @param  synPermDistalInc (float)
            Permanence increment for distal synapses

    @param  synPermDistalDec (float)
            Permanence decrement for distal synapses

    @param  sampleSizeDistal (int)
            Number of distal synapses a cell should grow to each lateral
            pattern, or -1 to connect to every active bit

    @param  initialDistalPermanence (float)
            Initial permanence value for distal synapses

    @param  activationThresholdDistal (int)
            Number of active synapses required to activate a distal segment

    @param  connectedPermanenceDistal (float)
            Permanence required for a distal synapse to be connected

    @param  inertiaFactor (float)
            The proportion of previously active cells that remain
            active in the next timestep due to inertia (in the absence of
            inhibition).  If onlineLearning is enabled, should be at most
            1 - learningTolerance, or representations may incorrectly become
            mixed.

    @param  seed (int)
            Random number generator seed
    """

    assert maxSdrSize is None or maxSdrSize >= sdrSize
    assert minSdrSize is None or minSdrSize <= sdrSize

    self.inputWidth = inputWidth
    self.cellCount = cellCount
    self.sdrSize = sdrSize
    self.onlineLearning = onlineLearning
    if maxSdrSize is None:
      self.maxSdrSize = sdrSize
    else:
      self.maxSdrSize = maxSdrSize
    if minSdrSize is None:
      self.minSdrSize = sdrSize
    else:
      self.minSdrSize = minSdrSize
    self.synPermProximalInc = synPermProximalInc
    self.synPermProximalDec = synPermProximalDec
    self.initialProximalPermanence = initialProximalPermanence
    self.connectedPermanenceProximal = connectedPermanenceProximal
    self.sampleSizeProximal = sampleSizeProximal
    self.minThresholdProximal = minThresholdProximal
    self.predictedInhibitionThreshold = predictedInhibitionThreshold
    self.synPermDistalInc = synPermDistalInc
    self.synPermDistalDec = synPermDistalDec
    self.initialDistalPermanence = initialDistalPermanence
    self.connectedPermanenceDistal = connectedPermanenceDistal
    self.sampleSizeDistal = sampleSizeDistal
    self.activationThresholdDistal = activationThresholdDistal
    self.inertiaFactor = inertiaFactor

    self.activeCells = numpy.empty(0, dtype="uint32")
    self._random = Random(seed)

    # These sparse matrices will hold the synapses for each segment.
    # Each row represents one segment on a cell, so each cell potentially has
    # 1 proximal segment and 1+len(lateralInputWidths) distal segments.
    self.proximalPermanences = SparseMatrix(cellCount, inputWidth)
    self.internalDistalPermanences = SparseMatrix(cellCount, cellCount)
    self.distalPermanences = tuple(SparseMatrix(cellCount, n)
                                   for n in lateralInputWidths)

    self.useInertia=True
Beispiel #23
0
    def __init__(
        self,
        name,
        numCorticalColumns=1,
        inputSize=1024,
        numInputBits=20,
        externalInputSize=1024,
        numExternalInputBits=20,
        L2Overrides=None,
        L4Overrides=None,
        seed=42,
        logCalls=False,
        objectNamesAreIndices=False,
    ):
        """
    Creates the network.

    Parameters:
    ----------------------------
    @param   TMOverrides (dict)
             Parameters to override in the TM region
    """

        # Handle logging - this has to be done first
        self.logCalls = logCalls

        registerAllResearchRegions()
        self.name = name

        self.numLearningPoints = 1
        self.numColumns = numCorticalColumns
        self.inputSize = inputSize
        self.externalInputSize = externalInputSize
        self.numInputBits = numInputBits
        self.objectNamesAreIndices = objectNamesAreIndices
        self.numExternalInputBits = numExternalInputBits

        # seed
        self.seed = seed
        random.seed(seed)

        # Create default parameters and then update with overrides
        self.config = {
            "networkType": "CombinedSequenceColumn",
            "numCorticalColumns": numCorticalColumns,
            "externalInputSize": externalInputSize,
            "sensorInputSize": inputSize,
            "enableFeedback": False,
            "L2Params": self.getDefaultL2Params(inputSize, numInputBits),
        }
        self.config["L4Params"] = self._getDefaultCombinedL4Params(
            self.numInputBits, self.inputSize, self.numExternalInputBits,
            self.externalInputSize, self.config["L2Params"]["cellCount"])

        if L2Overrides is not None:
            self.config["L2Params"].update(L2Overrides)

        if L4Overrides is not None:
            self.config["L4Params"].update(L4Overrides)

        pprint.pprint(self.config)

        # Recreate network including TM parameters
        self.network = createNetwork(self.config)
        self.sensorInputs = []
        self.externalInputs = []
        self.L2Regions = []
        self.L4Regions = []

        for i in xrange(self.numColumns):
            self.sensorInputs.append(self.network.regions["sensorInput_" +
                                                          str(i)].getSelf())
            self.externalInputs.append(self.network.regions["externalInput_" +
                                                            str(i)].getSelf())
            self.L2Regions.append(self.network.regions["L2Column_" + str(i)])
            self.L4Regions.append(self.network.regions["L4Column_" + str(i)])

        self.L2Columns = [region.getSelf() for region in self.L2Regions]
        self.L4Columns = [region.getSelf() for region in self.L4Regions]

        # will be populated during training
        self.objectL2Representations = {}
        self.objectL2RepresentationsMatrices = [
            SparseMatrix(0, self.config["L2Params"]["cellCount"])
            for _ in xrange(self.numColumns)
        ]
        self.objectNameToIndex = {}
        self.statistics = []
    def __init__(
        self,
        name,
        numCorticalColumns=1,
        inputSize=1024,
        numInputBits=20,
        externalInputSize=1024,
        numExternalInputBits=20,
        L2Overrides=None,
        L2RegionType="py.ColumnPoolerRegion",
        L4RegionType="py.ApicalTMPairRegion",
        networkType="MultipleL4L2Columns",
        implementation=None,
        longDistanceConnections=0,
        maxConnectionDistance=1,
        columnPositions=None,
        L4Overrides=None,
        numLearningPoints=3,
        seed=42,
        logCalls=False,
        enableLateralSP=False,
        lateralSPOverrides=None,
        enableFeedForwardSP=False,
        feedForwardSPOverrides=None,
        objectNamesAreIndices=False,
        enableFeedback=True,
        maxSegmentsPerCell=10,
    ):
        """
    Creates the network.

    Parameters:
    ----------------------------
    @param   name (str)
             Experiment name

    @param   numCorticalColumns (int)
             Number of cortical columns in the network

    @param   inputSize (int)
             Size of the sensory input

    @param   numInputBits (int)
             Number of ON bits in the generated input patterns

    @param   externalInputSize (int)
             Size of the lateral input to L4 regions

    @param   numExternalInputBits (int)
             Number of ON bits in the external input patterns

    @param   L2Overrides (dict)
             Parameters to override in the L2 region

    @param   L2RegionType (string)
             The type of region to use for L2

    @param   L4RegionType (string)
             The type of region to use for L4

    @param   networkType (string)
             Which type of L2L4 network to create.  If topology is being used,
             it should be specified here.  Possible values for this parameter
             are "MultipleL4L2Columns", "MultipleL4L2ColumnsWithTopology" and
             "L4L2Column"

    @param  longDistanceConnections (float)
             The probability that a column will randomly connect to a distant
             column.  Should be in [0, 1).  Only relevant when using multiple
             columns with topology.

    @param   L4Overrides (dict)
             Parameters to override in the L4 region

    @param   numLearningPoints (int)
             Number of times each pair should be seen to be learnt

    @param   logCalls (bool)
             If true, calls to main functions will be logged internally. The
             log can then be saved with saveLogs(). This allows us to recreate
             the complete network behavior using rerunExperimentFromLogfile
             which is very useful for debugging.

    @param   enableLateralSP (bool)
             If true, Spatial Pooler will be added between external input and
             L4 lateral input

    @param   lateralSPOverrides
             Parameters to override in the lateral SP region

    @param   enableFeedForwardSP (bool)
             If true, Spatial Pooler will be added between external input and
             L4 feed-forward input

    @param   feedForwardSPOverrides
             Parameters to override in the feed-forward SP region

    @param   objectNamesAreIndices (bool)
             If True, object names are used as indices in the
             getCurrentObjectOverlaps method. Object names must be positive
             integers. If False, object names can be strings, and indices will
             be assigned to each object name.

    @param   enableFeedback (bool)
             If True, enable feedback between L2 and L4

    """
        # Handle logging - this has to be done first
        self.logCalls = logCalls

        registerAllResearchRegions()
        self.name = name

        self.numLearningPoints = numLearningPoints
        self.numColumns = numCorticalColumns
        self.inputSize = inputSize
        self.externalInputSize = externalInputSize
        self.numInputBits = numInputBits
        self.objectNamesAreIndices = objectNamesAreIndices

        # seed
        self.seed = seed
        random.seed(seed)

        # update parameters with overrides
        if implementation is None:
            self.config = {
                "networkType":
                networkType,
                "longDistanceConnections":
                longDistanceConnections,
                "enableFeedback":
                enableFeedback,
                "numCorticalColumns":
                numCorticalColumns,
                "externalInputSize":
                externalInputSize,
                "sensorInputSize":
                inputSize,
                "L2RegionType":
                L2RegionType,
                "L4RegionType":
                L4RegionType,
                "L4Params":
                self.getDefaultL4Params(inputSize, numExternalInputBits),
                "L2Params":
                self.getDefaultL2Params(inputSize, numInputBits),
            }

        else:
            if "Bayesian" in implementation:
                self.config = {
                    "networkType":
                    networkType,
                    "longDistanceConnections":
                    longDistanceConnections,
                    "enableFeedback":
                    enableFeedback,
                    "numCorticalColumns":
                    numCorticalColumns,
                    "externalInputSize":
                    externalInputSize,
                    "sensorInputSize":
                    inputSize,
                    "L2RegionType":
                    L2RegionType,
                    "L4RegionType":
                    L4RegionType,
                    "L4Params":
                    self.getBayesianL4Params(inputSize, numExternalInputBits),
                    "L2Params":
                    self.getBayesianL2Params(inputSize, numInputBits),
                }
                self.config["L4Params"][
                    "maxSegmentsPerCell"] = maxSegmentsPerCell
                self.config["L4Params"]["implementation"] = implementation
                self.config["L2Params"]["implementation"] = implementation

        if enableLateralSP:
            self.config["lateralSPParams"] = self.getDefaultLateralSPParams(
                inputSize)
            if lateralSPOverrides:
                self.config["lateralSPParams"].update(lateralSPOverrides)

        if enableFeedForwardSP:
            self.config[
                "feedForwardSPParams"] = self.getDefaultFeedForwardSPParams(
                    inputSize)
            if feedForwardSPOverrides:
                self.config["feedForwardSPParams"].update(
                    feedForwardSPOverrides)

        if "Topology" in self.config["networkType"]:
            self.config["maxConnectionDistance"] = maxConnectionDistance

            # Generate a grid for cortical columns.  Will attempt to generate a full
            # square grid, and cut out positions starting from the bottom-right if the
            # number of cortical columns is not a perfect square.
            if columnPositions is None:
                columnPositions = []
                side_length = int(np.ceil(np.sqrt(numCorticalColumns)))
                for i in range(side_length):
                    for j in range(side_length):
                        columnPositions.append((i, j))
            self.config[
                "columnPositions"] = columnPositions[:numCorticalColumns]
            self.config["longDistanceConnections"] = longDistanceConnections

        if L2Overrides is not None:
            self.config["L2Params"].update(L2Overrides)

        if L4Overrides is not None:
            self.config["L4Params"].update(L4Overrides)

        # create network
        self.network = createNetwork(self.config)
        self.sensorInputs = []
        self.externalInputs = []
        self.L4Regions = []
        self.L2Regions = []

        for i in xrange(self.numColumns):
            self.sensorInputs.append(self.network.regions["sensorInput_" +
                                                          str(i)].getSelf())
            self.externalInputs.append(self.network.regions["externalInput_" +
                                                            str(i)].getSelf())
            self.L4Regions.append(self.network.regions["L4Column_" + str(i)])
            self.L2Regions.append(self.network.regions["L2Column_" + str(i)])

        self.L4Columns = [region.getSelf() for region in self.L4Regions]
        self.L2Columns = [region.getSelf() for region in self.L2Regions]

        # will be populated during training
        self.objectL2Representations = {}
        self.objectL2RepresentationsMatrices = [
            SparseMatrix(0, self.config["L2Params"]["cellCount"])
            for _ in xrange(self.numColumns)
        ]
        self.objectNameToIndex = {}
        self.statistics = []
Beispiel #25
0
def countBitFrequenciesForTerms(client,
                                lines,
                                acceptanceProbability=0.1,
                                usePlaceholderEncoding=True,
                                percentSparsity=0.0102):
    # Accumulate counts by inplace-adding sparse matrices
    skippedWords = {}
    counts = SparseMatrix()
    width = RETINA_SIZES[client.retina]["width"]
    height = RETINA_SIZES[client.retina]["height"]
    counts.resize(1, width * height)

    # Pre-allocate buffer sparse matrix
    sparseBitmap = SparseMatrix()
    sparseBitmap.resize(1, width * height)

    # Accumulate counts for each bit for each word
    numWords = 0
    numLines = 0
    for line in lines:
        tokens = TextPreprocess().tokenize(line)
        for term in tokens:

            p = random.uniform(0, 1)
            if p <= acceptanceProbability:
                if usePlaceholderEncoding:
                    random.seed(term)
                    bitmap = random.sample(
                        xrange(width * height),
                        int(width * height * percentSparsity))
                    bitmap.sort()
                    random.seed(p)
                else:
                    try:
                        bitmap = client.getBitmap(
                            term)["fingerprint"]["positions"]
                    except Exception as err:
                        print "Skipping '{}', reason: {}".format(
                            term, str(err))
                        continue

                    if not bitmap:
                        skippedWords[term] = skippedWords.get(term, 0) + 1
                        # print "Skipping '{}', reason: empty".format(term)
                        continue

                sparseBitmap.setRowFromSparse(0, bitmap, [1] * len(bitmap))
                counts += sparseBitmap
                numWords += 1

        numLines += 1
        if numLines % 1000 == 0:
            print "...processed=", numLines, "lines and", numWords, "words"

    # Compute normalized version of counts as a separate matrix
    frequencies = SparseMatrix()
    frequencies.resize(1, width * height)
    frequencies.copy(counts)
    frequencies.divide(float(numWords))

    # Wrap up by printing some statistics and then saving the normalized version
    print "Processed", numLines, "lines"
    printFrequencyStatistics(counts, frequencies, numWords, width * height)

    frequencyFilename = "bit_frequencies_" + client.retina + ".pkl"
    print "Saving frequency matrix in", frequencyFilename
    with open(frequencyFilename, "wb") as frequencyPickleFile:
        pickle.dump(frequencies, frequencyPickleFile)

    print "These words were skipped N times because of empty bitmap result"
    print skippedWords

    return counts
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,
               lateralInputWidths=(),
               cellCount=4096,
               sdrSize=40,
               onlineLearning = False,
               maxSdrSize = None,
               minSdrSize = None,

               # Proximal
               synPermProximalInc=0.1,
               synPermProximalDec=0.001,
               initialProximalPermanence=0.6,
               sampleSizeProximal=20,
               minThresholdProximal=10,
               connectedPermanenceProximal=0.50,
               predictedInhibitionThreshold=20,

               # Distal
               synPermDistalInc=0.1,
               synPermDistalDec=0.001,
               initialDistalPermanence=0.6,
               sampleSizeDistal=20,
               activationThresholdDistal=13,
               connectedPermanenceDistal=0.50,
               distalSegmentInhibitionFactor=0.999,
               inertiaFactor=1.,

               seed=42):
    """
    Parameters:
    ----------------------------
    @param  inputWidth (int)
            The number of bits in the feedforward input

    @param  lateralInputWidths (list of ints)
            The number of bits in each lateral input

    @param  sdrSize (int)
            The number of active cells in an object SDR

    @param  onlineLearning (Bool)
            Whether or not the column pooler should learn in online mode.

    @param  maxSdrSize (int)
            The maximum SDR size for learning.  If the column pooler has more
            than this many cells active, it will refuse to learn.  This serves
            to stop the pooler from learning when it is uncertain of what object
            it is sensing.

    @param  minSdrSize (int)
            The minimum SDR size for learning.  If the column pooler has fewer
            than this many active cells, it will create a new representation
            and learn that instead.  This serves to create separate
            representations for different objects and sequences.

            If online learning is enabled, this parameter should be at least
            inertiaFactor*sdrSize.  Otherwise, two different objects may be
            incorrectly inferred to be the same, as SDRs may still be active
            enough to learn even after inertial decay.

    @param  synPermProximalInc (float)
            Permanence increment for proximal synapses

    @param  synPermProximalDec (float)
            Permanence decrement for proximal synapses

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

    @param  sampleSizeProximal (int)
            Number of proximal synapses a cell should grow to each feedforward
            pattern, or -1 to connect to every active bit

    @param  minThresholdProximal (int)
            Number of active synapses required for a cell to have feedforward
            support

    @param  connectedPermanenceProximal (float)
            Permanence required for a proximal synapse to be connected

    @param  predictedInhibitionThreshold (int)
            How much predicted input must be present for inhibitory behavior
            to be triggered.  Only has effects if onlineLearning is true.

    @param  synPermDistalInc (float)
            Permanence increment for distal synapses

    @param  synPermDistalDec (float)
            Permanence decrement for distal synapses

    @param  sampleSizeDistal (int)
            Number of distal synapses a cell should grow to each lateral
            pattern, or -1 to connect to every active bit

    @param  initialDistalPermanence (float)
            Initial permanence value for distal synapses

    @param  activationThresholdDistal (int)
            Number of active synapses required to activate a distal segment

    @param  connectedPermanenceDistal (float)
            Permanence required for a distal synapse to be connected

    @param  distalSegmentInhibitionFactor (float)
            The proportion of the highest number of active lateral segments
            necessary for a cell not to be inhibited (must be <1).

    @param  inertiaFactor (float)
            The proportion of previously active cells that remain
            active in the next timestep due to inertia (in the absence of
            inhibition).  If onlineLearning is enabled, should be at most
            1 - learningTolerance, or representations may incorrectly become
            mixed.

    @param  seed (int)
            Random number generator seed
    """

    assert distalSegmentInhibitionFactor > 0.0
    assert distalSegmentInhibitionFactor < 1.0
    assert maxSdrSize is None or maxSdrSize >= sdrSize
    assert minSdrSize is None or minSdrSize <= sdrSize

    self.inputWidth = inputWidth
    self.cellCount = cellCount
    self.sdrSize = sdrSize
    self.onlineLearning = onlineLearning
    if maxSdrSize is None:
      self.maxSdrSize = sdrSize
    else:
      self.maxSdrSize = maxSdrSize
    if minSdrSize is None:
      self.minSdrSize = sdrSize
    else:
      self.minSdrSize = minSdrSize
    self.synPermProximalInc = synPermProximalInc
    self.synPermProximalDec = synPermProximalDec
    self.initialProximalPermanence = initialProximalPermanence
    self.connectedPermanenceProximal = connectedPermanenceProximal
    self.sampleSizeProximal = sampleSizeProximal
    self.minThresholdProximal = minThresholdProximal
    self.predictedInhibitionThreshold = predictedInhibitionThreshold
    self.synPermDistalInc = synPermDistalInc
    self.synPermDistalDec = synPermDistalDec
    self.initialDistalPermanence = initialDistalPermanence
    self.connectedPermanenceDistal = connectedPermanenceDistal
    self.sampleSizeDistal = sampleSizeDistal
    self.activationThresholdDistal = activationThresholdDistal
    self.distalSegmentInhibitionFactor = distalSegmentInhibitionFactor
    self.inertiaFactor = inertiaFactor

    self.activeCells = numpy.empty(0, dtype="uint32")
    self._random = Random(seed)

    # These sparse matrices will hold the synapses for each segment.
    # Each row represents one segment on a cell, so each cell potentially has
    # 1 proximal segment and 1+len(lateralInputWidths) distal segments.
    self.proximalPermanences = SparseMatrix(cellCount, inputWidth)
    self.internalDistalPermanences = SparseMatrix(cellCount, cellCount)
    self.distalPermanences = tuple(SparseMatrix(cellCount, n)
                                   for n in lateralInputWidths)

    self.useInertia=True


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

    @param  feedforwardInput (sequence)
            Sorted indices of active feedforward input bits

    @param  lateralInputs (list of sequences)
            For each lateral layer, a list of sorted indices of active lateral
            input bits

    @param  feedforwardGrowthCandidates (sequence or None)
            Sorted indices of feedforward input bits that active cells may grow
            new synapses to. If None, the entire feedforwardInput is used.

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

    @param predictedInput (sequence)
           Sorted indices of predicted cells in the TM layer.
    """
    if feedforwardGrowthCandidates is None:
      feedforwardGrowthCandidates = feedforwardInput

    if not learn:
      self._computeInferenceMode(feedforwardInput, lateralInputs)

    elif not self.onlineLearning:
      self._computeLearningMode(feedforwardInput, lateralInputs,
                                feedforwardGrowthCandidates)
    else:
      if (predictedInput is not None and
          len(predictedInput) > self.predictedInhibitionThreshold):
        predictedActiveInput = numpy.intersect1d(feedforwardInput,
                                                 predictedInput)
        predictedGrowthCandidates = numpy.intersect1d(
            feedforwardGrowthCandidates, predictedInput)
        self._computeInferenceMode(predictedActiveInput, lateralInputs)
        self._computeLearningMode(predictedActiveInput, lateralInputs,
                                  feedforwardGrowthCandidates)
      elif not self.minSdrSize <= len(self.activeCells) <= self.maxSdrSize:
        # If the pooler doesn't have a single representation, try to infer one,
        # before actually attempting to learn.
        self._computeInferenceMode(feedforwardInput, lateralInputs)
        self._computeLearningMode(feedforwardInput, lateralInputs,
                                  feedforwardGrowthCandidates)
      else:
        # If there isn't predicted input and we have a single SDR,
        # we are extending that representation and should just learn.
        self._computeLearningMode(feedforwardInput, lateralInputs,
                                  feedforwardGrowthCandidates)


  def _computeLearningMode(self, feedforwardInput, lateralInputs,
                                 feedforwardGrowthCandidates):
    """
    Learning mode: we are learning a new object in an online fashion. If there
    is no prior activity, we randomly activate 'sdrSize' cells and create
    connections to incoming input. If there was prior activity, we maintain it.
    If we have a union, we simply do not learn at all.

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

    Parameters:
    ----------------------------
    @param  feedforwardInput (sequence)
            Sorted indices of active feedforward input bits

    @param  lateralInputs (list of sequences)
            For each lateral layer, a list of sorted indices of active lateral
            input bits

    @param  feedforwardGrowthCandidates (sequence or None)
            Sorted indices of feedforward input bits that the active cells may
            grow new synapses to.  This is assumed to be the predicted active
            cells of the input layer.
    """
    prevActiveCells = self.activeCells

    # If there are not enough previously active cells, then we are no longer on
    # a familiar object.  Either our representation decayed due to the passage
    # of time (i.e. we moved somewhere else) or we were mistaken.  Either way,
    # create a new SDR and learn on it.
    # This case is the only way different object representations are created.
    if len(self.activeCells) < self.minSdrSize:
      self.activeCells = _sampleRange(self._random,
                                      0, self.numberOfCells(),
                                      step=1, k=self.sdrSize)
      self.activeCells.sort()

    # If we have a union of cells active, don't learn.  This primarily affects
    # online learning.
    if len(self.activeCells) > self.maxSdrSize:
      return

    # Finally, now that we have decided which cells we should be learning on, do
    # the actual learning.
    if len(feedforwardInput) > 0:
      self._learn(self.proximalPermanences, self._random,
                  self.activeCells, feedforwardInput,
                  feedforwardGrowthCandidates, self.sampleSizeProximal,
                  self.initialProximalPermanence, self.synPermProximalInc,
                  self.synPermProximalDec, self.connectedPermanenceProximal)

      # External distal learning
      for i, lateralInput in enumerate(lateralInputs):
        self._learn(self.distalPermanences[i], self._random,
                    self.activeCells, lateralInput, lateralInput,
                    self.sampleSizeDistal, self.initialDistalPermanence,
                    self.synPermDistalInc, self.synPermDistalDec,
                    self.connectedPermanenceDistal)

      # Internal distal learning
      self._learn(self.internalDistalPermanences, self._random,
                  self.activeCells, prevActiveCells, prevActiveCells,
                  self.sampleSizeDistal, self.initialDistalPermanence,
                  self.synPermDistalInc, self.synPermDistalDec,
                  self.connectedPermanenceDistal)


  def _computeInferenceMode(self, feedforwardInput, lateralInputs):
    """
    Inference mode: if there is some feedforward activity, perform
    spatial pooling on it to recognize previously known objects, then use
    lateral activity to activate a subset of the cells with feedforward
    support. If there is no feedforward activity, use lateral activity to
    activate a subset of the previous active cells.

    Parameters:
    ----------------------------
    @param  feedforwardInput (sequence)
            Sorted indices of active feedforward input bits

    @param  lateralInputs (list of sequences)
            For each lateral layer, a list of sorted indices of active lateral
            input bits
    """

    prevActiveCells = self.activeCells

    # Calculate the feedforward supported cells
    overlaps = self.proximalPermanences.rightVecSumAtNZGteThresholdSparse(
      feedforwardInput, self.connectedPermanenceProximal)
    feedforwardSupportedCells = numpy.where(
      overlaps >= self.minThresholdProximal)[0]

    # Calculate the number of active segments on each cell
    numActiveSegmentsByCell = numpy.zeros(self.cellCount, dtype="int")
    overlaps = self.internalDistalPermanences.rightVecSumAtNZGteThresholdSparse(
      prevActiveCells, self.connectedPermanenceDistal)
    numActiveSegmentsByCell[overlaps >= self.activationThresholdDistal] += 1
    for i, lateralInput in enumerate(lateralInputs):
      overlaps = self.distalPermanences[i].rightVecSumAtNZGteThresholdSparse(
        lateralInput, self.connectedPermanenceDistal)
      numActiveSegmentsByCell[overlaps >= self.activationThresholdDistal] += 1

    chosenCells = []
    minNumActiveCells = int(self.sdrSize * 0.75)

    numActiveSegsForFFSuppCells = numActiveSegmentsByCell[
        feedforwardSupportedCells]

    # First, activate the FF-supported cells that have the highest number of
    # lateral active segments (as long as it's not 0)
    if len(feedforwardSupportedCells) == 0:
      pass
    else:
      # This loop will select the FF-supported AND laterally-active cells, in
      # order of descending lateral activation, until we exceed the
      # minNumActiveCells quorum - but will exclude cells with 0 lateral
      # active segments.
      ttop = numpy.max(numActiveSegsForFFSuppCells)
      while ttop > 0 and len(chosenCells) <= minNumActiveCells:
        chosenCells = numpy.union1d(chosenCells,
                    feedforwardSupportedCells[numActiveSegsForFFSuppCells >
                    self.distalSegmentInhibitionFactor * ttop])
        ttop -= 1

    # If we still haven't filled the minNumActiveCells quorum, add in the
    # FF-supported cells with 0 lateral support AND the inertia cells.
    if len(chosenCells) < minNumActiveCells:
      if self.useInertia:
        prevCells = numpy.setdiff1d(prevActiveCells, chosenCells)
        inertialCap = int(len(prevCells) * self.inertiaFactor)
        if inertialCap > 0:
          numActiveSegsForPrevCells = numActiveSegmentsByCell[prevCells]
          # We sort the previously-active cells by number of active lateral
          # segments (this really helps).  We then activate them in order of
          # descending lateral activation.
          sortIndices = numpy.argsort(numActiveSegsForPrevCells)[::-1]
          prevCells = prevCells[sortIndices]
          numActiveSegsForPrevCells = numActiveSegsForPrevCells[sortIndices]

          # We use inertiaFactor to limit the number of previously-active cells
          # which can become active, forcing decay even if we are below quota.
          prevCells = prevCells[:inertialCap]
          numActiveSegsForPrevCells = numActiveSegsForPrevCells[:inertialCap]

          # Activate groups of previously active cells by order of their lateral
          # support until we either meet quota or run out of cells.
          ttop = numpy.max(numActiveSegsForPrevCells)
          while ttop >= 0 and len(chosenCells) <= minNumActiveCells:
            chosenCells = numpy.union1d(chosenCells,
                        prevCells[numActiveSegsForPrevCells >
                        self.distalSegmentInhibitionFactor * ttop])
            ttop -= 1

      # Finally, add remaining cells with feedforward support
      remFFcells = numpy.setdiff1d(feedforwardSupportedCells, chosenCells)
      # Note that this is 100% of the remaining FF-supported cells, there is no
      # attempt to select only certain ones or limit how many come active.
      chosenCells = numpy.append(chosenCells, remFFcells)

    chosenCells.sort()
    self.activeCells = numpy.asarray(chosenCells, dtype="uint32")


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


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


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


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

    Parameters:
    ----------------------------
    @param  cells (iterable)
            Indices of the cells. If None return count for all cells.
    """
    if cells is None:
      cells = xrange(self.numberOfCells())

    return _countWhereGreaterEqualInRows(self.proximalPermanences, cells,
                                         self.connectedPermanenceProximal)


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

    Parameters:
    ----------------------------
    @param  cells (iterable)
            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=None):
    """
    Returns the total number of distal segments for these cells.

    A segment "exists" if its row in the matrix has any permanence values > 0.

    Parameters:
    ----------------------------
    @param  cells (iterable)
            Indices of the cells
    """
    if cells is None:
      cells = xrange(self.numberOfCells())

    n = 0

    for cell in cells:
      if self.internalDistalPermanences.nNonZerosOnRow(cell) > 0:
        n += 1

      for permanences in self.distalPermanences:
        if permanences.nNonZerosOnRow(cell) > 0:
          n += 1

    return n


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

    Parameters:
    ----------------------------
    @param  cells (iterable)
            Indices of the cells. If None return count for all cells.
    """
    if cells is None:
      cells = xrange(self.numberOfCells())

    n = _countWhereGreaterEqualInRows(self.internalDistalPermanences, cells,
                                      self.connectedPermanenceDistal)

    for permanences in self.distalPermanences:
      n += _countWhereGreaterEqualInRows(permanences, cells,
                                         self.connectedPermanenceDistal)

    return n


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

    Parameters:
    ----------------------------
    @param  cells (iterable)
            Indices of the cells
    """
    if cells is None:
      cells = xrange(self.numberOfCells())
    n = 0
    for cell in cells:
      n += self.internalDistalPermanences.nNonZerosOnRow(cell)

      for permanences in self.distalPermanences:
        n += permanences.nNonZerosOnRow(cell)
    return n


  def reset(self):
    """
    Reset internal states. When learning this signifies we are to learn a
    unique new object.
    """
    self.activeCells = numpy.empty(0, dtype="uint32")

  def getUseInertia(self):
    """
    Get whether we actually use inertia  (i.e. a fraction of the
    previously active cells remain active at the next time step unless
    inhibited by cells with both feedforward and lateral support).
    @return (Bool) Whether inertia is used.
    """
    return self.useInertia

  def setUseInertia(self, useInertia):
    """
    Sets whether we actually use inertia (i.e. a fraction of the
    previously active cells remain active at the next time step unless
    inhibited by cells with both feedforward and lateral support).
    @param useInertia (Bool) Whether inertia is used.
    """
    self.useInertia = useInertia

  @staticmethod
  def _learn(# mutated args
             permanences, rng,

             # activity
             activeCells, activeInput, growthCandidateInput,

             # configuration
             sampleSize, initialPermanence, permanenceIncrement,
             permanenceDecrement, connectedPermanence):
    """
    For each active cell, reinforce active synapses, punish inactive synapses,
    and grow new synapses to a subset of the active input bits that the cell
    isn't already connected to.

    Parameters:
    ----------------------------
    @param  permanences (SparseMatrix)
            Matrix of permanences, with cells as rows and inputs as columns

    @param  rng (Random)
            Random number generator

    @param  activeCells (sorted sequence)
            Sorted list of the cells that are learning

    @param  activeInput (sorted sequence)
            Sorted list of active bits in the input

    @param  growthCandidateInput (sorted sequence)
            Sorted list of active bits in the input that the activeCells may
            grow new synapses to

    For remaining parameters, see the __init__ docstring.
    """

    permanences.incrementNonZerosOnOuter(
      activeCells, activeInput, permanenceIncrement)
    permanences.incrementNonZerosOnRowsExcludingCols(
      activeCells, activeInput, -permanenceDecrement)
    permanences.clipRowsBelowAndAbove(
      activeCells, 0.0, 1.0)
    if sampleSize == -1:
      permanences.setZerosOnOuter(
        activeCells, activeInput, initialPermanence)
    else:
      existingSynapseCounts = permanences.nNonZerosPerRowOnCols(
        activeCells, activeInput)

      maxNewByCell = numpy.empty(len(activeCells), dtype="int32")
      numpy.subtract(sampleSize, existingSynapseCounts, out=maxNewByCell)

      permanences.setRandomZerosOnOuter(
        activeCells, growthCandidateInput, maxNewByCell, initialPermanence, rng)
Beispiel #27
0
def countRandomBitFrequencies(numTerms=100000, percentSparsity=0.01):
    """Create a uniformly random counts matrix through sampling."""
    # Accumulate counts by inplace-adding sparse matrices
    counts = SparseMatrix()
    size = 128 * 128
    counts.resize(1, size)

    # Pre-allocate buffer sparse matrix
    sparseBitmap = SparseMatrix()
    sparseBitmap.resize(1, size)

    random.seed(42)

    # Accumulate counts for each bit for each word
    numWords = 0
    for term in xrange(numTerms):
        bitmap = random.sample(xrange(size), int(size * percentSparsity))
        bitmap.sort()

        sparseBitmap.setRowFromSparse(0, bitmap, [1] * len(bitmap))
        counts += sparseBitmap
        numWords += 1

    # Compute normalized version of counts as a separate matrix
    frequencies = SparseMatrix()
    frequencies.resize(1, size)
    frequencies.copy(counts)
    frequencies.divide(float(numWords))

    # Wrap up by printing some statistics and then saving the normalized version
    printFrequencyStatistics(counts, frequencies, numWords, size)

    frequencyFilename = "bit_frequencies_random.pkl"
    print "Saving frequency matrix in", frequencyFilename
    with open(frequencyFilename, "wb") as frequencyPickleFile:
        pickle.dump(frequencies, frequencyPickleFile)

    return counts
    def __init__(
        self,
        name,
        numCorticalColumns=1,
        inputSize=1024,
        numInputBits=20,
        externalInputSize=1024,
        numExternalInputBits=20,
        L2Overrides=None,
        networkType="L4L2TMColumn",
        L4Overrides=None,
        seed=42,
        logCalls=False,
        objectNamesAreIndices=False,
        TMOverrides=None,
    ):
        """
    Creates the network.

    Parameters:
    ----------------------------
    @param   TMOverrides (dict)
             Parameters to override in the TM region
    """

        # Handle logging - this has to be done first
        self.logCalls = logCalls

        registerAllResearchRegions()
        self.name = name

        self.numLearningPoints = 1
        self.numColumns = numCorticalColumns
        self.inputSize = inputSize
        self.externalInputSize = externalInputSize
        self.numInputBits = numInputBits
        self.objectNamesAreIndices = objectNamesAreIndices

        # seed
        self.seed = seed
        random.seed(seed)

        # update parameters with overrides
        self.config = {
            "networkType": networkType,
            "numCorticalColumns": numCorticalColumns,
            "externalInputSize": externalInputSize,
            "sensorInputSize": inputSize,
            "enableFeedback": False,
            "L4Params": self.getDefaultL4Params(inputSize,
                                                numExternalInputBits),
            "L2Params": self.getDefaultL2Params(inputSize, numInputBits),
            "TMParams": self.getDefaultTMParams(self.inputSize,
                                                self.numInputBits),
        }

        if L2Overrides is not None:
            self.config["L2Params"].update(L2Overrides)

        if L4Overrides is not None:
            self.config["L4Params"].update(L4Overrides)

        if TMOverrides is not None:
            self.config["TMParams"].update(TMOverrides)

        # Recreate network including TM parameters
        self.network = createNetwork(self.config)
        self.sensorInputs = []
        self.externalInputs = []
        self.L4Regions = []
        self.L2Regions = []
        self.TMRegions = []

        for i in xrange(self.numColumns):
            self.sensorInputs.append(self.network.regions["sensorInput_" +
                                                          str(i)].getSelf())
            self.externalInputs.append(self.network.regions["externalInput_" +
                                                            str(i)].getSelf())
            self.L4Regions.append(self.network.regions["L4Column_" + str(i)])
            self.L2Regions.append(self.network.regions["L2Column_" + str(i)])
            self.TMRegions.append(self.network.regions["TMColumn_" + str(i)])

        self.L4Columns = [region.getSelf() for region in self.L4Regions]
        self.L2Columns = [region.getSelf() for region in self.L2Regions]
        self.TMColumns = [region.getSelf() for region in self.TMRegions]

        # will be populated during training
        self.objectL2Representations = {}
        self.objectL2RepresentationsMatrices = [
            SparseMatrix(0, self.config["L2Params"]["cellCount"])
            for _ in xrange(self.numColumns)
        ]
        self.objectNameToIndex = {}
        self.statistics = []

        # Create classifier to hold supposedly unique TM states
        self.classifier = KNNClassifier(distanceMethod="rawOverlap")
        self.numTMCells = (self.TMColumns[0].cellsPerColumn *
                           self.TMColumns[0].columnCount)
  def __init__(self,
               name,
               numCorticalColumns=1,
               inputSize=1024,
               numInputBits=20,
               externalInputSize=1024,
               numExternalInputBits=20,
               L2Overrides=None,
               L4RegionType="py.ExtendedTMRegion",
               L4Overrides=None,
               numLearningPoints=3,
               seed=42,
               logCalls=False,
               enableLateralSP=False,
               lateralSPOverrides=None,
               enableFeedForwardSP=False,
               feedForwardSPOverrides=None,
               objectNamesAreIndices=False
               ):
    """
    Creates the network.

    Parameters:
    ----------------------------
    @param   name (str)
             Experiment name

    @param   numCorticalColumns (int)
             Number of cortical columns in the network

    @param   inputSize (int)
             Size of the sensory input

    @param   numInputBits (int)
             Number of ON bits in the generated input patterns

    @param   externalInputSize (int)
             Size of the lateral input to L4 regions

    @param   numExternalInputBits (int)
             Number of ON bits in the external input patterns

    @param   L2Overrides (dict)
             Parameters to override in the L2 region

    @param   L4RegionType (string)
             The type of region to use for L4

    @param   L4Overrides (dict)
             Parameters to override in the L4 region

    @param   numLearningPoints (int)
             Number of times each pair should be seen to be learnt

    @param   logCalls (bool)
             If true, calls to main functions will be logged internally. The
             log can then be saved with saveLogs(). This allows us to recreate
             the complete network behavior using rerunExperimentFromLogfile
             which is very useful for debugging.

    @param   enableLateralSP (bool)
             If true, Spatial Pooler will be added between external input and
             L4 lateral input

    @param   lateralSPOverrides
             Parameters to override in the lateral SP region

    @param   enableFeedForwardSP (bool)
             If true, Spatial Pooler will be added between external input and
             L4 feed-forward input

    @param   feedForwardSPOverrides
             Parameters to override in the feed-forward SP region

    @param   objectNamesAreIndices (bool)
             If True, object names are used as indices in the
             getCurrentObjectOverlaps method. Object names must be positive
             integers. If False, object names can be strings, and indices will
             be assigned to each object name.

    """
    # Handle logging - this has to be done first
    self.logCalls = logCalls

    registerAllResearchRegions()
    self.name = name

    self.numLearningPoints = numLearningPoints
    self.numColumns = numCorticalColumns
    self.inputSize = inputSize
    self.externalInputSize = externalInputSize
    self.numInputBits = numInputBits
    self.objectNamesAreIndices = objectNamesAreIndices

    # seed
    self.seed = seed
    random.seed(seed)

    # update parameters with overrides
    self.config = {
      "networkType": "MultipleL4L2Columns",
      "numCorticalColumns": numCorticalColumns,
      "externalInputSize": externalInputSize,
      "sensorInputSize": inputSize,
      "L4RegionType": L4RegionType,
      "L4Params": self.getDefaultL4Params(L4RegionType, inputSize,
                                          numExternalInputBits),
      "L2Params": self.getDefaultL2Params(inputSize, numInputBits),
    }

    if enableLateralSP:
      self.config["lateralSPParams"] = self.getDefaultLateralSPParams(inputSize)
      if lateralSPOverrides:
        self.config["lateralSPParams"].update(lateralSPOverrides)

    if enableFeedForwardSP:
      self.config["feedForwardSPParams"] = self.getDefaultFeedForwardSPParams(inputSize)
      if feedForwardSPOverrides:
        self.config["feedForwardSPParams"].update(feedForwardSPOverrides)

    if L2Overrides is not None:
      self.config["L2Params"].update(L2Overrides)

    if L4Overrides is not None:
      self.config["L4Params"].update(L4Overrides)

    # create network
    self.network = createNetwork(self.config)

    self.sensorInputs = []
    self.externalInputs = []
    self.L4Regions = []
    self.L2Regions = []

    for i in xrange(self.numColumns):
      self.sensorInputs.append(
        self.network.regions["sensorInput_" + str(i)].getSelf()
      )
      self.externalInputs.append(
        self.network.regions["externalInput_" + str(i)].getSelf()
      )
      self.L4Regions.append(
        self.network.regions["L4Column_" + str(i)]
      )
      self.L2Regions.append(
        self.network.regions["L2Column_" + str(i)]
      )

    self.L4Columns = [region.getSelf() for region in self.L4Regions]
    self.L2Columns = [region.getSelf() for region in self.L2Regions]

    # will be populated during training
    self.objectL2Representations = {}
    self.objectL2RepresentationsMatrices = [
      SparseMatrix(0, self.config["L2Params"]["cellCount"])
      for _ in xrange(self.numColumns)]
    self.objectNameToIndex = {}
    self.statistics = []