Files
sim-numerics/stage18_bulletproof.py
T
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

231 lines
9.1 KiB
Python

"""Stage 18 - attack the two claims that look like wins.
A claim is worth publishing only if it survives the objection a reader will
raise first. For each of the two candidate wins the objection is obvious, and
this file runs it rather than answering it in prose.
CLAIM A. The credit safeguard rescues unsafe interpolation rules: regula
falsi goes from 133 solved and 18799 evaluations to 193 and 8056.
OBJECTION: nobody uses raw regula falsi. It has been fixed since 1971 -
Illinois, Pegasus, Anderson-Bjorck - and those fixes are the real
comparison. Beating the unfixed version proves nothing.
TEST: the same rule, the same driver, the same everything, with the
stale-endpoint correction swapped for each classical fix in turn and for
the credit projection. Only the repair differs.
CLAIM B. In active noise control the measured step size beats Kwong-Johnston
by 7 dB.
OBJECTION: Kwong-Johnston's gains are scale-dependent, and yours are the
published defaults run on a signal they were not scaled for. Give it its
best constants and the gap will close.
TEST: an oracle over its (alpha, gamma) grid, chosen separately for every
scenario - not an achievable controller, exactly as the ITP oracle in the
root-finding work was not.
"""
from __future__ import annotations
from simroot.baselines import _opposite, _same_sign # 2026-09-21 fix
import math
import numpy as np
from scipy.optimize._tstutils import (_APS_TESTS_DICTS,
_CHANDRUPATLA_TESTS_DICTS)
from anc.bench import SCENARIOS, make_primary_path, make_secondary_path, run_nlms
from anc.rules import Fixed, KwongJohnston, Measured, Rule
from simroot.baselines import Counter, _Budget
from simroot.pure import _Safeguard
XTOL, MAXFEV, ACC = 1e-12, 200, 1e-6
# ==========================================================================
# CLAIM A: the credit projection against the classical repairs of regula falsi
# ==========================================================================
def falsi_driver(f, a, b, xtol, maxfev, repair):
"""Regula falsi with a pluggable repair for the stale endpoint.
Everything is shared: the interpolation, the bracket update, the budget,
the duplicate handling. `repair` is the only difference, so a difference
in the result cannot come from anywhere else.
"none" the unfixed rule
"illinois" halve the stale ordinate (Hafner/Dowell 1971)
"pegasus" f * fx / (fx + f_new) (Dowell/Jarratt 1972)
"anderson" f * (1 - f_new/f_moved) (Anderson/Bjorck 1973)
"credit" leave the ordinates alone; project the step onto the
interval the earned bisection budget allows
"""
g = Counter(f, maxfev)
x = 0.5 * (a + b)
try:
fa, fb = g(a), g(b)
if fa == 0.0:
return a, g.n, True
if fb == 0.0:
return b, g.n, True
if _same_sign(fa, fb):
raise ValueError("no sign change")
sg = _Safeguard("credit", abs(b - a), xtol)
while abs(b - a) > xtol:
lo, hi = (a, b) if a < b else (b, a)
denom = fb - fa
x_new = 0.5 * (a + b) if denom == 0.0 else (a * fb - b * fa) / denom
if repair == "credit":
x_new = sg.project_credit(x_new, lo, hi)
if not math.isfinite(x_new) or not (lo < x_new < hi):
x_new = 0.5 * (a + b)
if x_new in (a, b):
x_new = 0.5 * (a + b)
if x_new in (a, b):
return x_new, g.n, abs(b - a) <= xtol
w_before = abs(b - a)
fx = g(x_new)
if fx == 0.0:
return x_new, g.n, True
if _opposite(fa, fx):
if repair == "illinois":
fa *= 0.5
elif repair == "pegasus":
fa = fa * fb / (fb + fx)
elif repair == "anderson":
m = 1.0 - fx / fb
fa = fa * (m if m > 0.0 else 0.5)
b, fb = x_new, fx
else:
if repair == "illinois":
fb *= 0.5
elif repair == "pegasus":
fb = fb * fa / (fa + fx)
elif repair == "anderson":
m = 1.0 - fx / fa
fb = fb * (m if m > 0.0 else 0.5)
a, fa = x_new, fx
if repair == "credit":
sg.note_shrink(w_before, abs(b - a))
x = x_new
return 0.5 * (a + b), g.n, True
except _Budget:
return x, g.n, False
def load_roots():
out = []
for d in _APS_TESTS_DICTS:
a, b = d["bracket"]
args = d.get("args", ())
out.append(((lambda ff, aa: lambda t: ff(t, *aa))(d["f"], args),
float(a), float(b), float(d["root"])))
for d in _CHANDRUPATLA_TESTS_DICTS:
a, b = d["bracket"]
out.append(((lambda ff: lambda t: ff(t))(d["f"]),
float(a), float(b), float(d["root"])))
return out
def claim_a():
probs = load_roots()
print("=" * 84)
print("CLAIM A - the credit projection against the classical repairs")
print("=" * 84)
print("same interpolation, same driver, same budget; only the repair "
"differs\n")
print(f"{'repair':16s}{'year':>7s}{'evals':>10s}{'solved':>10s}"
f"{'worst':>8s}{'over bisect+3':>15s}")
print("-" * 84)
rows = {}
for repair, year in (("none", "300 BC"), ("illinois", "1971"),
("pegasus", "1972"), ("anderson", "1973"),
("credit", "here")):
total = solved = worst = over = 0
for f, a, b, root in probs:
try:
x, n, conv = falsi_driver(f, a, b, XTOL, MAXFEV, repair)
except Exception:
x, n, conv = float("nan"), MAXFEV, False
ok = bool(conv and math.isfinite(x) and abs(x - root) <= ACC)
total += n if ok else MAXFEV
solved += ok
if ok:
worst = max(worst, n)
if n > math.ceil(math.log2(abs(b - a) / XTOL)) + 4:
over += 1
rows[repair] = (total, solved, worst, over)
print(f"{repair:16s}{year:>7s}{total:>10d}{solved:>7d}/199"
f"{worst:>8d}{over:>15d}", flush=True)
best_classical = min(rows[r][0] for r in ("illinois", "pegasus",
"anderson"))
c = rows["credit"][0]
print(f"\n best classical repair {best_classical}, credit {c} "
f"=> {'SURVIVES' if c < best_classical else 'FAILS'} the objection")
return rows
# ==========================================================================
# CLAIM B: the measured step size against a Kwong-Johnston oracle
# ==========================================================================
class KJTuned(KwongJohnston):
def __init__(self, a, g):
self.A, self.G = a, g
self.name = f"KJ a={a:g} g={g:g}"
self.constants = 4
N, SEEDS, SNRS = 8000, 6, (30.0, 15.0)
KJ_GRID = [(a, g) for a in (0.9, 0.97, 0.99)
for g in (2e-4, 2e-3, 2e-2, 2e-1)]
def anc_score(rule, scen_gen, snr, seeds=SEEDS):
vals = []
for s in range(seeds):
rng = np.random.default_rng(1000 + s)
noise = scen_gen(N, rng)
nr = run_nlms(noise, make_primary_path(rng), make_secondary_path(rng),
rule, snr_db=snr, seed=s)
if math.isfinite(nr):
vals.append(nr)
return (sum(vals) / len(vals)) if vals else float("nan")
def claim_b():
print("\n" + "=" * 84)
print("CLAIM B - the measured step size against a Kwong-Johnston ORACLE")
print("=" * 84)
print(f"the oracle is given the best of {len(KJ_GRID)} (alpha, gamma) "
f"pairs for every scenario\nand noise level separately - not an "
f"achievable controller\n")
print(f"{'scenario':22s}{'SNR':>6s}{'KJ default':>12s}{'KJ oracle':>12s}"
f"{'measured':>11s}{'fixed 0.5':>11s}")
print("-" * 84)
tot = {"kj": 0.0, "orc": 0.0, "meas": 0.0, "fix": 0.0}
cells = 0
for sname, gen in SCENARIOS:
for snr in SNRS:
kj = anc_score(KwongJohnston(), gen, snr)
orc = max(anc_score(KJTuned(a, g), gen, snr) for a, g in KJ_GRID)
meas = anc_score(Measured(), gen, snr)
fix = anc_score(Fixed(0.5), gen, snr)
tot["kj"] += kj
tot["orc"] += orc
tot["meas"] += meas
tot["fix"] += fix
cells += 1
print(f"{sname:22s}{snr:>6.0f}{kj:>12.2f}{orc:>12.2f}"
f"{meas:>11.2f}{fix:>11.2f}", flush=True)
print("-" * 84)
print(f"{'mean':22s}{'':6s}{tot['kj'] / cells:>12.2f}"
f"{tot['orc'] / cells:>12.2f}{tot['meas'] / cells:>11.2f}"
f"{tot['fix'] / cells:>11.2f}")
survives = tot["meas"] > tot["orc"]
print(f"\n measured {tot['meas'] / cells:.2f} dB against the oracle "
f"{tot['orc'] / cells:.2f} dB => "
f"{'SURVIVES' if survives else 'FAILS'} the objection")
return survives
if __name__ == "__main__":
claim_a()
claim_b()