//Returns number of columns func (ssp *SpParams) NumColumns() int { return utils.ProdInt(ssp.ColumnDimensions) }
//Creates a new spatial pooler func NewSpatialPooler(spParams SpParams) *SpatialPooler { sp := SpatialPooler{} //Validate inputs sp.numColumns = utils.ProdInt(spParams.ColumnDimensions) sp.numInputs = utils.ProdInt(spParams.InputDimensions) if sp.numColumns < 1 { panic("Must have at least 1 column") } if sp.numInputs < 1 { panic("must have at least 1 input") } if spParams.NumActiveColumnsPerInhArea < 1 && (spParams.LocalAreaDensity < 1) && (spParams.LocalAreaDensity >= 0.5) { panic("Num active colums invalid") } sp.InputDimensions = spParams.InputDimensions sp.ColumnDimensions = spParams.ColumnDimensions sp.PotentialRadius = int(mathutil.Min(spParams.PotentialRadius, sp.numInputs)) sp.PotentialPct = spParams.PotentialPct sp.GlobalInhibition = spParams.GlobalInhibition sp.LocalAreaDensity = spParams.LocalAreaDensity sp.NumActiveColumnsPerInhArea = spParams.NumActiveColumnsPerInhArea sp.StimulusThreshold = spParams.StimulusThreshold sp.SynPermInactiveDec = spParams.SynPermInactiveDec sp.SynPermActiveInc = spParams.SynPermActiveInc sp.SynPermBelowStimulusInc = spParams.SynPermConnected / 10.0 sp.SynPermConnected = spParams.SynPermConnected sp.MinPctOverlapDutyCycles = spParams.MinPctOverlapDutyCycle sp.MinPctActiveDutyCycles = spParams.MinPctActiveDutyCycle sp.DutyCyclePeriod = spParams.DutyCyclePeriod sp.MaxBoost = spParams.MaxBoost sp.Seed = spParams.Seed sp.SpVerbosity = spParams.SpVerbosity // Extra parameter settings sp.SynPermMin = 0 sp.SynPermMax = 1 sp.SynPermTrimThreshold = sp.SynPermActiveInc / 2.0 if sp.SynPermTrimThreshold >= sp.SynPermConnected { panic("Syn perm threshold >= syn connected.") } sp.UpdatePeriod = 50 sp.InitConnectedPct = 0.5 /* # Internal state version = 1.0 iterationNum = 0 iterationLearnNum = 0 */ /* 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 falttenned 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. */ sp.potentialPools = NewDenseBinaryMatrix(sp.numColumns, sp.numInputs) /* Initialize the permanences for each column. Similar to the 'potentialPools', the permances are stored in a matrix whose rows represent the cortial columns, and whose columns represent the input bits. if 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. */ //Assumes 70% sparsity elms := make(map[int]float64, int(float64(sp.numColumns*sp.numInputs)*0.3)) sp.permanences = matrix.MakeSparseMatrix(elms, sp.numColumns, sp.numInputs) /* Initialize a tiny random tie breaker. This is used to determine winning columns where the overlaps are identical. */ sp.tieBreaker = make([]float64, sp.numColumns) for i := 0; i < len(sp.tieBreaker); i++ { sp.tieBreaker[i] = 0.01 * rand.Float64() } /* 'connectedSynapses' is a similar matrix to 'permanences' (rows represent cortial columns, columns represent input bits) whose entries represent whether the cortial column is connected to the input bit, i.e. its permanence value is greater than 'synPermConnected'. While this information is readily available from the 'permanence' matrix, it is stored separately for efficiency purposes. */ sp.connectedSynapses = NewDenseBinaryMatrix(sp.numColumns, sp.numInputs) /* Stores the number of connected synapses for each column. This is simply a sum of each row of 'ConnectedSynapses'. again, while this information is readily available from 'ConnectedSynapses', it is stored separately for efficiency purposes. */ sp.connectedCounts = make([]int, sp.numColumns) /* Initialize the set of permanence values for each columns. Ensure that each column is connected to enough input bits to allow it to be activated */ for i := 0; i < sp.numColumns; i++ { potential := sp.mapPotential(i, true) sp.potentialPools.ReplaceRow(i, potential) perm := sp.initPermanence(potential, sp.InitConnectedPct) sp.updatePermanencesForColumn(perm, i, true) } sp.overlapDutyCycles = make([]float64, sp.numColumns) sp.activeDutyCycles = make([]float64, sp.numColumns) sp.minOverlapDutyCycles = make([]float64, sp.numColumns) sp.minActiveDutyCycles = make([]float64, sp.numColumns) sp.boostFactors = make([]float64, sp.numColumns) for i := 0; i < len(sp.boostFactors); i++ { sp.boostFactors[i] = 1.0 } /* The inhibition radius determines the size of a column's local neighborhood. of a column. A cortical column must overcome the overlap score of columns in his neighborhood in order to become actives. This radius is updated every learning round. It grows and shrinks with the average number of connected synapses per column. */ sp.inhibitionRadius = 0 sp.updateInhibitionRadius(sp.avgConnectedSpanForColumnND, sp.avgColumnsPerInput) if sp.spVerbosity > 0 { sp.printParameters() } return &sp }
//Returns number of inputs func (ssp *SpParams) NumInputs() int { return utils.ProdInt(ssp.InputDimensions) }
func TestAvgConnectedSpanForColumnND(t *testing.T) { sp := SpatialPooler{} sp.InputDimensions = []int{4, 4, 2, 5} sp.numInputs = utils.ProdInt(sp.InputDimensions) sp.numColumns = 5 sp.ColumnDimensions = []int{0, 1, 2, 3, 4} sp.connectedSynapses = NewDenseBinaryMatrix(sp.numColumns, sp.numInputs) connected := make([]bool, sp.numInputs) connected[(1*40)+(0*10)+(1*5)+(0*1)] = true connected[(1*40)+(0*10)+(1*5)+(1*1)] = true connected[(3*40)+(2*10)+(1*5)+(0*1)] = true connected[(3*40)+(0*10)+(1*5)+(0*1)] = true connected[(1*40)+(0*10)+(1*5)+(3*1)] = true connected[(2*40)+(2*10)+(1*5)+(0*1)] = true //# span: 3 3 1 4, avg = 11/4 sp.connectedSynapses.ReplaceRow(0, connected) connected2 := make([]bool, sp.numInputs) connected2[(2*40)+(0*10)+(1*5)+(0*1)] = true connected2[(2*40)+(0*10)+(0*5)+(0*1)] = true connected2[(3*40)+(0*10)+(0*5)+(0*1)] = true connected2[(3*40)+(0*10)+(1*5)+(0*1)] = true //spn: 2 1 2 1, avg = 6/4 sp.connectedSynapses.ReplaceRow(1, connected2) connected3 := make([]bool, sp.numInputs) connected3[(0*40)+(0*10)+(1*5)+(4*1)] = true connected3[(0*40)+(0*10)+(0*5)+(3*1)] = true connected3[(0*40)+(0*10)+(0*5)+(1*1)] = true connected3[(1*40)+(0*10)+(0*5)+(2*1)] = true connected3[(0*40)+(0*10)+(1*5)+(1*1)] = true connected3[(3*40)+(3*10)+(1*5)+(1*1)] = true // span: 4 4 2 4, avg = 14/4 sp.connectedSynapses.ReplaceRow(2, connected3) connected4 := make([]bool, sp.numInputs) connected4[(3*40)+(3*10)+(1*5)+(4*1)] = true connected4[(0*40)+(0*10)+(0*5)+(0*1)] = true // span: 4 4 2 5, avg = 15/4 sp.connectedSynapses.ReplaceRow(3, connected4) connected5 := make([]bool, sp.numInputs) //# span: false false false false, avg = false sp.connectedSynapses.ReplaceRow(4, connected5) //t.Logf("width: %v", sp.connectedSynapses.Width) trueAvgConnectedSpan := []float64{11.0 / 4.0, 6.0 / 4.0, 14.0 / 4.0, 15.0 / 4.0, 0.0} for i, tspan := range trueAvgConnectedSpan { connectedSpan := sp.avgConnectedSpanForColumnND(i) if connectedSpan != tspan { t.Errorf("Connected span was: %v expected: %v", connectedSpan, tspan) } } }