//Replaces row with true values at specified indices func (sm *DenseBinaryMatrix) ReplaceRowByIndices(row int, indices []int) { sm.validateRow(row) start := row * sm.Width for i := 0; i < sm.Width; i++ { sm.entries[start+i] = utils.ContainsInt(i, indices) } }
/* The primary method in charge of learning. Adapts the permanence values of the synapses based on the input vector, and the chosen columns after inhibition round. Permanence values are increased for synapses connected to input bits that are turned on, and decreased for synapses connected to inputs bits that are turned off. Parameters: ---------------------------- inputVector: a numpy array of 0's and 1's thata comprises the input to the spatial pooler. There exists an entry in the array for every input bit. activeColumns: an array containing the indices of the columns that survived inhibition. */ func (sp *SpatialPooler) adaptSynapses(inputVector []bool, activeColumns []int) { var inputIndices []int for i, val := range inputVector { if val { inputIndices = append(inputIndices, i) } } permChanges := make([]float64, sp.numInputs) utils.FillSliceFloat64(permChanges, -1*sp.SynPermInactiveDec) for _, val := range inputIndices { permChanges[val] = sp.SynPermActiveInc } for _, ac := range activeColumns { perm := make([]float64, sp.numInputs) mask := sp.potentialPools.GetRowIndices(ac) for j := 0; j < sp.numInputs; j++ { if utils.ContainsInt(j, mask) { perm[j] = permChanges[j] + sp.permanences.Get(ac, j) } else { perm[j] = sp.permanences.Get(ac, j) } } sp.updatePermanencesForColumn(perm, ac, true) } }
//helper func (tm *TemporalMemory) lrnOnSegments(segments []int, isLearningSegments bool, prevActiveSynapsesForSegment map[int][]int, winnerCells []int, prevWinnerCells []int, connections *TemporalMemoryConnections) { for _, segment := range segments { isFromWinnerCell := utils.ContainsInt(connections.CellForSegment(segment), winnerCells) activeSynapses := tm.getConnectedActiveSynapsesForSegment(segment, prevActiveSynapsesForSegment, 0, connections) if isLearningSegments || isFromWinnerCell { tm.adaptSegment(segment, activeSynapses, connections) } if isLearningSegments { n := tm.params.MaxNewSynapseCount - len(activeSynapses) for _, sourceCell := range tm.pickCellsToLearnOn(n, segment, winnerCells, connections) { connections.CreateSynapse(segment, sourceCell, tm.params.InitialPermanence) } } } }
/* Phase 2: Burst unpredicted columns. Pseudocode: - for each unpredicted active column - mark all cells as active - mark the best matching cell as winner cell - (learning) - if it has no matching segment - (optimization) if there are prev winner cells - add a segment to it - mark the segment as learning */ func (tm *TemporalMemory) burstColumns(activeColumns []int, predictedColumns []int, prevActiveSynapsesForSegment map[int][]int, connections *TemporalMemoryConnections) (activeCells []int, winnerCells []int, learningSegments []int) { unpredictedColumns := utils.Complement(activeColumns, predictedColumns) for _, column := range unpredictedColumns { cells := connections.CellsForColumn(column) activeCells = utils.Add(activeCells, cells) bestCell, bestSegment := tm.getBestMatchingCell(column, prevActiveSynapsesForSegment, connections) winnerCells = append(winnerCells, bestCell) if bestSegment == -1 { //TODO: (optimization) Only do this if there are prev winner cells bestSegment = connections.CreateSegment(bestCell) } //TODO: change to set data structure if !utils.ContainsInt(bestSegment, learningSegments) { learningSegments = append(learningSegments, bestSegment) } } return activeCells, winnerCells, learningSegments }
/* Maps a column to its input bits. This method encapsultes the topology of the region. It takes the index of the column as an argument and determines what are the indices of the input vector that are located within the column's potential pool. The return value is a list containing the indices of the input bits. The current implementation of the base class only supports a 1 dimensional topology of columsn with a 1 dimensional topology of inputs. To extend this class to support 2-D topology you will need to override this method. Examples of the expected output of this method: * If the potentialRadius is greater than or equal to the entire input space, (global visibility), then this method returns an array filled with all the indices * If the topology is one dimensional, and the potentialRadius is 5, this method will return an array containing 5 consecutive values centered on the index of the column (wrapping around if necessary). * If the topology is two dimensional (not implemented), and the potentialRadius is 5, the method should return an array containing 25 '1's, where the exact indices are to be determined by the mapping from 1-D index to 2-D position. Parameters: ---------------------------- index: The index identifying a column in the permanence, potential and connectivity matrices. wrapAround: A boolean value indicating that boundaries should be region boundaries ignored. */ func (sp *SpatialPooler) mapPotential(index int, wrapAround bool) []bool { // Distribute column over inputs uniformly ratio := float64(index) / float64(mathutil.Max((sp.numColumns-1), 1)) index = int(float64(sp.numInputs-1) * ratio) var indices []int indLen := 2*sp.PotentialRadius + 1 for i := 0; i < indLen; i++ { temp := (i + index - sp.PotentialRadius) if wrapAround { temp = temp % sp.numInputs if temp < 0 { temp = sp.numInputs + temp } } else { if !(temp >= 0 && temp < sp.numInputs) { continue } } //no dupes if !utils.ContainsInt(temp, indices) { indices = append(indices, temp) } } // Select a subset of the receptive field to serve as the // the potential pool //shuffle indices for i := range indices { j := rand.Intn(i + 1) indices[i], indices[j] = indices[j], indices[i] } sampleLen := int(utils.RoundPrec(float64(len(indices))*sp.PotentialPct, 0)) sample := indices[:sampleLen] //project indices onto input mask mask := make([]bool, sp.numInputs) for i, _ := range mask { mask[i] = utils.ContainsInt(i, sample) } return mask }
//Returns # of rows with at least 1 true value func (sm *SparseBinaryMatrix) TotalTrueRows() int { var hitRows []int for _, val := range sm.entries { if !utils.ContainsInt(val.Row, hitRows) { hitRows = append(hitRows, val.Row) } } return len(hitRows) }
//Returns # of cols with at least 1 true value func (sm *SparseBinaryMatrix) TotalTrueCols() int { var hitCols []int for _, val := range sm.entries { if !utils.ContainsInt(val.Col, hitCols) { hitCols = append(hitCols, val.Col) } } return len(hitCols) }
//Returns row indexes with at least 1 true column func (sm *SparseBinaryMatrix) NonZeroRows() []int { var result []int for _, val := range sm.entries { if !utils.ContainsInt(val.Row, result) { result = append(result, val.Row) } } return result }
/* Phase 1: Activate the correctly predictive cells. Pseudocode: - for each prev predictive cell - if in active column - mark it as active - mark it as winner cell - mark column as predicted */ func (tm *TemporalMemory) activateCorrectlyPredictiveCells(prevPredictiveCells []int, activeColumns []int, connections *TemporalMemoryConnections) (activeCells []int, winnerCells []int, predictedColumns []int) { for _, cell := range prevPredictiveCells { column := connections.ColumnForCell(cell) if utils.ContainsInt(column, activeColumns) { activeCells = append(activeCells, cell) winnerCells = append(winnerCells, cell) //TODO: change this to a set data structure if !utils.ContainsInt(column, predictedColumns) { predictedColumns = append(predictedColumns, column) } } } return activeCells, winnerCells, predictedColumns }
// Updates synapses on segment. // Strengthens active synapses; weakens inactive synapses. func (tm *TemporalMemory) adaptSegment(segment int, activeSynapses []int, connections *TemporalMemoryConnections) { for _, synIdx := range connections.SynapsesForSegment(segment) { syn := connections.DataForSynapse(synIdx) perm := syn.Permanence if utils.ContainsInt(synIdx, activeSynapses) { perm += tm.params.PermanenceIncrement } else { perm -= tm.params.PermanenceDecrement } //enforce min/max bounds perm = math.Max(0.0, math.Min(1.0, perm)) connections.UpdateSynapsePermanence(synIdx, perm) } }
func (sp *SpatialPooler) getNeighborsND(columnIndex int, dimensions []int, radius int, wrapAround bool) []int { if len(dimensions) < 1 { panic("Dimensions empty") } bounds := append(dimensions[1:], 1) bounds = utils.RevCumProdInt(bounds) columnCoords := make([]int, len(bounds)) for j := 0; j < len(bounds); j++ { columnCoords[j] = utils.Mod(columnIndex/bounds[j], dimensions[j]) } rangeND := make([][]int, len(dimensions)) for i := 0; i < len(dimensions); i++ { if wrapAround { cRange := make([]int, (radius*2)+1) for j := 0; j < (2*radius)+1; j++ { cRange[j] = utils.Mod((columnCoords[i]-radius)+j, dimensions[i]) } rangeND[i] = cRange } else { var cRange []int for j := 0; j < (radius*2)+1; j++ { temp := columnCoords[i] - radius + j if temp >= 0 && temp < dimensions[i] { cRange = append(cRange, temp) } } rangeND[i] = cRange } } cp := utils.CartProductInt(rangeND) var neighbors []int for i := 0; i < len(cp); i++ { val := utils.DotInt(bounds, cp[i]) if val != columnIndex && !utils.ContainsInt(val, neighbors) { neighbors = append(neighbors, val) } } return neighbors }
/* This is the primary public method of the SpatialPooler class. This function takes a input vector and outputs the indices of the active columns. If 'learn' is set to True, this method also updates the permanences of the columns. Parameters: ---------------------------- inputVector: a numpy array of 0's and 1's thata comprises the input to the spatial pooler. The array will be treated as a one dimensional array, therefore the dimensions of the array do not have to much the exact dimensions specified in the class constructor. In fact, even a list would suffice. The number of input bits in the vector must, however, match the number of bits specified by the call to the constructor. Therefore there must be a '0' or '1' in the array for every input bit. learn: a boolean value indicating whether learning should be performed. Learning entails updating the permanence values of the synapses, and hence modifying the 'state' of the model. Setting learning to 'off' freezes the SP and has many uses. For example, you might want to feed in various inputs and examine the resulting SDR's. activeArray: an array whose size is equal to the number of columns. Before the function returns this array will be populated with 1's at the indices of the active columns, and 0's everywhere else. */ func (sp *SpatialPooler) Compute(inputVector []bool, learn bool, activeArray []bool, inhibitColumns inhibitColFunc) { if len(inputVector) != sp.numInputs { panic("input != numimputs") } sp.updateBookeepingVars(learn) overlaps := sp.calculateOverlap(inputVector) boostedOverlaps := make([]float64, len(overlaps)) // Apply boosting when learning is on if learn { for i, val := range sp.boostFactors { boostedOverlaps[i] = float64(overlaps[i]) * val } } // Apply inhibition to determine the winning columns activeColumns := inhibitColumns(boostedOverlaps, sp.inhibitColumnsGlobal, sp.inhibitColumnsLocal) overlapsf := make([]float64, len(overlaps)) for i, val := range overlaps { overlapsf[i] = float64(val) } if learn { sp.adaptSynapses(inputVector, activeColumns) sp.updateDutyCycles(overlapsf, activeColumns) sp.bumpUpWeakColumns() sp.updateBoostFactors() if sp.isUpdateRound() { sp.updateInhibitionRadius(sp.avgConnectedSpanForColumnND, sp.avgColumnsPerInput) sp.updateMinDutyCycles() } } else { activeColumns = sp.stripNeverLearned(activeColumns) } if len(activeColumns) > 0 { for i, _ := range activeArray { activeArray[i] = utils.ContainsInt(i, activeColumns) } } }
//Returns row indexes with at least 1 true column func (sm *DenseBinaryMatrix) NonZeroRows() []int { counts := make(map[int]int, sm.Height) for idx, val := range sm.entries { if val { r, _ := sm.toIndex(idx) counts[r]++ } } result := make([]int, 0, sm.Height) for k, v := range counts { if v > 0 && !utils.ContainsInt(k, result) { result = append(result, k) } } return result }
/* This function applies segment update information to a segment in a cell. Synapses on the active list get their permanence counts incremented by permanenceInc. All other synapses get their permanence counts decremented by permanenceDec. We also increment the positiveActivations count of the segment. param segUpdate SegmentUpdate instance returns True if some synapses were decremented to 0 and the segment is a candidate for trimming */ func (segUpdate *SegmentUpdate) adaptSegments(tp *TemporalPooler) bool { // This will be set to True if detect that any syapses were decremented to 0 trimSegment := false // segUpdate.segment is None when creating a new segment //c, i, segment := segUpdate.columnIdx, segUpdate.cellIdx, segUpdate.segment c := segUpdate.columnIdx i := segUpdate.cellIdx segment := segUpdate.segment // update.activeSynapses can be empty. // If not, it can contain either or both integers and tuples. // The integers are indices of synapses to update. // The tuples represent new synapses to create (src col, src cell in col). // We pre-process to separate these various element types. // synToCreate is not empty only if positiveReinforcement is True. // NOTE: the synapse indices start at *1* to skip the segment flags. activeSynapses := segUpdate.activeSynapses var synToUpdate []int for _, val := range activeSynapses { if !val.New { synToUpdate = append(synToUpdate, val.Index) } } //fmt.Printf("Entering adapt seg %v %v \n", segment, len(activeSynapses)) if segment != nil { if tp.params.Verbosity >= 4 { fmt.Printf("Reinforcing segment #%v for cell[%v,%v] \n", segment.segId, c, i) } //modify existing segment // Mark it as recently useful segment.lastActiveIteration = tp.lrnIterationIdx // Update frequency and positiveActivations segment.positiveActivations++ segment.dutyCycle(true, false) // First, decrement synapses that are not active lastSynIndex := len(segment.syns) - 1 var inactiveSynIndices []int for i := 0; i < lastSynIndex+1; i++ { if !utils.ContainsInt(i, synToUpdate) { inactiveSynIndices = append(inactiveSynIndices, i) } } trimSegment = segment.updateSynapses(inactiveSynIndices, -tp.params.PermanenceDec) // Now, increment active synapses var activeSynIndices []int for _, val := range activeSynapses { if val.Index <= lastSynIndex { activeSynIndices = append(activeSynIndices, val.Index) } } segment.updateSynapses(activeSynIndices, tp.params.PermanenceInc) // Finally, create new synapses if needed var synsToAdd []SynapseUpdateState for _, val := range activeSynapses { if val.New { synsToAdd = append(synsToAdd, val) } } // If we have fixed resources, get rid of some old syns if necessary if tp.params.MaxSynapsesPerSegment > 0 && len(synsToAdd)+len(segment.syns) > tp.params.MaxSynapsesPerSegment { numToFree := (len(segment.syns) + len(synsToAdd)) - tp.params.MaxSynapsesPerSegment segment.freeNSynapses(numToFree, inactiveSynIndices) } for _, val := range synsToAdd { segment.AddSynapse(val.Index, val.CellIndex, tp.params.InitialPerm) } } else { //create new segment newSegment := NewSegment(tp, segUpdate.sequenceSegment) for _, val := range activeSynapses { newSegment.AddSynapse(val.Index, val.CellIndex, tp.params.InitialPerm) } if tp.params.Verbosity >= 3 { fmt.Printf("New segment #%v for cell[%v,%v] \n", tp.segId, c, i) fmt.Print(newSegment.ToString()) } tp.cells[c][i] = append(tp.cells[c][i], *newSegment) } return trimSegment }
func TestGetNeighborsND(t *testing.T) { sp := SpatialPooler{} dimensions := []int{5, 7, 2} var layout [5][7][2]int counter := 0 for i := range layout { for j := range layout[i] { for k := range layout[i][j] { layout[i][j][k] = counter counter++ } } } radius := 1 x := 1 y := 3 z := 2 columnIndex := layout[z][y][x] neighbors := sp.getNeighborsND(columnIndex, dimensions, radius, true) var expected []int for i := radius * -1; i <= radius; i++ { for j := radius * -1; j <= radius; j++ { for k := radius * -1; k <= radius; k++ { zprime := (z + i) % dimensions[0] yprime := (y + j) % dimensions[1] xprime := (x + k) % dimensions[2] if layout[zprime][yprime][xprime] != columnIndex && !utils.ContainsInt(layout[zprime][yprime][xprime], expected) { expected = append(expected, layout[zprime][yprime][xprime]) } } } } assert.Equal(t, expected, neighbors) dimensions = []int{5, 7, 9} var layoutb [5][7][9]int counter = 0 for i := range layoutb { for j := range layoutb[i] { for k := range layoutb[i][j] { layoutb[i][j][k] = counter counter++ } } } radius = 3 x = 0 y = 0 z = 3 columnIndex = layoutb[z][y][x] neighbors = sp.getNeighborsND(columnIndex, dimensions, radius, true) expected = []int{} for i := radius * -1; i <= radius; i++ { for j := radius * -1; j <= radius; j++ { for k := radius * -1; k <= radius; k++ { zprime := utils.Mod((z + i), (dimensions[0])) yprime := utils.Mod((y + j), (dimensions[1])) xprime := utils.Mod((x + k), (dimensions[2])) if layoutb[zprime][yprime][xprime] != columnIndex && !utils.ContainsInt(layoutb[zprime][yprime][xprime], expected) { expected = append(expected, layoutb[zprime][yprime][xprime]) } } } } assert.Equal(t, expected, neighbors) dimensions = []int{5, 10, 7, 6} var layoutc [5][10][7][6]int counter = 0 for i := range layoutc { for j := range layoutc[i] { for k := range layoutc[i][j] { for m := range layoutc[i][j][k] { layoutc[i][j][k][m] = counter counter++ } } } } radius = 4 w := 2 x = 5 y = 6 z = 2 columnIndex = layoutc[z][y][x][w] neighbors = sp.getNeighborsND(columnIndex, dimensions, radius, true) expected = []int{} for i := radius * -1; i <= radius; i++ { for j := radius * -1; j <= radius; j++ { for k := radius * -1; k <= radius; k++ { for m := radius * -1; m <= radius; m++ { zprime := utils.Mod((z + i), (dimensions[0])) yprime := utils.Mod((y + j), (dimensions[1])) xprime := utils.Mod((x + k), (dimensions[2])) wprime := utils.Mod((w + m), (dimensions[3])) if layoutc[zprime][yprime][xprime][wprime] != columnIndex && !utils.ContainsInt(layoutc[zprime][yprime][xprime][wprime], expected) { expected = append(expected, layoutc[zprime][yprime][xprime][wprime]) } } } } } assert.Equal(t, expected, neighbors) layoutd := []bool{false, false, true, false, true, false, false, false} columnIndex = 3 dimensions = []int{8} radius = 1 mask := sp.getNeighborsND(columnIndex, dimensions, radius, true) t.Log("mask", mask) for idx, val := range layoutd { if utils.ContainsInt(idx, mask) { assert.Equal(t, true, val) } else { assert.Equal(t, false, val) } } layoute := []bool{false, true, true, false, true, true, false, false} columnIndex = 3 dimensions = []int{8} radius = 2 mask = sp.getNeighborsND(columnIndex, dimensions, radius, true) t.Log("mask", mask) for idx, val := range layoute { if utils.ContainsInt(idx, mask) { assert.Equal(t, true, val) } else { assert.Equal(t, false, val) } } // Wrap around layoutf := []bool{false, true, true, false, false, false, true, true} columnIndex = 0 dimensions = []int{8} radius = 2 mask = sp.getNeighborsND(columnIndex, dimensions, radius, true) t.Log("mask", mask) for idx, val := range layoutf { if utils.ContainsInt(idx, mask) { assert.Equal(t, true, val) } else { assert.Equal(t, false, val) } } //Radius too big layoutg := []bool{true, true, true, true, true, true, false, true} columnIndex = 6 dimensions = []int{8} radius = 20 mask = sp.getNeighborsND(columnIndex, dimensions, radius, true) t.Log("mask", mask) for idx, val := range layoutg { if utils.ContainsInt(idx, mask) { assert.Equal(t, true, val) } else { assert.Equal(t, false, val) } } //These are all the same tests from 2D ints := [][]int{{0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 1, 1, 1, 0}, {0, 1, 0, 1, 0}, {0, 1, 1, 1, 0}, {0, 0, 0, 0, 0}} layouth := NewDenseBinaryMatrixFromInts(ints) t.Log(layouth.ToString()) columnIndex = 3*5 + 2 dimensions = []int{6, 5} radius = 1 mask = sp.getNeighborsND(columnIndex, dimensions, radius, true) t.Log("mask", mask) t.Log("1d", layouth.Flatten()) for idx, val := range layouth.Flatten() { if utils.ContainsInt(idx, mask) { assert.Equal(t, true, val) } else { assert.Equal(t, false, val) } } ints = [][]int{{0, 0, 0, 0, 0}, {1, 1, 1, 1, 1}, {1, 1, 1, 1, 1}, {1, 1, 0, 1, 1}, {1, 1, 1, 1, 1}, {1, 1, 1, 1, 1}} layoutj := NewDenseBinaryMatrixFromInts(ints) t.Log(layouth.ToString()) columnIndex = 3*5 + 2 radius = 2 mask = sp.getNeighborsND(columnIndex, dimensions, radius, true) t.Log("mask", mask) t.Log("1d", layouth.Flatten()) for idx, val := range layoutj.Flatten() { if utils.ContainsInt(idx, mask) { assert.Equal(t, true, val) } else { assert.Equal(t, false, val) } } //Radius too big ints = [][]int{{1, 1, 1, 1, 1}, {1, 1, 1, 1, 1}, {1, 1, 1, 1, 1}, {1, 1, 0, 1, 1}, {1, 1, 1, 1, 1}, {1, 1, 1, 1, 1}} layoutk := NewDenseBinaryMatrixFromInts(ints) t.Log(layouth.ToString()) columnIndex = 3*5 + 2 radius = 7 mask = sp.getNeighborsND(columnIndex, dimensions, radius, true) t.Log("mask", mask) t.Log("1d", layoutk.Flatten()) for idx, val := range layoutk.Flatten() { if utils.ContainsInt(idx, mask) { assert.Equal(t, true, val) } else { assert.Equal(t, false, val) } } //Wrap-around ints = [][]int{{1, 0, 0, 1, 1}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {1, 0, 0, 1, 1}, {1, 0, 0, 1, 0}} layoutl := NewDenseBinaryMatrixFromInts(ints) t.Log(layoutl.ToString()) columnIndex = 29 radius = 1 mask = sp.getNeighborsND(columnIndex, dimensions, radius, true) t.Log("mask", mask) t.Log("1d", layoutl.Flatten()) for idx, val := range layoutl.Flatten() { if utils.ContainsInt(idx, mask) { assert.Equal(t, true, val) } else { assert.Equal(t, false, val) } } //No wrap around columnIndex = 8 radius = 2 dimensions = []int{10} neighbors = sp.getNeighborsND(columnIndex, dimensions, radius, false) expected = []int{6, 7, 9} assert.Equal(t, expected, neighbors) }
/* Free up some synapses in this segment. We always free up inactive synapses (lowest permanence freed up first) before we start to free up active ones. param numToFree number of synapses to free up param inactiveSynapseIndices list of the inactive synapse indices. */ func (s *Segment) freeNSynapses(numToFree int, inactiveSynapseIndices []int) { //Make sure numToFree isn't larger than the total number of syns we have if numToFree > len(s.syns) { panic("Number to free cannot be larger than existing synapses.") } if s.tp.params.Verbosity >= 5 { fmt.Println("freeNSynapses with numToFree=", numToFree) fmt.Println("inactiveSynapseIndices= ", inactiveSynapseIndices) } var candidates []int // Remove the lowest perm inactive synapses first if len(inactiveSynapseIndices) > 0 { perms := make([]float64, len(inactiveSynapseIndices)) for idx, _ := range perms { perms[idx] = s.syns[idx].Permanence } var indexes []int floats.Argsort(perms, indexes) //sort perms cSize := mathutil.Min(numToFree, len(perms)) candidates = make([]int, cSize) //indexes[0:cSize] for i := 0; i < cSize; i++ { candidates[i] = inactiveSynapseIndices[indexes[i]] } } // Do we need more? if so, remove the lowest perm active synapses too var activeSynIndices []int if len(candidates) < numToFree { for i := 0; i < len(s.syns); i++ { if !utils.ContainsInt(i, inactiveSynapseIndices) { activeSynIndices = append(activeSynIndices, i) } } perms := make([]float64, len(activeSynIndices)) for i := range perms { perms[i] = s.syns[i].Permanence } var indexes []int floats.Argsort(perms, indexes) moreToFree := numToFree - len(candidates) //moreCandidates := make([]int, moreToFree) for i := 0; i < moreToFree; i++ { candidates = append(candidates, activeSynIndices[indexes[i]]) } } if s.tp.params.Verbosity >= 4 { fmt.Printf("Deleting %v synapses from segment to make room for new ones: %v \n", len(candidates), candidates) fmt.Println("Before:", s.ToString()) } // Delete candidate syns by copying undeleted to new slice var newSyns []Synapse for idx, val := range s.syns { if !utils.ContainsInt(idx, candidates) { newSyns = append(newSyns, val) } } s.syns = newSyns if s.tp.params.Verbosity >= 4 { fmt.Println("After:", s.ToString()) } }
/* This function produces goodness-of-match scores for a set of input patterns, by checking for their presence in the current and predicted output of the TP. Returns a global count of the number of extra and missing bits, the confidence scores for each input pattern, and (if requested) the bits in each input pattern that were not present in the TP's prediction. param patternNZs a list of input patterns that we want to check for. Each element is a list of the non-zeros in that pattern. param output The output of the TP. If not specified, then use the TP's current output. This can be specified if you are trying to check the prediction metric for an output from the past. param colConfidence The column confidences. If not specified, then use the TP's current colConfidence. This can be specified if you are trying to check the prediction metrics for an output from the past. param details if True, also include details of missing bits per pattern. returns list containing: [ totalExtras, totalMissing, [conf_1, conf_2, ...], [missing1, missing2, ...] ] retval totalExtras a global count of the number of 'extras', i.e. bits that are on in the current output but not in the or of all the passed in patterns retval totalMissing a global count of all the missing bits, i.e. the bits that are on in the or of the patterns, but not in the current output retval conf_i the confidence score for the i'th pattern inpatternsToCheck This consists of 3 items as a tuple: (predictionScore, posPredictionScore, negPredictionScore) retval missing_i the bits in the i'th pattern that were missing in the output. This list is only returned if details is True. */ func (tp *TemporalPooler) checkPrediction2(patternNZs [][]int, output *SparseBinaryMatrix, colConfidence []float64, details bool) (int, int, []confidence, []int) { // Get the non-zeros in each pattern numPatterns := len(patternNZs) // Compute the union of all the expected patterns var orAll []int for _, row := range patternNZs { for _, col := range row { if !utils.ContainsInt(col, orAll) { orAll = append(orAll, col) } } } var outputIdxs []int // Get the list of active columns in the output if output == nil { if tp.CurrentOutput == nil { panic("Expected tp output") } outputIdxs = tp.CurrentOutput.NonZeroRows() } else { outputIdxs = output.NonZeroRows() } // Compute the total extra and missing in the output totalExtras := 0 totalMissing := 0 for _, val := range outputIdxs { if !utils.ContainsInt(val, orAll) { totalExtras++ } } for _, val := range orAll { if !utils.ContainsInt(val, outputIdxs) { totalMissing++ } } // Get the percent confidence level per column by summing the confidence // levels of the cells in the column. During training, each segment's // confidence number is computed as a running average of how often it // correctly predicted bottom-up activity on that column. A cell's // confidence number is taken from the first active segment found in the // cell. Note that confidence will only be non-zero for predicted columns. if colConfidence == nil { if tp.params.Verbosity >= 5 { fmt.Println("Col confidence nil, copying from tp state...") } colConfidence = make([]float64, len(tp.DynamicState.ColConfidence)) copy(colConfidence, tp.DynamicState.ColConfidence) } // Assign confidences to each pattern var confidences []confidence for i := 0; i < numPatterns; i++ { // Sum of the column confidences for this pattern //positivePredictionSum = colConfidence[patternNZs[i]].sum() positivePredictionSum := floats.Sum(floats.SubSet(colConfidence, patternNZs[i])) // How many columns in this pattern positiveColumnCount := len(patternNZs[i]) // Sum of all the column confidences totalPredictionSum := floats.Sum(colConfidence) // Total number of columns totalColumnCount := len(colConfidence) negativePredictionSum := totalPredictionSum - positivePredictionSum negativeColumnCount := totalColumnCount - positiveColumnCount positivePredictionScore := 0.0 // Compute the average confidence score per column for this pattern if positiveColumnCount != 0 { positivePredictionScore = positivePredictionSum } // Compute the average confidence score per column for the other patterns negativePredictionScore := 0.0 if negativeColumnCount != 0 { negativePredictionScore = negativePredictionSum } // Scale the positive and negative prediction scores so that they sum to // 1.0 currentSum := negativePredictionScore + positivePredictionScore if currentSum > 0 { positivePredictionScore *= 1.0 / currentSum negativePredictionScore *= 1.0 / currentSum } predictionScore := positivePredictionScore - negativePredictionScore newConf := confidence{predictionScore, positivePredictionScore, negativePredictionScore} confidences = append(confidences, newConf) } // Include detail? (bits in each pattern that were missing from the output) if details { var missingPatternBits []int for _, pattern := range patternNZs { for _, val := range pattern { if !utils.ContainsInt(val, outputIdxs) && !utils.ContainsInt(val, missingPatternBits) { missingPatternBits = append(missingPatternBits, val) } } } return totalExtras, totalMissing, confidences, missingPatternBits } else { return totalExtras, totalMissing, confidences, nil } }