/** * Perform global inhibition. Performing global inhibition entails picking the top 'numActive' * columns with the highest overlap score in the entire region. At most half of the columns in a * local neighborhood are allowed to be active. * * @param c the {@link Connections} matrix * @param overlaps an array containing the overlap score for each column. The overlap score for a * column is defined as the number of synapses in a "connected state" (connected synapses) * that are connected to input bits which are turned on. * @param density The fraction of columns to survive inhibition. * @return */ public int[] inhibitColumnsGlobal(Connections c, double[] overlaps, double density) { int numCols = c.getNumColumns(); int numActive = (int) (density * numCols); Comparator<Pair<Integer, Double>> comparator = (p1, p2) -> { int p1key = p1.getKey(); int p2key = p2.getKey(); double p1val = p1.getValue(); double p2val = p2.getValue(); if (Math.abs(p2val - p1val) < 0.000000001) { return Math.abs(p2key - p1key) < 0.000000001 ? 0 : p2key > p1key ? 1 : -1; } else { return p2val > p1val ? 1 : -1; } }; int[] inhibit = IntStream.range(0, overlaps.length) .mapToObj(i -> new Pair<>(i, overlaps[i])) .sorted(comparator) .mapToInt(Pair<Integer, Double>::getKey) .limit(numActive) .sorted() .toArray(); return inhibit; }
/** * Update the boost factors for all columns. The boost factors are used to increase the overlap of * inactive columns to improve their chances of becoming active. and hence encourage participation * of more columns in the learning process. This is a line defined as: y = mx + b boost = * (1-maxBoost)/minDuty * dutyCycle + maxFiringBoost. Intuitively this means that columns that * have been active enough have a boost factor of 1, meaning their overlap is not boosted. Columns * whose active duty cycle drops too much below that of their neighbors are boosted depending on * how infrequently they have been active. The more infrequent, the more they are boosted. The * exact boost factor is linearly interpolated between the points (dutyCycle:0, * boost:maxFiringBoost) and (dutyCycle:minDuty, boost:1.0). * * <p>boostFactor ^ maxBoost _ | |\ | \ 1 _ | \ _ _ _ _ _ _ _ | +--------------------> * activeDutyCycle | minActiveDutyCycle */ public void updateBoostFactors(Connections c) { double[] activeDutyCycles = c.getActiveDutyCycles(); double[] minActiveDutyCycles = c.getMinActiveDutyCycles(); // Indexes of values > 0 int[] mask = ArrayUtils.where(minActiveDutyCycles, ArrayUtils.GREATER_THAN_0); double[] boostInterim; if (mask.length < 1) { boostInterim = c.getBoostFactors(); } else { double[] numerator = new double[c.getNumColumns()]; Arrays.fill(numerator, 1 - c.getMaxBoost()); boostInterim = ArrayUtils.divide(numerator, minActiveDutyCycles, 0, 0); boostInterim = ArrayUtils.multiply(boostInterim, activeDutyCycles, 0, 0); boostInterim = ArrayUtils.d_add(boostInterim, c.getMaxBoost()); } ArrayUtils.setIndexesTo( boostInterim, ArrayUtils.where( activeDutyCycles, new Condition.Adapter<Object>() { int i = 0; @Override public boolean eval(double d) { return d > minActiveDutyCycles[i++]; } }), 1.0d); c.setBoostFactors(boostInterim); }
/** * Updates the duty cycles for each column. The OVERLAP duty cycle is a moving average of the * number of inputs which overlapped with each column. The ACTIVITY duty cycles is a moving * average of the frequency of activation for each column. * * @param c the {@link Connections} (spatial pooler memory) * @param overlaps an array containing the overlap score for each column. The overlap score for a * column is defined as the number of synapses in a "connected state" (connected synapses) * that are connected to input bits which are turned on. * @param activeColumns An array containing the indices of the active columns, the sparse set of * columns which survived inhibition */ public void updateDutyCycles(Connections c, int[] overlaps, int[] activeColumns) { double[] overlapArray = new double[c.getNumColumns()]; double[] activeArray = new double[c.getNumColumns()]; ArrayUtils.greaterThanXThanSetToYInB(overlaps, overlapArray, 0, 1); if (activeColumns.length > 0) { ArrayUtils.setIndexesTo(activeArray, activeColumns, 1); } int period = c.getDutyCyclePeriod(); if (period > c.getIterationNum()) { period = c.getIterationNum(); } c.setOverlapDutyCycles( updateDutyCyclesHelper(c, c.getOverlapDutyCycles(), overlapArray, period)); c.setActiveDutyCycles(updateDutyCyclesHelper(c, c.getActiveDutyCycles(), activeArray, period)); }
/** * Updates the minimum duty cycles. The minimum duty cycles are determined locally. Each column's * minimum duty cycles are set to be a percent of the maximum duty cycles in the column's * neighborhood. Unlike {@link #updateMinDutyCyclesGlobal(Connections)}, here the values can be * quite different for different columns. * * @param c */ public void updateMinDutyCyclesLocal(Connections c) { int len = c.getNumColumns(); for (int i = 0; i < len; i++) { int[] maskNeighbors = getNeighborsND(c, i, c.getMemory(), c.getInhibitionRadius(), true).toArray(); c.getMinOverlapDutyCycles()[i] = ArrayUtils.max(ArrayUtils.sub(c.getOverlapDutyCycles(), maskNeighbors)) * c.getMinPctOverlapDutyCycles(); c.getMinActiveDutyCycles()[i] = ArrayUtils.max(ArrayUtils.sub(c.getActiveDutyCycles(), maskNeighbors)) * c.getMinPctActiveDutyCycles(); } }
/** * Performs inhibition. This method calculates the necessary values needed to actually perform * inhibition and then delegates the task of picking the active columns to helper functions. * * @param c the {@link Connections} matrix * @param overlaps an array containing the overlap score for each column. The overlap score for a * column is defined as the number of synapses in a "connected state" (connected synapses) * that are connected to input bits which are turned on. * @param density The fraction of columns to survive inhibition. This value is only an intended * target. Since the surviving columns are picked in a local fashion, the exact fraction of * surviving columns is likely to vary. * @return indices of the winning columns */ public int[] inhibitColumnsLocal(Connections c, double[] overlaps, double density) { int numCols = c.getNumColumns(); int[] activeColumns = new int[numCols]; double addToWinners = ArrayUtils.max(overlaps) / 1000.0; for (int i = 0; i < numCols; i++) { TIntArrayList maskNeighbors = getNeighborsND(c, i, c.getMemory(), c.getInhibitionRadius(), false); double[] overlapSlice = ArrayUtils.sub(overlaps, maskNeighbors.toArray()); int numActive = (int) (0.5 + density * (maskNeighbors.size() + 1)); int numBigger = ArrayUtils.valueGreaterCount(overlaps[i], overlapSlice); if (numBigger < numActive) { activeColumns[i] = 1; overlaps[i] += addToWinners; } } return ArrayUtils.where(activeColumns, ArrayUtils.INT_GREATER_THAN_0); }
/** * Update the inhibition radius. The inhibition radius is a measure of the square (or hypersquare) * of columns that each a column is "connected to" on average. Since columns are are not connected * to each other directly, we determine this quantity by first figuring out how many *inputs* a * column is connected to, and then multiplying it by the total number of columns that exist for * each input. For multiple dimension the aforementioned calculations are averaged over all * dimensions of inputs and columns. This value is meaningless if global inhibition is enabled. * * @param c the {@link Connections} (spatial pooler memory) */ public void updateInhibitionRadius(Connections c) { if (c.getGlobalInhibition()) { c.setInhibitionRadius(ArrayUtils.max(c.getColumnDimensions())); return; } TDoubleArrayList avgCollected = new TDoubleArrayList(); int len = c.getNumColumns(); for (int i = 0; i < len; i++) { avgCollected.add(avgConnectedSpanForColumnND(c, i)); } double avgConnectedSpan = ArrayUtils.average(avgCollected.toArray()); double diameter = avgConnectedSpan * avgColumnsPerInput(c); double radius = (diameter - 1) / 2.0d; radius = Math.max(1, radius); c.setInhibitionRadius((int) Math.round(radius)); }
/** * Removes the set of columns who have never been active from the set of active columns selected * in the inhibition round. Such columns cannot represent learned pattern and are therefore * meaningless if only inference is required. This should not be done when using a random, * unlearned SP since you would end up with no active columns. * * @param activeColumns An array containing the indices of the active columns * @return a list of columns with a chance of activation */ public int[] stripUnlearnedColumns(Connections c, int[] activeColumns) { TIntHashSet active = new TIntHashSet(activeColumns); TIntHashSet aboveZero = new TIntHashSet(); int numCols = c.getNumColumns(); double[] colDutyCycles = c.getActiveDutyCycles(); for (int i = 0; i < numCols; i++) { if (colDutyCycles[i] <= 0) { aboveZero.add(i); } } active.removeAll(aboveZero); TIntArrayList l = new TIntArrayList(active); l.sort(); // return l; return Arrays.stream(activeColumns).filter(i -> c.getActiveDutyCycles()[i] > 0).toArray(); }
/** * Step two of pooler initialization kept separate from initialization of static members so that * they may be set at a different point in the initialization (as sometimes needed by tests). * * <p>This step prepares the proximal dendritic synapse pools with their initial permanence values * and connected inputs. * * @param c the {@link Connections} memory */ public void connectAndConfigureInputs(Connections c) { // 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. int numColumns = c.getNumColumns(); for (int i = 0; i < numColumns; i++) { int[] potential = mapPotential(c, i, true); Column column = c.getColumn(i); c.getPotentialPools().set(i, column.createPotentialPool(c, potential)); double[] perm = initPermanence(c, potential, i, c.getInitConnectedPct()); updatePermanencesForColumn(c, perm, column, potential, true); } // The inhibition radius determines the size of a column's local // neighborhood. A cortical column must overcome the overlap score of // columns in its neighborhood in order to become active. This radius is // updated every learning round. It grows and shrinks with the average // number of connected synapses per column. updateInhibitionRadius(c); }
/** * Performs inhibition. This method calculates the necessary values needed to actually perform * inhibition and then delegates the task of picking the active columns to helper functions. * * @param c the {@link Connections} matrix * @param overlaps an array containing the overlap score for each column. The overlap score for a * column is defined as the number of synapses in a "connected state" (connected synapses) * that are connected to input bits which are turned on. * @return */ public int[] inhibitColumns(Connections c, double[] overlaps) { overlaps = Arrays.copyOf(overlaps, overlaps.length); double density; double inhibitionArea; if ((density = c.getLocalAreaDensity()) <= 0) { inhibitionArea = Math.pow(2 * c.getInhibitionRadius() + 1, c.getColumnDimensions().length); inhibitionArea = Math.min(c.getNumColumns(), inhibitionArea); density = c.getNumActiveColumnsPerInhArea() / inhibitionArea; density = Math.min(density, 0.5); } // Add our fixed little bit of random noise to the scores to help break ties. // ArrayUtils.d_add(overlaps, c.getTieBreaker()); if (c.getGlobalInhibition() || c.getInhibitionRadius() > ArrayUtils.max(c.getColumnDimensions())) { return inhibitColumnsGlobal(c, overlaps, density); } return inhibitColumnsLocal(c, overlaps, density); }
/** * This function determines each column's overlap with the current input vector. The overlap of a * column is the number of synapses for that column that are connected (permanence value is * greater than '_synPermConnected') to input bits which are turned on. Overlap values that are * lower than the 'stimulusThreshold' are ignored. The implementation takes advantage of the * SpraseBinaryMatrix class to perform this calculation efficiently. * * @param c the {@link Connections} memory encapsulation * @param inputVector an input array of 0's and 1's that comprises the input to the spatial * pooler. * @return */ public int[] calculateOverlap(Connections c, int[] inputVector) { int[] overlaps = new int[c.getNumColumns()]; c.getConnectedCounts().rightVecSumAtNZ(inputVector, overlaps, c.getStimulusThreshold()); return overlaps; }