/// <summary>
        /// Mencari solusi model neural network
        /// </summary>
        private void searchSolution()
        {
            // Normalize Data
            switch (this.selectedActivationFunction)
            {
                case ActivationFunctionEnumeration.SemiLinearFunction:
                    this.activationFunction = new SemiLinearFunction();
                    this.normalizeData(0.1, 0.9);
                    break;
                case ActivationFunctionEnumeration.SigmoidFunction:
                    this.activationFunction = new SigmoidFunction();
                    this.normalizeData(0.1, 0.9);
                    break;
                case ActivationFunctionEnumeration.BipolarSigmoidFunction:
                    this.activationFunction = new BipolarSigmoidFunction();
                    this.normalizeData(-0.9, 0.9);
                    break;
                case ActivationFunctionEnumeration.HyperbolicTangentFunction:
                    this.activationFunction = new HyperbolicTangentFunction();
                    this.normalizeData(-0.9, 0.9);
                    break;
                default:
                    this.activationFunction = new BipolarSigmoidFunction();
                    this.normalizeData(-0.9, 0.9);
                    break;
            }

            //create network
            this.network = new BasicNetwork();
            this.network.AddLayer(new FeedforwardLayer(this.activationFunction, this.inputLayerNeurons));
            this.network.AddLayer(new FeedforwardLayer(this.activationFunction, this.hiddenLayerNeurons));
            this.network.AddLayer(new FeedforwardLayer(this.activationFunction, this.outputLayerNeurons));
            this.network.Reset();

            //variable for looping
            //needToStop = false;
            double mse = 0.0, error = 0.0, mae=0.0;
            int iteration = 1;

            // parameters
            double msle = 0.0, mspe = 0.0, generalizationLoss = 0.0, pq = 0.0;
            double[] trainingErrors = new double[this.strip];
            for (int i = 0; i < this.strip; i++) trainingErrors[i] = double.MaxValue / strip;

            double lastMSE = double.MaxValue;

            // advanced early stopping
            int n = this.data.Length - this.network.InputLayer.NeuronCount;
            int validationSet = (int)Math.Round(this.validationSetRatio * n);
            int trainingSet = n - validationSet;
            double[][] networkTrainingInput = new double[trainingSet][];
            double[][] networkTrainingOutput = new double[trainingSet][];
            for (int i = 0; i < trainingSet; i++)
            {
                networkTrainingInput[i] = new double[this.network.InputLayer.NeuronCount];
                networkTrainingOutput[i] = new double[1];
            }
            for (int i = 0; i < trainingSet; i++)
            {
                for (int j = 0; j < this.network.InputLayer.NeuronCount; j++)
                {
                    networkTrainingInput[i][j] = this.networkInput[i][j];
                }
                networkTrainingOutput[i][0] = this.networkOutput[i][0];
            }

            // validation set
            double[] solutionValidation = new double[validationSet];
            double[] inputForValidation = new double[this.network.InputLayer.NeuronCount];
            double[] inputForValidationNetwork = new double[this.network.InputLayer.NeuronCount];

            // array for saving neural weights and parameters
            this.bestValidationError = double.MaxValue;
            this.bestWeightMatrix = new double[this.network.Layers.Count -1][,];
            this.bestSolution = new double[n];

            for (int i = 0; i < this.network.Layers.Count - 1; i++)
            {
                this.bestWeightMatrix[i] = new double[this.network.Layers[i].WeightMatrix.Rows, this.network.Layers[i].WeightMatrix.Cols];
            }

            //best network criterion
            double bestNetworkError = double.MaxValue, bestNetworkMSE = double.MaxValue, bestNetworkMAE = double.MaxValue;

            // build array for graph
            this.solutionData = new double[n];
            this.predictedPoint = new cPoint[n];
            this.validationPoint = new cPoint[validationSet];

            //initialize point for graph
            predictedDS.Samples = predictedPoint;
            validationDS.Samples = validationPoint;
            this.predictedDS.Active = true;

            // prepare training data
            INeuralDataSet dataset;
            if (this.useAdvanceEarlyStopping)
                dataset = new BasicNeuralDataSet(networkTrainingInput, networkTrainingOutput);
            else
                dataset = new BasicNeuralDataSet(this.networkInput, this.networkOutput);

            // initialize trainer
            this.learning = new Backpropagation(this.network, dataset, this.learningRate, this.momentum);

            //training
            while (!needToStop)
            {
                double sse = 0.0;
                double sae = 0.0;
                double ssle = 0.0;
                double sspe = 0.0;

                this.learning.Iteration();
                error = learning.Error;

                if (this.useAdvanceEarlyStopping)
                {
                    this.validationDS.Active = true;
                }
                else
                {
                    this.validationDS.Active = false;
                }

                for (int i = 0; i < n; i++)
                {
                    INeuralData neuraldata = new BasicNeuralData(this.networkInput[i]);

                    this.solutionData[i] = (this.network.Compute(neuraldata)[0]
                        - this.minNormalizedData) / this.factor + this.minData;

                    this.predictedPoint[i].x = i + this.network.InputLayer.NeuronCount;
                    this.predictedPoint[i].y = (float)this.solutionData[i];

                    sse += Math.Pow(this.solutionData[i] - this.data[i + this.network.InputLayer.NeuronCount], 2);
                    sae += Math.Abs(this.solutionData[i] - this.data[i + this.network.InputLayer.NeuronCount]);

                    //calculate advance early stopping
                    if (this.useAdvanceEarlyStopping)
                    {
                        if (i < n - validationSet)
                        {
                            ssle += Math.Pow(this.solutionData[i] - this.data[i + this.network.InputLayer.NeuronCount], 2);
                        }
                        else
                        {

                            // initialize the first validation set input
                            if (i == n - validationSet)
                            {
                                for (int j = 0; j < this.network.InputLayer.NeuronCount; j++)
                                {
                                    inputForValidation[this.network.InputLayer.NeuronCount - 1 - j] = this.data[this.data.Length - (n - i) - 1 - j];
                                }
                            }

                            for (int j = 0; j < this.network.InputLayer.NeuronCount; j++)
                            {
                                inputForValidationNetwork[j] = (inputForValidation[j] - this.minData) * this.factor + this.minNormalizedData;
                            }

                            INeuralData neuraldataval = new BasicNeuralData(inputForValidationNetwork);
                            solutionValidation[i - n + validationSet] = (this.network.Compute(neuraldataval)[0] - this.minNormalizedData) / this.factor + this.minData;

                            this.validationPoint[i - n + validationSet].x = i + this.network.InputLayer.NeuronCount;
                            this.validationPoint[i - n + validationSet].y = (float)solutionValidation[i - n + validationSet];

                            sspe += Math.Pow(this.data[i + this.network.InputLayer.NeuronCount] - solutionValidation[i - n + validationSet], 2);

                            // initialize the next validation set input from the current validation set input
                            for (int j = 0; j < this.network.InputLayer.NeuronCount - 1; j++)
                            {
                                inputForValidation[j] = inputForValidation[j + 1];
                            }

                            inputForValidation[this.network.InputLayer.NeuronCount - 1] = solutionValidation[i - n + validationSet];

                        }
                    }

                }

                mse = sse / this.solutionData.Length;
                mae = sae / this.solutionData.Length;

                //Console.WriteLine(error.ToString());

                //Display it
                this.iterationBox.Text = iteration.ToString();
                this.maeBox.Text = mae.ToString("F5");
                this.mseBox.Text = mse.ToString("F5");
                this.errorBox.Text = error.ToString("F5");

                seriesGraph.Refresh();

                if (this.useAdvanceEarlyStopping)
                {
                    //calculate advance early stopping 2
                    mspe = sspe / validationSet;
                    msle = ssle / (this.solutionData.Length - validationSet);

                    //save best weight
                    if (this.bestValidationError > mspe)
                    {
                        this.bestValidationError = mspe;
                        this.bestSolution = this.solutionData;

                        // weight matrix
                        for (int i = 0; i < this.network.Layers.Count - 1; i++)
                            for (int j = 0; j < this.network.Layers[i].WeightMatrix.Rows; j++)
                                for (int k = 0; k < this.network.Layers[i].WeightMatrix.Cols; k++)
                                    this.bestWeightMatrix[i][j,k] = this.network.Layers[i].WeightMatrix[j, k];

                        bestNetworkError = error;
                        bestNetworkMAE = mae;
                        bestNetworkMSE = mse;

                    }
                    //calculate generalization loss &pq
                    generalizationLoss = 100 * (mspe / this.bestValidationError - 1);

                    trainingErrors[(iteration - 1) % this.strip] = msle;
                    double minStripTrainingError = double.MaxValue, sumStripTrainingError = 0.0;
                    for (int i = 0; i < this.strip; i++)
                    {
                        sumStripTrainingError += trainingErrors[i];
                        if (trainingErrors[i] < minStripTrainingError) minStripTrainingError = trainingErrors[i];
                    }
                    double trainingProgress = 1000 * ((sumStripTrainingError / (this.strip * minStripTrainingError)) - 1);
                    pq = generalizationLoss / trainingProgress;

                    //display advance early stopping
                    this.learningErrorBox.Text = msle.ToString("F5");
                    this.validationErrorBox.Text = mspe.ToString("F5");
                    this.generalizationLossBox.Text = generalizationLoss.ToString("F5");
                    this.pqBox.Text = pq.ToString("F5");
                    this.seriesGraph.Refresh();

                    //stopping
                    switch (this.advanceStoppingMethod)
                    {
                        case AdvanceStoppingMethodEnumeration.GeneralizationLoss:
                            if (generalizationLoss > this.generalizationLossTreshold) needToStop = true;
                            break;
                        case AdvanceStoppingMethodEnumeration.ProgressQuotient:
                            if (pq > this.pqTreshold) needToStop = true;
                            break;
                    }

                }

                if (this.withCheckingCycle && iteration % this.checkingCycle == 0)
                {
                    switch (this.checkingMethod)
                    {
                        case CheckingMethodEnumeration.byMSEValue:
                            if (mse <= this.byMSEValueStopping) needToStop = true;
                            break;
                        case CheckingMethodEnumeration.byMSEChange:
                            if (lastMSE - mse <= this.byMSEChangeStopping) needToStop = true;
                            break;
                    }
                    lastMSE = mse;
                }
                if (iteration >= this.maxIteration)
                {
                    needToStop = true;
                }

                iteration++;
            }

            //restore weight
            if (this.useAdvanceEarlyStopping)
            {
                this.solutionData = this.bestSolution;

                // weight matrix

                for (int i = 0; i < this.network.Layers.Count - 1; i++)
                    for (int j = 0; j < this.network.Layers[i].WeightMatrix.Rows; j++)
                        for (int k = 0; k < this.network.Layers[i].WeightMatrix.Cols; k++)
                            this.network.Layers[i].WeightMatrix[j, k] = this.bestWeightMatrix[i][j, k];

                //best network criterion
                this.error = bestNetworkError;
                this.mse = bestNetworkMSE;
                this.mae = bestNetworkMAE;
            }
            else
            {
                this.error = error;
                this.mse = mse;
                this.mae = mae;
            }

            this.enableControls(true);
        }
        /// <summary>
        /// Mencari solusi model neural network
        /// </summary>
        private void searchSolution()
        {
            // Normalize Data
            switch (this.selectedActivationFunction)
            {
            case ActivationFunctionEnumeration.SemiLinearFunction:
                this.activationFunction = new SemiLinearFunction();
                this.normalizeData(0.1, 0.9);
                break;

            case ActivationFunctionEnumeration.SigmoidFunction:
                this.activationFunction = new SigmoidFunction();
                this.normalizeData(0.1, 0.9);
                break;

            case ActivationFunctionEnumeration.BipolarSigmoidFunction:
                this.activationFunction = new BipolarSigmoidFunction();
                this.normalizeData(-0.9, 0.9);
                break;

            case ActivationFunctionEnumeration.HyperbolicTangentFunction:
                this.activationFunction = new HyperbolicTangentFunction();
                this.normalizeData(-0.9, 0.9);
                break;

            default:
                this.activationFunction = new BipolarSigmoidFunction();
                this.normalizeData(-0.9, 0.9);
                break;
            }

            //create network
            this.network = new BasicNetwork();
            this.network.AddLayer(new FeedforwardLayer(this.activationFunction, this.inputLayerNeurons));
            this.network.AddLayer(new FeedforwardLayer(this.activationFunction, this.hiddenLayerNeurons));
            this.network.AddLayer(new FeedforwardLayer(this.activationFunction, this.outputLayerNeurons));
            this.network.Reset();

            //variable for looping
            //needToStop = false;
            double mse = 0.0, error = 0.0, mae = 0.0;
            int    iteration = 1;

            // parameters
            double msle = 0.0, mspe = 0.0, generalizationLoss = 0.0, pq = 0.0;

            double[] trainingErrors = new double[this.strip];
            for (int i = 0; i < this.strip; i++)
            {
                trainingErrors[i] = double.MaxValue / strip;
            }

            double lastMSE = double.MaxValue;

            // advanced early stopping
            int n             = this.data.Length - this.network.InputLayer.NeuronCount;
            int validationSet = (int)Math.Round(this.validationSetRatio * n);
            int trainingSet   = n - validationSet;

            double[][] networkTrainingInput  = new double[trainingSet][];
            double[][] networkTrainingOutput = new double[trainingSet][];
            for (int i = 0; i < trainingSet; i++)
            {
                networkTrainingInput[i]  = new double[this.network.InputLayer.NeuronCount];
                networkTrainingOutput[i] = new double[1];
            }
            for (int i = 0; i < trainingSet; i++)
            {
                for (int j = 0; j < this.network.InputLayer.NeuronCount; j++)
                {
                    networkTrainingInput[i][j] = this.networkInput[i][j];
                }
                networkTrainingOutput[i][0] = this.networkOutput[i][0];
            }

            // validation set
            double[] solutionValidation        = new double[validationSet];
            double[] inputForValidation        = new double[this.network.InputLayer.NeuronCount];
            double[] inputForValidationNetwork = new double[this.network.InputLayer.NeuronCount];

            // array for saving neural weights and parameters
            this.bestValidationError = double.MaxValue;
            this.bestWeightMatrix    = new double[this.network.Layers.Count - 1][, ];
            this.bestSolution        = new double[n];

            for (int i = 0; i < this.network.Layers.Count - 1; i++)
            {
                this.bestWeightMatrix[i] = new double[this.network.Layers[i].WeightMatrix.Rows, this.network.Layers[i].WeightMatrix.Cols];
            }

            //best network criterion
            double bestNetworkError = double.MaxValue, bestNetworkMSE = double.MaxValue, bestNetworkMAE = double.MaxValue;

            // build array for graph
            this.solutionData    = new double[n];
            this.predictedPoint  = new cPoint[n];
            this.validationPoint = new cPoint[validationSet];

            //initialize point for graph
            predictedDS.Samples     = predictedPoint;
            validationDS.Samples    = validationPoint;
            this.predictedDS.Active = true;


            // prepare training data
            INeuralDataSet dataset;

            if (this.useAdvanceEarlyStopping)
            {
                dataset = new BasicNeuralDataSet(networkTrainingInput, networkTrainingOutput);
            }
            else
            {
                dataset = new BasicNeuralDataSet(this.networkInput, this.networkOutput);
            }


            // initialize trainer
            this.learning = new Backpropagation(this.network, dataset, this.learningRate, this.momentum);


            //training
            while (!needToStop)
            {
                double sse  = 0.0;
                double sae  = 0.0;
                double ssle = 0.0;
                double sspe = 0.0;

                this.learning.Iteration();
                error = learning.Error;


                if (this.useAdvanceEarlyStopping)
                {
                    this.validationDS.Active = true;
                }
                else
                {
                    this.validationDS.Active = false;
                }

                for (int i = 0; i < n; i++)
                {
                    INeuralData neuraldata = new BasicNeuralData(this.networkInput[i]);

                    this.solutionData[i] = (this.network.Compute(neuraldata)[0]
                                            - this.minNormalizedData) / this.factor + this.minData;

                    this.predictedPoint[i].x = i + this.network.InputLayer.NeuronCount;
                    this.predictedPoint[i].y = (float)this.solutionData[i];

                    sse += Math.Pow(this.solutionData[i] - this.data[i + this.network.InputLayer.NeuronCount], 2);
                    sae += Math.Abs(this.solutionData[i] - this.data[i + this.network.InputLayer.NeuronCount]);

                    //calculate advance early stopping
                    if (this.useAdvanceEarlyStopping)
                    {
                        if (i < n - validationSet)
                        {
                            ssle += Math.Pow(this.solutionData[i] - this.data[i + this.network.InputLayer.NeuronCount], 2);
                        }
                        else
                        {
                            // initialize the first validation set input
                            if (i == n - validationSet)
                            {
                                for (int j = 0; j < this.network.InputLayer.NeuronCount; j++)
                                {
                                    inputForValidation[this.network.InputLayer.NeuronCount - 1 - j] = this.data[this.data.Length - (n - i) - 1 - j];
                                }
                            }

                            for (int j = 0; j < this.network.InputLayer.NeuronCount; j++)
                            {
                                inputForValidationNetwork[j] = (inputForValidation[j] - this.minData) * this.factor + this.minNormalizedData;
                            }

                            INeuralData neuraldataval = new BasicNeuralData(inputForValidationNetwork);
                            solutionValidation[i - n + validationSet] = (this.network.Compute(neuraldataval)[0] - this.minNormalizedData) / this.factor + this.minData;

                            this.validationPoint[i - n + validationSet].x = i + this.network.InputLayer.NeuronCount;
                            this.validationPoint[i - n + validationSet].y = (float)solutionValidation[i - n + validationSet];

                            sspe += Math.Pow(this.data[i + this.network.InputLayer.NeuronCount] - solutionValidation[i - n + validationSet], 2);

                            // initialize the next validation set input from the current validation set input
                            for (int j = 0; j < this.network.InputLayer.NeuronCount - 1; j++)
                            {
                                inputForValidation[j] = inputForValidation[j + 1];
                            }

                            inputForValidation[this.network.InputLayer.NeuronCount - 1] = solutionValidation[i - n + validationSet];
                        }
                    }
                }

                mse = sse / this.solutionData.Length;
                mae = sae / this.solutionData.Length;

                //Console.WriteLine(error.ToString());

                //Display it
                this.iterationBox.Text = iteration.ToString();
                this.maeBox.Text       = mae.ToString("F5");
                this.mseBox.Text       = mse.ToString("F5");
                this.errorBox.Text     = error.ToString("F5");


                seriesGraph.Refresh();

                if (this.useAdvanceEarlyStopping)
                {
                    //calculate advance early stopping 2
                    mspe = sspe / validationSet;
                    msle = ssle / (this.solutionData.Length - validationSet);

                    //save best weight
                    if (this.bestValidationError > mspe)
                    {
                        this.bestValidationError = mspe;
                        this.bestSolution        = this.solutionData;

                        // weight matrix
                        for (int i = 0; i < this.network.Layers.Count - 1; i++)
                        {
                            for (int j = 0; j < this.network.Layers[i].WeightMatrix.Rows; j++)
                            {
                                for (int k = 0; k < this.network.Layers[i].WeightMatrix.Cols; k++)
                                {
                                    this.bestWeightMatrix[i][j, k] = this.network.Layers[i].WeightMatrix[j, k];
                                }
                            }
                        }

                        bestNetworkError = error;
                        bestNetworkMAE   = mae;
                        bestNetworkMSE   = mse;
                    }
                    //calculate generalization loss &pq
                    generalizationLoss = 100 * (mspe / this.bestValidationError - 1);

                    trainingErrors[(iteration - 1) % this.strip] = msle;
                    double minStripTrainingError = double.MaxValue, sumStripTrainingError = 0.0;
                    for (int i = 0; i < this.strip; i++)
                    {
                        sumStripTrainingError += trainingErrors[i];
                        if (trainingErrors[i] < minStripTrainingError)
                        {
                            minStripTrainingError = trainingErrors[i];
                        }
                    }
                    double trainingProgress = 1000 * ((sumStripTrainingError / (this.strip * minStripTrainingError)) - 1);
                    pq = generalizationLoss / trainingProgress;



                    //display advance early stopping
                    this.learningErrorBox.Text      = msle.ToString("F5");
                    this.validationErrorBox.Text    = mspe.ToString("F5");
                    this.generalizationLossBox.Text = generalizationLoss.ToString("F5");
                    this.pqBox.Text = pq.ToString("F5");
                    this.seriesGraph.Refresh();

                    //stopping
                    switch (this.advanceStoppingMethod)
                    {
                    case AdvanceStoppingMethodEnumeration.GeneralizationLoss:
                        if (generalizationLoss > this.generalizationLossTreshold)
                        {
                            needToStop = true;
                        }
                        break;

                    case AdvanceStoppingMethodEnumeration.ProgressQuotient:
                        if (pq > this.pqTreshold)
                        {
                            needToStop = true;
                        }
                        break;
                    }
                }

                if (this.withCheckingCycle && iteration % this.checkingCycle == 0)
                {
                    switch (this.checkingMethod)
                    {
                    case CheckingMethodEnumeration.byMSEValue:
                        if (mse <= this.byMSEValueStopping)
                        {
                            needToStop = true;
                        }
                        break;

                    case CheckingMethodEnumeration.byMSEChange:
                        if (lastMSE - mse <= this.byMSEChangeStopping)
                        {
                            needToStop = true;
                        }
                        break;
                    }
                    lastMSE = mse;
                }
                if (iteration >= this.maxIteration)
                {
                    needToStop = true;
                }

                iteration++;
            }

            //restore weight
            if (this.useAdvanceEarlyStopping)
            {
                this.solutionData = this.bestSolution;

                // weight matrix

                for (int i = 0; i < this.network.Layers.Count - 1; i++)
                {
                    for (int j = 0; j < this.network.Layers[i].WeightMatrix.Rows; j++)
                    {
                        for (int k = 0; k < this.network.Layers[i].WeightMatrix.Cols; k++)
                        {
                            this.network.Layers[i].WeightMatrix[j, k] = this.bestWeightMatrix[i][j, k];
                        }
                    }
                }

                //best network criterion
                this.error = bestNetworkError;
                this.mse   = bestNetworkMSE;
                this.mae   = bestNetworkMAE;
            }
            else
            {
                this.error = error;
                this.mse   = mse;
                this.mae   = mae;
            }

            this.enableControls(true);
        }