83 lines
3.4 KiB
Python
83 lines
3.4 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, 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)
|