public void train(List <float> input_list, List <float> target_list)
        {
            NNMatrix inputs = NNMatrix.fromList(input_list);

            NNMatrix hidden = NNMatrix.multiply(this.weights_ih, inputs);

            hidden.add(this.bias_h);
            // activate function
            hidden.map(activate);

            NNMatrix outputs = NNMatrix.multiply(this.weights_ho, hidden);

            outputs.add(this.bias_o);
            outputs.map(activate);

            NNMatrix targets = NNMatrix.fromList(target_list);

            // calculate the error
            // ERROR = TARGETS(answer) - OUTPUTS(actual outputs)
            NNMatrix output_errors = NNMatrix.subtract(targets, outputs);


            //var gradients = outputs * (1-outputs);
            // calculate gradient
            NNMatrix gradients = NNMatrix.map(outputs, dactivate);

            gradients.multiply(output_errors);
            gradients.multiply(this.learning_rate);

            // calculate deltas
            NNMatrix hidden_T          = NNMatrix.transpose(hidden);
            NNMatrix weights_ho_deltas = NNMatrix.multiply(gradients, hidden_T);

            // Adjust the weights by deltas
            this.weights_ho.add(weights_ho_deltas);

            // Adjust bias by its delta (which is just the gradients)
            this.bias_o.add(gradients);

            // calculate the hidden layer errors
            NNMatrix who_t         = NNMatrix.transpose(this.weights_ho);
            NNMatrix hidden_errors = NNMatrix.multiply(who_t, output_errors);

            // calculate hidden gradients
            NNMatrix hidden_gradient = NNMatrix.map(hidden, dactivate);

            hidden_gradient.multiply(hidden_errors);
            hidden_gradient.multiply(this.learning_rate);

            // calculate input->hidden deltas
            NNMatrix input_T          = NNMatrix.transpose(inputs);
            NNMatrix weight_ih_deltas = NNMatrix.multiply(hidden_gradient, input_T);

            this.weights_ih.add(weight_ih_deltas);

            // Adjust bias by its delta (which is just the hiddent gradients)
            this.bias_h.add(hidden_gradient);
        }
        // feedforward
        public List <float> predict(List <float> input_list)
        {
            NNMatrix inputs = NNMatrix.fromList(input_list);

            NNMatrix hidden = NNMatrix.multiply(this.weights_ih, inputs);

            hidden.add(this.bias_h);

            // activate function
            hidden.map(activate);

            NNMatrix output = NNMatrix.multiply(this.weights_ho, hidden);

            output.add(this.bias_o);
            output.map(activate);

            return(output.toList());
        }
 public void mutate()
 {
     this.weights_ih = NNMatrix.map(this.weights_ih, NNUtils.mutate);
     this.weights_ho = NNMatrix.map(this.weights_ho, NNUtils.mutate);
 }