Files
sim-numerics/diag_who_caps_us.py
T
vvsandClaude Opus 5 1b6c626f67 Valeri's decisions (1) competitors from the library, (2) variant a. (1) Chandrupatla is scipy.optimize.elementwise.find_root, Brent is brentq, no more per-problem best_of. Our Chandrupatla stopped on a bracket twice as wide as every other method (the factor the protocol corrects for ITP); fixed (+0.5*xtol) in baselines.py and in the two copies found by grep (stage16_blend, stage19_modern) and the traced mirror, and pinned: ours == library on every problem. Regenerating sweep_full.json changed exactly three rows - chandrupatla APS 2785->2787, CHA 2423->2441, brent CHA 4099->4115 - and nothing else (diff_sweeps.py: 0 unexpected).
(2) The proposed method is again the scheduled projection (2806/3220/6026), its budget multiplier DECLARED as the one free constant: the canonical total is the same for every multiplier 1.25-4, the wide-bracket one is not. The earned projection is the constant-free variant (3568/3020/6588). What the old tables hid: problem by problem the proposed rule beats Chandrupatla on the canonical set 87/24/42, exact sign test p = 9e-5, while trailing him by 0.7% in total; on the wide-bracket set he is genuinely stronger (26 to 9, p = 0.006). Gap decomposition recomputed with the library: 244 (was 262), 160 of it on the flat families (two thirds), fun1 58 (a quarter, not a third as the abstract said).

Also corrected on the way: the 'subsumed by Chandrupatla' pin claimed he is better on the worst single problem - true only with the looser tolerance (84 against the credit rule's 83 once fixed); §8.3 still called zero slack degenerate, which Table 1a refutes; §5.2's Brent figure is our pure-Python implementation and now says so. verify_article.py 286/286, 802 tests pass twice.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 10:53:00 +01:00

72 lines
3.0 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 CHANDRUPATLA_BASELINE
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_BASELINE,): # the library alone (2026-09-21)
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()