示例#1
0
        /// <summary>
        ///
        /// </summary>
        private async Task RunExperimentNeuroVisualizer(int inputBits, Parameters p, EncoderBase encoder, List <double> inputValues)
        {
            Stopwatch sw = new Stopwatch();

            sw.Start();

            int                maxMatchCnt = 0;
            bool               learn       = true;
            INeuroVisualizer   vis         = new WSNeuroVisualizer();
            GenerateNeuroModel model       = new GenerateNeuroModel();

            await vis.ConnectToWSServerAsync();

            await vis.InitModelAsync(model.CreateNeuroModel(new int[] { 1 }, (long[, ])p[KEY.COLUMN_DIMENSIONS], (int)p[KEY.CELLS_PER_COLUMN]));

            CortexNetwork       net     = new CortexNetwork("my cortex");
            List <CortexRegion> regions = new List <CortexRegion>();
            CortexRegion        region0 = new CortexRegion("1st Region");

            regions.Add(region0);

            SpatialPoolerMT sp1 = new SpatialPoolerMT();
            TemporalMemory  tm1 = new TemporalMemory();
            var             mem = new Connections();

            p.apply(mem);
            sp1.Init(mem, UnitTestHelpers.GetMemory());
            tm1.Init(mem);

            CortexLayer <object, object> layer1 = new CortexLayer <object, object>("L1");

            //
            // NewBorn learning stage.
            region0.AddLayer(layer1);
            layer1.HtmModules.Add("encoder", encoder);
            layer1.HtmModules.Add("sp", sp1);

            //HtmClassifier<double, ComputeCycle> cls = new HtmClassifier<double, ComputeCycle>();
            HtmClassifier <string, ComputeCycle> cls = new HtmClassifier <string, ComputeCycle>();

            double[] inputs         = inputValues.ToArray();
            int[]    prevActiveCols = new int[0];

            int maxSPLearningCycles = 50;

            //
            // This trains SP on input pattern.
            // It performs some kind of unsupervised new-born learning.
            foreach (var input in inputs)
            {
                List <(int Cycle, double Similarity)> elementOscilationResult = new List <(int Cycle, double Similarity)>();

                Debug.WriteLine($"Learning  ** {input} **");

                for (int i = 0; i < maxSPLearningCycles; i++)
                {
                    var lyrOut = layer1.Compute((object)input, learn) as ComputeCycle;

                    var activeColumns = layer1.GetResult("sp") as int[];

                    var actCols = activeColumns.OrderBy(c => c).ToArray();

                    var similarity = MathHelpers.CalcArraySimilarity(prevActiveCols, actCols);
                    await vis.UpdateColumnAsync(GetColumns(actCols));

                    Debug.WriteLine($" {i.ToString("D4")} SP-OUT: [{actCols.Length}/{similarity.ToString("0.##")}] - {Helpers.StringifyVector(actCols)}");

                    prevActiveCols = activeColumns;
                }
            }

            // Here we add TM module to the layer.
            layer1.HtmModules.Add("tm", tm1);

            int cycle   = 0;
            int matches = 0;

            string lastPredictedValue = "0";

            Dictionary <double, List <List <int> > > activeColumnsLst = new Dictionary <double, List <List <int> > >();

            foreach (var input in inputs)
            {
                if (activeColumnsLst.ContainsKey(input) == false)
                {
                    activeColumnsLst.Add(input, new List <List <int> >());
                }
            }

            int maxCycles = 3500;

            //
            // Now training with SP+TM. SP is pretrained on the given input pattern.
            for (int i = 0; i < maxCycles; i++)
            {
                matches = 0;

                cycle++;

                Debug.WriteLine($"-------------- Cycle {cycle} ---------------");

                string prevInput = "-1.0";

                //
                // Activate the 'New - Born' effect.
                //if (i == 300)
                //{
                //    mem.setMaxBoost(0.0);
                //    mem.updateMinPctOverlapDutyCycles(0.0);
                //    cls.ClearState();
                //}

                foreach (var input in inputs)
                {
                    Debug.WriteLine($"-------------- {input} ---------------");

                    var lyrOut = layer1.Compute(input, learn) as ComputeCycle;

                    var activeColumns = layer1.GetResult("sp") as int[];

                    activeColumnsLst[input].Add(activeColumns.ToList());

                    //cls.Learn(input, lyrOut.ActiveCells.ToArray());
                    cls.Learn(GetKey(prevInput, input), lyrOut.ActiveCells.ToArray());

                    List <Synapse> synapses = new List <Synapse>();
                    Cell           cell     = new Cell(0, 1, 6, 0, CellActivity.ActiveCell); // where to get all these values
                    Synapse        synap    = new Synapse(cell, 1, 1, 0.78);                 // here is just supposed to update the permanence, all other values remains same; where do we get all other values
                    synapses.Add(synap);
                    await vis.UpdateSynapsesAsync(synapses);                                 //update Synapse or add new ones

                    await vis.UpdateCellsAsync(GetCells(lyrOut.ActiveCells));

                    if (learn == false)
                    {
                        Debug.WriteLine($"Inference mode");
                    }

                    if (GetKey(prevInput, input) == lastPredictedValue)
                    {
                        matches++;
                        Debug.WriteLine($"Match {input}");
                    }
                    else
                    {
                        Debug.WriteLine($"Missmatch Actual value: {GetKey(prevInput, input)} - Predicted value: {lastPredictedValue}");
                    }

                    if (lyrOut.PredictiveCells.Count > 0)
                    {
                        var predictedInputValue = cls.GetPredictedInputValue(lyrOut.PredictiveCells.ToArray());

                        Debug.WriteLine($"Current Input: {input} \t| Predicted Input: {predictedInputValue}");

                        lastPredictedValue = predictedInputValue;
                    }
                    else
                    {
                        Debug.WriteLine($"NO CELLS PREDICTED for next cycle.");
                    }

                    prevInput = input.ToString();
                }

                //tm1.reset(mem);

                double accuracy = (double)matches / (double)inputs.Length * 100.0;

                Debug.WriteLine($"Cycle: {cycle}\tMatches={matches} of {inputs.Length}\t {accuracy}%");

                if (accuracy == 100.0)
                {
                    maxMatchCnt++;
                    Debug.WriteLine($"100% accuracy reched {maxMatchCnt} times.");
                    if (maxMatchCnt >= 20)
                    {
                        sw.Stop();
                        Debug.WriteLine($"Exit experiment in the stable state after 10 repeats with 100% of accuracy. Elapsed time: {sw.ElapsedMilliseconds / 1000 / 60} min.");
                        learn = false;
                        //var testInputs = new double[] { 0.0, 2.0, 3.0, 4.0, 5.0, 6.0, 5.0, 4.0, 3.0, 7.0, 1.0, 9.0, 12.0, 11.0, 0.0, 1.0 };

                        // C-0, D-1, E-2, F-3, G-4, H-5
                        //var testInputs = new double[] { 0.0, 0.0, 4.0, 4.0, 5.0, 5.0, 4.0, 3.0, 3.0, 2.0, 2.0, 1.0, 1.0, 0.0 };

                        //// Traverse the sequence and check prediction.
                        //foreach (var input in inputValues)
                        //{
                        //    var lyrOut = layer1.Compute(input, learn) as ComputeCycle;
                        //    predictedInputValue = cls.GetPredictedInputValue(lyrOut.predictiveCells.ToArray());
                        //    Debug.WriteLine($"I={input} - P={predictedInputValue}");
                        //}

                        //
                        // Here we let the HTM predict seuence five times on its own.
                        // We start with last predicted value.
                        int cnt = 5 * inputValues.Count;

                        Debug.WriteLine("---- Start Predicting the Sequence -----");

                        // We take a random value to start somwhere in the sequence.
                        var predictedInputValue = inputValues[new Random().Next(0, inputValues.Count - 1)].ToString();

                        List <string> predictedValues = new List <string>();

                        while (--cnt > 0)
                        {
                            //var lyrOut = layer1.Compute(predictedInputValue, learn) as ComputeCycle;
                            var lyrOut = layer1.Compute(double.Parse(predictedInputValue[predictedInputValue.Length - 1].ToString()), learn) as ComputeCycle;
                            predictedInputValue = cls.GetPredictedInputValue(lyrOut.PredictiveCells.ToArray());
                            predictedValues.Add(predictedInputValue);
                        }
                        ;

                        foreach (var item in predictedValues)
                        {
                            Debug.Write(item);
                            Debug.Write(" ,");
                        }
                        break;
                    }
                }
                else if (maxMatchCnt > 0)
                {
                    Debug.WriteLine($"At 100% accuracy after {maxMatchCnt} repeats we get a drop of accuracy with {accuracy}. This indicates instable state. Learning will be continued.");
                    maxMatchCnt = 0;
                }
            }

            Debug.WriteLine("---- cell state trace ----");

            cls.TraceState($"cellState_MinPctOverlDuty-{p[KEY.MIN_PCT_OVERLAP_DUTY_CYCLES]}_MaxBoost-{p[KEY.MAX_BOOST]}.csv");

            Debug.WriteLine("---- column state trace ----");

            foreach (var input in activeColumnsLst)
            {
                using (StreamWriter colSw = new StreamWriter($"ColumState_MinPctOverlDuty-{p[KEY.MIN_PCT_OVERLAP_DUTY_CYCLES]}_MaxBoost-{p[KEY.MAX_BOOST]}_input-{input.Key}.csv"))
                {
                    Debug.WriteLine($"------------ {input} ------------");

                    foreach (var actCols in input.Value)
                    {
                        Debug.WriteLine(Helpers.StringifyVector(actCols.ToArray()));
                        colSw.WriteLine(Helpers.StringifyVector(actCols.ToArray()));
                    }
                }
            }

            Debug.WriteLine("------------ END ------------");
        }
示例#2
0
        /// <summary>
        ///
        /// </summary>
        private void RunExperiment(int inputBits, Parameters p, EncoderBase encoder, List <double> inputValues)
        {
            Stopwatch sw = new Stopwatch();

            sw.Start();

            int  maxMatchCnt = 0;
            bool learn       = true;

            CortexNetwork       net     = new CortexNetwork("my cortex");
            List <CortexRegion> regions = new List <CortexRegion>();
            CortexRegion        region0 = new CortexRegion("1st Region");

            regions.Add(region0);

            var mem = new Connections();

            p.apply(mem);

            //bool isInStableState = false;

            //HtmClassifier<double, ComputeCycle> cls = new HtmClassifier<double, ComputeCycle>();
            HtmClassifier <string, ComputeCycle> cls = new HtmClassifier <string, ComputeCycle>();

            var numInputs = inputValues.Distinct().ToList().Count;

            TemporalMemory tm1 = new TemporalMemory();

            HomeostaticPlasticityController hpa = new HomeostaticPlasticityController(mem, numInputs * 55, (isStable, numPatterns, actColAvg, seenInputs) =>
            {
                if (isStable)
                {
                    // Event should be fired when entering the stable state.
                    Debug.WriteLine($"STABLE: Patterns: {numPatterns}, Inputs: {seenInputs}, iteration: {seenInputs / numPatterns}");
                }
                else
                {
                    // Ideal SP should never enter unstable state after stable state.
                    Debug.WriteLine($"INSTABLE: Patterns: {numPatterns}, Inputs: {seenInputs}, iteration: {seenInputs / numPatterns}");
                }

                Assert.IsTrue(numPatterns == numInputs);
                //isInStableState = true;
                cls.ClearState();

                tm1.Reset(mem);
            }, numOfCyclesToWaitOnChange: 25);


            SpatialPoolerMT sp1 = new SpatialPoolerMT(hpa);

            sp1.Init(mem, UnitTestHelpers.GetMemory());
            tm1.Init(mem);

            CortexLayer <object, object> layer1 = new CortexLayer <object, object>("L1");

            region0.AddLayer(layer1);
            layer1.HtmModules.Add("encoder", encoder);
            layer1.HtmModules.Add("sp", sp1);
            layer1.HtmModules.Add("tm", tm1);

            double[] inputs         = inputValues.ToArray();
            int[]    prevActiveCols = new int[0];

            int cycle   = 0;
            int matches = 0;

            string lastPredictedValue = "0";

            Dictionary <double, List <List <int> > > activeColumnsLst = new Dictionary <double, List <List <int> > >();

            foreach (var input in inputs)
            {
                if (activeColumnsLst.ContainsKey(input) == false)
                {
                    activeColumnsLst.Add(input, new List <List <int> >());
                }
            }

            int           maxCycles      = 3500;
            int           maxPrevInputs  = inputValues.Count - 1;
            List <string> previousInputs = new List <string>();

            previousInputs.Add("-1.0");

            //
            // Now training with SP+TM. SP is pretrained on the given input pattern.
            for (int i = 0; i < maxCycles; i++)
            {
                matches = 0;

                cycle++;

                Debug.WriteLine($"-------------- Cycle {cycle} ---------------");

                foreach (var input in inputs)
                {
                    Debug.WriteLine($"-------------- {input} ---------------");

                    var lyrOut = layer1.Compute(input, learn) as ComputeCycle;

                    var activeColumns = layer1.GetResult("sp") as int[];

                    activeColumnsLst[input].Add(activeColumns.ToList());

                    previousInputs.Add(input.ToString());
                    if (previousInputs.Count > maxPrevInputs + 1)
                    {
                        previousInputs.RemoveAt(0);
                    }

                    string key = GetKey(previousInputs, input);


                    List <Cell> actCells;

                    if (lyrOut.ActiveCells.Count == lyrOut.WinnerCells.Count)
                    {
                        actCells = lyrOut.ActiveCells;
                    }
                    else
                    {
                        actCells = lyrOut.WinnerCells;
                    }

                    cls.Learn(key, actCells.ToArray());

                    if (learn == false)
                    {
                        Debug.WriteLine($"Inference mode");
                    }

                    Debug.WriteLine($"Col  SDR: {Helpers.StringifyVector(lyrOut.ActivColumnIndicies)}");
                    Debug.WriteLine($"Cell SDR: {Helpers.StringifyVector(actCells.Select(c => c.Index).ToArray())}");

                    if (key == lastPredictedValue)
                    {
                        matches++;
                        Debug.WriteLine($"Match. Actual value: {key} - Predicted value: {lastPredictedValue}");
                    }
                    else
                    {
                        Debug.WriteLine($"Missmatch! Actual value: {key} - Predicted value: {lastPredictedValue}");
                    }

                    if (lyrOut.PredictiveCells.Count > 0)
                    {
                        var predictedInputValue = cls.GetPredictedInputValue(lyrOut.PredictiveCells.ToArray());

                        Debug.WriteLine($"Current Input: {input} \t| Predicted Input: {predictedInputValue}");

                        lastPredictedValue = predictedInputValue;
                    }
                    else
                    {
                        Debug.WriteLine($"NO CELLS PREDICTED for next cycle.");
                        lastPredictedValue = string.Empty;
                    }
                }

                // The brain does not do that this way, so we don't use it.
                // tm1.reset(mem);

                double accuracy = matches / (double)inputs.Length * 100.0;

                Debug.WriteLine($"Cycle: {cycle}\tMatches={matches} of {inputs.Length}\t {accuracy}%");

                if (accuracy == 100.0)
                {
                    maxMatchCnt++;
                    Debug.WriteLine($"100% accuracy reched {maxMatchCnt} times.");
                    if (maxMatchCnt >= 30)
                    {
                        sw.Stop();
                        Debug.WriteLine($"Exit experiment in the stable state after 30 repeats with 100% of accuracy. Elapsed time: {sw.ElapsedMilliseconds / 1000 / 60} min.");
                        learn = false;
                        //var testInputs = new double[] { 0.0, 2.0, 3.0, 4.0, 5.0, 6.0, 5.0, 4.0, 3.0, 7.0, 1.0, 9.0, 12.0, 11.0, 0.0, 1.0 };

                        // C-0, D-1, E-2, F-3, G-4, H-5
                        //var testInputs = new double[] { 0.0, 0.0, 4.0, 4.0, 5.0, 5.0, 4.0, 3.0, 3.0, 2.0, 2.0, 1.0, 1.0, 0.0 };

                        //// Traverse the sequence and check prediction.
                        //foreach (var input in inputValues)
                        //{
                        //    var lyrOut = layer1.Compute(input, learn) as ComputeCycle;
                        //    predictedInputValue = cls.GetPredictedInputValue(lyrOut.predictiveCells.ToArray());
                        //    Debug.WriteLine($"I={input} - P={predictedInputValue}");
                        //}

                        /*
                         * //
                         * // Here we let the HTM predict sequence five times on its own.
                         * // We start with last predicted value.
                         * int cnt = 5 * inputValues.Count;
                         *
                         * Debug.WriteLine("---- Start Predicting the Sequence -----");
                         *
                         * //
                         * // This code snippet starts with some input value and tries to predict all next inputs
                         * // as they have been learned as a sequence.
                         * // We take a random value to start somwhere in the sequence.
                         * var predictedInputValue = inputValues[new Random().Next(0, inputValues.Count - 1)].ToString();
                         *
                         * List<string> predictedValues = new List<string>();
                         *
                         * while (--cnt > 0)
                         * {
                         *  //var lyrOut = layer1.Compute(predictedInputValue, learn) as ComputeCycle;
                         *  var lyrOut = layer1.Compute(double.Parse(predictedInputValue[predictedInputValue.Length - 1].ToString()), false) as ComputeCycle;
                         *  predictedInputValue = cls.GetPredictedInputValue(lyrOut.PredictiveCells.ToArray());
                         *  predictedValues.Add(predictedInputValue);
                         * };
                         *
                         * // Now we have a sequence of elements and watch in the trace if it matches to defined input set.
                         * foreach (var item in predictedValues)
                         * {
                         *  Debug.Write(item);
                         *  Debug.Write(" ,");
                         * }*/
                        break;
                    }
                }
                else if (maxMatchCnt > 0)
                {
                    Debug.WriteLine($"At 100% accuracy after {maxMatchCnt} repeats we get a drop of accuracy with {accuracy}. This indicates instable state. Learning will be continued.");
                    maxMatchCnt = 0;
                }
            }

            Debug.WriteLine("---- cell state trace ----");

            cls.TraceState($"cellState_MinPctOverlDuty-{p[KEY.MIN_PCT_OVERLAP_DUTY_CYCLES]}_MaxBoost-{p[KEY.MAX_BOOST]}.csv");

            Debug.WriteLine("---- Spatial Pooler column state  ----");

            foreach (var input in activeColumnsLst)
            {
                using (StreamWriter colSw = new StreamWriter($"ColumState_MinPctOverlDuty-{p[KEY.MIN_PCT_OVERLAP_DUTY_CYCLES]}_MaxBoost-{p[KEY.MAX_BOOST]}_input-{input.Key}.csv"))
                {
                    Debug.WriteLine($"------------ {input.Key} ------------");

                    foreach (var actCols in input.Value)
                    {
                        Debug.WriteLine(Helpers.StringifyVector(actCols.ToArray()));
                        colSw.WriteLine(Helpers.StringifyVector(actCols.ToArray()));
                    }
                }
            }

            Debug.WriteLine("------------ END ------------");
        }
示例#3
0
        /// <summary>
        ///
        /// </summary>
        private static void RunExperiment(int inputBits, HtmConfig cfg, EncoderBase encoder, List <double> inputValues)
        {
            Stopwatch sw = new Stopwatch();

            sw.Start();

            int  maxMatchCnt = 0;
            bool learn       = true;

            CortexNetwork       net     = new CortexNetwork("my cortex");
            List <CortexRegion> regions = new List <CortexRegion>();
            CortexRegion        region0 = new CortexRegion("1st Region");

            regions.Add(region0);

            var  mem = new Connections(cfg);
            bool isInStableState;

            HtmClassifier <string, ComputeCycle> cls = new HtmClassifier <string, ComputeCycle>();

            var numInputs = inputValues.Distinct <double>().ToList().Count;

            TemporalMemory tm1 = new TemporalMemory();

            HomeostaticPlasticityController hpa = new HomeostaticPlasticityController(mem, numInputs * 55, (isStable, numPatterns, actColAvg, seenInputs) =>
            {
                if (isStable)
                {
                    // Event should be fired when entering the stable state.
                    Debug.WriteLine($"STABLE: Patterns: {numPatterns}, Inputs: {seenInputs}, iteration: {seenInputs / numPatterns}");
                }
                else
                {
                    // Ideal SP should never enter unstable state after stable state.
                    Debug.WriteLine($"INSTABLE: Patterns: {numPatterns}, Inputs: {seenInputs}, iteration: {seenInputs / numPatterns}");
                }

                if (numPatterns != numInputs)
                {
                    throw new InvalidOperationException("Stable state must observe all input patterns");
                }

                isInStableState = true;
                cls.ClearState();

                tm1.Reset(mem);
            }, numOfCyclesToWaitOnChange: 25);


            SpatialPoolerMT sp1 = new SpatialPoolerMT(hpa);

            sp1.Init(mem, new DistributedMemory()
            {
                ColumnDictionary = new InMemoryDistributedDictionary <int, NeoCortexApi.Entities.Column>(1),
            });

            tm1.Init(mem);

            CortexLayer <object, object> layer1 = new CortexLayer <object, object>("L1");

            region0.AddLayer(layer1);
            layer1.HtmModules.Add("encoder", encoder);
            layer1.HtmModules.Add("sp", sp1);
            layer1.HtmModules.Add("tm", tm1);

            double[] inputs = inputValues.ToArray();

            int[] prevActiveCols = new int[0];

            int cycle   = 0;
            int matches = 0;

            string lastPredictedValue = "0";
            String prediction         = null;

            Dictionary <double, List <List <int> > > activeColumnsLst = new Dictionary <double, List <List <int> > >();

            foreach (var input in inputs)
            {
                if (activeColumnsLst.ContainsKey(input) == false)
                {
                    activeColumnsLst.Add(input, new List <List <int> >());
                }
            }

            int           maxCycles      = 3500;
            int           maxPrevInputs  = inputValues.Count - 1;
            List <string> previousInputs = new List <string>();

            previousInputs.Add("-1.0");

            //
            // Now training with SP+TM. SP is pretrained on the given input pattern.
            for (int i = 0; i < maxCycles; i++)
            {
                matches = 0;

                cycle++;

                Debug.WriteLine($"-------------- Cycle {cycle} ---------------");

                foreach (var input in inputs)
                {
                    Debug.WriteLine($"-------------- {input} ---------------");

                    var lyrOut = layer1.Compute(input, learn) as ComputeCycle;

                    var activeColumns = layer1.GetResult("sp") as int[];

                    activeColumnsLst[input].Add(activeColumns.ToList());

                    previousInputs.Add(input.ToString());
                    if (previousInputs.Count > (maxPrevInputs + 1))
                    {
                        previousInputs.RemoveAt(0);
                    }

                    string key = GetKey(previousInputs, input);

                    cls.Learn(key, lyrOut.ActiveCells.ToArray());

                    if (learn == false)
                    {
                        Debug.WriteLine($"Inference mode");
                    }

                    Debug.WriteLine($"Col  SDR: {Helpers.StringifyVector(lyrOut.ActivColumnIndicies)}");
                    Debug.WriteLine($"Cell SDR: {Helpers.StringifyVector(lyrOut.ActiveCells.Select(c => c.Index).ToArray())}");

                    if (key == lastPredictedValue)
                    {
                        matches++;
                        Debug.WriteLine($"Match. Actual value: {key} - Predicted value: {lastPredictedValue}");
                    }
                    else
                    {
                        Debug.WriteLine($"Missmatch! Actual value: {key} - Predicted value: {lastPredictedValue}");
                    }

                    if (lyrOut.PredictiveCells.Count > 0)
                    {
                        var predictedInputValue = cls.GetPredictedInputValues(lyrOut.PredictiveCells.ToArray(), 3);

                        Debug.WriteLine($"Current Input: {input}");
                        Debug.WriteLine("The predictions with similarity greater than 50% are");

                        foreach (var t in predictedInputValue)
                        {
                            if (t.Similarity >= (double)50.00)
                            {
                                Debug.WriteLine($"Predicted Input: {string.Join(", ", t.PredictedInput)},\tSimilarity Percentage: {string.Join(", ", t.Similarity)}, \tNumber of Same Bits: {string.Join(", ", t.NumOfSameBits)}");
                            }
                        }
                        lastPredictedValue = predictedInputValue.First().PredictedInput;
                    }
                    else
                    {
                        Debug.WriteLine($"NO CELLS PREDICTED for next cycle.");
                        lastPredictedValue = String.Empty;
                    }
                }


                double accuracy = (double)matches / (double)inputs.Length * 100.0;

                Debug.WriteLine($"Cycle: {cycle}\tMatches={matches} of {inputs.Length}\t {accuracy}%");

                if (accuracy == 100.0)
                {
                    maxMatchCnt++;
                    Debug.WriteLine($"100% accuracy reched {maxMatchCnt} times.");
                    if (maxMatchCnt >= 30)
                    {
                        sw.Stop();
                        Debug.WriteLine($"Exit experiment in the stable state after 30 repeats with 100% of accuracy. Elapsed time: {sw.ElapsedMilliseconds / 1000 / 60} min.");
                        learn = false;
                        break;
                    }
                }
                else if (maxMatchCnt > 0)
                {
                    Debug.WriteLine($"At 100% accuracy after {maxMatchCnt} repeats we get a drop of accuracy with {accuracy}. This indicates instable state. Learning will be continued.");
                    maxMatchCnt = 0;
                }
            }

            Debug.WriteLine("---- cell state trace ----");

            cls.TraceState($"cellState_MinPctOverlDuty-{cfg.MinPctOverlapDutyCycles}_MaxBoost-{cfg.MaxBoost}.csv");

            Debug.WriteLine("---- Spatial Pooler column state  ----");

            foreach (var input in activeColumnsLst)
            {
                using (StreamWriter colSw = new StreamWriter($"ColumState_MinPctOverlDuty-{cfg.MinPctOverlapDutyCycles}_MaxBoost-{cfg.MaxBoost}_input-{input.Key}.csv"))
                {
                    Debug.WriteLine($"------------ {input.Key} ------------");

                    foreach (var actCols in input.Value)
                    {
                        Debug.WriteLine(Helpers.StringifyVector(actCols.ToArray()));
                        colSw.WriteLine(Helpers.StringifyVector(actCols.ToArray()));
                    }
                }
            }

            Debug.WriteLine("------------ END ------------");

            Console.WriteLine("\n Please enter a number that has been learnt");
            int inputNumber = Convert.ToInt16(Console.ReadLine());

            Inference(inputNumber, false, layer1, cls);
        }
示例#4
0
        /// <summary>
        ///
        /// </summary>
        private void RunExperiment(int inputBits, HtmConfig cfg, EncoderBase encoder, List <double> inputValues)
        {
            Stopwatch sw = new Stopwatch();

            sw.Start();

            int  maxMatchCnt = 0;
            bool learn       = true;

            var mem = new Connections(cfg);

            bool isInStableState = false;

            HtmClassifier <string, ComputeCycle> cls = new HtmClassifier <string, ComputeCycle>();

            var numInputs = inputValues.Distinct <double>().ToList().Count;

            CortexLayer <object, object> layer1 = new CortexLayer <object, object>("L1");

            TemporalMemory tm = new TemporalMemory();

            HomeostaticPlasticityController hpa = new HomeostaticPlasticityController(mem, numInputs * 150, (isStable, numPatterns, actColAvg, seenInputs) =>
            {
                if (isStable)
                {
                    // Event should be fired when entering the stable state.
                    Debug.WriteLine($"STABLE: Patterns: {numPatterns}, Inputs: {seenInputs}, iteration: {seenInputs / numPatterns}");
                }
                else
                {
                    // Ideal SP should never enter unstable state after stable state.
                    Debug.WriteLine($"INSTABLE: Patterns: {numPatterns}, Inputs: {seenInputs}, iteration: {seenInputs / numPatterns}");
                }

                // We are not learning in instable state.
                learn = isInStableState = isStable;

                //if (isStable && layer1.HtmModules.ContainsKey("tm") == false)
                //    layer1.HtmModules.Add("tm", tm);

                // Clear all learned patterns in the classifier.
                cls.ClearState();

                // Clear active and predictive cells.
                //tm.Reset(mem);
            }, numOfCyclesToWaitOnChange: 50);


            SpatialPoolerMT sp = new SpatialPoolerMT(hpa);

            sp.Init(mem);
            tm.Init(mem);

            layer1.HtmModules.Add("encoder", encoder);
            layer1.HtmModules.Add("sp", sp);

            double[] inputs         = inputValues.ToArray();
            int[]    prevActiveCols = new int[0];

            int cycle   = 0;
            int matches = 0;

            string lastPredictedValue = "0";

            //Dictionary<double, List<List<int>>> activeColumnsLst = new Dictionary<double, List<List<int>>>();

            //foreach (var input in inputs)
            //{
            //    if (activeColumnsLst.ContainsKey(input) == false)
            //        activeColumnsLst.Add(input, new List<List<int>>());
            //}

            int           maxCycles      = 3500;
            int           maxPrevInputs  = inputValues.Count - 1;
            List <string> previousInputs = new List <string>();

            previousInputs.Add("-1.0");

            //
            // Training SP to get stable. New-born stage.
            //

            for (int i = 0; i < maxCycles; i++)
            {
                matches = 0;

                cycle++;

                Debug.WriteLine($"-------------- Newborn Cycle {cycle} ---------------");

                foreach (var input in inputs)
                {
                    Debug.WriteLine($" -- {input} --");

                    var lyrOut = layer1.Compute(input, learn);

                    if (isInStableState)
                    {
                        break;
                    }
                }

                if (isInStableState)
                {
                    break;
                }
            }

            layer1.HtmModules.Add("tm", tm);

            //
            // Now training with SP+TM. SP is pretrained on the given input pattern set.
            for (int i = 0; i < maxCycles; i++)
            {
                matches = 0;

                cycle++;

                Debug.WriteLine($"-------------- Cycle {cycle} ---------------");

                foreach (var input in inputs)
                {
                    Debug.WriteLine($"-------------- {input} ---------------");

                    var lyrOut = layer1.Compute(input, learn) as ComputeCycle;

                    // lyrOut is null when the TM is added to the layer inside of HPC callback by entering of the stable state.
                    //if (isInStableState && lyrOut != null)
                    {
                        var activeColumns = layer1.GetResult("sp") as int[];

                        //layer2.Compute(lyrOut.WinnerCells, true);
                        //activeColumnsLst[input].Add(activeColumns.ToList());

                        previousInputs.Add(input.ToString());
                        if (previousInputs.Count > (maxPrevInputs + 1))
                        {
                            previousInputs.RemoveAt(0);
                        }

                        // In the pretrained SP with HPC, the TM will quickly learn cells for patterns
                        // In that case the starting sequence 4-5-6 might have the sam SDR as 1-2-3-4-5-6,
                        // Which will result in returning of 4-5-6 instead of 1-2-3-4-5-6.
                        // HtmClassifier allways return the first matching sequence. Because 4-5-6 will be as first
                        // memorized, it will match as the first one.
                        if (previousInputs.Count < maxPrevInputs)
                        {
                            continue;
                        }

                        string key = GetKey(previousInputs, input);

                        List <Cell> actCells;

                        if (lyrOut.ActiveCells.Count == lyrOut.WinnerCells.Count)
                        {
                            actCells = lyrOut.ActiveCells;
                        }
                        else
                        {
                            actCells = lyrOut.WinnerCells;
                        }

                        cls.Learn(key, actCells.ToArray());

                        if (learn == false)
                        {
                            Debug.WriteLine($"Inference mode");
                        }

                        Debug.WriteLine($"Col  SDR: {Helpers.StringifyVector(lyrOut.ActivColumnIndicies)}");
                        Debug.WriteLine($"Cell SDR: {Helpers.StringifyVector(actCells.Select(c => c.Index).ToArray())}");

                        if (key == lastPredictedValue)
                        {
                            matches++;
                            Debug.WriteLine($"Match. Actual value: {key} - Predicted value: {lastPredictedValue}");
                        }
                        else
                        {
                            Debug.WriteLine($"Missmatch! Actual value: {key} - Predicted value: {lastPredictedValue}");
                        }

                        if (lyrOut.PredictiveCells.Count > 0)
                        {
                            var predictedInputValue = cls.GetPredictedInputValue(lyrOut.PredictiveCells.ToArray());

                            Debug.WriteLine($"Current Input: {input} \t| Predicted Input: {predictedInputValue}");

                            lastPredictedValue = predictedInputValue;
                        }
                        else
                        {
                            Debug.WriteLine($"NO CELLS PREDICTED for next cycle.");
                            lastPredictedValue = String.Empty;
                        }
                    }
                }

                // The brain does not do that this way, so we don't use it.
                // tm1.reset(mem);

                double accuracy = (double)matches / (double)inputs.Length * 100.0;

                Debug.WriteLine($"Cycle: {cycle}\tMatches={matches} of {inputs.Length}\t {accuracy}%");

                if (accuracy == 100.0)
                {
                    maxMatchCnt++;
                    Debug.WriteLine($"100% accuracy reched {maxMatchCnt} times.");
                    //
                    // Experiment is completed if we are 30 cycles long at the 100% accuracy.
                    if (maxMatchCnt >= 30)
                    {
                        sw.Stop();
                        Debug.WriteLine($"Exit experiment in the stable state after 30 repeats with 100% of accuracy. Elapsed time: {sw.ElapsedMilliseconds / 1000 / 60} min.");
                        learn = false;
                        break;
                    }
                }
                else if (maxMatchCnt > 0)
                {
                    Debug.WriteLine($"At 100% accuracy after {maxMatchCnt} repeats we get a drop of accuracy with {accuracy}. This indicates instable state. Learning will be continued.");
                    maxMatchCnt = 0;
                }
            }

            Debug.WriteLine("------------ END ------------");
        }
        private void RunExperiment(int inputBits, HtmConfig cfgL4, EncoderBase encoder, List <double> inputValues, HtmConfig cfgL2)
        {
            Stopwatch swL2 = new Stopwatch();

            int  maxMatchCnt = 0;
            bool learn       = true;
            bool isSP4Stable = false;
            bool isSP2STable = false;

            Connections memL4 = new Connections(cfgL4);
            Connections memL2 = new Connections(cfgL2);

            int numInputs = inputValues.Distinct <double>().ToList().Count;
            HtmClassifier <string, ComputeCycle> cls = new HtmClassifier <string, ComputeCycle>();

            layerL4 = new CortexLayer <object, object>("L4");
            layerL2 = new CortexLayer <object, object>("L2");
            //tm4 = new TemporalMemoryMT();
            //tm2 = new TemporalMemoryMT();
            tm4 = new TemporalMemory();
            tm2 = new TemporalMemory();

            //
            // HPC for Layer 4 SP
            HomeostaticPlasticityController hpa_sp_L4 = new HomeostaticPlasticityController(memL4, numInputs * 50, (isStable, numPatterns, actColAvg, seenInputs) =>
            {
                if (isStable)
                {
                    Debug.WriteLine($"SP L4 STABLE: Patterns: {numPatterns}, Inputs: {seenInputs}, iteration: {seenInputs / numPatterns}");
                }
                else
                {
                    Debug.WriteLine($"SP L4 INSTABLE: Patterns: {numPatterns}, Inputs: {seenInputs}, iteration: {seenInputs / numPatterns}");
                }
                learn = isSP4Stable = isStable;
                cls.ClearState();
            }, numOfCyclesToWaitOnChange: 50);


            //
            // HPC for Layer 2 SP
            HomeostaticPlasticityController hpa_sp_L2 = new HomeostaticPlasticityController(memL2, numInputs * 50, (isStable, numPatterns, actColAvg, seenInputs) =>
            {
                if (isStable)
                {
                    Debug.WriteLine($"SP L2 STABLE: Patterns: {numPatterns}, Inputs: {seenInputs}, iteration: {seenInputs / numPatterns}");
                }
                else
                {
                    Debug.WriteLine($"SP L2 INSTABLE: Patterns: {numPatterns}, Inputs: {seenInputs}, iteration: {seenInputs / numPatterns}");
                }

                learn = isSP2STable = isStable;
                cls.ClearState();
            }, numOfCyclesToWaitOnChange: 50);

            SpatialPooler sp4 = new SpatialPooler(hpa_sp_L4);

            SpatialPooler sp2 = new SpatialPooler(hpa_sp_L2);

            sp4.Init(memL4);
            sp2.Init(memL2);

            // memL2.TraceInputPotential();

            tm4.Init(memL4);
            tm2.Init(memL2);

            layerL4.HtmModules.Add("encoder", encoder);
            layerL4.HtmModules.Add("sp", sp4);
            layerL4.HtmModules.Add("tm", tm4);

            layerL2.HtmModules.Add("sp", sp2);
            layerL2.HtmModules.Add("tm", tm2);

            int[] inpCellsL4ToL2 = new int[cfgL4.CellsPerColumn * cfgL4.NumColumns];

            double[]      inputs             = inputValues.ToArray();
            int[]         prevActiveCols     = new int[0];
            int           cycle              = 0;
            int           matches            = 0;
            string        lastPredictedValue = "0";
            int           maxCycles          = 3500;
            int           maxPrevInputs      = inputValues.Count - 1;
            List <string> previousInputs     = new List <string>();


            //
            // Training SP at Layer 4 to get stable. New-born stage.
            //
            using (StreamWriter swL4Sdrs = new StreamWriter($"L4-SDRs-in_{cfgL2.NumInputs}-col_{cfgL2.NumColumns}-r_{cfgL2.PotentialRadius}.txt"))
            {
                using (StreamWriter sw = new StreamWriter($"in_{cfgL2.NumInputs}-col_{cfgL2.NumColumns}-r_{cfgL2.PotentialRadius}.txt"))
                {
                    for (int i = 0; i < maxCycles; i++)
                    {
                        matches = 0;
                        cycle   = i;
                        Debug.WriteLine($"-------------- Newborn Cycle {cycle} at L4 SP region  ---------------");

                        foreach (double input in inputs)
                        {
                            Debug.WriteLine($" INPUT: '{input}'\t Cycle:{cycle}");
                            Debug.Write("L4: ");
                            object lyrOut           = layerL4.Compute(input, learn);
                            int[]  activeColumns    = layerL4.GetResult("sp") as int[];
                            int[]  cellSdrL4Indexes = memL4.ActiveCells.Select(c => c.Index).ToArray();
                            Debug.WriteLine($"L4out Active Coloumn for input: {input}: {Helpers.StringifyVector(activeColumns)}");
                            Debug.WriteLine($"L4out SDR for input: {input}: {Helpers.StringifyVector(cellSdrL4Indexes)}");


                            if (isSP4Stable)
                            {
                                /// <summary>
                                /// Checking Layer4 SP is giving similar or different
                                /// Active Cell Sdr Indexes After it reaches to stable state.
                                /// This portion is actuallly to hold all acive cell sdr
                                /// indexes after Layer 4 SP reaches at stable state via HPC.
                                ///
                                /// But why we have done this?
                                ///
                                /// Actually, In Necortex api we have obeserved during severel
                                /// experiments that whenvever Layer4 SP reaches to STABLE sate
                                /// it doesnt give us smilar pattern of Active Cell SDR Indexes
                                /// for each particular input data.
                                ///
                                /// Instead active cell sdr patern of L4 varried  though it has reached
                                /// to stable sate!
                                ///
                                /// So we want to obeserve whether Layer 4 SP is giving similar active cell sdr indexes
                                /// or different acrive cell sdr indexes after it reaches to stable state.
                                ///
                                /// If we receive similar acrive cell sdr indexes from Layer 4 sp after it reaches
                                /// to stable state then do train Layer 2 sp by as usual process in NeocortexApi by
                                /// calling Layer4.Compute().
                                ///
                                /// But if we receive different active cell sdr indexes then we will train Layer 2 sp
                                /// from L4_ActiveCell_sdr_log so that during training
                                /// Layer 2 SP gets similar stable active cell sdr indexes of layer4
                                /// from that dictionary. As a result, L2 SP will get similar sequence
                                /// of active cell sdr indexes during SP Tarining and reach to STABLE state
                                /// </summary>

                                Array.Sort(cellSdrL4Indexes);
                                if (!L4_ActiveCell_sdr_log.ContainsKey(input))
                                {
                                    L4_ActiveCell_sdr_log.Add(input, cellSdrL4Indexes);
                                }
                                else
                                {
                                    if (L4_ActiveCell_sdr_log[input].SequenceEqual(cellSdrL4Indexes))
                                    {
                                        Debug.WriteLine($"Layer4.Compute() is giving similar cell sdr indexes for input : {input} after reaching to stable state");
                                        isSimilar_L4_active_cell_sdr = true;
                                    }
                                    else
                                    {
                                        isSimilar_L4_active_cell_sdr = false;
                                        Debug.WriteLine($"Layer4.Compute() is giving different cell sdr indexes for input : {input} after reaching to stable state");
                                        Debug.WriteLine($"Sdr Mismatch with L4_ActiveCell_sdr_log after reaching to stable state");
                                        Debug.WriteLine($" L4_ActiveCell_sdr_log output for input {input}: { Helpers.StringifyVector(L4_ActiveCell_sdr_log[input])}");
                                        Debug.WriteLine($"L4 out sdr input:{input} {Helpers.StringifyVector(cellSdrL4Indexes)}");
                                        //Debug.WriteLine($"L4 out ac input: {input}: {Helpers.StringifyVector(activeColumns)}");
                                    }
                                }


                                if (!isSimilar_L4_active_cell_sdr)
                                {
                                    cellSdrL4Indexes = L4_ActiveCell_sdr_log[input];
                                }


                                //
                                // Training SP at Layer 2 to get stable. New-born stage.
                                //

                                // Write SDR as output of L4 and input of L2
                                // swL4Sdrs.WriteLine($"{input} - {Helpers.StringifyVector(cellSdrL4Indexes)}");
                                // Set the output active cell array

                                InitArray(inpCellsL4ToL2, 0);
                                ArrayUtils.SetIndexesTo(inpCellsL4ToL2, cellSdrL4Indexes, 1);
                                Debug.WriteLine($"L4 cell sdr to L2 SP Train for Input {input}: ");
                                layerL2.Compute(inpCellsL4ToL2, true);
                                int[]  overlaps    = ArrayUtils.IndexWhere(memL2.Overlaps, o => o > 0);
                                string strOverlaps = Helpers.StringifyVector(overlaps);
                                Debug.WriteLine($"Potential columns: {overlaps.Length}, overlaps: {strOverlaps}");
                            }
                        }


                        if (isSP4Stable && isSP2STable)
                        {
                            break;
                        }
                    }
                }
            }


            //
            // SP+TM at L2
            for (int i = 0; i < maxCycles; i++)
            {
                matches = 0;

                cycle = i;

                Debug.WriteLine($"-------------- L2 TM Train region Cycle {cycle} ---------------");

                foreach (double input in inputs)
                {
                    Debug.WriteLine($"-------------- {input} ---------------");

                    // Reset tha array

                    //var cellSdrL4Indexes = L4_ActiveCell_sdr_log[input];
                    object layerL4Out = layerL4.Compute(input, learn);
                    previousInputs.Add(input.ToString());

                    if (previousInputs.Count > (maxPrevInputs + 1))
                    {
                        previousInputs.RemoveAt(0);
                    }
                    if (previousInputs.Count < maxPrevInputs)
                    {
                        continue;
                    }

                    key = GetKey(previousInputs, input);

                    List <Cell> actCells;
                    InitArray(inpCellsL4ToL2, 0);
                    int[] cellSdrL4Indexes;

                    if (!isSimilar_L4_active_cell_sdr)
                    {
                        cellSdrL4Indexes = L4_ActiveCell_sdr_log[input];
                    }
                    else
                    {
                        cellSdrL4Indexes = memL4.ActiveCells.Select(c => c.Index).ToArray();
                    }


                    // Set the output active cell array
                    ArrayUtils.SetIndexesTo(inpCellsL4ToL2, cellSdrL4Indexes, 1);

                    ComputeCycle layerL2Out = layerL2.Compute(inpCellsL4ToL2, true) as ComputeCycle;


                    if (layerL2Out.ActiveCells.Count == layerL2Out.WinnerCells.Count)
                    {
                        actCells = layerL2Out.ActiveCells;
                    }
                    else
                    {
                        actCells = layerL2Out.WinnerCells;
                    }

                    /// <summary>
                    /// HTM Classifier has added for Layer 2
                    /// </summary>

                    cls.Learn(key, actCells.ToArray());

                    if (key == lastPredictedValue)
                    {
                        matches++;
                        Debug.WriteLine($"Match. Actual  Sequence: {key} - Last Predicted Sequence: {lastPredictedValue}");
                    }
                    else
                    {
                        Debug.WriteLine($"Missmatch! Actual Sequence: {key} - Last Predicted Sequence: {lastPredictedValue}");
                    }


                    /// <summary>
                    /// Classifier is taking Predictive Cells from Layer 2
                    /// </summary>

                    if (layerL2Out.PredictiveCells.Count > 0)
                    {
                        string predictedInputValue = cls.GetPredictedInputValue(layerL2Out.PredictiveCells.ToArray());

                        Debug.WriteLine($"Current Input: {input} \t| New Predicted Input Sequence: {predictedInputValue}");

                        lastPredictedValue = predictedInputValue;
                    }
                    else
                    {
                        Debug.WriteLine($"NO CELLS PREDICTED for next cycle.");
                        lastPredictedValue = String.Empty;
                    }
                }

                double accuracy = (double)matches / (double)inputs.Length * 100.0;

                Debug.WriteLine($"Cycle: {cycle}\tMatches={matches} of {inputs.Length}\t {accuracy}%");

                if (accuracy >= 100.0)
                {
                    maxMatchCnt++;
                    Debug.WriteLine($"100% accuracy reched {maxMatchCnt} times.");
                    //
                    // Experiment is completed if we are 20 cycles long at the 100% accuracy.
                    if (maxMatchCnt >= 20)
                    {
                        Debug.WriteLine($"Exit experiment in the stable state after 20 repeats with 100% of accuracy.");
                        learn = false;
                        break;
                    }
                }
                else if (maxMatchCnt > 0)
                {
                    Debug.WriteLine($"At 100% accuracy after {maxMatchCnt} repeats we get a drop of accuracy with {accuracy}. This indicates instable state. Learning will be continued.");
                }
            }
        }
示例#6
0
        /// <summary>
        /// Implements the experiment.
        /// </summary>
        /// <param name="cfg"></param>
        /// <param name="encoder"></param>
        /// <param name="inputValues"></param>
        private static void RunExperiment(HtmConfig cfg, EncoderBase encoder, List <double> inputValues)
        {
            // Creates the htm memory.
            var mem = new Connections(cfg);

            bool isInStableState = false;

            //
            // HPC extends the default Spatial Pooler algorithm.
            // The purpose of HPC is to set the SP in the new-born stage at the begining of the learning process.
            // In this stage the boosting is very active, but the SP behaves instable. After this stage is over
            // (defined by the second argument) the HPC is controlling the learning process of the SP.
            // Once the SDR generated for every input gets stable, the HPC will fire event that notifies your code
            // that SP is stable now.
            HomeostaticPlasticityController hpa = new HomeostaticPlasticityController(mem, inputValues.Count * 15,
                                                                                      (isStable, numPatterns, actColAvg, seenInputs) =>
            {
                // Event should only be fired when entering the stable state.
                // Ideal SP should never enter unstable state after stable state.
                if (isStable == false)
                {
                    Debug.WriteLine($"INSTABLE");
                    // This should usually not happen.
                    isInStableState = false;
                }
                else
                {
                    Debug.WriteLine($"STABILITY");
                    // Here you can perform any action if required.
                    isInStableState = true;
                }
            });

            // It creates the instance of Spatial Pooler Multithreaded version.
            SpatialPooler sp = new SpatialPoolerMT(hpa);

            // Initializes the
            sp.Init(mem, new DistributedMemory()
            {
                ColumnDictionary = new InMemoryDistributedDictionary <int, NeoCortexApi.Entities.Column>(1)
            });

            // It creates the instance of the neo-cortex layer.
            // Algorithm will be performed inside of that layer.
            CortexLayer <object, object> cortexLayer = new CortexLayer <object, object>("L1");

            // Add encoder as the very first module. This model is connected to the sensory input cells
            // that receive the input. Encoder will receive the input and forward the encoded signal
            // to the next module.
            cortexLayer.HtmModules.Add("encoder", encoder);

            // The next module in the layer is Spatial Pooler. This module will receive the output of the
            // encoder.
            cortexLayer.HtmModules.Add("sp", sp);

            double[] inputs = inputValues.ToArray();

            // Will hold the SDR of every inputs.
            Dictionary <double, int[]> prevActiveCols = new Dictionary <double, int[]>();

            // Will hold the similarity of SDKk and SDRk-1 fro every input.
            Dictionary <double, double> prevSimilarity = new Dictionary <double, double>();

            //
            // Initiaize start similarity to zero.
            foreach (var input in inputs)
            {
                prevSimilarity.Add(input, 0.0);
                prevActiveCols.Add(input, new int[0]);
            }

            // Learning process will take 1000 iterations (cycles)
            int maxSPLearningCycles = 1000;

            for (int cycle = 0; cycle < maxSPLearningCycles; cycle++)
            {
                Debug.WriteLine($"Cycle  ** {cycle} ** Stability: {isInStableState}");

                //
                // This trains the layer on input pattern.
                foreach (var input in inputs)
                {
                    double similarity;

                    // Learn the input pattern.
                    // Output lyrOut is the output of the last module in the layer.
                    //
                    var lyrOut = cortexLayer.Compute((object)input, true) as int[];

                    // This is a general way to get the SpatialPooler result from the layer.
                    var activeColumns = cortexLayer.GetResult("sp") as int[];

                    var actCols = activeColumns.OrderBy(c => c).ToArray();

                    similarity = MathHelpers.CalcArraySimilarity(activeColumns, prevActiveCols[input]);

                    Debug.WriteLine($"[cycle={cycle.ToString("D4")}, i={input}, cols=:{actCols.Length} s={similarity}] SDR: {Helpers.StringifyVector(actCols)}");

                    prevActiveCols[input] = activeColumns;
                    prevSimilarity[input] = similarity;
                }
            }
        }
        /// <summary>
        ///
        /// </summary>
        private HtmPredictionEngine RunExperiment(int inputBits, HtmConfig cfg, EncoderBase encoder, Dictionary <string, List <double> > sequences)
        {
            Stopwatch sw = new Stopwatch();

            sw.Start();

            int maxMatchCnt = 0;

            var mem = new Connections(cfg);

            bool isInStableState = false;

            HtmClassifier <string, ComputeCycle> cls = new HtmClassifier <string, ComputeCycle>();

            var numUniqueInputs = GetNumberOfInputs(sequences);

            CortexLayer <object, object> layer1 = new CortexLayer <object, object>("L1");

            TemporalMemory tm = new TemporalMemory();

            // For more information see following paper: https://www.scitepress.org/Papers/2021/103142/103142.pdf
            HomeostaticPlasticityController hpc = new HomeostaticPlasticityController(mem, numUniqueInputs * 150, (isStable, numPatterns, actColAvg, seenInputs) =>
            {
                if (isStable)
                {
                    // Event should be fired when entering the stable state.
                    Debug.WriteLine($"STABLE: Patterns: {numPatterns}, Inputs: {seenInputs}, iteration: {seenInputs / numPatterns}");
                }
                else
                {
                    // Ideal SP should never enter unstable state after stable state.
                    Debug.WriteLine($"INSTABLE: Patterns: {numPatterns}, Inputs: {seenInputs}, iteration: {seenInputs / numPatterns}");
                }

                // We are not learning in instable state.
                isInStableState = isStable;

                // Clear active and predictive cells.
                //tm.Reset(mem);
            }, numOfCyclesToWaitOnChange: 50);


            SpatialPoolerMT sp = new SpatialPoolerMT(hpc);

            sp.Init(mem);
            tm.Init(mem);

            // Please note that we do not add here TM in the layer.
            // This is omitted for practical reasons, because we first eneter the newborn-stage of the algorithm
            // In this stage we want that SP get boosted and see all elements before we start learning with TM.
            // All would also work fine with TM in layer, but it would work much slower.
            // So, to improve the speed of experiment, we first ommit the TM and then after the newborn-stage we add it to the layer.
            layer1.HtmModules.Add("encoder", encoder);
            layer1.HtmModules.Add("sp", sp);

            //double[] inputs = inputValues.ToArray();
            int[] prevActiveCols = new int[0];

            int cycle   = 0;
            int matches = 0;

            var lastPredictedValues = new List <string>(new string[] { "0" });

            int maxCycles = 3500;

            //
            // Training SP to get stable. New-born stage.
            //

            for (int i = 0; i < maxCycles && isInStableState == false; i++)
            {
                matches = 0;

                cycle++;

                Debug.WriteLine($"-------------- Newborn Cycle {cycle} ---------------");

                foreach (var inputs in sequences)
                {
                    foreach (var input in inputs.Value)
                    {
                        Debug.WriteLine($" -- {inputs.Key} - {input} --");

                        var lyrOut = layer1.Compute(input, true);

                        if (isInStableState)
                        {
                            break;
                        }
                    }

                    if (isInStableState)
                    {
                        break;
                    }
                }
            }

            // Clear all learned patterns in the classifier.
            cls.ClearState();

            // We activate here the Temporal Memory algorithm.
            layer1.HtmModules.Add("tm", tm);

            //
            // Loop over all sequences.
            foreach (var sequenceKeyPair in sequences)
            {
                Debug.WriteLine($"-------------- Sequences {sequenceKeyPair.Key} ---------------");

                int maxPrevInputs = sequenceKeyPair.Value.Count - 1;

                List <string> previousInputs = new List <string>();

                previousInputs.Add("-1.0");

                //
                // Now training with SP+TM. SP is pretrained on the given input pattern set.
                for (int i = 0; i < maxCycles; i++)
                {
                    matches = 0;

                    cycle++;

                    Debug.WriteLine("");

                    Debug.WriteLine($"-------------- Cycle {cycle} ---------------");
                    Debug.WriteLine("");

                    foreach (var input in sequenceKeyPair.Value)
                    {
                        Debug.WriteLine($"-------------- {input} ---------------");

                        var lyrOut = layer1.Compute(input, true) as ComputeCycle;

                        var activeColumns = layer1.GetResult("sp") as int[];

                        previousInputs.Add(input.ToString());
                        if (previousInputs.Count > (maxPrevInputs + 1))
                        {
                            previousInputs.RemoveAt(0);
                        }

                        // In the pretrained SP with HPC, the TM will quickly learn cells for patterns
                        // In that case the starting sequence 4-5-6 might have the sam SDR as 1-2-3-4-5-6,
                        // Which will result in returning of 4-5-6 instead of 1-2-3-4-5-6.
                        // HtmClassifier allways return the first matching sequence. Because 4-5-6 will be as first
                        // memorized, it will match as the first one.
                        if (previousInputs.Count < maxPrevInputs)
                        {
                            continue;
                        }

                        string key = GetKey(previousInputs, input, sequenceKeyPair.Key);

                        List <Cell> actCells;

                        if (lyrOut.ActiveCells.Count == lyrOut.WinnerCells.Count)
                        {
                            actCells = lyrOut.ActiveCells;
                        }
                        else
                        {
                            actCells = lyrOut.WinnerCells;
                        }

                        cls.Learn(key, actCells.ToArray());

                        Debug.WriteLine($"Col  SDR: {Helpers.StringifyVector(lyrOut.ActivColumnIndicies)}");
                        Debug.WriteLine($"Cell SDR: {Helpers.StringifyVector(actCells.Select(c => c.Index).ToArray())}");

                        //
                        // If the list of predicted values from the previous step contains the currently presenting value,
                        // we have a match.
                        if (lastPredictedValues.Contains(key))
                        {
                            matches++;
                            Debug.WriteLine($"Match. Actual value: {key} - Predicted value: {lastPredictedValues.FirstOrDefault(key)}.");
                        }
                        else
                        {
                            Debug.WriteLine($"Missmatch! Actual value: {key} - Predicted values: {String.Join(',', lastPredictedValues)}");
                        }

                        if (lyrOut.PredictiveCells.Count > 0)
                        {
                            //var predictedInputValue = cls.GetPredictedInputValue(lyrOut.PredictiveCells.ToArray());
                            var predictedInputValues = cls.GetPredictedInputValues(lyrOut.PredictiveCells.ToArray(), 3);

                            foreach (var item in predictedInputValues)
                            {
                                Debug.WriteLine($"Current Input: {input} \t| Predicted Input: {item.PredictedInput} - {item.Similarity}");
                            }

                            lastPredictedValues = predictedInputValues.Select(v => v.PredictedInput).ToList();
                        }
                        else
                        {
                            Debug.WriteLine($"NO CELLS PREDICTED for next cycle.");
                            lastPredictedValues = new List <string> ();
                        }
                    }

                    // The first element (a single element) in the sequence cannot be predicted
                    double maxPossibleAccuraccy = (double)((double)sequenceKeyPair.Value.Count - 1) / (double)sequenceKeyPair.Value.Count * 100.0;

                    double accuracy = (double)matches / (double)sequenceKeyPair.Value.Count * 100.0;

                    Debug.WriteLine($"Cycle: {cycle}\tMatches={matches} of {sequenceKeyPair.Value.Count}\t {accuracy}%");

                    if (accuracy >= maxPossibleAccuraccy)
                    {
                        maxMatchCnt++;
                        Debug.WriteLine($"100% accuracy reched {maxMatchCnt} times.");

                        //
                        // Experiment is completed if we are 30 cycles long at the 100% accuracy.
                        if (maxMatchCnt >= 30)
                        {
                            sw.Stop();
                            Debug.WriteLine($"Sequence learned. The algorithm is in the stable state after 30 repeats with with accuracy {accuracy} of maximum possible {maxMatchCnt}. Elapsed sequence {sequenceKeyPair.Key} learning time: {sw.Elapsed}.");
                            break;
                        }
                    }
                    else if (maxMatchCnt > 0)
                    {
                        Debug.WriteLine($"At 100% accuracy after {maxMatchCnt} repeats we get a drop of accuracy with accuracy {accuracy}. This indicates instable state. Learning will be continued.");
                        maxMatchCnt = 0;
                    }

                    // This resets the learned state, so the first element starts allways from the beginning.
                    tm.Reset(mem);
                }
            }

            Debug.WriteLine("------------ END ------------");

            return(new HtmPredictionEngine {
                Layer = layer1, Classifier = cls, Connections = mem
            });
        }