Files
sim-numerics/measure_budget_factor.py
vvsandClaude Opus 5 862feaff86 Revision 1, step 1: one scoring criterion, the correct order, and the internal inconsistencies.
Point 1. The base step's order is Muller's 1.839, not 2: the derivative of the parabola through
the last three points errs by f'''/6 (x_n - x_{n-1})(x_n - x_{n-2}) = O(e_{n-1} e_{n-2}).
order_high_precision.py (3000 digits, to 1e-2900): 1.840 on cos x - x, e^x - 2, x^3 - x - 2,
sin x - 1/2; 2.000 only on x^2 - 2; two-point 1.618. The double-precision medians (2.000, 1.94)
are removed from the text.

Point 3. Every protocol number is re-scored under the criterion of section 9 - a sign change
within 1e-6 or f(x) == 0. rescore_uniform.py re-runs all 2779 distinct configurations of the 25
protocol sweeps on APS and CHA (the runs did not keep x) -> sweep_uniform.json, same row format;
it reproduces the APS/CHA lines of Table 10 exactly (built-in check). verify_article.py,
audit_similarity_places.py and the gap diagnostics read the re-scored data; the multiplier and
endowment measurements score by the same criterion. Table 7 (two criteria side by side) is gone;
7.3 now explains why the criterion is a sign change. Consequences stated in the text: the base
rule 2634 / 2357 against Chandrupatla 2595 / 1505 (1.5% behind on APS, no longer "level"); A'
2259 / 1074 (12.9% and 28.6% below Chandrupatla); the defect reading 2351 is second to the
shape reading 2337, not the record; the remembered analogy is level with no regime (2633 vs
2634) under this criterion; Table 9a's range is 19% to 425%.

Point 8. report_sweep.head_to_head counts every problem, failures charged (it counted only the
problems both methods solved, which is why Table 8 rows summed to 153 and 40). Family-level
comparisons added (A' vs Chandrupatla 8/1/6 of 15 and 7/0/2 of 9 families, not significant), and
an independence paragraph in section 9.

Point 11. sweep_itp.py: ITP (recommended constants and per-problem oracle) on all six corpora ->
Table 10; behind A' on every corpus, 0 bound violations.

Points 7, 9, 10 and the internals: the 2x bound is ours, not the rivals' promise; the law
predicts the group order only; "outside the proven bracket" explained (narrowest bracket of all
observed signs); 7.4's fun1 paragraph corrected (five costs under safe; the unguarded numbers
belong to fun1.4, fun1.5 and to fun3.1, fun5.1 separately); Table 1's last cell; the corpus-4
composition and odd multiplicities stated, also in the abstract.

build_tex.py: pdflatex output decoded with errors="replace" (a UTF-8 character cut by TeX's log
wrap crashed the build). Table 10 column widths rebalanced.

Checks: verify_article on the server 500 OK / 0 BAD (467 existing with the new values, 33 new);
regression x2 on the server 1232 passed, 0 failed; PDF 28 pages, 0 errors, 0 undefined, 0 overfull.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 07:01:36 +01:00

61 lines
2.7 KiB
Python

"""Part 3 of stage12_validation.py alone: is the projection's doubling load-bearing?
The canonical text (§3.1 of article/sim_numerics.md) says "the doubling is not a tuning
constant". simroot/pure.py (project_credit) says the opposite: the argument is circular
and 1.25 is cheaper. stage12 measured it but its output was never kept, so this reruns
exactly that part - the same `ours`, the same problems, the same charging - and returns
the numbers for verify_article.py. The credit safeguard, which needs no multiplier, is
put beside it from the same run.
python -u measure_budget_factor.py
"""
from __future__ import annotations
from simroot.pure import pure_root
from stage12_validation import XTOL, load, ours, per_problem, totals
FACTORS = (1.0, 1.25, 1.5, 2.0, 3.0, 4.0)
def credit(f, a, b, xtol, maxfev):
return pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived3",
guard="credit")
def main():
probs = load()
out = {}
print(f"{'factor':>8} {'APS':>6} {'solved':>7} {'CHA':>6} {'solved':>7} {'both':>6}")
for fac in FACTORS:
p = per_problem(lambda f, a, b, xt, mx, _f=fac: ours(f, a, b, xt, mx, _f),
probs, 200, criterion="sign")
t, ok = totals(p, probs)
out[fac] = (t["APS"], ok["APS"], t["CHA"], ok["CHA"])
mark = " <- the text's choice" if fac == 2.0 else ""
print(f"{fac:8.2f} {t['APS']:6d} {ok['APS']:4d}/154 {t['CHA']:6d} {ok['CHA']:4d}/45 "
f"{t['APS'] + t['CHA']:6d}{mark}")
p = per_problem(credit, probs, 200, criterion="sign")
t, ok = totals(p, probs)
out["credit"] = (t["APS"], ok["APS"], t["CHA"], ok["CHA"])
print(f"{'credit':>8} {t['APS']:6d} {ok['APS']:4d}/154 {t['CHA']:6d} {ok['CHA']:4d}/45 "
f"{t['APS'] + t['CHA']:6d} <- no multiplier, bound = bisection + 1 step")
# The credit rule's own number, the endowment C0, promises bisection + ceil(C0)
# steps. Every C0 in (0,1] promises the same single step; the claim in the code is
# that 1, the most permissive of them, is also the cheapest - measured here.
print(f"\n{'C0':>8} {'APS':>6} {'CHA':>6} {'both':>6} promise")
out["endowment"] = {}
for c0 in (0.1, 0.5, 1.0, 2.0, 5.0):
p = per_problem(lambda f, a, b, xt, mx, _c=c0: pure_root(
f, a, b, xt, mx, similarity="ratio", scale="derived3", guard="credit",
endowment=_c), probs, 200, criterion="sign")
t, ok = totals(p, probs)
out["endowment"][c0] = (t["APS"], t["CHA"])
steps = -(-c0 // 1)
print(f"{c0:8.2f} {t['APS']:6d} {t['CHA']:6d} {t['APS'] + t['CHA']:6d} "
f"bisection + {int(steps)} step(s)")
return out
if __name__ == "__main__":
main()