Files
sim-numerics/stage10_claim_checks.py
vvsandClaude Opus 5 68df1d25db Sign tests by product underflowed: FIXED everywhere (branch, not master - the paper is not yet updated). FA * fnew < 0.0 with FA ~ -1e-200, fnew ~ 1e-150 is -0.0 and '-0.0 < 0.0' is False, so on flat functions (x^19) the bracket update moved the wrong end, the root left the bracket and the run still reported convergence - the proposed rule returned 4.9e-9, bisection 3.5e-9, ITP 1.6e-8 with a claimed 1e-12 bracket; the 1e-6 harness scored them solved. Same defect in our bisection, false-position family, Ridders, Brent, ITP and twelve copies in stage scripts, simroot/sim.py and one test; all replaced by _opposite / _same_sign, pinned by test_sign_underflow.
Impact, every sweep regenerated: the paper's key configurations are unchanged to the evaluation (proposed 2806/3220, credit 3568/3020, Chandrupatla, TOMS 748, Brent, bisection); 46 stated numbers move (ITP CHA 2857 -> 2896, ITP oracle 2611 -> 2647, record CHA figures by 14-18, the grid means by ~1%) and one finding weakens: the measured-order exponent of section 7.1 was cheaper in 29 of 60 far cells and 7 of 7 safeguards; with the fix 12 of 61 and 6 of 7, and on the wide-bracket set it now loses in every safeguard and drops 5 problems.

PREREG G1-G3 outcome on the fixed code: G2 (repeating step pattern, period from the data) PASSES Y1-Y4 and is ACCEPTED by the stop rule - proposed rule APS 2806 -> 2782 (below Chandrupatla's 2787), CHA 3220 -> 3169, both 6026 -> 5951, per problem vs Chandrupatla on APS 95/20/38 p = 8e-7. G1 fails Y1/Y2 (its Y3 miss was the sign bug), G3 fails Y2, A fails Y2. Regression 887 pass; the two red tests are the paper-number check (awaiting the paper update) and test_blend (fixed since, copy in stage16).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 12:49:01 +01:00

160 lines
6.6 KiB
Python

"""Stage 10 - checking the technical claims independently.
Five of them change the content of the paper rather than its wording, so none
is accepted on authority. Each is recomputed here from scratch.
A. The proposed similarity IS the one Table 1's last row calls degenerate,
and the degeneracy is specific to the CHOSEN scale.
B. Proposition 1 is true for the ratio form only, not for similarity rules
in general.
C. The bracket-endpoint divided difference plus the tent similarity is
regula falsi exactly - so the words "most recent" in the definition of
f-hat are load-bearing.
D. Table 1's c = 0 rows report a window artefact; the true behaviour is
sublinear, e_n ~ sigma^2 / (c2 f'(r)^2 |D| n).
E. The order of a Newton step with an O(h^2) derivative estimate is 2, not
the 1.84 the paper claims.
"""
from __future__ import annotations
from simroot.baselines import _opposite, _same_sign # 2026-09-21 fix
import math
r2 = math.sqrt(2.0)
f = lambda x: x * x - 2.0
TENT = lambda z: max(0.0, 1.0 - abs(z))
RATIO = lambda z: 1.0 / (1.0 + abs(z))
RAT_SQ = lambda z: 1.0 / (1.0 + z * z)
EXP_SQ = lambda z: math.exp(-z * z)
COSINE = lambda z: 1.0 / math.sqrt(1.0 + z * z)
def iterate(S, n, sigma=2.0, x0=0.0, xfar=2.0):
x, errs = x0, []
for _ in range(n):
x = x + (1.0 - S(f(x) / sigma)) * (xfar - x)
errs.append(abs(x - r2))
return errs
def main():
print("=" * 76)
print("A. the tent similarity under the CHOSEN scale")
print("=" * 76)
e = iterate(TENT, 200)
print(f" z at x=0 is {f(0.0) / 2.0:+.1f}, so S = {TENT(f(0.0) / 2.0):.1f} "
f"and the step is the whole bracket")
print(f" |e| after 1 step: {e[0]:.4f} after 200: {e[-1]:.4f} "
f"{'CAPTURED at the far end' if abs(e[0] - e[-1]) < 1e-15 else 'moves'}")
print(" => the paper proposes, in section 3, the function its own")
print(" Table 1 calls degenerate. CONFIRMED.")
print("\n but under the DERIVED scale the clip never engages:")
# replay the proposed configuration and record how often |f|/sigma >= 1
from simroot.pure import _slope3
clipped = total = 0
a, b = 0.0, 2.0
fa, fb = f(a), f(b)
A, FA, B = a, fa, b
x, fx, xp, fp = a, fa, b, fb
xp2 = fp2 = None
for _ in range(40):
if abs(B - A) <= 1e-12:
break
far = B if abs(B - x) >= abs(A - x) else A
slope = _slope3(x, fx, xp, fp, xp2, fp2)
sc = abs(slope) * abs(far - x)
if sc == 0.0:
break
z = abs(fx) / sc
total += 1
clipped += z >= 1.0
xn = x + min(1.0, z) * (far - x)
lo, hi = min(A, B), max(A, B)
if not (lo < xn < hi):
xn = 0.5 * (A + B)
fn = f(xn)
xp2, fp2, xp, fp, x, fx = xp, fp, x, fx, xn, fn
if _opposite(FA, fn):
B = xn
else:
A, FA = xn, fn
print(f" steps {total}, of which clipped at z>=1: {clipped}")
print("\n" + "=" * 76)
print("B/C. Proposition 1: which similarity reproduces regula falsi?")
print("=" * 76)
a, b = 1.0, 4.0
fa, fb = f(a), f(b)
falsi = (a * fb - b * fa) / (fb - fa)
print(f" regula falsi point on [1,4]: {falsi:.6f}\n")
print(f" {'similarity':14s}{'scale = |f(x_far)|':>22s}{'match':>8s}")
print(" " + "-" * 44)
for name, S in (("1/(1+|z|)", RATIO), ("exp(-|z|)", lambda z: math.exp(-abs(z))),
("1/(1+z^2)", RAT_SQ), ("1/sqrt(1+z^2)", COSINE),
("tent", TENT)):
pt = a + (1.0 - S(f(a) / abs(fb))) * (b - a)
print(f" {name:14s}{pt:>22.6f}{'YES' if abs(pt - falsi) < 1e-12 else 'no':>8s}")
print("\n => the identity holds for the ratio form only. The paper's")
print(" conclusion 'a similarity rule whose scale is taken from the")
print(" bracket is not a new method' is too broad. CONFIRMED.")
sigma = abs((fb - fa) / (b - a)) * abs(b - a)
pt = a + min(1.0, abs(fa) / sigma) * (b - a)
print(f"\n tent + divided difference over the BRACKET ENDS: {pt!r}")
print(f" equals regula falsi exactly: {pt == falsi}")
print(" => 'most recent' in the definition of f-hat is load-bearing:")
print(" ends give regula falsi, recent iterates give the secant.")
print("\n" + "=" * 76)
print("D. Table 1, the c = 0 rows")
print("=" * 76)
print(f" {'n':>6s}" + "".join(f"{s:>14s}" for s in
("1/(1+z^2)", "exp(-z^2)", "1/sqrt(1+z^2)")))
print(" " + "-" * 48)
seqs = {n: iterate(S, 4001) for n, S in
(("1/(1+z^2)", RAT_SQ), ("exp(-z^2)", EXP_SQ),
("1/sqrt(1+z^2)", COSINE))}
for n in (5, 10, 15, 40, 100, 1000, 4000):
row = "".join(f"{seqs[k][n] / seqs[k][n - 1]:>14.4f}" for k in seqs)
print(f" {n:>6d}{row}")
print("\n the ratio creeps to 1 - there is no rate to measure, and the")
print(" paper's 0.9357 / 0.9444 / 0.9327 are the value at n ~ 10-15.")
print("\n the real law: 1 - S = c2 z^2 + O(z^4) gives")
print(" e_{n+1} = e_n - c2 f'(r)^2 |D| e_n^2 / sigma^2 + ...")
print(" => e_n ~ sigma^2 / (c2 f'(r)^2 |D|) . 1/n")
fp, D, sig = 2.0 * r2, 2.0 - r2, 2.0
# c2 is measured, not transcribed from a Taylor expansion done by hand:
# a verification script carrying my own derived constant would be checking
# my arithmetic against itself.
for name, S in (("1/(1+z^2)", RAT_SQ), ("exp(-z^2)", EXP_SQ),
("1/sqrt(1+z^2)", COSINE)):
h = 1e-4
c2 = (1.0 - S(h)) / (h * h)
pred = sig * sig / (c2 * fp * fp * D)
meas = 4000 * seqs[name][4000]
print(f" {name:14s} predicted n.e_n -> {pred:7.4f} "
f"measured at n=4000: {meas:7.4f}")
print("\n" + "=" * 76)
print("E. the order of the three-point rule")
print("=" * 76)
print(" Newton with a derivative estimate of error O(h^2):")
print(" e' = e - f/f-hat = e - (e - c e^2 + ...)(1 + O(e^2)) = c e^2")
print(" so the order is 2, not the 1.84 of Muller's method, which uses")
print(" the quadratic's ROOT rather than its DERIVATIVE.")
from simroot.pure import pure_root
from stage5_three_point import PROBLEMS, order_of
ps = [order_of(lambda f_, a_, b_, xt, mx: pure_root(
f_, a_, b_, 1e-12, 200, scale="derived3", guard="project"),
fn, aa, bb, rr) for _n, fn, aa, bb, rr in PROBLEMS]
ps = [p for p in ps if math.isfinite(p) and 0.5 < p < 5.0]
print(f" measured median over the eight problems: "
f"{sorted(ps)[len(ps) // 2]:.3f} predicted: 2")
if __name__ == "__main__":
main()