Files
sim-numerics/tables_for_md.py
vvsandClaude Opus 5 1b6c626f67 Valeri's decisions (1) competitors from the library, (2) variant a. (1) Chandrupatla is scipy.optimize.elementwise.find_root, Brent is brentq, no more per-problem best_of. Our Chandrupatla stopped on a bracket twice as wide as every other method (the factor the protocol corrects for ITP); fixed (+0.5*xtol) in baselines.py and in the two copies found by grep (stage16_blend, stage19_modern) and the traced mirror, and pinned: ours == library on every problem. Regenerating sweep_full.json changed exactly three rows - chandrupatla APS 2785->2787, CHA 2423->2441, brent CHA 4099->4115 - and nothing else (diff_sweeps.py: 0 unexpected).
(2) The proposed method is again the scheduled projection (2806/3220/6026), its budget multiplier DECLARED as the one free constant: the canonical total is the same for every multiplier 1.25-4, the wide-bracket one is not. The earned projection is the constant-free variant (3568/3020/6588). What the old tables hid: problem by problem the proposed rule beats Chandrupatla on the canonical set 87/24/42, exact sign test p = 9e-5, while trailing him by 0.7% in total; on the wide-bracket set he is genuinely stronger (26 to 9, p = 0.006). Gap decomposition recomputed with the library: 244 (was 262), 160 of it on the flat families (two thirds), fun1 58 (a quarter, not a third as the abstract said).

Also corrected on the way: the 'subsumed by Chandrupatla' pin claimed he is better on the worst single problem - true only with the looser tolerance (84 against the credit rule's 83 once fixed); §8.3 still called zero slack degenerate, which Table 1a refutes; §5.2's Brent figure is our pure-Python implementation and now says so. verify_article.py 286/286, 802 tests pass twice.

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

66 lines
3.3 KiB
Python

"""Tables 2-3 of article/sim_numerics.md regenerated from sweep_full.json.
The published tables predate Chandrupatla's own method as a baseline (it is first on
Alefeld-Potra-Shi) and name the projection variant as proposed, while the code and
the proposed method is the scheduled projection with its multiplier declared (Valeri,
2026-09-21, variant a), the earned projection the constant-free variant. Baselines are
the library alone (Chandrupatla = scipy elementwise.find_root, Brent = brentq). This
prints the rows and head-to-head counts from the data, with the SAME ranking function the
verifier uses, so nothing in the new tables is transcribed by hand.
python -u tables_for_md.py
"""
from __future__ import annotations
from report_sweep import CREDIT, PROPOSED, TUNED, head_to_head, load, place
NONE = "pure.ratio.derived3.none"
NAMES = {PROPOSED: "**proposed: tent, 3-point, projection ($c=2$)**",
CREDIT: "constant-free variant: earned projection",
NONE: "the same rule with no safeguard at all",
"chandrupatla": "Chandrupatla", "toms748": "TOMS 748", "brent": "Brent",
"dekker": "Dekker", "itp_oracle": "ITP, per-problem oracle",
"ridders": "Ridders", "itp": "ITP, recommended constants",
"illinois": "Illinois", "bisection": "bisection",
"regula_falsi": "false position",
"pure.sqrt.bracket.project": "similarity, $1-\\sqrt{|z|}$, bracket scale, projection",
"pure.exp_abs.bracket.both": "similarity, $e^{-|z|}$, bracket scale, both guards"}
APS_ROWS = ("chandrupatla", CREDIT, NONE, "toms748", "brent", "dekker", "itp_oracle",
"ridders", PROPOSED, "itp", "illinois", "bisection", "regula_falsi")
CHA_ROWS = ("pure.sqrt.bracket.project", "chandrupatla", "itp_oracle",
"pure.exp_abs.bracket.both", "itp", PROPOSED, "bisection", CREDIT, NONE,
"toms748", "brent")
OPPONENTS = ("chandrupatla", "toms748", "brent", "itp", "itp_oracle", "bisection")
def main():
rows = load()
by = {r["label"]: r for r in rows}
print(f"configurations in the sweep: {len(rows)}")
for setname, keys in (("APS", APS_ROWS), ("CHA", CHA_ROWS)):
n = by[PROPOSED][setname]["n"]
print(f"\n{setname}, {n} problems")
print("| # | configuration | tuned | solved | total evals | $>2\\times$ |")
print("|---|---|---|---|---|---|")
for k in sorted(keys, key=lambda k: place(rows, setname, k)):
d = by[k][setname]
tuned = "yes" if k in TUNED else "no"
over = "—" if k in ("regula_falsi",) else str(d["over"])
print(f"| {place(rows, setname, k)} | {NAMES[k]} | {tuned} | "
f"{d['solved']}/{n} | {d['total']} | {over} |")
for lbl in (PROPOSED, CREDIT):
print(f"\nhead to head, {lbl} (wins/ties/losses on problems both solve)")
for setname in ("APS", "CHA"):
cells = []
for opp in OPPONENTS:
w, t, l, _ = head_to_head(rows, lbl, opp, setname)
cells.append(f"{opp} {w}/{t}/{l}")
print(f" {setname}: " + ", ".join(cells))
for lbl in (PROPOSED, CREDIT, "chandrupatla", "toms748", "brent", "itp_oracle"):
d = by[lbl]
print(f" both sets {lbl:34s} {d['APS']['total'] + d['CHA']['total']}")
if __name__ == "__main__":
main()