Files
sim-numerics/stage10_claim_checks.py
T

158 lines
6.5 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
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 FA * fn < 0.0:
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()