(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>
91 lines
3.8 KiB
Python
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)
|