public void CheckBpWithSampleNetwork()
        {
            float GetWeightDifference(NetworkConnection[][] first, NetworkConnection[][] second)
            {
                var difference = 0f;

                for (var i = 0; i < first.Length; i++)
                {
                    for (var j = 0; j < first[i].Length; j++)
                    {
                        difference += Math.Abs(first[i][j].Weight - second[i][j].Weight);
                    }
                }

                return(difference);
            }

            float[][] GenerateSampleDataset(RecurrentNetwork sample, int size)
            {
                var result = new float[size][];

                for (var i = 0; i < size; i++)
                {
                    result[i] = new float[3];
                    var input1 = Neat.Utils.RandomSource.Range(-1f, 1f);
                    var input2 = Neat.Utils.RandomSource.Range(-1f, 1f);
                    sample.Sensors[0] = input1;
                    sample.Sensors[1] = input2;
                    sample.Activate();
                    result[i][0] = input1;
                    result[i][1] = input2;
                    result[i][2] = sample.Effectors[0];
                }

                return(result);
            }

            // Arrange
            var sampleConnections =
                new NetworkConnection[BiasCount + 4][]; // 0 : bias, 1, 2: inputs, 3: output, 4: hidden

            sampleConnections[0]    = new NetworkConnection[1];
            sampleConnections[0][0] = new NetworkConnection(3, 1f);    // bias to output
            sampleConnections[1]    = new NetworkConnection[1];
            sampleConnections[1][0] = new NetworkConnection(4, 0.5f);  // input to hidden
            sampleConnections[2]    = new NetworkConnection[1];
            sampleConnections[2][0] = new NetworkConnection(4, -0.5f); // input to hidden
            sampleConnections[3]    = new NetworkConnection[1];
            sampleConnections[3][0] = new NetworkConnection(4, 0.2f);  // output to hidden
            sampleConnections[4]    = new NetworkConnection[1];
            sampleConnections[4][0] = new NetworkConnection(3, -1f);   // hidden to output
            var sampleNetwork     = new RecurrentNetwork(sampleConnections, 3, 1, new DummyNeatChromosomeEncoder());
            var targetConnections =
                new NetworkConnection[BiasCount + 4][]; // 0 : bias, 1, 2: inputs, 3: output, 4: hidden

            targetConnections[0]    = new NetworkConnection[1];
            targetConnections[0][0] = new NetworkConnection(3, 0.8f);  // bias to output
            targetConnections[1]    = new NetworkConnection[1];
            targetConnections[1][0] = new NetworkConnection(4, 0.7f);  // input to hidden
            targetConnections[2]    = new NetworkConnection[1];
            targetConnections[2][0] = new NetworkConnection(4, -0.3f); // input to hidden
            targetConnections[3]    = new NetworkConnection[1];
            targetConnections[3][0] = new NetworkConnection(4, 0.1f);  // output to hidden
            targetConnections[4]    = new NetworkConnection[1];
            targetConnections[4][0] = new NetworkConnection(3, -0.8f); // hidden to output
            var targetNetwork = new RecurrentNetwork(targetConnections, 3, 1, new DummyNeatChromosomeEncoder());

            // Act
            var beforeTrainDifference = GetWeightDifference(sampleConnections, targetConnections);

            for (var i = 0; i < 5000; i++)
            {
                targetNetwork.Train(GenerateSampleDataset(sampleNetwork, 5), 0.5f);
            }

            var afterTrainDifference = GetWeightDifference(sampleConnections, targetConnections);

            // Assert
            Assert.True(afterTrainDifference < beforeTrainDifference, "Training failed!");
        }
        public void CheckRecurrentBp()
        {
            float GetSampleSetError(float[][] floats, RecurrentNetwork network1)
            {
                var error = 0f;

                foreach (var sample in floats)
                {
                    network1.Sensors[0] = sample[0];
                    network1.Activate();
                    error += Math.Abs(sample[1] - network1.Effectors[0]);
                }

                return(error);
            }

            void FlushNetworkState(RecurrentNetwork recurrentNetwork)
            {
                for (var i = 0; i < 3; i++)
                {
                    recurrentNetwork.Sensors[0] = 0f;
                    recurrentNetwork.Activate();
                }
            }

            float[][] GenerateSampleDataset(int size)
            {
                var result   = new float[size][];
                var previous = 0f;
                var current  = 0f;

                for (var i = 0; i < size; i++)
                {
                    result[i]    = new float[2];
                    result[i][1] = current + previous; // previous inputs sum for output
                    previous     = current;
                    current      = Neat.Utils.RandomSource.Range(-0.5f, 0.5f);
                    result[i][0] = current;
                }

                return(result);
            }

            // Arrange
            var connections = new NetworkConnection[BiasCount + 3][]; // 0 : bias, 1: input, 2: output, 3: hidden

            connections[0]    = new NetworkConnection[1];
            connections[0][0] = new NetworkConnection(2, 1f);  // bias to output
            connections[1]    = new NetworkConnection[2];
            connections[1][0] = new NetworkConnection(2, 2f);  // input to output
            connections[1][1] = new NetworkConnection(3, -1f); // input to hidden
            connections[2]    = new NetworkConnection[0];      // no output self-links
            connections[3]    = new NetworkConnection[1];
            connections[3][0] = new NetworkConnection(2, 0f);  // hidden to output
            var network = new RecurrentNetwork(connections, 2, 1, new DummyNeatChromosomeEncoder());

            // Act
            FlushNetworkState(network);
            var sampleDataSet    = GenerateSampleDataset(10);
            var beforeTrainError = GetSampleSetError(sampleDataSet, network);

            for (var i = 0; i < 20; i++)
            {
                // flush network state
                FlushNetworkState(network);

                network.Train(sampleDataSet, 0.1f);
            }

            FlushNetworkState(network);
            var afterTrainError = GetSampleSetError(sampleDataSet, network);

            for (var i = 0; i < 20; i++)
            {
                // flush network state
                FlushNetworkState(network);

                network.Train(sampleDataSet, 0.1f);
            }

            FlushNetworkState(network);
            var afterSecondTrainError = GetSampleSetError(sampleDataSet, network);

            // Assert
            Assert.True(afterTrainError < beforeTrainError, "Training failed!");
            Assert.True(afterSecondTrainError < afterTrainError, "Second training failed!");
        }