Пример #1
0
        //smoothed.area.estimate
        internal static double SmoothedAreaEstimate(Func <double, SGNFnAParam, double> area, double x, SGNFnAParam A, double mathLowerLimit, double[] refRange, bool inestimableLowerLimit, bool remoteRight = false, int hpMult = 1, int maxCount = 10000)
        {
            double expectedSlope = Tools.NA; // non défini
            double fX            = A.F(x, A);

            double[] a = Tools.Rep(Tools.NA, 3);
            // # values around x
            double d   = 1e-4;
            double tmp = (refRange[1] - refRange[0]) / 1000.0;

            if (tmp < d)
            {
                d = tmp;
            }

            double[] xStar = Tools.Combine(x - d, x, x + d);

            if (xStar[0] < mathLowerLimit)
            {
                //xStar[0] = inestimableLowerLimit ? ((x + mathLowerLimit) / 2.0) : mathLowerLimit;
                if (inestimableLowerLimit)
                {
                    xStar[0] = (x + mathLowerLimit) / 2.0;
                }
                else
                {
                    xStar[0] = mathLowerLimit;
                }
            }

            for (int i = 0; i < 3; i++)
            {
                a[i] = area(xStar[i], A);
            }

            double aX = a[1];

            double[] observedSlopes = a.Diff().Divide(xStar.Diff()).Multiply(hpMult);
            expectedSlope = fX;
            double[] observedSlopeAngles = Tools.Combine(Math.Atan(observedSlopes[0]), Math.Atan(observedSlopes[1]));
            double   expectedSlopeAngle  = Math.Atan(expectedSlope);
            double   angleDiff           = Math.Abs(expectedSlopeAngle - observedSlopeAngles[0]);

            tmp = Math.Abs(expectedSlopeAngle - observedSlopeAngles[1]);
            if (tmp > angleDiff)
            {
                angleDiff = tmp;
            }

            bool estimateOk = (angleDiff < 0.035) && observedSlopes.All(z => z > 0);
            //# 0.035 radians is approximately 2 degrees
            bool    cntn  = !estimateOk;
            SAEList found = new SAEList();

            if (cntn)
            {
                /*
                 * # We look for a point on the left side of x and one on its right side
                 * # where the integral [area] seems to be correctly estimated
                 * # (with positive slopes of value close to expected value [density])
                 * # If we are estimating the area on the remote right end of the distrn (remote.right=T)
                 * # then we will be happy if an estimate to the left of x is obtained
                 *
                 */
                int side = -1;
                //# Store starting points
                double[] tmpX = xStar.Copy(); //?
                double[] tmpA = a.Copy();     //?
                while (cntn)
                {
                    side = side + 1;    // # side = 0 -> Left  of x
                                        // # side = 1 -> Right of x
                    int direction = side == 0 ? -1 : 1;
                    if (side == 1)
                    {
                        xStar = tmpX.Copy(); //?
                        a     = tmpA.Copy();
                    }

                    int    j        = side == 0 ? 0 : 2;
                    double z        = xStar[j];
                    int    count    = 0;
                    bool   onBorder = false;
                    double aZ       = Tools.NA;
                    while (cntn)
                    {
                        z = z + direction * d;
                        //# make sure that we don't step to the left of mathematical lower limit
                        if (z <= mathLowerLimit)
                        {
                            if (inestimableLowerLimit)
                            {
                                z = (mathLowerLimit + z + d) / 2.0;
                            }
                            else
                            {
                                z        = mathLowerLimit;
                                onBorder = true;
                            }
                        }

                        if ((side == 0) && z == mathLowerLimit)
                        {
                            //# This is still possible even with the above 'protection' against
                            //# this scenario, due to numerical imprecision
                            aZ         = 0;
                            cntn       = false;
                            estimateOk = true;
                        }
                        else
                        {
                            aZ = area(z, A);
                            if (side == 0)
                            {
                                xStar = Tools.Combine(z, xStar[0], xStar[1]); //[-3]
                                a     = Tools.Combine(aZ, a[0], a[1]);
                            }
                            else
                            {
                                xStar = Tools.Combine(xStar[1], xStar[1], z); //[-1]
                                a     = Tools.Combine(a[1], a[2], aZ);
                            }

                            //a.Diff().Divide(xStar.Diff()).Multiply(hpMult);
                            observedSlopes      = a.Diff().Divide(xStar.Diff()).Multiply(hpMult);
                            expectedSlope       = A.F(z, A);
                            observedSlopeAngles = Tools.Combine(Math.Atan(observedSlopes[0]), Math.Atan(observedSlopes[1]));
                            expectedSlopeAngle  = Math.Atan(expectedSlope);
                            angleDiff           = Math.Abs(expectedSlopeAngle - observedSlopeAngles[0]);
                            tmp = Math.Abs(expectedSlopeAngle - observedSlopeAngles[1]);
                            if (tmp > angleDiff)
                            {
                                angleDiff = tmp;
                            }
                            estimateOk = ((angleDiff < 0.035) && observedSlopes.All(w => w > 0)) || (angleDiff < 1E-6);
                            //# 0.035 radians is approximately 2 degrees
                            cntn = !estimateOk && (!onBorder);
                        }
                        if (cntn)
                        {
                            if (remoteRight && (side == 0))
                            {
                                cntn = (expectedSlope / fX) < 10000;
                            }
                            else
                            {
                                count = count + 1;
                                cntn  = count <= maxCount;
                            }
                        }
                    }

                    if ((side == 0) && remoteRight)
                    {
                        cntn = !estimateOk;
                        if (estimateOk)
                        {
                            aX = aZ;
                        }
                    }
                    else
                    {
                        cntn = side == 0;
                    }

                    //# Store results
                    found.Ok[side] = estimateOk;
                    found.X[side]  = z;
                    found.A[side]  = aZ;
                    found.F[side]  = expectedSlope;
                } //# end of while-continue

                if (!remoteRight || !found.Ok[0])
                {
                    if (found.Ok[0] && found.Ok[1])
                    {
                        double fDiff       = found.F.Diff()[0];
                        bool   extrapolate = fDiff == 0;
                        if (!extrapolate)
                        {
                            //- (diff(found$a) - diff(found$f*found$x)) / f.diff
                            double t1         = (found.A[1] - found.A[0]);                             //diff(found$a)
                            double t2         = (found.F[1] * found.X[1]) - (found.F[0] * found.X[0]); //diff(found$f*found$x)
                            double xIntersect = -(t1 - t2) / fDiff;
                            extrapolate = (xIntersect < found.X[0]) || (xIntersect > found.X[1]);
                            if (!extrapolate)
                            {
                                int j = x <= xIntersect ? 0 : 1;
                                aX = found.A[j] + found.F[j] * (x - found.X[j]);
                            }
                        }

                        if (extrapolate)
                        {
                            aX = found.A[0] + Diff(found.A)[0] / Diff(found.X)[0] * (x - found.X[0]);
                        }
                    }
                    else if (found.Ok[0] || found.Ok[1])
                    {
                        int j = found.Ok[0] ? 0 : 1;
                        aX = found.A[j] + found.F[j] * (x - found.X[j]);
                    }
                    else
                    {
                        //TODO Exception
                        throw new WEException("Could not get a smoothed cumulative density estimate.");
                    }
                }
            }
            return(aX);
        } // end of smoothed.area.estimate
Пример #2
0
        internal static double PingpongTieBreaker(
            Func <double, SGNFnAParam, double> area,
            SGNFnAParam A,
            double start,
            double areaX,
            double target,
            double mathLowerLimit,
            double[] refRange,
            bool inestimableLowerLimit,
            double epsilon,
            bool distrnLeftSide = false,
            int maxCount        = 100)
        {
            int    hpMult   = distrnLeftSide ? -1 : 1;
            bool   cntn     = true;
            int    count    = 0;
            double x        = start;
            Visits visitedX = new Visits(maxCount);

            visitedX.Add(x);
            bool converged    = false;
            bool caughtInLoop = false;

            while (cntn)
            {
                double hp     = A.F(x, A) * hpMult;
                double change = (target - areaX) / hp;
                x = x + change;
                // new_0.11
                if (x < refRange[0])
                {
                    x = (refRange[0] == mathLowerLimit) && inestimableLowerLimit ? (x - change + mathLowerLimit) / 2.0 : refRange[0];
                }

                areaX = WebExpoFunctions3.SmoothedAreaEstimate(area, x, A, mathLowerLimit, refRange, inestimableLowerLimit, hpMult: hpMult);
                count++;
                converged = Math.Abs(areaX - target) < epsilon;
                visitedX.Add(x);
                caughtInLoop = visitedX.CaughtInLoop; // La propiété CaughtInLoop est mise à jour à chaque ajout d'un x.
                                                      // x doit être fini, et avoir déjà été visité.
                cntn = !converged && (visitedX.Count <= maxCount) && !caughtInLoop;
            }

            if (!converged && caughtInLoop)
            {
                //  We have found the series of points that are repeatedly visited:
                // recalibrate and try Newton-Raphson again
                double[] tail = visitedX.GetFromTail(x);

                x     = (distrnLeftSide) ? x = tail.Max() : tail.Min();
                areaX = WebExpoFunctions3.SmoothedAreaEstimate(area, x, A, mathLowerLimit, refRange, inestimableLowerLimit, hpMult: hpMult);

                if (distrnLeftSide)
                {
                    A.UpperLimit = x;
                }
                else
                {
                    A.LowerLimit = x;
                }

                target = target - areaX;
                areaX  = 0.0;
                count  = 0;
                cntn   = true;
                while (cntn)
                {
                    double hp     = A.F(x, A) * hpMult;
                    double change = (target - areaX) / hp;
                    x         = x + change;
                    areaX     = area(x, A);
                    count     = count + 1;
                    converged = Math.Abs(areaX - target) < epsilon;
                    cntn      = !converged && (count <= maxCount);
                }
            }

            if (!converged)
            {
                //TODO Exception
                throw new WEException("Newton-Raphson algorithm did not converge. Sorry.\n");
            }

            return(x);
        } // end of ping.pong.tie.breaker