[DataRow(20, 30)] //Cells =20 , iter = 30
        public void CellPerColumn(int C, int loop)
        {
            string filename = "Cells=" + C + ".csv";

            using (StreamWriter writer = new StreamWriter(filename))
            {
                Debug.WriteLine($"Learning Cycles: {400}");
                Debug.WriteLine("Cycle;Similarity");

                // Parent Loop
                //This loop defines the number of times the experiment will run for the given data
                for (int j = 0; j < loop; j++)
                {
                    int        inputBits = 200;
                    bool       learn     = true;
                    Parameters p         = Parameters.getAllDefaultParameters();
                    p.Set(KEY.RANDOM, new ThreadSafeRandom(42));
                    p.Set(KEY.INPUT_DIMENSIONS, new int[] { inputBits });

                    p.Set(KEY.COLUMN_DIMENSIONS, new int[] { 500 });
                    p.Set(KEY.CELLS_PER_COLUMN, C);

                    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);

                    Dictionary <string, object> settings = new Dictionary <string, object>()
                    {
                        { "W", 15 },
                        { "N", inputBits },
                        { "Radius", -1.0 },
                        { "MinVal", 0.0 },
                        // { "MaxVal", 20.0 },
                        { "Periodic", false },
                        { "Name", "scalar" },
                        { "ClipInput", false },
                    };

                    double max = 10;

                    List <double> lst = new List <double>()
                    {
                        0, 1, 2, 3, 4, 5, 6, 7, 8, 9
                    };

                    // Later we run the whole experiment for all the below input sequences

                    // List<double> lst = new List<double>() { 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8 };
                    // List<double> lst = new List<double>() { 1, 2, 3, 1, 2, 4 };
                    // List<double> lst = new List<double>() { 1, 2, 3, 4, 1, 2, 3, 5 };
                    // List<double> lst = new List<double>() { 1, 2, 3, 4, 5, 1, 2, 3, 4, 5 };

                    settings["MaxVal"] = max;

                    EncoderBase encoder = new ScalarEncoder(settings);

                    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>();

                    double[] inputs = lst.ToArray();

                    //
                    // This trains SP.
                    foreach (var input in inputs)
                    {
                        Debug.WriteLine($" ** {input} **");
                        for (int i = 0; i < 3; i++)
                        {
                            var lyrOut = layer1.Compute((object)input, learn) as ComputeCycle;
                        }
                    }

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

                    int cycle   = 0;
                    int matches = 0;

                    double lastPredictedValue = 0;
                    //
                    // Now, training with SP+TM. SP is pretrained on pattern.
                    //Child loop / Inner loop

                    for (int i = 0; i < 400; i++)
                    {
                        matches = 0;
                        cycle++;
                        foreach (var input in inputs)
                        {
                            var lyrOut = layer1.Compute(input, learn) as ComputeCycle;

                            cls.Learn(input, lyrOut.ActiveCells.ToArray(), lyrOut.PredictiveCells.ToArray());

                            Debug.WriteLine($"-------------- {input} ---------------");

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

                            Debug.WriteLine($"W: {Helpers.StringifyVector(lyrOut.WinnerCells.Select(c => c.Index).ToArray())}");
                            Debug.WriteLine($"P: {Helpers.StringifyVector(lyrOut.PredictiveCells.Select(c => c.Index).ToArray())}");

                            var predictedValue = cls.GetPredictedInputValue(lyrOut.PredictiveCells.ToArray());

                            Debug.WriteLine($"Current Input: {input} \t| - Predicted value in previous cycle: {lastPredictedValue} \t| Predicted Input for the next cycle: {predictedValue}");

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

                            lastPredictedValue = predictedValue;
                        }

                        if (i == 500)
                        {
                            Debug.WriteLine("Stop Learning From Here. Entering inference mode.");
                            learn = false;
                        }

                        //tm1.reset(mem);

                        Debug.WriteLine($"Cycle: {cycle}\tMatches={matches} of {inputs.Length}\t {(double)matches / (double)inputs.Length * 100.0}%");
                        if ((double)matches / (double)inputs.Length == 1)
                        {
                            writer.WriteLine($"{cycle}");
                            break;
                        }
                    }
                }
                Debug.WriteLine("New Iteration");
            }
            //cls.TraceState();
            Debug.WriteLine("------------------------------------------------------------------------\n----------------------------------------------------------------------------");
        }
Exemplo n.º 2
0
        public void TestCortexLayer()
        {
            int inputBits = 100;

            double max        = 20;
            int    numColumns = 2048;

            List <double> inputValues = new List <double>(new double[] { 0.0, 1.0, 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, 12.0, 13.0, 14.0, 11.0, 12.0, 14.0, 5.0, 7.0, 6.0, 9.0, 3.0, 4.0, 3.0, 4.0, 3.0, 4.0 });
            int           numInputs   = inputValues.Distinct().ToList().Count;

            var inputs = inputValues.ToArray();

            Dictionary <string, object> settings = new Dictionary <string, object>()
            {
                { "W", 15 },
                { "N", inputBits },
                { "Radius", -1.0 },
                { "MinVal", 0.0 },
                { "Periodic", false },
                { "Name", "scalar" },
                { "ClipInput", false },
                { "MaxVal", max }
            };

            EncoderBase encoder = new ScalarEncoder(settings);

            HtmConfig htmConfig = new HtmConfig(new int[] { inputBits }, new int[] { numColumns })
            {
                Random                     = new ThreadSafeRandom(42),
                CellsPerColumn             = 25,
                GlobalInhibition           = true,
                LocalAreaDensity           = -1,
                NumActiveColumnsPerInhArea = 0.02 * numColumns,
                PotentialRadius            = 50,
                InhibitionRadius           = 15,
                MaxBoost                   = 10.0,
                DutyCyclePeriod            = 25,
                MinPctOverlapDutyCycles    = 0.75,
                MaxNewSynapseCount         = (int)(0.02 * numColumns),
                ActivationThreshold        = 15,
                ConnectedPermanence        = 0.5,
                PermanenceDecrement        = 0.25,
                PermanenceIncrement        = 0.15,
                PredictedSegmentDecrement  = 0.1
            };

            Connections memory = new Connections(htmConfig);

            HomeostaticPlasticityController hpa = new HomeostaticPlasticityController(memory, 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}");
                }
            }, numOfCyclesToWaitOnChange: 25);

            SpatialPoolerMT spatialPooler = new SpatialPoolerMT(hpa);

            spatialPooler.Init(memory, UnitTestHelpers.GetMemory());

            TemporalMemory temporalMemory = new TemporalMemory();

            temporalMemory.Init(memory);

            List <CortexRegion> regions = new List <CortexRegion>();
            CortexRegion        region0 = new CortexRegion("1st Region");

            regions.Add(region0);

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

            region0.AddLayer(layer1);
            layer1.HtmModules.Add("encoder", encoder);
            layer1.HtmModules.Add("sp", spatialPooler);
            layer1.HtmModules.Add("tm", temporalMemory);

            bool learn = true;

            int maxCycles = 3500;

            for (int i = 0; i < maxCycles; i++)
            {
                foreach (var input in inputs)
                {
                    var lyrOut = layer1.Compute(input, learn) as ComputeCycle;
                }
            }
        }
        private void RunSpStabilityExperiment(double maxBoost, double minOverlapCycles, int inputBits, Parameters p, EncoderBase encoder, List <double> inputValues)
        {
            string path = nameof(RunSpStabilityExperiment);

            if (Directory.Exists(path))
            {
                Directory.Delete(path, true);
            }

            Directory.CreateDirectory(path);

            Stopwatch sw = new Stopwatch();

            sw.Start();

            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();

            bool isInStableState = false;

            HomeostaticPlasticityController hpa = new HomeostaticPlasticityController(mem, inputValues.Count * 30, (isStable, numPatterns, actColAvg, seenInputs) =>
            {
                Assert.IsTrue(numPatterns == inputValues.Count);

                // Event should only be fired when entering the stable state.
                // Ideal SP should never enter unstable state after stable state.
                if (isStable == false)
                {
                    isInStableState = false;
                    Debug.WriteLine($"INSTABLE!: Patterns: {numPatterns}, Inputs: {seenInputs}, iteration: {seenInputs / numPatterns}");
                }
                else
                {
                    //Assert.IsTrue(isStable);

                    isInStableState = true;
                    Debug.WriteLine($"STABLE: Patterns: {numPatterns}, Inputs: {seenInputs}, iteration: {seenInputs / numPatterns}");
                }
            }, numOfCyclesToWaitOnChange: 50, requiredSimilarityThreshold: 0.975);

            SpatialPooler sp = new SpatialPooler(hpa);

            p.apply(mem);
            sp.Init(mem, UnitTestHelpers.GetMemory());

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

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

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

            double[] inputs = inputValues.ToArray();
            Dictionary <double, List <string[]> > boostedOverlaps = new Dictionary <double, List <string[]> >();
            Dictionary <double, int[]>            prevActiveCols  = new Dictionary <double, int[]>();
            Dictionary <double, double>           prevSimilarity  = new Dictionary <double, double>();

            foreach (var input in inputs)
            {
                prevSimilarity.Add(input, 0.0);
                prevActiveCols.Add(input, new int[0]);
            }

            int maxSPLearningCycles = 2000;

            List <(double Element, (int Cycle, double Similarity)[] Oscilations)> oscilationResult = new List <(double Element, (int Cycle, double Similarity)[] Oscilations)>();
Exemplo n.º 4
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 ------------");
        }
Exemplo n.º 5
0
        public void RunPowerPredictionExperiment()
        {
            const int inputBits = 300; /* without datetime component */ // 13420; /* with 4096 scalar bits */ // 10404 /* with 1024 bits */;

            Parameters p = Parameters.getAllDefaultParameters();

            p.Set(KEY.RANDOM, new ThreadSafeRandom(42));
            p.Set(KEY.COLUMN_DIMENSIONS, new int[] { 2048 });
            p.Set(KEY.INPUT_DIMENSIONS, new int[] { inputBits });
            p.Set(KEY.CELLS_PER_COLUMN, 10 /* 50 */);
            p.Set(KEY.GLOBAL_INHIBITION, true);
            p.Set(KEY.CONNECTED_PERMANENCE, 0.1);
            // N of 40 (40= 0.02*2048 columns) active cells required to activate the segment.
            p.setNumActiveColumnsPerInhArea(0.02 * 2048);
            // Activation threshold is 10 active cells of 40 cells in inhibition area.
            p.setActivationThreshold(10 /*15*/);
            p.setInhibitionRadius(15);
            p.Set(KEY.MAX_BOOST, 0.0);
            p.Set(KEY.DUTY_CYCLE_PERIOD, 100000);
            p.setActivationThreshold(10);
            p.setMaxNewSynapsesPerSegmentCount((int)(0.02 * 2048));
            p.setPermanenceIncrement(0.17);

            //p.Set(KEY.MAX_SYNAPSES_PER_SEGMENT, 32);
            //p.Set(KEY.MAX_SEGMENTS_PER_CELL, 128);
            //p.Set(KEY.MAX_NEW_SYNAPSE_COUNT, 200);

            //p.Set(KEY.POTENTIAL_RADIUS, 700);
            //p.Set(KEY.POTENTIAL_PCT, 0.5);
            //p.Set(KEY.NUM_ACTIVE_COLUMNS_PER_INH_AREA, 42);

            //p.Set(KEY.LOCAL_AREA_DENSITY, -1);

            CortexRegion region0 = new CortexRegion("1st Region");

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

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

            Dictionary <string, object> settings = new Dictionary <string, object>();

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

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

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

            Stopwatch sw = new Stopwatch();

            sw.Start();
            Train(inputBits, layer1, cls, true); // New born mode.
            sw.Stop();

            Debug.WriteLine($"NewBorn stage duration: {sw.ElapsedMilliseconds / 1000} s");

            layer1.AddModule("tm", tm1);

            sw.Start();

            int hunderdAccCnt = 0;

            for (int i = 0; i < 1000; i++)
            {
                float acc = Train(inputBits, layer1, cls, false);

                Debug.WriteLine($"Accuracy = {acc}, Cycle = {i}");

                if (acc == 100.0)
                {
                    hunderdAccCnt++;
                }

                if (hunderdAccCnt >= 10)
                {
                    break;
                }
                //tm1.reset(mem);
            }

            if (hunderdAccCnt >= 10)
            {
                Debug.WriteLine($"EXPERIMENT SUCCESS. Accurracy 100% reached.");
            }
            else
            {
                Debug.WriteLine($"Experiment FAILED!. Accurracy 100% was not reached.");
            }

            cls.TraceState();

            sw.Stop();

            Debug.WriteLine($"Training duration: {sw.ElapsedMilliseconds / 1000} s");
        }