//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
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