"""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, on the worst case, and on stability, with the same number of constants (none) and the same 193 solved.""" 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 cha_w <= ours_w, (cha_w, ours_w)