From 781bd1525398946efe91781377f9a9a37dc6f8de Mon Sep 17 00:00:00 2001 From: Valeri Sitnikov Date: Mon, 21 Sep 2026 11:46:59 +0100 Subject: [PATCH] PREREG A before the run: similarity of successive steps as the multiplier, step d_n/(1-S), S = ratio similarity of the two displacements (0 if opposite). Replay check, one extra evaluation per state for our step, step A and the midpoint; criteria A1-A4 and falsifier declared; the one-step limitation declared. Co-Authored-By: Claude Opus 5 --- PREREG_step_extrapolation_2026-09-21.md | 50 +++++++++++++ diag_step_extrapolation.py | 99 +++++++++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 PREREG_step_extrapolation_2026-09-21.md create mode 100644 diag_step_extrapolation.py diff --git a/PREREG_step_extrapolation_2026-09-21.md b/PREREG_step_extrapolation_2026-09-21.md new file mode 100644 index 0000000..5be5287 --- /dev/null +++ b/PREREG_step_extrapolation_2026-09-21.md @@ -0,0 +1,50 @@ +# PREREG A — похожесть соседних шагов как множитель (экстраполяция) + +Составлено до прогона. Валерий: «похожесть с предыдущими шагами можно развить дальше» → «пред-регистрируй +и проверяй A». + +## Основание + +На плоских функциях метод ходит циклом из 4 вычислений: два недолёта с отношением сжатия +(1−1/m)^m, перескок, почти пустой шаг (`results_diag_contraction_sequence.txt`). В фазе недолёта +итерация ползёт с одной стороны, и соседние шаги — почти постоянная доля друг друга. + +## Правило (без констант) + +Пусть dₙ — предложенный шаг из текущей точки, dₙ₋₁ = x − x_prev — предыдущее перемещение. + + S = min(|dₙ|, |dₙ₋₁|) / max(|dₙ|, |dₙ₋₁|), если dₙ и dₙ₋₁ одного знака; иначе S = 0 + шаг A = dₙ / (1 − S) + +Оставшийся путь при геометрическом ряде — dₙ·S/(1−S), отсюда множитель 1/(1−S). Для xᵐ под шагом +Ньютона S = 1 − 1/m, множитель = m, шаг A = m·f/f′ попадает в корень. На простом корне шаги +сжимаются сверхлинейно, S → 0, шаг A = dₙ: метод не меняется. + +Известное родство: ускорение Эйткена, модифицированный Ньютон для кратного корня. Отличие от +опровергнутых `derived3_m` (m из параболы, слепа — 5.8%) и `rho` (дёргался у корня): m читается +из двух шагов, у корня S → 0. + +## Проверка (метод не меняется) + +На трассах предложенного метода и чтения (3), в каждом состоянии с известным предыдущим шагом: точка +приземления шага A, **одно** дополнительное вычисление f в ней, обновление скобки, половинения +скобки. То же для нашего шага и для деления пополам — из того же состояния. Шаг A, вышедший за +скобку, считается недопустимым (реальный метод его спроецировал бы), доля таких случаев сообщается. + +Группы — те же, что во всех проверках этой линии: APS, CHA fun1, CHA плоские fun3–6, CHA прочие. + +## Критерии + +| # | что | ПРОЙДЕНО, если | +|---|---|---| +| **A1** | плоские: средние половинения на вычисление, шаг A против нашего шага, парно по состояниям с S > 0 | A больше | +| **A2** | APS: то же | A не меньше (вреда нет) | +| **A3** | плоские: шаг A против деления пополам (1 половинение) | сообщается; ≥ 1 — значит, A обгоняет деление | +| **A4** | доля состояний с S > 0 на APS | сообщается (чем реже, тем инертнее) | + +**Фальсификатор:** A1 ложна — экстраполяция не бьёт наш шаг там, где мы проигрываем; правило не +строится. A2 ложна — вредит там, где мы выигрываем; строить без отдельного решения нельзя. + +**Ограничение, объявленное заранее:** проверка меряет один шаг из каждого состояния. Разрыв цикла +меняет дальнейшую траекторию, поэтому итог по вычислениям на весь прогон покажет только +реализация в методе — отдельная пред-регистрация, если A пройдёт. diff --git a/diag_step_extrapolation.py b/diag_step_extrapolation.py new file mode 100644 index 0000000..e30372f --- /dev/null +++ b/diag_step_extrapolation.py @@ -0,0 +1,99 @@ +"""Replay check for PREREG_step_extrapolation_2026-09-21.md (A). Method untouched. + +At every traced state with a known previous displacement, three candidate landings from +the SAME state get one evaluation each: our step, step A = d_n/(1 - S) with S the ratio +similarity of the two successive steps (0 if they point opposite ways), and the midpoint. +Measured: halvings of the proven bracket that single evaluation buys. + + python -u diag_step_extrapolation.py +""" +from __future__ import annotations + +import math + +from scipy.optimize._tstutils import _APS_TESTS_DICTS, _CHANDRUPATLA_TESTS_DICTS + +from diag_contraction_similarity import CONFIGS, FLAT +from diag_model_order import bracket_step +from simroot.pure import pure_root + +XTOL, MAXFEV, ACC = 1e-12, 200, 1e-6 + + +def halvings_of(f, lo, flo, hi, fhi, landing, r): + W = hi - lo + Wn = bracket_step(f, lo, flo, hi, fhi, landing) + return math.log2(W / max(Wn, math.ulp(r) if r != 0.0 else 5e-324)) + + +def replay(f, a, b, r, cfg): + st = {"trace": []} + x, _n, conv = pure_root(f, a, b, XTOL, MAXFEV, stats=st, **cfg) + if not (conv and abs(float(x) - r) <= ACC): + return [] + rows = [] + for s in st["trace"]: + if s["xp"] is None: + continue + A, FA, B, FB = s["A"], s["FA"], s["B"], s["FB"] + lo, flo, hi, fhi = (A, FA, B, FB) if A < B else (B, FB, A, FA) + if hi - lo <= 0.0: + continue + d_n, d_p = s["raw"], s["x"] - s["xp"] + if d_n == 0.0 or d_p == 0.0 or (d_n > 0.0) != (d_p > 0.0): + S = 0.0 + else: + S = min(abs(d_n), abs(d_p)) / max(abs(d_n), abs(d_p)) + if S >= 1.0: + continue # a geometric series needs S < 1 + ours_L = s["x"] + d_n + ext_L = s["x"] + d_n / (1.0 - S) + mid_L = 0.5 * (lo + hi) + if not (lo < ours_L < hi): + continue + h_ours = halvings_of(f, lo, flo, hi, fhi, ours_L, r) + h_mid = halvings_of(f, lo, flo, hi, fhi, mid_L, r) + ext_ok = lo < ext_L < hi + h_ext = halvings_of(f, lo, flo, hi, fhi, ext_L, r) if ext_ok else None + rows.append((S, h_ours, h_ext, h_mid, ext_ok)) + return rows + + +def main(): + out = {} + for cname, cfg in CONFIGS.items(): + groups = {"APS": [], "CHA fun1": [], "CHA flat fun3-6": [], "CHA other": []} + for d in _APS_TESTS_DICTS: + a, b = (float(v) for v in d["bracket"]) + f = (lambda ff, aa: lambda t: ff(t, *aa))(d["f"], d.get("args", ())) + groups["APS"] += replay(f, a, b, float(d["root"]), cfg) + for d in _CHANDRUPATLA_TESTS_DICTS: + a, b = (float(v) for v in d["bracket"]) + fam = str(d["ID"]).split(".")[0] + g = ("CHA flat fun3-6" if fam in FLAT else "CHA fun1" if fam == "fun1" + else "CHA other") + groups[g] += replay(d["f"], a, b, float(d["root"]), cfg) + print(f"\n== {cname}") + print(f" {'group':<16} {'states':>6} {'S>0':>6} {'A lands out':>11} " + f"{'paired n':>8} {'ours':>6} {'step A':>7} {'bisect':>7} (bracket halvings per eval)") + res = {} + for g, rows in groups.items(): + n = len(rows) + pos = [r_ for r_ in rows if r_[0] > 0.0] + out_ = sum(1 for r_ in pos if not r_[4]) + paired = [r_ for r_ in pos if r_[4]] + m = lambda v: sum(v) / len(v) if v else float("nan") # noqa: E731 + ho = m([p[1] for p in paired]) + he = m([p[2] for p in paired]) + hm = m([p[3] for p in paired]) + res[g] = dict(states=n, s_pos=len(pos), out=out_, paired=len(paired), + ours=ho, ext=he, mid=hm) + print(f" {g:<16} {n:6d} {100.0 * len(pos) / max(n, 1):5.1f}% " + f"{100.0 * out_ / max(len(pos), 1):10.1f}% {len(paired):8d} " + f"{ho:6.2f} {he:7.2f} {hm:7.2f}") + out[cname] = res + return out + + +if __name__ == "__main__": + main()