/// <summary>
        /// Brings the model to steady state or throws an exception if it cant
        /// </summary>
        /// <param name="verifyConcentrations">if true, the floating species concentration will be checked</param>
        /// <param name="cutoff">the value considered as close enough to steady state</param>
        /// <param name="retries">number of tries</param>
        /// <returns></returns>
        private double BringToSteadyStateOrThrow(int retries = 20, bool verifyConcentrations = false, double cutoff = 10E-5)
        {
            double steadyStateValue = 1;
            bool   fndSteadyState   = false;
            int    simCount         = 1;

            while (!fndSteadyState && simCount < retries)
            {
                rr.reset();
                try
                {
                    for (int k = 0; k < simCount; k++)
                    {
                        rr.simulate();
                    }
                    steadyStateValue = rr.steadyState();
                }
                catch (SBWException)
                {
                    //simCount++;
                }
                if (steadyStateValue < cutoff)
                {
                    fndSteadyState = true;
                    if (verifyConcentrations)
                    {
                        double[] ststSpeciesValues = rr.getFloatingSpeciesConcentrations();
                        for (int i = 0; i < ststSpeciesValues.Length; i++)
                        {
                            if (ststSpeciesValues[i] < 0)
                            {
                                fndSteadyState = false;
                                simCount++;
                                break;
                            }
                        }
                    }
                }
                else
                {
                    simCount++;
                }
            }
            if (simCount == retries)
            {
                var ae = new BifException("Error in sbwBifGAOptimize",
                                          "Model has not reached steady state!!!");
                throw ae;
            }
            return(steadyStateValue);
        }
        private void NotifyAboutNumberOfSImulations(Action <ArrayList> updateStatus, int i)
        {
            if (i % nUpdate != 0)
            {
                return;
            }

            if (!hasUpdateStatusMethod)
            {
                return;
            }

            try
            {
                updateStatus(new ArrayList(new object[] { 0, i }));
            }
            catch (SBWException ae)
            {
                ae = new BifException("Error in sbwBifGAOptimize",
                                      "Unable to call update status!!!");
                throw ae;
            }
        }
        public OptimizationResult optimizeWithGeneticAlgo(string sbmlInput, AlgorithmArguments algoArgs
                                                          , Action <ArrayList> updateStatus = null)
        {
            hasUpdateStatusMethod = updateStatus != null;
            evalOption            = algoArgs.EvalOption;
            try
            {
                isProcessActive = true;
                if (rr == null)
                {
                    rr = new RoadRunner();
                }

                rr.loadSBML(sbmlInput);
                rr.setNumPoints(numPoints);

                #region OSCillator

                if (algoArgs.EvalOption == OSCILLATOR || algoArgs.EvalOption == TURNINGPOINT)
                {
                    //Read num of Generations
                    numOfGenerations = algoArgs.Args.NumOfGenerations;

                    //Read num of Members in Population
                    numOfMembers = algoArgs.Args.NumOfMembers;

                    //Read num of Parameters in Member
                    numOfParameters = 0;

                    pNames.Clear();
                    pValues.Clear();
                    minBoundsArray.Clear();
                    maxBoundsArray.Clear();
                    foreach (var nameValueBound in algoArgs.NameValueBounds)
                    {
                        pNames.Add(nameValueBound.Item1);
                        pValues.Add(nameValueBound.Item2);
                        minBoundsArray.Add(nameValueBound.Item3);
                        maxBoundsArray.Add(nameValueBound.Item4);
                        ++numOfParameters;
                    }

                    //Read num of Members to Select from Population
                    numOfMemSelect = algoArgs.Args.NumOfMemSelect;

                    //Read the value of Pc - Crossover probability
                    Pc = algoArgs.Args.Pc;

                    //Read the value of Pm - Mutation probability
                    Pm = algoArgs.Args.Pm;

                    //Read the value of TOLERANCE Level
                    Tolerance = algoArgs.Args.Tolerance;

                    //Read the value of Random_Number Seed
                    randomSeed = algoArgs.Args.RandomSeed;

                    simulationTime = algoArgs.Args.SimulationTime;

                    //Read the number of iterations after which you want to receive the update
                    nUpdate = algoArgs.Args.NUpdate;

                    //Try to compute the steady state using the initial parameter values.
                    //If steady state cannot be reached throw an exception to user asking him
                    //to reset the initial parameter values.
                    initMember = new Member {
                        numOfParameters = pValues.Count, pValuesArray = new List <double>(pValues)
                    };

                    try
                    {
                        compObjFnFitnessForInitMem(ref initMember);
                    }
                    catch (SBWException)
                    {
                        var ex =
                            new BifException(
                                "Unable to calculate steadystate value with the current initial parameter values.",
                                "Please start with different set of parameter values!!! ");
                        throw ex;
                    }

                    var generationArray = new[] { new Generation(), new Generation() };

                    //using only two generation objects
                    generationArray[0].allocMemberArray(algoArgs.Args.NumOfMembers);
                    var m = new Member();

                    rnd = algoArgs.Args.RandomSeed == 0
                        ? new MT19937Generator()
                        : new MT19937Generator(algoArgs.Args.RandomSeed);

                    for (int i = 0; i < algoArgs.Args.NumOfMembers; i++)
                    {
                        m.initParamArray(pValues, minBoundsArray, maxBoundsArray);
                        evalObjFnFitness(ref m);
                        generationArray[0].setMember(i, m);
                        //cout << "Member: " << i << endl;
                        if (!isProcessActive)
                        {
                            return(new OptimizationResult
                            {
                                Iterations = 0,
                                Score = generationArray[0].genFitScore,
                                Values = generationArray[0].memArray[0].pValuesArray,
                                RealEigenValues = generationArray[0].memArray[0].realEigenValueArray,
                                ImagEigenValues = generationArray[0].memArray[0].complexEigenValueArray
                            });
                        }

                        //Send status update after every nUpdate simulations.
                        NotifyAboutNumberOfSImulations(updateStatus, i);
                    }

                    generationArray[0].sortMemberArray();
                    generationArray[0].setFitScore();


                    //update status after first generation
                    var    updateParamValuesArray = new List <double>();
                    Member best = generationArray[0].getMember(0);
                    for (int i = 0; i < numOfParameters; i++)
                    {
                        updateParamValuesArray.Add(best.pValuesArray[i]);
                    }

                    int minRealEigenIndex;
                    if (hasUpdateStatusMethod)
                    {
                        try
                        {
                            var update = new ArrayList {
                                1, 0, generationArray[0].genFitScore, updateParamValuesArray
                            };
                            if (best.realEigenValueArray.Count > 0)
                            {
                                minRealEigenIndex = findMinRealEigenValueIndex(
                                    best.realEigenValueArray);
                                update.Add(best.realEigenValueArray[minRealEigenIndex]);
                                update.Add(best.complexEigenValueArray[minRealEigenIndex]);
                            }
                            else
                            {
                                update.Add(0.0);
                                update.Add(0.0);
                            }
                            update.Add(best.realEigenValueArray);
                            update.Add(best.complexEigenValueArray);
                            updateStatus(update);
                        }
                        catch (SBWException ae)
                        {
                            ae = new BifException("Error in sbwBifGAOptimize",
                                                  "Unable to call update status!!!");
                            throw ae;
                        }
                    }

                    generationArray[1].allocMemberArray(algoArgs.Args.NumOfMembers);
                    if (algoArgs.Args.NumOfGenerations == 1)
                    {
                        generationArray[1] = generationArray[0];
                    }

                    int Iterations = 1;
                    for (int j = 1; j < algoArgs.Args.NumOfGenerations; j++)
                    {
                        for (int k = 0; k < (algoArgs.Args.NumOfMembers); k = k + 2)
                        {
                            var parent1 = selectMember(generationArray[0]);

                            var parent2 = selectMember(generationArray[0]);

                            var child1 = new Member();
                            var child2 = new Member();

                            crossOver(parent1, parent2, ref child1, ref child2);

                            mutate(ref child1);

                            evalObjFnFitness(ref child1);

                            mutate(ref child2);

                            evalObjFnFitness(ref child2);

                            Member bestMem1;
                            Member bestMem2;
                            selectBestTwo(parent1, parent2, child1, child2, out bestMem1, out bestMem2);


                            generationArray[1].setMember(k, bestMem1);
                            generationArray[1].setMember(k + 1, bestMem2);

                            if (!isProcessActive)
                            {
                                return(new OptimizationResult
                                {
                                    Iterations = 0,
                                    Score = generationArray[0].genFitScore,
                                    Values = generationArray[0].memArray[0].pValuesArray,
                                    RealEigenValues = generationArray[0].memArray[0].realEigenValueArray,
                                    ImagEigenValues = generationArray[0].memArray[0].complexEigenValueArray
                                });
                            }

                            //Send status update after every predefined number of simulations.
                            NotifyAboutNumberOfSImulations(updateStatus, k);
                        }
                        generationArray[1].sortMemberArray();
                        generationArray[1].setFitScore();

                        if (generationArray[1].genFitScore > generationArray[0].genFitScore)
                        {
                            generationArray[1].setMember(0, best);
                            generationArray[1].genFitScore = generationArray[0].genFitScore;
                        }
                        //os << j << "\t" << generationArray[1].getGenFitScore() << "\t";

                        updateParamValuesArray.Clear();
                        best = generationArray[1].getMember(0);
                        for (int i = 0; i < numOfParameters; i++)
                        {
                            updateParamValuesArray.Add(best.pValuesArray[i]);
                            //os << generationArray[1].getMember(0).pValuesArray[i] << "\t";
                        }
                        //os << endl;

                        //Call update status once you finish calculating the fitness score.
                        if (hasUpdateStatusMethod)
                        {
                            try
                            {
                                var update = new ArrayList
                                {
                                    1,
                                    j,
                                    generationArray[1].genFitScore,
                                    updateParamValuesArray
                                };


                                if (best.realEigenValueArray.Count > 0)
                                {
                                    minRealEigenIndex = findMinRealEigenValueIndex(best.realEigenValueArray);
                                    update.Add(best.realEigenValueArray[minRealEigenIndex]);
                                    update.Add(best.complexEigenValueArray[minRealEigenIndex]);
                                }
                                else
                                {
                                    update.Add(0.0);
                                    update.Add(0.0);
                                }
                                update.Add(best.realEigenValueArray);
                                update.Add(best.complexEigenValueArray);
                                updateStatus(update);
                            }
                            catch (SBWException ae)
                            {
                                ae = new BifException("Error in sbwBifGAOptimize",
                                                      "Unable to call update status!!!");
                                throw ae;
                            }
                        }

                        //Code to check if the Tolerance level has reached
                        var bufferArray = new List <double>();
                        bufferArray.Clear();
                        double realPart;
                        double complexPart;
                        int    eigenArraySize = best.eigenValueArrayDim;

                        if (algoArgs.EvalOption == OSCILLATOR)
                        {
                            for (int i = 0; i < eigenArraySize; i++)
                            {
                                realPart    = best.realEigenValueArray[i];
                                complexPart = best.complexEigenValueArray[i];
                                if (Math.Abs(complexPart) == 0.0) // Check if the imaginary part is eqaul to zero.
                                {
                                    // If it is so then assign 10^6 to bufferArray Element.
                                    bufferArray.Add(10E6);
                                }
                                else // Else store the absolute value of real eigen value.
                                {
                                    bufferArray.Add(Math.Abs(realPart));
                                }
                            }
                        }
                        if (algoArgs.EvalOption == TURNINGPOINT)
                        {
                            for (int i = 0; i < eigenArraySize; i++)
                            {
                                realPart    = best.realEigenValueArray[i];
                                complexPart = best.complexEigenValueArray[i];
                                if (Math.Abs(complexPart) > 0.0) // Check if the imaginary part is greater than zero.
                                {
                                    // If it is so then assign 10^6 to bufferArray Element.
                                    bufferArray.Add(10E6);
                                }
                                else // Else store the absolute value of real eigen value.
                                {
                                    bufferArray.Add(Math.Abs(realPart));
                                }
                            }
                        }

                        if (bufferArray.Count > 0)
                        {
                            minRealEigenIndex = findMinRealEigenValueIndex(bufferArray);

                            if (Math.Abs(bufferArray[minRealEigenIndex]) < algoArgs.Args.Tolerance)
                            {
                                break;
                            }
                        }

                        generationArray[0] = generationArray[1];

                        ++Iterations;

                        //Check the isProcessActive Flag here and gracefully exit the optimizer if it is false.
                        if (!isProcessActive)
                        {
                            break;
                        }
                    }

                    return(new OptimizationResult
                    {
                        Iterations = Iterations,
                        Score = generationArray[0].genFitScore,
                        Values = generationArray[0].memArray[0].pValuesArray,
                        RealEigenValues = generationArray[0].memArray[0].realEigenValueArray,
                        ImagEigenValues = generationArray[0].memArray[0].complexEigenValueArray
                    });
                }

                #endregion
            }
            catch (SBWException)
            {
                throw;
            }
            catch
            {
                throw new BifException("Problem occured in main method", " ");
            }

            return(null);
        }
        private double ComputeScore(int N, List <double> wr, List <double> wi)
        {
            double norm;
            double realFit;
            double imagFit;

            if (evalOption == OSCILLATOR)
            {
                /*Eliminate networks with small real and imag eigen values*/
                bool fndZeroEigenValueFlag = false;
                for (int i = 0; i < N; i++)
                {
                    if ((Math.Abs(wr[i]) < 10E-6) && (Math.Abs(wi[i]) < 10E-3))
                    {
                        fndZeroEigenValueFlag = true;
                    }
                }

                if (fndZeroEigenValueFlag)
                {
                    var ae = new BifException("Error in sbwBifGAOptimize",
                                              "Need to reset parameterValues!!!");
                    throw ae;
                }

                /*Calculate the fitness based on the eigen values*/
                realFit = 1;
                imagFit = 1;
                for (int i = 0; i < N; i++)
                {
                    realFit = realFit * wr[i];
                    imagFit = imagFit * (1 - (0.99) * Math.Exp(-1 * Math.Abs(wi[i])));
                }
                realFit = Math.Abs(realFit);
                norm    = realFit / imagFit;
            }
            else
            {
                double eigenValsProd = 0.0;
                realFit = wr[0];
                imagFit = wi[0];
                double tempRealFit;
                for (int i = 1; i < N; i++)
                {
                    tempRealFit = realFit;
                    realFit     = realFit * wr[i] - imagFit * wi[i];
                    imagFit     = tempRealFit * wi[i] + imagFit * wr[i];
                }
                eigenValsProd = Math.Abs(realFit); //Using only real part because, we assume the eigen values
                //come in complex conjugates, so the imag parts will be gone
                //when we take the product.

                int    minEigenIndex;
                bool   hasRealEigenValue;
                var    bufferArray            = new List <double>();
                double eigenValsExceptMinProd = 0.0;

                hasRealEigenValue = false;
                for (int i = 0; i < N; i++)
                {
                    if (Math.Abs(wi[i]) > 0.0) // Check if the imaginary part is greater than zero.
                    {
                        // If it is so then assign 10^6 to bufferArray Element.
                        bufferArray.Add(10E6);
                    }
                    else // Else store the absolute value of real eigen value.
                    {
                        bufferArray.Add(Math.Abs(wr[i]));
                        hasRealEigenValue = true;
                    }
                }

                if (hasRealEigenValue)
                {
                    minEigenIndex = findMinRealEigenValueIndex(bufferArray);

                    realFit = 1.0;
                    imagFit = 0.0;
                    for (int i = 0; i < N; i++)
                    {
                        if (i != minEigenIndex)
                        {
                            tempRealFit = realFit;
                            realFit     = realFit * wr[i] - imagFit * wi[i];
                            imagFit     = tempRealFit * wi[i] + imagFit * wr[i];
                        }
                    }
                    if (N == 1) //If there is only one eigenValue and that is real then
                    {
                        // the product of eigen values without that will be zero
                        eigenValsExceptMinProd = 0.0;
                    }
                    else
                    {
                        eigenValsExceptMinProd = Math.Abs(realFit);
                    }
                }
                else
                {
                    eigenValsExceptMinProd = eigenValsProd;
                }
                norm = eigenValsProd / (1.000 - 0.999 * Math.Exp(-1.0 * eigenValsExceptMinProd));
            }
            return(norm);
        }