Files
sim-numerics/diag_who_caps_us.py
T

71 lines
2.9 KiB
Python

"""On wide brackets we spend what bisection spends. WHO caps us — the step rule or the guard?
Measured (diag_fun1_endgame.py): on the same function our cost grows with the bracket width
(7, 12, 19, 59, 63 against Chandrupatla's 8, 12, 15, 24, 44) while our observed ORDER is
higher than theirs, so the endgame is not the problem. 59 evaluations on a 2e4-wide bracket
is what bisection needs (log2(2e16) ~ 54), i.e. we degrade to one halving per evaluation.
Two candidate causes, and they are distinguishable by a run:
* the STEP RULE cannot march (the similarity step is an absolute length), or
* the GUARANTEE's projection clamps the accepted step to a halving-sized move.
So the same runs are repeated with each guard, including guard="none" (no guarantee at all).
If "none" marches, the guard is the cap; if it also crawls, the step rule is.
python -u diag_who_caps_us.py
"""
from __future__ import annotations
import math
from scipy.optimize._tstutils import _CHANDRUPATLA_TESTS_DICTS
from simroot.baselines import bisection, chandrupatla, chandrupatla_scipy
from simroot.pure import pure_root
from sweep_full import ACC, MAXFEV, XTOL
GUARDS = ("none", "step", "project", "safe", "credit")
def main():
print(f"{'problem':<9} {'width':>10} {'bisect':>7} {'CH':>5} " +
" ".join(f"{g:>8}" for g in GUARDS))
for d in _CHANDRUPATLA_TESTS_DICTS:
pid = str(d["ID"])
if not (pid.startswith("fun1.") or pid in ("fun3.1", "fun5.1", "fun4.3")):
continue
f = d["f"]
a, b = (float(v) for v in d["bracket"])
root = float(d["root"])
ok = lambda x, c: bool(c and math.isfinite(float(x)) # noqa: E731
and (abs(float(x) - root) <= ACC or f(float(x)) == 0.0))
_xb, nb, cb = bisection(f, a, b, XTOL, MAXFEV)
best = None
for impl in (chandrupatla, chandrupatla_scipy):
try:
xc, nc, cc = impl(f, a, b, XTOL, MAXFEV)
except Exception:
continue
if ok(xc, cc) and (best is None or nc < best):
best = int(nc)
cells = []
for g in GUARDS:
try:
x, n, c = pure_root(f, a, b, XTOL, MAXFEV, similarity="ratio",
scale="derived3", guard=g, prestep="slope",
direction="dissimilar")
cells.append(f"{n:8d}" if ok(x, c) else f"{n:7d}✗")
except Exception:
cells.append(f"{'err':>8}")
print(f"{pid:<9} {abs(b - a):10.3g} {nb:7d} {best if best else 0:5d} " +
" ".join(cells))
print("\nlegend: ✗ = the run did not reach the root (an unguarded method may not)")
print("if guard='none' marches where 'safe'/'project' crawl, the GUARANTEE is the cap;")
print("if 'none' crawls too, the STEP RULE is the cap.")
if __name__ == "__main__":
main()