/// <summary> /// Uniform Column Mapping <br></br> /// Maps a column to its respective input index, keeping to the topology of the region. It takes the index of the column as an argument and determines /// what is the index of the flattened input vector that is to be the center of the column's potential pool. It distributes the columns over the inputs /// uniformly. The return value is an integer representing the index of the input bit. Examples of the expected output of this method: /// <list type="bullet"> /// <item> /// If the topology is one dimensional, and the column index is 0, this method will return the input index 0. If the column index is 1, and there are /// 3 columns over 7 inputs, this method will return the input index 3. /// </item> /// <item>If the topology is two dimensional, with column dimensions [3, 5] and input dimensions [7, 11], and the column index is 3, the method returns /// input index 8. /// </item> /// </list> /// </summary> /// <param name="columnIndex">The index identifying a column in the permanence, potential and connectivity matrices.</param> /// <param name="colTop"></param> /// <param name="inpTop"></param> /// <returns>Flat index of mapped column.</returns> public static int MapColumn(int columnIndex, HtmModuleTopology colTop, HtmModuleTopology inpTop) { int[] columnCoords = AbstractFlatMatrix.ComputeCoordinates(colTop.NumDimensions, colTop.DimensionMultiplies, colTop.IsMajorOrdering, columnIndex); double[] colCoords = ArrayUtils.ToDoubleArray(columnCoords); double[] columnRatios = ArrayUtils.Divide( colCoords, ArrayUtils.ToDoubleArray(colTop.Dimensions), 0, 0); double[] inputCoords = ArrayUtils.Multiply( ArrayUtils.ToDoubleArray(inpTop.Dimensions), columnRatios, 0, 0); var colSpanOverInputs = ArrayUtils.Divide( ArrayUtils.ToDoubleArray(inpTop.Dimensions), ArrayUtils.ToDoubleArray(colTop.Dimensions), 0, 0); inputCoords = ArrayUtils.AddOffset(inputCoords, ArrayUtils.Multiply(colSpanOverInputs, 0.5)); // Makes sure that inputCoords are in range [0, inpDims] int[] inputCoordInts = ArrayUtils.Clip(ArrayUtils.ToIntArray(inputCoords), inpTop.Dimensions, -1); return(AbstractFlatMatrix.ComputeIndex(inputCoordInts, inpTop.Dimensions, inpTop.NumDimensions, inpTop.DimensionMultiplies, inpTop.IsMajorOrdering, true)); }
/// <summary> /// Method computes the result after the data is provided by the temporal memory and encoder /// </summary> /// <param name="recordNum">Record number of this input pattern. /// Record numbers should normally increase sequentially by 1 each time unless there are missing records in the dataset. /// Knowing this information insures that we don't get confused by missing records.</param> /// <param name="classification">{@link Map} of the classification information: /// bucketIdx: index of the encoder bucket</param> /// <param name="patternNZ">list of the active indices from the output below</param> /// <param name="learn">if true, learn this sample</param> /// <param name="infer">if true, perform inference</param> /// <returns>ClassificationExperiment Object</returns> public ClassificationExperiment <T> Compute(int recordNum, Dictionary <string, object> classification, int[] patternNZ, bool learn, bool infer) { if (classification == null) { throw new ArgumentNullException(nameof(classification)); } if (patternNZ == null) { throw new ArgumentNullException(nameof(patternNZ)); } ClassificationExperiment <T> retVal = new ClassificationExperiment <T>(); List <T> actualValues = (List <T>) this.actualValues; // Save the offset between recordNum and learnIteration if this is the first // compute if (recordNumMinusLearnIteration == -1) { recordNumMinusLearnIteration = recordNum - learnIteration; } // Update the learn iteration learnIteration = recordNum - recordNumMinusLearnIteration; if (patternNZHistory.Any(x => x.Item1 == learnIteration)) { patternNZHistory.Add(Tuple.Create(learnIteration, patternNZ)); } //------------------------------------------------------------- // For each active bit in the activationPattern, get the classification // votes // // Return value dict. For buckets which we don't have an actual value // for yet, just plug in any valid actual value. It doesn't matter what // we use because that bucket won't have non-zero likelihood anyways. if (infer) { // If doing 0-step prediction, we shouldn't use any knowledge // of the classification input during inference. object defaultValue = default(T); if (steps[0] == 0) { defaultValue = 0; } else { defaultValue = classification["actValue"]; if (defaultValue == null) { if (IsNumericType(default(T))) { defaultValue = ConversionExtensions.Convert <T>(-1); } } } T[] actValues = new T[this.actualValues.Count]; for (int i = 0; i < actualValues.Count; i++) { if (IsNumericType(default(T))) { actValues[i] = (T)(actualValues[i].Equals(ConversionExtensions.Convert <T>(-1)) ? ConversionExtensions.Convert <T>(defaultValue) : actualValues[i]); } else { actValues[i] = (T)(actualValues[i].Equals(default(T)) ? defaultValue : actualValues[i]); } } retVal.setActualValues(actValues); // For each n-step prediction... foreach (int nSteps in steps) { // Accumulate bucket index votes and actValues into these arrays double[] sumVotes = new double[maxBucketIdx + 1]; double[] bitVotes = new double[maxBucketIdx + 1]; foreach (var bit in patternNZ) { var key = Tuple.Create(bit, nSteps); BitHistory history = null; activeBitHistory.TryGetValue(key, out history); if (history == null) { continue; } history.Infer(bitVotes); sumVotes = ArrayUtils.AddOffset(sumVotes, bitVotes); } // Return the votes for each bucket, normalized double total = sumVotes.Sum(); if (total > 0) { sumVotes = ArrayUtils.Divide(sumVotes, total); } else { // If all buckets have zero probability then simply make all of the // buckets equally likely. There is no actual prediction for this // timestep so any of the possible predictions are just as good. if (sumVotes.Length > 0) { double val = 1.0 / sumVotes.Length; for (int i = 0; i < sumVotes.Length; i++) { sumVotes[i] = val; } } } retVal.setStats(nSteps, sumVotes); } } // ------------------------------------------------------------------------ // Learning: // For each active bit in the activationPattern, store the classification // info. If the bucketIdx is None, we can't learn. This can happen when the // field is missing in a specific record. if (learn && classification["bucketIdx"] != null) { // Get classification info int bucketIdx = (int)classification["bucketIdx"]; object actValue = null; if (classification["actValue"] == null) { if (IsNumericType(default(T))) { actValue = ConversionExtensions.Convert <T>(-1); } } else { actValue = ConversionExtensions.Convert <T>(classification["actValue"]); } // Update maxBucketIndex maxBucketIdx = (int)Math.Max(maxBucketIdx, bucketIdx); // Update rolling average of actual values if it's a scalar. If it's // not, it must be a category, in which case each bucket only ever // sees one category so we don't need a running average. while (maxBucketIdx > actualValues.Count - 1) { actualValues.Add(IsNumericType(default(T)) ? ConversionExtensions.Convert <T>(-1) : default(T)); } if (actualValues[bucketIdx].Equals(IsNumericType(default(T)) ? ConversionExtensions.Convert <T>(-1) : default(T))) { actualValues[bucketIdx] = (T)actValue; } else { if (IsNumericType(actValue)) { double val = ((1.0 - actValueAlpha) * Convert.ToDouble(actualValues[bucketIdx], enusFormatProvider)) + (actValueAlpha * (Convert.ToDouble(actValue, enusFormatProvider))); actualValues[bucketIdx] = ConversionExtensions.Convert <T>(val); } else { actualValues[bucketIdx] = (T)actValue; } } // Train each pattern that we have in our history that aligns with the // steps we have in steps int nSteps = -1; int iteration = 0; int[] learnPatternNZ = null; foreach (var n in steps) { nSteps = n; // Do we have the pattern that should be assigned to this classification // in our pattern history? If not, skip it bool found = false; foreach (var t in patternNZHistory) { iteration = t.Item1; learnPatternNZ = t.Item2; if (iteration == learnIteration - nSteps) { found = true; break; } iteration++; } if (!found) { continue; } // Store classification info for each active bit from the pattern // that we got nSteps time steps ago. foreach (int bit in learnPatternNZ) { // Get the history structure for this bit and step var key = Tuple.Create(bit, nSteps); BitHistory history = null; activeBitHistory.TryGetValue(key, out history); if (history == null) { history = new BitHistory(alpha); activeBitHistory.Add(key, history); } history.store(learnIteration, bucketIdx); } } } return(retVal); }