/// <summary>
                /// Get the current error of training dataset with multitherading
                /// </summary>
                /// <returns>Errors of the set</returns>
                public static double[] GetCurrentErrorMultitherading(this AdamTrainer adam)
                {
                    double[] Errors = new double[adam.TrainingDataset.Size];
                    int      i      = 0;

                    foreach (IOMetaDataSetItem <double[]> item in adam.TrainingDataset)
                    {
                        adam.TargetNetwork.UpdatePositiveMultitherading(item.DataIn);

                        Errors[i] = 0.5 * adam.GetMeanSquareError(adam.TargetNetwork.OutputValues, item.DataOut).Select((double v) => (v * v)).Sum();
                        i++;
                    }

                    return(Errors);
                }
                /// <summary>
                /// Training the network with multitherading
                /// </summary>
                /// <param name="Iteration">Number of iteration</param>
                /// <param name="MinimunError">Minimun error of training</param>
                /// <param name="AutoExit">Auto exit when error less then minimun error</param>
                /// <returns>A enumerable for errors</returns>
                public static IEnumerable <double> TrainMultitherading(this AdamTrainer adam, int Iteration, double MinimunError, bool AutoExit)
                {
                    // A global gradient summary for adam-grad
                    // double[] -- synapse of one neuron
                    // List<double[]> -- neurons of one layer
                    // List<List<double[]>> -- layers of the network
                    List <List <double[]> > MSummary = new List <List <double[]> >();
                    List <List <double[]> > NSummary = new List <List <double[]> >();

                    adam.InitializeNetworkArray(ref MSummary);
                    adam.InitializeNetworkArray(ref NSummary);

                    // A global gradient summary for adam-grad
                    // double[] -- synapse of one layer of bias neuron
                    // List<double[]> -- layers of the network
                    List <double[]> biasMSummary = new List <double[]>();
                    List <double[]> biasNSummary = new List <double[]>();

                    adam.InitializeBiasArray(ref biasMSummary);
                    adam.InitializeBiasArray(ref biasNSummary);

                    adam.adamDecent.ClearCorrectAccumulate();

                    for (int iteration = 0; iteration < Iteration; iteration++)
                    {
                        foreach (IOMetaDataSetItem <double[]> item in adam.TrainingDataset)
                        {
                            // A new weight array
                            List <List <double[]> > newWeights = new List <List <double[]> >();
                            adam.InitializeNetworkArray(ref newWeights);

                            // A new bias array
                            List <double[]> newBias = new List <double[]>();
                            adam.InitializeBiasArray(ref newBias);

                            // Update the network with sample
                            adam.TargetNetwork.UpdatePositiveMultitherading(item.DataIn);

                            // Traversing all layers
                            double[] nexterrors = new double[0];

                            for (int layer = adam.TargetNetwork.Layers - 1; layer >= 0; layer--)
                            {
                                // Get number of synapse pre neurons in this layer
                                int SynapseCount = (layer == 0) ? (adam.TargetNetwork.Inputs) : (adam.TargetNetwork.Neurons[layer - 1].Length);

                                // Get the number of neurons in this layer
                                int NeuronsCount = adam.TargetNetwork.Neurons[layer].Length;

                                // Get the error from the back layer (backpropagation)
                                // When start (in the final layer), the error is network error
                                double[] errors = new double[NeuronsCount];

                                if (layer == adam.TargetNetwork.Layers - 1)
                                {
                                    errors = adam.GetMeanSquareError(adam.TargetNetwork.OutputValues, item.DataOut); // Start, final layer
                                }
                                else
                                {
                                    errors = nexterrors;
                                }

                                // Create a new error array for next layer
                                nexterrors = new double[SynapseCount];
                                Array.Fill(nexterrors, 0);
                                // Create a variable to storage bias errors
                                // Traversing all the neurons in this layer
                                Parallel.For(0, NeuronsCount, delegate(int neuron)
                                {
                                    Neuron n = adam.TargetNetwork.Neurons[layer][neuron];

                                    // Traversing all synapse in this neuron and calculate the gradients
                                    double[] weights   = n.Weights;
                                    double[] gradients = new double[SynapseCount];
                                    double[] msummary  = MSummary[layer][neuron];
                                    double[] nsummary  = NSummary[layer][neuron];

                                    for (int synapse = 0; synapse < SynapseCount; synapse++)
                                    {
                                        double prev = (layer == 0) ? (adam.TargetNetwork.InputNeurons[synapse].OutputValue) : (adam.TargetNetwork.Neurons[layer - 1][synapse].OutputValue);

                                        // Calculate gradient => g = - E * d(f(Is))/d(Is) * Os
                                        double gradient = -errors[neuron] * n.TransferFunction.Derivatives(n.MiddleValue) * prev;

                                        // Check the gradient to avoid NaN values
                                        if (double.IsNaN(gradient))
                                        {
                                            throw new Exception("Gradient is NaN");
                                        }

                                        // Save the gradient
                                        gradients[synapse] = gradient;

                                        // Statistic next error => next error = Σ(Ws * e)
                                        nexterrors[synapse] += adam.TargetNetwork.Neurons[layer][neuron].Weights[synapse] * errors[neuron];
                                    }

                                    // Gradient decent
                                    double[] biasunused  = new double[0];
                                    object biasparameter = new List <double[]>()
                                    {
                                        msummary, nsummary
                                    };
                                    adam.adamDecent.Update(ref weights, gradients, ref biasunused, null, ref biasparameter);

                                    // Update the summary
                                    MSummary[layer][neuron] = (biasparameter as List <double[]>)[0];
                                    NSummary[layer][neuron] = (biasparameter as List <double[]>)[1];

                                    // Save the new weights
                                    newWeights[layer][neuron] = weights;
                                });

                                // Now calculate the bias, but only apart from the last layer have bias
                                // The bias[a] is actually connect to layer[b]
                                // We had get the errors from neurons calculation
                                double[] bias          = adam.TargetNetwork.Neurons[layer].Select((Neuron p) => (p.Bias)).ToArray();
                                double[] biasGradients = new double[NeuronsCount];
                                double[] biasmSummary  = biasMSummary[layer];
                                double[] biasnSummary  = biasNSummary[layer];
                                for (int synapse = 0; synapse < NeuronsCount; synapse++)
                                {
                                    // Calculate gradient => g = - E * Os
                                    double gradient = -errors[synapse] * adam.TargetNetwork.BiasNeurons[layer].OutputValue;

                                    // Save the gradient
                                    biasGradients[synapse] = gradient;
                                }

                                // Gradient decent
                                double[] unused    = new double[0];
                                object   parameter = new List <double[]>()
                                {
                                    biasmSummary, biasnSummary
                                };
                                adam.adamDecent.Update(ref bias, biasGradients, ref unused, null, ref parameter);

                                // Update the summary
                                biasMSummary[layer] = (parameter as List <double[]>)[0];
                                biasNSummary[layer] = (parameter as List <double[]>)[1];

                                // Save the new bias
                                newBias[layer] = bias;
                            }

                            // Update the weights by new weights
                            for (int layer = 0; layer < adam.TargetNetwork.Layers; layer++)
                            {
                                // Get number of synapse pre neurons in this layer
                                int SynapseCount = (layer == 0) ? (adam.TargetNetwork.Inputs) : (adam.TargetNetwork.Neurons[layer - 1].Length);

                                // Get the number of neurons in this layer
                                int NeuronsCount = adam.TargetNetwork.Neurons[layer].Length;

                                // Traversing all the neurons in this layer
                                for (int neuron = 0; neuron < NeuronsCount; neuron++)
                                {
                                    adam.TargetNetwork.Neurons[layer][neuron].Weights = newWeights[layer][neuron];
                                    adam.TargetNetwork.Neurons[layer][neuron].Bias    = newBias[layer][neuron];
                                }
                            }

                            // Correct accumulate
                            adam.adamDecent.CorrectAccumulate();
                        }

                        // Return the deviation of the new iteration
                        double error = adam.GetCurrentErrorMultitherading().Average();
                        yield return(error);

                        if (error <= MinimunError)
                        {
                            if (AutoExit == true)
                            {
                                break;
                            }
                        }
                    }
                }