コード例 #1
0
        public static void Run(int pllproc)
        {
            double[] @params  = new double[6];
            double   maxgrad  = 0;
            double   stpthrsh = 0;
            double   maxeigen;
            double   mineigen;
            double   wr = 0;

            double[] alpha = { 0.50, 1.00 };
            double   rfMax = 2.75;
            int      td    = 0;

            int[]  prtls   = { -1, -1, -1, -1 };
            int    iterint = 1;
            long   sn      = (long)Math.Pow(10.0, 7);
            int    prec    = (int)Math.Pow(10.0, 4);
            string type    = "";
            string alg     = "";

            const string rootdir = Config.ConfigsRootDir;

            // Read setup details from control file.
            try
            {
                var getParams = File.ReadAllText(Path.Combine(rootdir, Config.ControlFile));
                var s         = getParams.Split(new[] { ' ', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);

                @params  = s.Take(6).Select(i => double.Parse(i, CultureInfo.InvariantCulture)).ToArray();
                td       = int.Parse(s[6]);
                wr       = double.Parse(s[7]);
                stpthrsh = double.Parse(s[8]);
                alg      = s[9];
                type     = s[10];
                if (type.Equals("sim"))
                {
                    sn       = int.Parse(s[11]);
                    alpha[0] = double.Parse(s[12]);
                    alpha[1] = double.Parse(s[13]);
                }
                else if (type.Equals("dp"))
                {
                    prec  = int.Parse(s[11]);
                    rfMax = double.Parse(s[12]);
                }
            }
            catch (Exception ex)
            {
                Trace.Write("ERROR: Could not read file: ");
                Trace.WriteLine(Path.Combine(rootdir, Config.ControlFile) + $". {ex.Message}");
                Trace.WriteLine("EXITING...main()...");
                Console.Read();
                Environment.Exit(1);
            }


            // Read initial glide-path from file.
            var gp = new double[td];

            var gpPath = Path.Combine(rootdir, Config.InitGladepathFile);

            try
            {
                gp = File.ReadAllLines(gpPath).Select(double.Parse)
                     .Take(td).ToArray();
            }
            catch (Exception ex)
            {
                Trace.Write("ERROR: Could not read file: ");
                Trace.WriteLine(gpPath + ". Error: " + ex.Message);
                Trace.WriteLine("EXITING...main()...");
                Console.Read();
                Environment.Exit(1);
            }

            if (gp.Length != td)
            {
                Trace.Write("ERROR: File: ");
                Trace.Write(gpPath);
                Trace.Write(" needs ");
                Trace.Write(td);
                Trace.WriteLine(" initial asset allocations, but has fewer.");
                Trace.WriteLine("EXITING...main()...");
                Console.Read();
                Environment.Exit(1);
            }

            // Display optimization algorithm.
            Trace.Write(@"===> Optimization algorithm: ");
            if (alg == "nr")
            {
                Trace.WriteLine(@"Newton's Method");
            }
            else if (alg == "ga")
            {
                Trace.WriteLine(@"Gradient Ascent");
            }

            // Display estimation method.
            Trace.WriteLine("");
            Trace.Write(@"===> Estimation method: ");
            if (type == "sim")
            {
                Trace.WriteLine(@"Simulation");
            }
            else if (type == "dp")
            {
                Trace.WriteLine(@"Dynamic Program");
                pllproc = 4 * pllproc;
            }

            // Declare variables that depend on data read from the control file for sizing.
            double[,] hess;
            double[] ngrdnt = new double[td];
            EigenvalueDecomposition hevals;

            var grad = new double[td];

            // Take some steps (1 full iteration but no more than 50 steps) in the direction of steepest ascent. This can move us off
            // the boundary region where computations may be unstable (infinite), especially when constructing the Hessian for Newton's method.
            // Also, this initial stepping usually makes improvements very quickly before proceeding with the optimization routine.
            double probnr = GetPNR.Run(type, @params, gp, td, wr, 4 * sn, (int)(rfMax * prec), prec, prtls, pllproc);

            Trace.WriteLine("");
            Trace.WriteLine("Initial Glide-Path (w/Success Probability):");
            WrtAry.Run(probnr, gp, "GP", td);
            for (int s = 1; s <= 2; ++s)
            {
                maxgrad = BldGrad.Run(type, @params, gp, td, wr, sn, (int)(rfMax * prec), prec, probnr, 4 * sn, alpha[1], pllproc, grad);
                if (maxgrad <= stpthrsh)
                {
                    Trace.Write("The glide-path supplied satisfies the EPSILON convergence criteria: ");
                    Trace.WriteLine($"{maxgrad:F15} vs. {stpthrsh:F15}");
                    s = s + 1;
                }
                else if (s != 2)
                {
                    probnr = Climb.Run(type, @params, gp, td, wr, 2 * sn, (int)(rfMax * prec), prec, pllproc, maxgrad,
                                       probnr, 4 * sn, grad, alpha[0], 50);
                    Trace.WriteLine("");
                    Trace.WriteLine("New (Post Initial Climb) Glide-Path (w/Success Probability):");
                    WrtAry.Run(probnr, gp, "GP", td);
                }
                else if (maxgrad <= stpthrsh)
                {
                    Trace.Write("The glide-path supplied satisfies the EPSILON convergence criteria after intial climb without iterating: ");
                    Trace.WriteLine($"{maxgrad:F15} vs. {stpthrsh:F15}");
                }
            }

            // Negate the gradient if using NR method.
            if (alg == "nr")
            {
                for (int y = 0; y < td; ++y)
                {
                    ngrdnt[y] = -1.00 * grad[y];
                }
            }

            // If convergence is not achieved after initial climb then launch into full iteration mode.
            while (maxgrad > stpthrsh)
            {
                Trace.WriteLine("");
                Trace.WriteLine("=========================");
                Trace.WriteLine($"Start Iteration #{iterint}");
                Trace.WriteLine("=========================");
                if (alg == "nr")
                {
                    // Record the probability before iterating.
                    double strtpnr = probnr;

                    // Build the Hessian matrix for this glide-path and derive its eigenvalues. (Display the largest & smallest value.)
                    // This is required when method=nr. When either procedure ends with convergence we recompute the Hessian matrix to
                    // ensure we are at a local/global maximum (done below after convergence).
                    hess = DrvHess.Run(type, @params, gp, td, wr, sn, (int)(rfMax * prec), prec, pllproc, grad, probnr);
                    //hevals.compute(hess, false);
                    hevals = new EigenvalueDecomposition(hess);

                    var reals = hevals.RealEigenvalues;
                    maxeigen = reals.Max();
                    mineigen = reals.Min();

                    // Display the smallest/largest eigenvalues.
                    Trace.WriteLine("");
                    Trace.Write("Min Hessian eigenvalue for this iteration (>=0.00 --> convex region): ");
                    Trace.WriteLine(mineigen);
                    Trace.WriteLine("");
                    Trace.Write("Max Hessian eigenvalue for this iteration (<=0.00 --> concave region): ");
                    Trace.WriteLine(maxeigen);

                    // Update the glidepath and recompute the probability using the new glidepath.
                    //sol = hess.colPivHouseholderQr().solve(ngrdnt);
                    var qr  = new QrDecomposition(hess);
                    var sol = qr.Solve(ngrdnt);

                    for (int y = 0; y < td; ++y)
                    {
                        gp[y] += sol[y];
                    }
                    probnr = GetPNR.Run(type, @params, gp, td, wr, 4 * sn, (int)(rfMax * prec), prec, prtls, pllproc);

                    // If success probability has worsened alert the user.
                    if (probnr < strtpnr)
                    {
                        Trace.WriteLine("");
                        Trace.WriteLine("NOTE: The success probability has worsened during the last iteration. This could happen for different reasons:");
                        Trace.WriteLine(" 1.) The difference in probabilities is beyond the system's ability to measure accurately (i.e., beyond 15 significant digits).");
                        Trace.WriteLine(" 2.) The difference is due to estimation/approximation error.");
                        Trace.WriteLine(" 3.) You may be operating along the boundary region. In general the procedure is not well defined on the boundaries. (Try gradient ascent.)");
                    }
                }
                else if (alg == "ga")
                {
                    // Update the glide-path and recompute the probability using the new glide-path.
                    probnr = Climb.Run(type, @params, gp, td, wr, 2 * sn, (int)(rfMax * prec), prec, pllproc, maxgrad,
                                       probnr, 4 * sn, grad, alpha[0]);
                }

                // Display the new glide-path.
                Trace.WriteLine("");
                Trace.Write("New Glide-Path:");
                WrtAry.Run(probnr, gp, "GP", td);

                // Rebuild the gradient and negate it when using NR.
                maxgrad = BldGrad.Run(type, @params, gp, td, wr, 1 * sn, (int)(rfMax * prec), prec, probnr,
                                      4 * sn, alpha[1], pllproc, grad);
                if (alg == "nr")
                {
                    for (int y = 0; y < td; ++y)
                    {
                        ngrdnt[y] = -1.00 * grad[y];
                    }
                }
                // Report the convergence status.
                Trace.WriteLine("");
                Trace.WriteLine($"EPSILON Convergence Criteria: {maxgrad:F15} vs. {stpthrsh:F15}");
                if (maxgrad <= stpthrsh)
                {
                    Trace.WriteLine("");
                    Trace.WriteLine("==========> EPSILON Convergence criteria satisfied. <==========");
                }
                Trace.WriteLine("");
                Trace.WriteLine(new String('=', 25));
                Trace.Write("End Iteration #");
                Trace.WriteLine(iterint);
                Trace.WriteLine(new String('=', 25));
                iterint++;
            }

            // Build Hessian and confirm we are at a maximum, not a saddle-point or plateau for example.
            Trace.WriteLine("");
            Trace.WriteLine("Convergence Achieved: Final step is to confirm we are at a local/global maximum. Hessian is being built.");
            hess = DrvHess.Run(type, @params, gp, td, wr, sn, (int)(rfMax * prec), prec, pllproc, grad, probnr);

            hevals = new EigenvalueDecomposition(hess);
            var r = hevals.RealEigenvalues;

            maxeigen = r.Max();
            mineigen = r.Min();

            // Display the smallest/largest eigenvalues.
            Trace.WriteLine("");
            Trace.Write("Min Hessian eigenvalue at solution [>=0.00 --> convex region --> (local/global) minimum]: ");
            Trace.WriteLine(mineigen);
            Trace.WriteLine("");
            Trace.Write("Max Hessian eigenvalue at solution [<=0.00 --> concave region --> (local/global) maximum]: ");
            Trace.WriteLine(maxeigen);

            // Write final GP to the output file.
            Trace.WriteLine("");
            if (maxeigen <= 0 || mineigen >= 0)
            {
                Trace.Write("(Local/Global) Optimal ");
            }
            Trace.WriteLine("Glide-Path:");
            WrtAry.Run(probnr, gp, "GP", td, Path.Combine(rootdir, Config.Outfile));
            Trace.WriteLine("");
        }
コード例 #2
0
        public static double Run(string type, double[] prms, double[] gpath, int fxTD, double rf0, long n, int nbuckets, int prec, int plproc, double mxgrd, double stdpnr, long npnr, double[] grdnt, double alpha, int nitrs = 0)
        {
            double maxpnr = stdpnr;
            double iter   = 1.00;

            int[] prtls    = { -1, -1, -1, -1 };
            long  maxn     = npnr;
            int   cont     = 1;
            int   fstimpr  = 0;
            int   iindx    = 0;
            int   tryup    = 0;
            int   origindx = 0;
            NormalDistribution normdist = new NormalDistribution(0.00, 1.00);

            // Get initial glidepath provided, assign to both original GP array and prior GP array.
            var prevGP = new double[fxTD];
            var origGP = new double[fxTD];

            for (int y = 0; y < fxTD; ++y)
            {
                origGP[y] = prevGP[y] = gpath[y];
            }

            // Define the step size for climbing. The step size depends on the largest gradient element and grows exponentially.
            // This is a heuristic that has worked well and can be modified if desired. Better step sizes can reduce runtimes.
            for (int i = 1; i <= 10; ++i)
            {
                if (mxgrd >= 1.00 / (10.00 * Math.Pow(10.00, i)) && mxgrd < 1.00 / (10.00 * Math.Pow(10.00, i - 1)))
                {
                    iindx = i;
                    iter  = Math.Pow(Math.Exp(Math.Log(5.00) / 4.00), iindx);
                }
            }

            // Output details for the current iteration.
            Trace.WriteLine(new String('=', 70));
            Trace.WriteLine($"Iteration step size = {iter:F10}");
            Trace.Write("Trying to improve on success probability = ");
            Trace.WriteLine(stdpnr);
            Trace.WriteLine(new String('=', 70));
            // Climb in the direction of the gradient.
            for (int t = 0; cont == 1 || fstimpr == 0; ++t) // Iterate until no more progress is made
            {
                if (cont == 0 && fstimpr == 0)              // If no progress is made reduce step size and try again.
                {
                    // Set the original index value when entering this problematic scenario.
                    if (origindx == 0)
                    {
                        origindx = iindx;
                    }
                    // Adjust glidepath back one iteration since it failed to improve the probability.
                    for (int y = 0; y < fxTD; ++y)
                    {
                        gpath[y] = prevGP[y]; // Reverse final update, since no progress was made.
                    }
                    if (iindx != 0)
                    {
                    }
                    Trace.WriteLine("");
                    Trace.Write("No Progress Made: Iteration step size changed from ");
                    Trace.Write(iter);
                    if (iindx == 0)
                    {
                        Trace.WriteLine(" to 0.00. (No additional climbing attempts will be made.)");
                        Trace.WriteLine("");
                        Trace.WriteLine("ERROR: No progress can be made, the procedure is stuck. (Step size has been reduced to 0.)");
                        Trace.WriteLine(" You may be operating along the boundary where the process is not well defined or your");
                        Trace.WriteLine(" estimation/approximation precision level is not adequate for your epsilon level.");
                        Trace.WriteLine("");
                        Trace.WriteLine("");
                        Trace.Write("Current Glide-Path: ");
                        WrtAry.Run(maxpnr, gpath, "GP", fxTD);
                        Trace.WriteLine("");
                        Trace.WriteLine("EXITING...Climb()...");
                        Console.Read();
                        Environment.Exit(1);
                    }
                    else if (iindx == 1 && tryup == 5)
                    {
                        iindx = iindx - 1;
                        iter  = iter / 2.00;
                    }
                    else if (iindx > 1 && tryup == 5)
                    {
                        if (iindx == origindx + 5)
                        {
                            iindx = origindx - 1;
                        }
                        else
                        {
                            iindx = iindx - 1;
                        }
                        iter = Math.Pow(Math.Exp(Math.Log(5.00) / 4.00), iindx);
                    }
                    else if (iindx > 1 && tryup < 5)
                    {
                        iindx = iindx + 1;
                        iter  = Math.Pow(Math.Exp(Math.Log(5.00) / 4.00), iindx);
                        tryup = tryup + 1;
                    }
                    Trace.Write(" to ");
                    Trace.Write(iter);
                    Trace.WriteLine(". (Attempting to climb again.)");
                    cont = 1;
                }
                for (int y = 0; y < fxTD; ++y)                // Iterate over glide-path and update it
                {
                    prevGP[y] = gpath[y];                     // Reset the previous glide-path element
                    gpath[y]  = gpath[y] + (iter) * grdnt[y]; // Update each individual glide-path element
                    if (gpath[y] < Funcs.mva(prms) + 0.0001)
                    {
                        gpath[y] = Funcs.mva(prms) + 0.0001; // Stay above MVA and consistent with ThrdPNRdyn() and ThrdPNRsim().
                    }
                    else if (gpath[y] > 1.00)
                    {
                        gpath[y] = 1.00; // Consistent with ThrdPNRdyn() and ThrdPNRsim().
                    }
                }
                double newpnr = GetPNR.Run(type, prms, gpath, fxTD, rf0, n, nbuckets, prec, prtls, plproc);
                Trace.WriteLine("");
                Trace.Write("Base Prob(NR) = ");
                Trace.Write(maxpnr);
                if (type == "sim")
                {
                    Trace.Write(" (N=");
                    Trace.Write(maxn);
                    Trace.Write(")");
                }
                Trace.WriteLine("");
                Trace.Write("New Prob(NR) = ");
                Trace.Write(newpnr);
                if (type == "sim")
                {
                    Trace.Write($" (N={n})");
                }
                else if (newpnr > maxpnr)
                {
                    Trace.Write(" (Better, CONTINUE climbing ...)");
                }
                else
                {
                    Trace.Write(" (Worse, STOP climbing ...)");
                }
                Trace.WriteLine("");

                // If using simulation, conduct a non-inferiority test of the new vs max base GP.
                // =====> Continue to climb if the new GP is at least as good as the max base GP.
                // Otherwise, compare new probability with old and climb while making progress.
                if (type == "sim")
                {
                    double cmbvar = maxpnr * (1.00 - maxpnr) / maxn + newpnr * (1.00 - newpnr) / n;
                    double ts     = (newpnr - maxpnr) / Math.Sqrt(cmbvar);
                    double pval   = normdist.DistributionFunction(ts);
                    Trace.Write("Test Statistic = ");
                    Trace.WriteLine(ts);
                    Trace.Write("P-Value = ");
                    Trace.Write(pval);
                    Trace.Write(" (Alpha=");
                    Trace.Write(alpha);
                    Trace.WriteLine(")");
                    if (pval > alpha)
                    {
                        fstimpr = 1;
                        Trace.WriteLine("=====> Accept Ho (non-inferiority), CONTINUE climbing ...");
                    }
                    else
                    {
                        cont = 0;
                        Trace.WriteLine("=====> Reject Ho (non-inferiority), STOP climbing ...");
                    }
                    // Update PNR and sample size for base GP.
                    if (newpnr > maxpnr)
                    {
                        maxpnr = newpnr;
                        maxn   = n;
                    }
                }
                else if (type == "dp")
                {
                    if (newpnr > maxpnr)
                    {
                        fstimpr = 1;
                        maxpnr  = newpnr;
                    }
                    else
                    {
                        cont = 0;
                    }
                }
                // For lengthy climbing, display the current glidepath at 100 iteration intervals.
                if ((t + 1) % 100 == 0)
                {
                    Trace.WriteLine("");
                    Trace.Write("Current Glide-Path at Iteration: ");
                    Trace.Write(t + 1);
                    WrtAry.Run(newpnr, gpath, "GP", fxTD);
                }
                // Stop when maximum number of iterations has been reached, if specified.
                //=========================================================================
                if (nitrs > 0 && t + 1 == nitrs && cont == 1)
                {
                    Trace.WriteLine("");
                    Trace.WriteLine($"Climbing limit reached at {nitrs} iterations.");
                    cont = 2;
                }
            }

            // Adjust glidepath back one iteration since it failed to improve the probability.
            // (This is only done when climbing failed to improve, not when limit is reached.)
            if (cont != 2)
            {
                for (int y = 0; y < fxTD; ++y)
                {
                    gpath[y] = prevGP[y];
                }
            }
            if (type == "sim")
            {
                Trace.WriteLine("");
                Trace.WriteLine("Resetting the probability (to remove any built-in upward sampling bias) ...");
                maxpnr = ThrdPNRsim.Run(prms, gpath, fxTD, rf0, 2 * n, prtls, plproc);
            }

            // Return the max success probability.
            return(maxpnr);
        }
コード例 #3
0
        public static double[,] Run(string type, double[] prms, double[] a, int fxTD, double rf0, long n, int nbuckets, int prec, int plproc, double[] gradvctr, double stdpnr)
        {
            double[,] hess = new double[fxTD, fxTD];

            // Derive the Hessian matrix.
            Trace.WriteLine("");
            Trace.Write("Building Hessian ");
            for (int i = 0; i < fxTD; ++i)
            {
                var blnks = new String(' ', (i > 0 ? 17 : 0) + i);
                Trace.Write(blnks);
                for (int j = 0; j < fxTD; ++j)
                {
                    if (j >= i)
                    {
                        Trace.Write(".");
                        if (i < 0 || j < 0 || i >= fxTD || j >= fxTD)
                        {
                            Trace.Write("ERROR: Both i and j must be integers between 0 and ");
                            Trace.Write(fxTD - 1);
                            Trace.Write(", i=");
                            Trace.Write(i);
                            Trace.Write(" and j=");
                            Trace.WriteLine(j);
                            Trace.WriteLine("EXITING...DrvHess()...");
                            Console.Read();
                            Environment.Exit(1);
                        }
                        else if (i != j) // ***** Off-diagonal elements ***** //
                        {
                            // Define needed quantities and return the off-diagonal Hessian element.
                            //========================================================================
                            double k1 = Funcs.vp(prms, a[i]) / (2.00 * Funcs.v(prms, a[i])) +
                                        Math.Pow(Funcs.mp(prms), 2) / (2.00 * Funcs.vp(prms, a[i]));
                            double k2 = Funcs.vp(prms, a[j]) / (2.00 * Funcs.v(prms, a[j])) +
                                        Math.Pow(Funcs.mp(prms), 2) / (2.00 * Funcs.vp(prms, a[j]));
                            int[] prtls = { i, j, -1, -1 };
                            hess[i, j] =
                                k1 * k2 * (GetPNR.Run(type, prms, a, fxTD, rf0, n, nbuckets, prec, prtls, plproc) -
                                           gradvctr[i] / k1 - gradvctr[j] / k2 - stdpnr);
                        }
                        else // ***** Diagonal elements ***** //
                        {
                            // Define needed quantities and return the diagonal Hessian element.
                            // [Note #1: K1 is for h1, K2 is for h2, and K3 is for f(). And the diagonal term is K1*h1() + K2*h2() + K3*f().]
                            // [Note #2: The Hessian diagonals will divide by zero for one alpha, check for that value and exit if encountered.]
                            if (Math.Abs(Funcs.v(prms, a[i]) * Funcs.vpp(prms) - 2.00 * Math.Pow(Funcs.vp(prms, a[i]), 2)) < 1e-15)
                            {
                                Trace.Write("ERROR: Hessian diagonal element does not exist for alpha value=");
                                Trace.WriteLine($"{a[i]} encountered at time point t={i}.");
                                Trace.WriteLine("");
                                Trace.WriteLine("EXITING...DrvHess()...");
                                Console.Read();
                                Environment.Exit(1);
                            }
                            else
                            {
                                double k1 = (Funcs.v(prms, a[i]) + Math.Pow(Funcs.kh1(prms, a[i]), 2)) *
                                            (Funcs.v(prms, a[i]) * Funcs.vpp(prms) - 2.00 * Math.Pow(Funcs.vp(prms, a[i]), 2)) /
                                            (2.00 * Math.Pow(Funcs.v(prms, a[i]), 3));
                                double k2 = (Math.Pow(Funcs.vp(prms, a[i]), 2) + 2.00 * Funcs.v(prms, a[i]) * Math.Pow(Funcs.mp(prms), 2)) /
                                            (2.00 * Math.Pow(Funcs.v(prms, a[i]), 2));
                                double k3 = -((Funcs.vpp(prms) * Funcs.v(prms, a[i]) - Math.Pow(Funcs.vp(prms, a[i]), 2) +
                                               2.00 * Funcs.v(prms, a[i]) * Math.Pow(Funcs.mp(prms), 2)) /
                                              (2.00 * Math.Pow(Funcs.v(prms, a[i]), 2)) +
                                              (2.00 * Math.Pow(Funcs.vp(prms, a[i]), 2) * Math.Pow(Funcs.mp(prms), 2)) /
                                              (Math.Pow(Funcs.v(prms, a[i]), 2) * Funcs.vpp(prms) -
                                               2.00 * Math.Pow(Funcs.vp(prms, a[i]), 2) * Funcs.v(prms, a[i])));
                                int[]  h1Prtls = { -1, -1, i, -1 };
                                double h1      = GetPNR.Run(type, prms, a, fxTD, rf0, n, nbuckets, prec, h1Prtls, plproc);
                                int[]  h2Prtls = { -1, -1, -1, i };
                                double h2      = GetPNR.Run(type, prms, a, fxTD, rf0, n, nbuckets, prec, h2Prtls, plproc);
                                hess[i, j] = k1 * h1 + k2 * h2 + k3 * stdpnr;
                            }
                        }
                    }
                    else
                    {
                        hess[i, j] = hess[j, i];
                    }
                }
                if (i == fxTD - 1)
                {
                    Trace.Write(" (Done)");
                }
                Trace.WriteLine("");
            }
            Trace.WriteLine("");

            // Return the Hessian matrix.
            return(hess);
        }
コード例 #4
0
ファイル: BldGrad.cs プロジェクト: mshumilov/optimalequity
        public static double Run(string type, double[] prms, double[] a, int fxTD, double rf0, long n, int nbuckets,
                                 int prec, double stdpnr, long npnr, double alpha, int plproc, double[] gradvctr)
        {
            int[]              prtls    = { -1, -1, -1, -1 };
            double[]           k        = new double[fxTD];
            double             maxval   = 0;
            NormalDistribution normdist = new NormalDistribution(0.00, 1.00);

            // Iterate over each time point deriving each gradient entry.
            Trace.WriteLine("");
            Trace.Write("Building gradient ");
            for (int g = 0; g < fxTD; ++g)
            {
                // Construct the constant needed for gradient entries.
                k[g] = Funcs.vp(prms, a[g]) / (2.00 * Funcs.v(prms, a[g])) +
                       Math.Pow(Funcs.mp(prms), 2) / (2.00 * Funcs.vp(prms, a[g]));

                // Populate the gradient vector for this time point.
                prtls[0] = g;
                double grdpnr = GetPNR.Run(type, prms, a, fxTD, rf0, n, nbuckets, prec, prtls, plproc);
                gradvctr[g] = k[g] * (grdpnr - stdpnr);
                Trace.Write(".");

                // Maximum effective absolute value of this gradient vector.
                if (a[g] + gradvctr[g] > 1.00)
                {
                    if (maxval < 1.00 - a[g])
                    {
                        maxval = 1.00 - a[g];
                    }
                }
                else if (a[g] + gradvctr[g] < Funcs.mva(prms) + 0.0001)
                {
                    if (a[g] - (Funcs.mva(prms) + 0.0001) > maxval)
                    {
                        maxval = a[g] - (Funcs.mva(prms) + 0.0001);
                    }
                }
                else if (Math.Abs(gradvctr[g]) > maxval)
                {
                    maxval = Math.Abs(gradvctr[g]);
                }
            }
            Trace.WriteLine(" (Done)");

            // Print the unadjusted gradient vector entries.
            Trace.WriteLine("");
            Trace.Write("Gradient (no adjustment):");
            WrtAry.Run(-1, gradvctr, "Grd", fxTD);

            // If using simulation, test each element for equality with zero. If test holds set the element to zero.
            if (type == "sim" && alpha < 1.00)
            {
                maxval = 0;
                for (int g = 0; g < fxTD; ++g)
                {
                    double cmbpnr = (n * (stdpnr + gradvctr[g] / k[g]) + npnr * stdpnr) / (n + npnr);
                    double ts     = (stdpnr + gradvctr[g] / k[g] - stdpnr) /
                                    Math.Sqrt(cmbpnr * (1.00 - cmbpnr) * (1.00 / n + 1.00 / npnr));
                    double pval = 2.00 * Math.Min(normdist.DistributionFunction(ts),
                                                  1.00 - normdist.DistributionFunction(ts));
                    if (pval > alpha)
                    {
                        gradvctr[g] = 0.00; // The element is not different from zero at significance level alpha.
                    }
                    // Maximum effective absolute value of this gradient vector.
                    if (a[g] + gradvctr[g] > 1.00)
                    {
                        if (maxval < 1.00 - a[g])
                        {
                            maxval = 1.00 - a[g];
                        }
                    }
                    else if (a[g] + gradvctr[g] < Funcs.mva(prms) + 0.0001)
                    {
                        if (a[g] - (Funcs.mva(prms) + 0.0001) > maxval)
                        {
                            maxval = a[g] - (Funcs.mva(prms) + 0.0001);
                        }
                    }
                    else if (Math.Abs(gradvctr[g]) > maxval)
                    {
                        maxval = Math.Abs(gradvctr[g]);
                    }
                }

                // Print the adjusted gradient vector entries.
                Trace.WriteLine("");
                Trace.Write("ADJUSTED gradient:");
                WrtAry.Run(-1, gradvctr, "Adj-Grd", fxTD);
            }

            // Display the maximum effective absolute value of the gradient vector (used to determine convergence).
            Trace.WriteLine("");
            Trace.WriteLine($"Maximum effective absolute value of this gradient: {maxval:F10}");

            // This function returns the maximum absolute value of the gradient elements.
            // (Which is used to define the stopping/convergence criteria.)
            return(maxval);
        }