Files
sim-numerics/stage0e_guard_cost.py

58 lines
2.0 KiB
Python

"""Stage 0e - how much is the parameter-free safeguard costing?
derived.ratio is, algebraically, the Newton/secant step:
S = 1 - |z|, z = |f| / (|f'_secant| * (far - x))
step = (1 - S)*(far - x) = |f| / |f'_secant|
so it should be superlinear (order ~1.618) and land near Brent. It measured
150 evaluations over the eight problems against Brent's 82. The gap has to
come from somewhere, and the only extra machinery it carries is the
safeguard: a secant step often shrinks the ERROR fast while barely moving the
proven BRACKET, so the "did the bracket halve" test fires and buys a bisection
step that the search did not need.
This measures the price directly: the same method with the safeguard on and
off, side by side.
"""
from __future__ import annotations
import math
from simroot.baselines import bisection, brent
from stage0d_derived_similarity import FAST_SIMS, PROBLEMS, derived_step
XTOL = 1e-12
MAXFEV = 200
def main():
methods = [("bisection", lambda f, a, b: bisection(f, a, b, XTOL, MAXFEV)),
("brent [reference]", lambda f, a, b: brent(f, a, b, XTOL, MAXFEV))]
for s in FAST_SIMS:
for gu in (True, False):
methods.append((
f"derived.{s}" + (".guard" if gu else ".raw"),
(lambda ss, g: lambda f, a, b: derived_step(
f, a, b, XTOL, MAXFEV, sim=ss, guard=g))(s, gu)))
width = max(len(n) for n, _ in methods) + 1
header = (f"{'method':{width}s}" +
"".join(f"{p[0][:13]:>15s}" for p in PROBLEMS) +
f"{'TOTAL':>8s}{'FAIL':>6s}")
print(header)
print("-" * len(header))
for name, fn in methods:
cells, total, fails = [], 0, 0
for _, f, a, b, root in PROBLEMS:
x, n, conv = fn(f, a, b)
ok = conv and abs(x - root) <= 1e-8
cells.append(f"{n:>15d}" if ok else f"{'FAIL(' + str(n) + ')':>15s}")
total += n
fails += 0 if ok else 1
print(f"{name:{width}s}" + "".join(cells) + f"{total:>8d}{fails:>6d}")
if __name__ == "__main__":
main()