Why the wide-bracket gap remains, taken apart on the SAME evaluations: 'order of the model' explains only a third. Of the 262 evaluations lost on 33 problems, 178 sit on fun3-fun6 - pure powers of order 3, 5, 9, 19 - where no interpolating model works (our step 0.41-0.81 bracket halvings per evaluation, IQI from our own points 0.01-0.17) and Chandrupatla wins by BISECTING 93-100% of his steps; our step is slower than bisection there and our 2x-bisection guarantee licenses it. His band inside our step confirms it per family: fun4/5/6 573/456/456 -> 296/239/235 against his 293/228/228, while +164/+111/+170/+144 on fun1/2/8/9 where it refuses good steps - slot 2's total was two opposite effects summed. Only fun1 (58) is model order: IQI from our three points 10.37 halvings against our 0.62; a convex two-point step cannot carry IQI's negative weight (428/428 non-degenerate states; five underflowed to -0.0).
The flat regime is not detectable by the three-point order estimate: m >= 2 (the exact condition for a tangent step to lose to a halving) fires on 0.2% of APS states and on only 5.8% of states of pure powers of order 3-19. Paper corrected where it said the whole gap is model order (widelimit, decision, abstract, future work - the refuted log transform removed). WARNING recorded in FINDINGS_model_order.md: article/sim_numerics.tex has carried a STALE header since the first commit (current text = sim_numerics.md, unchanged since 08-24); every paper edit of 09-20/21 went into the .tex, the .md has none of them. Which file is canonical is Valeri's call. The new text numbers trace to results_diag_*.txt but are not yet machine-checked by verify_article.py. Trace hook in pure_root is behaviour-neutral and pinned; 762 pass on the server twice. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
"""Two checks on diag_model_order.py before its result may be stated.
|
||||
|
||||
C1 WHAT Chandrupatla does on the problems where we lose, by family: the share of his
|
||||
steps that are bisections, from the traced mirror whose evaluation count is asserted
|
||||
equal to the original's. If the flat families are won by bisecting, the gap there
|
||||
is not a better model on his side.
|
||||
C2 the 5 states where "exactly one negative Lagrange weight" failed: print them. The
|
||||
sign argument is exact in real arithmetic, so each exception must be a
|
||||
floating-point degeneracy (a weight that underflowed to 0, or f values so close
|
||||
that a difference cancelled) - or the argument is wrong and must be withdrawn.
|
||||
|
||||
python -u diag_model_order_checks.py
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from collections import defaultdict
|
||||
|
||||
from scipy.optimize._tstutils import _CHANDRUPATLA_TESTS_DICTS
|
||||
|
||||
from diag_model_order import ACC, MAXFEV, OURS, XTOL, ch_cost, iqi
|
||||
from explain_chandrupatla import chandrupatla_traced
|
||||
from simroot.baselines import chandrupatla
|
||||
from simroot.pure import pure_root
|
||||
|
||||
|
||||
def main():
|
||||
fam = defaultdict(lambda: [0, 0, 0, 0, 0]) # bisect, iqi, ours n, ch n, problems
|
||||
exceptions = []
|
||||
for d in _CHANDRUPATLA_TESTS_DICTS:
|
||||
pid = str(d["ID"])
|
||||
f = d["f"]
|
||||
a, b = (float(v) for v in d["bracket"])
|
||||
r = float(d["root"])
|
||||
st = {"trace": []}
|
||||
x, n, conv = pure_root(f, a, b, XTOL, MAXFEV, stats=st, **OURS)
|
||||
if not (conv and abs(float(x) - r) <= ACC):
|
||||
continue
|
||||
ch = ch_cost(f, a, b, r)
|
||||
if ch is None or n <= ch:
|
||||
continue
|
||||
_xc, nc, okc, tr = chandrupatla_traced(f, a, b, XTOL, MAXFEV)
|
||||
_xo, no, _oko = chandrupatla(f, a, b, XTOL, MAXFEV)
|
||||
assert nc == no, f"mirror drifted on {pid}: {nc} vs {no}"
|
||||
k = pid.split(".")[0]
|
||||
cell = fam[k]
|
||||
cell[0] += tr["bisect"]
|
||||
cell[1] += tr["iqi"]
|
||||
cell[2] += n
|
||||
cell[3] += ch
|
||||
cell[4] += 1
|
||||
for s in st["trace"]:
|
||||
if s["xp2"] is None:
|
||||
continue
|
||||
pts = ((s["x"], s["fx"]), (s["xp"], s["fp"]), (s["xp2"], s["fp2"]))
|
||||
fs = [p[1] for p in pts]
|
||||
if not (min(fs) < 0.0 < max(fs)):
|
||||
continue
|
||||
_xq, w = iqi(*pts)
|
||||
if w is not None and sum(v < 0.0 for v in w) != 1:
|
||||
exceptions.append((pid, fs, w))
|
||||
|
||||
print("C1 Chandrupatla on the problems where we lose, by family")
|
||||
print(f" {'family':<6} {'probs':>5} {'ours':>6} {'CH':>6} {'gap':>5} "
|
||||
f"{'CH bisect share':>16}")
|
||||
tot_gap = 0
|
||||
for k in sorted(fam):
|
||||
bi, iq, on, cn, p = fam[k]
|
||||
tot_gap += on - cn
|
||||
share = 100.0 * bi / max(bi + iq, 1)
|
||||
print(f" {k:<6} {p:5d} {on:6d} {cn:6d} {on - cn:5d} {share:15.0f}%")
|
||||
print(f" total gap on the losing problems: {tot_gap}")
|
||||
|
||||
print(f"\nC2 the {len(exceptions)} states breaking 'exactly one negative weight'")
|
||||
for pid, fs, w in exceptions:
|
||||
tiny = min(abs(v) for v in fs)
|
||||
print(f" {pid:<8} f = [{', '.join(f'{v:.3e}' for v in fs)}] "
|
||||
f"w = [{', '.join(f'{v:.3e}' for v in w)}] "
|
||||
f"zero weights: {sum(v == 0.0 for v in w)} min|f| {tiny:.1e}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user