Files
sim-numerics/tests/test_reusability.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

91 lines
3.8 KiB
Python

"""The credit safeguard applied to step rules it was not designed for.
This is the finding that decides what the work is about. As a
one-dimensional root finder the similarity method is subsumed by
Chandrupatla 1997 on every axis measured. The safeguard is not: it is
independent of the similarity framing and it converts unsafe interpolating
rules into safe ones without a constant and without a cost.
"""
from __future__ import annotations
import math
import os
import sys
import pytest
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from simroot.baselines import chandrupatla
from stage14_is_anything_reusable import RULES, load, measure
PROBS = load()
@pytest.mark.parametrize("name,rule", RULES, ids=[n for n, _ in RULES])
def test_the_guard_makes_an_unsafe_rule_solve_everything(name, rule):
"""Every rule, guarded, must reach the 193 of 199 that is the most any
method here achieves - including bisection and Chandrupatla."""
_t_raw, s_raw, _w_raw = measure(rule, False, PROBS)
_t_g, s_g, _w_g = measure(rule, True, PROBS)
assert s_g == 193, (name, s_g)
assert s_g > s_raw, (name, s_raw, s_g)
@pytest.mark.parametrize("name,rule", RULES, ids=[n for n, _ in RULES])
def test_the_guard_is_free_or_better(name, rule):
"""Safety normally costs evaluations. Here it saves them, because the
failures it prevents were being charged the full budget."""
t_raw, _s, _w = measure(rule, False, PROBS)
t_g, _s2, _w2 = measure(rule, True, PROBS)
assert t_g < t_raw, (name, t_raw, t_g)
@pytest.mark.parametrize("name,rule", RULES, ids=[n for n, _ in RULES])
def test_the_guarded_worst_case_is_at_bisection_level(name, rule):
"""The point of a safeguard. Bisection's worst problem here costs 82
evaluations; every guarded rule must be in that neighbourhood, where the
raw rules run to 135, 178 and the 200-evaluation cap."""
_t, _s, w_g = measure(rule, True, PROBS)
assert w_g <= 90, (name, w_g)
def test_the_similarity_method_is_subsumed_by_chandrupatla():
"""Stated as a test so the paper cannot drift back to claiming otherwise.
Chandrupatla is better on total, with the same number of constants (none)
and the same 193 solved.
CORRECTED 2026-09-21. This test used to assert that he is also better on
the worst single problem. That held only while our implementation of his
method stopped on a bracket twice as wide as every other method's; with
the tolerance corrected (it now matches scipy's elementwise.find_root on
all 199 problems) his worst problem costs 84 evaluations against the
credit rule's 83. The worst cases are level, to one evaluation, and the
test now says exactly that."""
from simroot.pure import pure_root
XTOL, MAXFEV, ACC = 1e-12, 200, 1e-6
ours_t = ours_w = ours_s = 0
cha_t = cha_w = cha_s = 0
for f, a, b, root in PROBS:
for meth, which in ((lambda: pure_root(f, a, b, XTOL, MAXFEV,
similarity="ratio",
scale="derived3",
guard="credit"), "o"),
(lambda: chandrupatla(f, a, b, XTOL, MAXFEV), "c")):
try:
x, n, conv = meth()
except Exception:
x, n, conv = float("nan"), MAXFEV, False
ok = bool(conv and math.isfinite(x) and abs(x - root) <= ACC)
if which == "o":
ours_t += n if ok else MAXFEV
ours_s += ok
ours_w = max(ours_w, n) if ok else ours_w
else:
cha_t += n if ok else MAXFEV
cha_s += ok
cha_w = max(cha_w, n) if ok else cha_w
assert cha_s == ours_s == 193
assert cha_t < ours_t, (cha_t, ours_t)
assert abs(cha_w - ours_w) <= 1, (cha_w, ours_w)