diff --git a/diag_fun9_well.py b/diag_fun9_well.py new file mode 100644 index 0000000..14b97b0 --- /dev/null +++ b/diag_fun9_well.py @@ -0,0 +1,67 @@ +"""Why the fun9 well is closed only partly (Valeri, 2026-09-21). + +fun9 = exp(x) - 2 - 0.01/x^2 + 2e-6/x^3, brackets [2e-4, 1 / 3 / 9 / 27 / 81], root 0.7032. +Every evaluation is recorded for the library Chandrupatla and for our variants. A run is +split at its first evaluation with f > 0 inside the bracket (the left end is negative, the +right end positive): before it the method is still looking for the root's side of the well, +after it it converges. Our stats give how many steps the decision/contra/regime refused +(pre_refused) and how many the safeguard forced. Nothing in the method changes. + + python -u diag_fun9_well.py counts and phases + python -u diag_fun9_well.py fun9.4 plus the point sequences for one problem +""" +from __future__ import annotations + +import sys + +from simroot.baselines import chandrupatla_lib +from simroot.pure import pure_root +from sweep_full import MAXFEV, XTOL, load_problems + +VARIANTS = ("none", "contra", "defect", "contra+flatanalogmempt", "defect+flatanalogmempt") + + +def recorded(f): + pts = [] + + def g(x): + v = f(x) + pts.append((float(x), float(v))) + return v + return g, pts + + +def phases(pts, a, b): + inner = [i for i, (x, v) in enumerate(pts) if x not in (a, b)] + first_pos = next((i for i in inner if pts[i][1] > 0.0), None) + n = len(pts) + return (first_pos + 1 if first_pos is not None else n), (n - first_pos - 1 if first_pos is not None else 0) + + +def main(show=None): + print(f"{'problem':<8} {'method':<26} {'n':>4} {'to first f>0':>13} {'after':>6} " + f"{'refused':>8} {'forced':>7}") + for s, pid, f, a, b, root in load_problems(): + if not pid.startswith("fun9"): + continue + g, pts = recorded(f) + chandrupatla_lib(g, a, b, XTOL, MAXFEV) + p1, p2 = phases(pts, a, b) + print(f"{pid:<8} {'Chandrupatla':<26} {len(pts):4d} {p1:13d} {p2:6d}") + if pid == show: + print(" Chandrupatla: " + " ".join(f"{x:.4g}:{v:+.2g}" for x, v in pts)) + for pre in VARIANTS: + g, pts = recorded(f) + st = {} + pure_root(g, a, b, XTOL, MAXFEV, similarity="ratio", scale="derived3", + guard="project", prestep=pre, stats=st) + p1, p2 = phases(pts, a, b) + print(f"{'':<8} {pre:<26} {len(pts):4d} {p1:13d} {p2:6d} " + f"{st.get('pre_refused', 0):8d} {st.get('forced', 0):7d}") + if pid == show: + print(f" {pre}: " + " ".join(f"{x:.4g}:{v:+.2g}" for x, v in pts)) + print("diag done") + + +if __name__ == "__main__": + main(*sys.argv[1:2]) diff --git a/diag_near_zero_steps.py b/diag_near_zero_steps.py new file mode 100644 index 0000000..a7962d0 --- /dev/null +++ b/diag_near_zero_steps.py @@ -0,0 +1,67 @@ +"""How common are near-zero steps - the model promising 'the root is right here' at a point +that is not the root, so the next evaluation lands almost on the same x and f barely moves? + +Seen on fun9 (diag_fun9_well.py): 0.0002 -> 0.0002, 6.75 -> 6.734, 1.964 -> 1.941, 20.2502 -> +20.2501. On a steep flank the three-point slope is dominated by a huge value, the scale +sigma = |slope| D is huge, z = |f|/sigma ~ 0 and S(z) ~ 1. + +DIAGNOSTIC thresholds (reporting only, never in the method): a step is near-zero when +|x_new - x| < 1% of the bracket, and wasted when after it |f| kept >= 90% of its value. +Counted on the recorded evaluations of the proposed rule (projection) and of the best +variant, per group. Nothing in the method changes. + + python -u diag_near_zero_steps.py +""" +from __future__ import annotations + +from collections import defaultdict + +from simroot.pure import pure_root +from sweep_full import MAXFEV, XTOL, load_problems + +FLAT = ("fun3", "fun4", "fun5", "fun6") + + +def main(): + for pre in ("none", "defect+flatanalogmempt"): + agg = defaultdict(lambda: [0, 0, 0, 0]) # problems, evals, near-zero, wasted + for s, pid, f, a, b, _root in load_problems(): + pts = [] + + def g(x, f=f): + v = f(x) + pts.append((float(x), float(v))) + return v + pure_root(g, a, b, XTOL, MAXFEV, similarity="ratio", scale="derived3", + guard="project", prestep=pre) + fam = pid.split(".")[0] + grp = "APS" if s == "APS" else ("CHA-flat" if fam in FLAT else f"CHA-{fam}") + lo, hi = min(a, b), max(a, b) + near = wasted = 0 + for (x0, f0), (x1, f1) in zip(pts[2:], pts[3:]): + width = hi - lo + if width > 0 and abs(x1 - x0) < 0.01 * width: + near += 1 + if f0 != 0.0 and abs(f1) >= 0.9 * abs(f0): + wasted += 1 + if f1 != 0.0 and lo < x1 < hi: + # track the bracket as the method does (sign against the left end) + if (f1 > 0) == (f(lo) > 0): + lo = x1 + else: + hi = x1 + ag = agg[grp] + ag[0] += 1 + ag[1] += len(pts) + ag[2] += near + ag[3] += wasted + print(f"\n== {pre}") + print(f" {'group':<9} {'problems':>8} {'evals':>6} {'near-zero':>10} {'wasted':>7}") + for grp in sorted(agg): + n, e, nz, w = agg[grp] + print(f" {grp:<9} {n:8d} {e:6d} {nz:10d} {w:7d}") + print("diag done") + + +if __name__ == "__main__": + main() diff --git a/results_diag_fun9_well.txt b/results_diag_fun9_well.txt new file mode 100644 index 0000000..744068c --- /dev/null +++ b/results_diag_fun9_well.txt @@ -0,0 +1,53 @@ +problem method n to first f>0 after refused forced +fun9.1 Chandrupatla 8 4 4 + none 8 4 4 0 0 + contra 8 4 4 0 0 + defect 8 4 4 0 0 + contra+flatanalogmempt 8 4 4 0 0 + defect+flatanalogmempt 8 4 4 0 0 +fun9.2 Chandrupatla 9 3 6 + none 11 4 7 0 0 + contra 11 4 7 1 0 + defect 11 4 7 2 0 + contra+flatanalogmempt 11 4 7 1 0 + defect+flatanalogmempt 11 4 7 2 0 +fun9.3 Chandrupatla 11 3 8 + none 21 13 8 0 0 + contra 15 4 11 1 0 + defect 14 4 10 2 0 + contra+flatanalogmempt 15 4 11 1 0 + defect+flatanalogmempt 14 4 10 2 0 +fun9.4 Chandrupatla 13 3 10 + none 39 33 6 0 0 + contra 21 4 17 1 0 + defect 17 4 13 2 0 + contra+flatanalogmempt 21 4 17 1 0 + defect+flatanalogmempt 17 4 13 2 0 +fun9.5 Chandrupatla 14 3 11 + none 26 3 23 0 0 + contra 26 3 23 0 0 + defect 26 3 23 1 0 + contra+flatanalogmempt 25 3 22 4 0 + defect+flatanalogmempt 25 3 22 5 0 +diag done + +== none + group problems evals near-zero wasted + APS 154 2634 425 30 + CHA-flat 20 1823 695 188 + CHA-fun1 5 99 44 3 + CHA-fun2 5 141 18 3 + CHA-fun7 5 137 99 12 + CHA-fun8 5 52 15 0 + CHA-fun9 5 105 56 27 + +== defect+flatanalogmempt + group problems evals near-zero wasted + APS 154 2384 438 52 + CHA-flat 20 789 447 147 + CHA-fun1 5 99 19 4 + CHA-fun2 5 144 13 2 + CHA-fun7 5 134 91 41 + CHA-fun8 5 57 16 0 + CHA-fun9 5 75 22 8 +diag done