89 lines
3.6 KiB
Python
89 lines
3.6 KiB
Python
"""Stage 6 - the projection safeguard: guarantee AND order, or neither?
|
|
|
|
The paper's weak point: the configuration that tops the Alefeld-Potra-Shi
|
|
ranking carries no worst-case bound, and the best configuration that does is
|
|
14th, 18% behind Brent. Comparing an unguaranteed method's average against
|
|
guaranteed ones is not a comparison a reader will accept.
|
|
|
|
The three earlier safeguards all REPLACE the computed step with a midpoint,
|
|
which throws the computation away and, with the three-point scale, collapses
|
|
the order from 1.94 to 1.00. The projection guard instead keeps as much of
|
|
the step as the bisection budget allows:
|
|
|
|
r_j = 2^(n_half - j) * (xtol/2) - (b-a)/2
|
|
x = x_sim if |x_sim - x_mid| <= r_j
|
|
= x_mid + sign(d) * r_j otherwise
|
|
|
|
Because the bracket after a step is at most (b-a)/2 + r_j = 2^(n_half-j)*eps,
|
|
induction bounds the run by n_half steps: the worst case is EXACTLY bisection.
|
|
No constant is introduced - ITP's kappa1, kappa2 and n0 all belong to its
|
|
truncation stage, which repairs regula falsi and which the similarity step
|
|
does not need.
|
|
|
|
What must be true for this to be worth putting in the paper:
|
|
(1) zero runs exceed the bisection bound, on all 199 problems;
|
|
(2) the order stays near 1.94, not 1.00;
|
|
(3) the total is close to the unguarded configuration's.
|
|
Each is checked below. This is the SMOKE run.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import math
|
|
|
|
from simroot.baselines import bisection, brent, itp, toms748
|
|
from simroot.pure import GUARDS, pure_root
|
|
from stage5_three_point import PROBLEMS, order_of
|
|
|
|
XTOL = 1e-12
|
|
MAXFEV = 200
|
|
|
|
|
|
def main():
|
|
methods = [
|
|
("bisection", lambda f, a, b: bisection(f, a, b, XTOL, MAXFEV)),
|
|
("brent", lambda f, a, b: brent(f, a, b, XTOL, MAXFEV)),
|
|
("toms748", lambda f, a, b: toms748(f, a, b, XTOL, MAXFEV)),
|
|
("itp (3 constants)", lambda f, a, b: itp(f, a, b, XTOL, MAXFEV)),
|
|
]
|
|
for gu in GUARDS:
|
|
methods.append((f"OURS 3pt .{gu}",
|
|
(lambda g: lambda f, a, b: pure_root(
|
|
f, a, b, XTOL, MAXFEV, scale="derived3",
|
|
guard=g))(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}{'>bisect bound':>15s}")
|
|
print(header)
|
|
print("-" * len(header))
|
|
for name, fn in methods:
|
|
cells, total, fails, over = [], 0, 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
|
|
# the bisection bound itself, not twice it: 2 endpoint evaluations
|
|
# plus ceil(log2(w/xtol)) steps
|
|
if ok and n > math.ceil(math.log2(abs(b - a) / XTOL)) + 2:
|
|
over += 1
|
|
print(f"{name:{width}s}" + "".join(cells) +
|
|
f"{total:>8d}{fails:>6d}{over:>15d}")
|
|
|
|
print("\nobserved order (median over the eight problems)")
|
|
print(f"{'method':22s}{'order':>9s}")
|
|
print("-" * 33)
|
|
for gu in GUARDS:
|
|
ps = [order_of((lambda g: lambda f_, a_, b_, xt, mx: pure_root(
|
|
f_, a_, b_, XTOL, MAXFEV, scale="derived3", guard=g))(gu),
|
|
f, a, b, root) for _, f, a, b, root in PROBLEMS]
|
|
ps = [p for p in ps if math.isfinite(p) and 0.5 < p < 5.0]
|
|
med = sorted(ps)[len(ps) // 2] if ps else float("nan")
|
|
print(f"{'OURS 3pt .' + gu:22s}{med:>9.3f}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|