Why the fun9 well is closed only partly (diagnostics only, method unchanged). The well itself IS closed: reaching the root's side takes 4 evaluations with contra/defect against Chandrupatla's 3 (13 and 33 before). The remaining +20 (75 vs 55) is after it: (1) the exponential flank on the widest brackets - our step moves ~1 per evaluation down exp(x) (f/f' = 1), 10 evaluations where Chandrupatla halves 3 times; the analogy entry comes late because exp is self-similar by TRANSLATION, not scaling - the pair's x-scale does not shrink, so m = ln(f ratio)/ln(x ratio) is undefined or negative; (2) overconfident near-zero steps - on a steep flank the three-point slope is huge, z ~ 0, S ~ 1, the model promises the root at a point that is not it: 8 wasted evaluations on fun9 (27 without the decisions).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -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])
|
||||
@@ -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()
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user