Files
sim-numerics/apply_article_revision2.py
vvsandClaude Opus 5 6a1c264910 Revision 2 into the paper.
2: 'pre-registered' -> 'recorded in advance'; Appendix D states the order is attested only by the author's git history. 3: Dekker, Illinois and false position exhaust the budget on aps.13.00 under either criterion (stalled where |f|<1e-62, no sign change within 1e-6) - hence unchanged totals. 4: exact-zero stops in Table 11 (CHA: Brent 15/20, Chandrupatla 4/20, A' 2/20; corpus 1: none, transformation still 3.4x cheaper). 5: Table 12, the multiplicity estimated during the run (A' ahead; the analogy order inside Chandrupatla closes 31-59% of the gap on multiple roots and pays on simple ones; Lagouanelle blind on side-alternating triples); 6.6, conclusion, future work. 6: Algorithms 1-2 - lo/hi, return on a stall, L_x/L_f and the empty memory, the step pattern as a formula, O(n^2). 7: Table 3's two bracket-scaled rows, dashes in Table 5, the abstract's last sentence (250 words). verify_revision2: 17 checks. Server: verify 544 OK / 0 BAD, regression x2 1238 passed, PDF 32 pages, 0 errors, 0 overfull.

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

134 lines
12 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Revision 2: points 2-7 into article/sim_numerics.md.
2 'pre-registered' -> 'recorded in advance'; Appendix D says the order is attested only by the
author's git history
3 why Dekker, Illinois and false position show the same totals under both criteria
4 exact-zero stops in Table 11, per method
5 control K3 (PREREG_estimated_transform): Table 12 and its paragraph; 6.6, conclusion, future work
6 Algorithms 1-2: lo/hi, the return on a stall, L_x and L_f, an empty memory, the step pattern
as a formula, the O(n^2) search
7 Table 3's two missing bracket-scaled rows; dashes in Table 5; the abstract's last sentence
Exact anchors, each found exactly once, or nothing is written.
python -u apply_article_revision2.py
"""
from __future__ import annotations
PATH = "article/sim_numerics.md"
R = []
def E(old, new):
R.append((old, new))
# ---- point 2
E("The code, the corpora and the pre-registrations are released", "The code, the corpora and the documents that recorded each test before its run are released")
E("Every rule below was pre-registered with a stop rule (Appendix D);", "Every rule below was recorded, with its criteria and stop rule, before its code and run (Appendix D);")
E("because the pre-registered rule removes only a component", "because the rule recorded in advance removes only a component")
E("Pre-registration protects each decision but not the chain:", "Recording each decision in advance protects the decision but not the chain:")
E("by every pre-registered criterion (per problem", "by every criterion recorded in advance (per problem")
E("Both were pre-registered, with what the paper would say under every outcome, before the run", "Both were recorded, with what the paper would say under every outcome, before the run")
E("as the pre-registered criterion required.", "as the criterion recorded in advance required.")
E("the pre-registrations with the commits of Appendix D;", "the advance records with the commits of Appendix D;")
E("and it passed every pre-registered criterion there.", "and it passed every criterion recorded for it there.")
E("was rejected by its own pre-registration,", "was rejected by its own advance record,")
E("## Appendix D. Pre-registrations and the frozen code", "## Appendix D. Advance records and the frozen code")
E("The code of A′ was committed (`8027d7f`)",
"We call these documents advance records, not pre-registrations: they live in the author's repository, and their order relative to the runs is attested only by the author's git history, whose timestamps the author sets; no external registry was used. A deposit with an external timestamp (Zenodo, OSF) before a run would remove that limitation for future tests. The code of A′ was committed (`8027d7f`)")
E("**Table D1.** Pre-registrations, in the order they were written.", "**Table D1.** Advance records, in the order they were written.")
E("| pre-registration | commit | results file | commit | order |", "| advance record | commit | results file | commit | order |")
# ---- point 3
E("and 79/5/70 against Brent.",
"and 79/5/70 against Brent. Dekker, Illinois and false position fail `aps.13.00` under either criterion and so keep their totals: they spend the whole budget without converging, Dekker and Illinois stalled on one side of the zero where $|f|<10^{-62}$ but $f$ does not change sign within $10^{-6}$.")
# ---- point 4
E("With the multiplicity known the transformation is 2.4 to 6.8 times cheaper than A′.",
"With the multiplicity known the transformation is 2.4 to 6.8 times cheaper than A′. Every method, A′ included, stops on an exact zero of the function it evaluates, and on the pure powers of the Chandrupatla set $g$ is exactly linear, so the secant lands on the root: 15 of Brent's 20 runs and 4 of Chandrupatla's end that way there (A′: 2). On new corpus 1 no transformed run ends on a zero, and the transformation is still 3.4 times cheaper.")
# ---- point 5: K3
K3 = """
**The multiplicity estimated during the run.** Between A′ and the oracle lies the natural classical method: Chandrupatla's interpolation on $g=\\operatorname{sign}(f)|f|^{1/\\hat m}$ with $\\hat m$ estimated during the run. Our implementation of Chandrupatla's method (identical to the library on all 199 problems, and to itself when $\\hat m\\equiv1$) keeps the true $f$ at its three points and reads $G(v)=\\operatorname{sign}(v)|v|^{1/\\hat m}$ only in the acceptance band and the interpolation; $\\hat m$ starts at 1 and takes an order read from the last three evaluations whenever that order lies on a branch of §6.1 ($m\\ge2$ or $0<m\\le\\tfrac23$), without reset — the hysteresis of the controls above. The order is read either by Lagouanelle's estimate or by A′'s own analogy (recorded in advance, `sweep_estimated.py`):
**Table 12.** Chandrupatla on the transformed function: the multiplicity estimated during the run, against A′, the oracle of Table 11 and Chandrupatla on $f$. Totals, failures charged; the 148 multiple and fractional roots of Table 11, and the simple-root canonical set.
| method | corpus 4 | six corpora | multiple/fractional (148) | APS |
|---|---|---|---|---|
| **A′** | **3656** | **13032** | 4042 | **2259** |
| Chandrupatla on $g$, $\\hat m$ by analogy | 3806 | 15733 | 2809 | 3453 |
| Chandrupatla on $g$, $\\hat m$ by Lagouanelle | 7526 | 30991 | 6041 | 7215 |
| Chandrupatla on $g$, $m^*$ known (Table 11) | — | — | **1355** | — |
| Chandrupatla on $f$ | 5717 | 16481 | 7011 | 2595 |
A′ is ahead of both estimated forms on corpus 4, in total and problem by problem (against the analogy form 126/38/76, $p=5\\cdot10^{-4}$), and over the six corpora. The two estimators behave differently in this frame. Lagouanelle's reads the parabola through Chandrupatla's points, which alternate sides of the root; there it reports a fractional order on 115 of the 154 simple roots of the canonical set, holds it, and turns the method into little more than bisection. (A variant restricted to the branch $m\\ge2$ was not among the controls recorded.) The analogy's order is the better one here too, and on the multiple and fractional roots it does better inside Chandrupatla's interpolation than inside A′'s split: it closes 59%, 58% and 31% of the gap from A′ to the oracle on the Chandrupatla set, corpus 1 and corpus 4 (322 against 654, 721 against 1229, 1766 against 2159). It pays on simple roots, where it has no exit to take a false reading back (3453 against 2595 on the canonical set). So the decision layer of A′ earns its place against the classical method with an estimated order as well; and the split (7), not the order estimate, is where A′ leaves most to the transformation."""
E("large, and the clearest direction for improving the method.", "large, and the clearest direction for improving the method." + K3)
E("§7.1 puts this claim against the detectors a reader would try first, and against the transformation with the multiplicity known in advance.",
"§7.1 puts this claim against the detectors a reader would try first, and against the transformation with the multiplicity known in advance or estimated during the run.")
E("Its decision layer beats Lagouanelle's estimate, Chandrupatla's band and no detector put in its place;",
"Its decision layer beats Lagouanelle's estimate, Chandrupatla's band and no detector put in its place, and Chandrupatla's method on the transformed function with the order estimated during the run;")
E("the order read from the analogous pair, or its use in the split, loses most of what the transformation could deliver.",
"closing that gap is open.")
# ---- point 6: the algorithms
E(" xn <- clamp(xn, hi - allow, lo + allow) # projection, eq (3)",
" lo, hi <- min(A,B), max(A,B)\n xn <- clamp(xn, hi - allow, lo + allow) # projection, eq (3)")
E(" if xn outside (A,B) or already evaluated: xn <- midpoint(A,B)",
" if xn outside (A,B) or already evaluated: xn <- midpoint(A,B)\n (if the midpoint too was evaluated: return it)")
E(""" # order (6) of the analogous pair: the past configuration
# nearest to c (Euclidean in (xi, phi)), if nearer than the
# line phi = xi; inf if the pair shows L_f < 1 <= L_x; else none
m <- order of the analogous pair""",
""" # order (6) of the analogous pair. q <- the configuration in
# configs nearest to c (Euclidean in (xi, phi)); no m if c or
# configs is empty or q is not nearer than the line phi = xi.
# L_x <- |x-xp| / q.dx, L_f <- |fx-fp| / q.df
# m <- inf if L_f < 1 <= L_x (translation), else ln L_f / ln L_x
m <- order of the analogous pair""")
E(""" # step pattern: the lag 2 .. half the history of highest
# mean similarity of successive length ratios has mean > 1/2,
# and step and x - xp point the same way
if the pattern repeats:""",
""" # step pattern: q_j <- |d_j|/|d_(j-1)| over successive nonzero
# moves d (k of them); rho(u,v) <- min(u,v)/max(u,v); repeats iff
# max over p = 2..floor(k/2) of mean_j rho(q_j, q_(j-p)) > 1/2
if repeats and step, x - xp point the same way:""")
E(" xn <- clamp(xn, hi - allow, lo + allow) # projection (3)",
" lo, hi <- min(A,B), max(A,B)\n xn <- clamp(xn, hi - allow, lo + allow) # projection (3)")
E(" xn <- mid; if mid in {x, xp}: stop", " xn <- mid; if mid in {x, xp}: return mid")
E("implement exactly this and agree bit for bit.",
"implement exactly this and agree bit for bit. The nearest-configuration search runs over the run's memory: $O(n)$ per step, $O(n^2)$ per run of $n$ evaluations ($n\\le200$ here), arithmetic that matters only when $f$ is cheap (Appendix B).")
# ---- point 7
E("| 3 | similarity, $e^{-\\lvert z\\rvert}$, bracket scale, both guards | no | 45/45 | 1778 | 0 |",
"| 3 | similarity, $e^{-\\lvert z\\rvert}$, bracket scale, both guards | no | 45/45 | 1778 | 0 |\n| 4 | similarity, $e^{-\\lvert z\\rvert}$, bracket scale, bracket guard | no | 45/45 | 1848 | 0 |")
E("| 5 | ITP, recommended constants | yes | 45/45 | 1946 | 0 |",
"| 5 | ITP, recommended constants | yes | 45/45 | 1946 | 0 |\n| 6 | similarity, $e^{-\\lvert z\\rvert}$, bracket scale, earned projection | no | 45/45 | 1952 | 0 |")
E("| A′, earned budget (no free constant) | 3417 | 1852 | 5269 | | |", "| A′, earned budget (no free constant) | 3417 | 1852 | 5269 | — | — |")
E("| base rule (§3) | 2634 | 2357 | 4991 | | |", "| base rule (§3) | 2634 | 2357 | 4991 | — | — |")
E("We replace bisection's midpoint rule by a similarity rule — the step grows with how dissimilar $f(x)$ is to the target 0, under a similarity $S$ with $S(0)=1$ — and derive",
"We replace bisection's midpoint rule by a similarity rule — the step grows with the dissimilarity of $f(x)$ to 0 under a similarity $S$ — and derive")
E("With the multiplicity known, the classical transformation $\\operatorname{sign}(f)|f|^{1/m}$ is 2.4 to 6.8 times cheaper on multiple roots.",
"With the multiplicity known, the classical transformation $\\operatorname{sign}(f)|f|^{1/m}$ is 2.4 to 6.8 times cheaper on multiple roots; what A′ adds is an estimate of the order made without knowing it, within a guarantee of twice bisection.")
def main():
src = open(PATH, encoding="utf8").read()
bad = [o[:80] for o, _ in R if src.count(o) != 1]
if bad:
for b in bad:
print("ANCHOR NOT FOUND EXACTLY ONCE:", repr(b))
raise SystemExit(f"{len(bad)} anchors failed; nothing written")
for o, n in R:
src = src.replace(o, n)
open(PATH, "w", encoding="utf8").write(src)
ab = src.split("## Abstract\n\n")[1].split("\n\n---\n")[0]
code = src.split("**Algorithm 2 (A′).**")[1].split("```")[1]
print(f"applied {len(R)}; abstract {len(ab.split())} words; longest Algorithm 2 line "
f"{max(len(l) for l in code.split(chr(10)))}; 'pre-regist' left: {src.lower().count('pre-regist')}")
if __name__ == "__main__":
main()