From 2fe6d729a8e7c8191d18a1741f94456723a7191b Mon Sep 17 00:00:00 2001 From: Valeri Sitnikov Date: Tue, 22 Sep 2026 08:46:50 +0100 Subject: [PATCH] PREREG_estimated_transform_2026-09-22 (revision 2, point 5), before code and run: Chandrupatla on sign(f)|f|^(1/m_hat) with m_hat estimated during the run - Lagouanelle (K3-L) or A's analogy order (K3-A), hysteresis on the branches m>=2, m<=2/3; decisive on corpus 4; outcome rule including withdrawal of the 6.6 claim. Also diag_revision2.py (points 3, 4: the three unchanged Table 2 rows exhaust the budget on aps.13.00 under both criteria; exact-zero stops per method in Table 11). Co-Authored-By: Claude Opus 5 --- PREREG_estimated_transform_2026-09-22.md | 71 +++++++++++++++++++ diag_revision2.py | 86 ++++++++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 PREREG_estimated_transform_2026-09-22.md create mode 100644 diag_revision2.py diff --git a/PREREG_estimated_transform_2026-09-22.md b/PREREG_estimated_transform_2026-09-22.md new file mode 100644 index 0000000..ba9e93c --- /dev/null +++ b/PREREG_estimated_transform_2026-09-22.md @@ -0,0 +1,71 @@ +# PREREG — классическое преобразование с оценённой кратностью (К3) + +Составлено 2026-09-22 до кода и до прогона, ветка `release`. Доработка 2, п. 5; +Валерий: «контроль п. 5 делаем». Документ коммитится до кода; внешней метки времени нет (депозит на +Zenodo не делался — это решение автора, не принятое на момент записи), так что порядок подтверждается +только git, как и у прежних документов. + +## Вопрос + +Табл. 11 (К1): Chandrupatla на sign(f)|f|^{1/m\*} с **истинной** кратностью в 2.4–6.8 раза дешевле A′ +на кратных и дробных корнях. Между ней и A′ — самая естественная классическая альтернатива: +тот же Chandrupatla на sign(f)|f|^{1/m̂}, где m̂ **оценивается по ходу прогона**. Она отвечает, сколько +стоит оценка порядка в классической рамке, а не в рамке A′, и закрывает (или нет) утверждение §6.6 о +том, что выигрыш несёт решающий слой A′. + +## Определение К3 + +**Решатель.** Наша реализация Chandrupatla (`simroot/baselines.py::chandrupatla`), совпадающая с +`scipy.optimize.elementwise.find_root` на всех 199 задачах (`audit_chandrupatla_library.py`). Метод +хранит **истинные** значения f в своих трёх точках. Знаки, выбор лучшей точки и остановка — по f, как +у исходного метода (g и f имеют одинаковые знаки и одинаково упорядочены по модулю). В ξ, φ полосы +приёмки и в формуле обратной квадратичной интерполяции значения f заменяются на +G(v) = sign(v)·|v|^{1/m̂}, пересчитанные из хранимых f при текущем m̂. Лишних вычислений f нет. + +**Оценка m̂.** Начальное m̂ = 1 (преобразования нет). После каждого вычисления по трём последним +вычисленным точкам (в порядке вычисления) читается порядок m одним из двух оценщиков: + +- **К3-Л (Лагуанель):** m_L из параболы через три точки, θ = f·f''/f'², m = 1/(1 − θ) при θ < 1, + m = ∞ при θ ≥ 1 — ровно определение К2-Л (`_lagouanelle_raw`); +- **К3-А (аналогия):** порядок аналогичной пары A′ (формула (6), предел трансляции m = ∞), по памяти + конфигураций этого прогона — ровно `_config3` / `_analogy_order` A′. + +**Гистерезис** (как в К2): m̂ заменяется прочитанным m, только если m ≥ 2 или 0 < m ≤ ⅔ (ветви §6.1); +чтения из (⅔, 2) и неопределённые m̂ не меняют. Сброса нет. При m̂ = ∞ G(v) = sign(v), и полоса почти +всегда отвергает интерполяцию — метод делит пополам. + +Ни одного нового числа: ½, ⅔, 2 — константы A′ и К2. Кроме оценщика, ничего не выбирается. + +**Проверка реализации до прогона:** при оценщике «нет» (m̂ ≡ 1) К3 совпадает с `chandrupatla` бит в +бит (x и число вычислений) на всех 199 задачах — тест. + +## Где и как считать + +Шесть корпусов §7, единый критерий (смена знака f в 10⁻⁶ или f = 0), бюджет 200, провал за 200, +остановка — ширина скобки ≤ εx или точный ноль f (как у всех). Нарушения границы 2⌈log₂(w/εx)⌉ + 4 — +сообщаются (Chandrupatla её не обещает). + +## Критерии и что пишется в статье + +| # | сообщается / решается | +|---|---| +| **E1** | **корпус 4, решающий:** для каждого оценщика — сумма К3 против A′, по задачам w/t/l, знаковый тест | +| **E2** | шесть корпусов: суммы К3-Л, К3-А, A′, Chandrupatla на f — сообщается | +| **E3** | кратные/дробные корни (группа табл. 11, m\* ≥ 2 или ≤ ⅔): К3-Л, К3-А, A′, оракул К1 — какая доля разрыва A′ → оракул закрыта оценкой в классической рамке | +| **E4** | простые корни (остальные): не хуже ли К3 обычного Chandrupatla — цена ложных срабатываний оценщика | + +**Правило вывода (при любом исходе в статью):** + +- Если хотя бы один вариант К3 на корпусе 4 **не хуже A′ по сумме**, или выигрывает у A′ по задачам со + знаковым тестом p < 0.05, — утверждение §6.6 о том, что выигрыш несёт решающий слой A′, **снимается**: + пишется, что классическое преобразование с оценкой порядка по ходу прогона даёт то же (или больше), и + вклад статьи сужается до вывода и сравнения; аннотация и заключение следуют за этим. +- Если A′ дешевле обоих вариантов на корпусе 4 и выигрывает по задачам с p < 0.05, — утверждение + остаётся, с числами К3 рядом с К1 в табл. 11. +- Промежуточный исход (A′ дешевле по сумме, но по задачам незначимо) пишется как есть, без вывода в + пользу A′. +- Сравнение К3-А и К3-Л отделяет оценщик от рамки: если К3-А лучше К3-Л, оценка по аналогии лучше и в + классической рамке; если К3-А хуже A′, выигрыш A′ — в рамке (режим, выход, остальные правила). + +**Стоп-правило.** Один прогон. Определения после него не меняются; поправки, если понадобятся после +смоука, записываются здесь с перечнем уже увиденного, как в `PREREG_controls_2026-09-22.md`. diff --git a/diag_revision2.py b/diag_revision2.py new file mode 100644 index 0000000..a7a33dc --- /dev/null +++ b/diag_revision2.py @@ -0,0 +1,86 @@ +"""Revision 2, points 3 and 4 - facts only, no new method. + +Point 3: Dekker, Illinois and false position show the same APS totals under both criteria. Which +problems do they fail, and why do they fail aps.13.00 under 'sign change or f = 0' as well? +Point 4: how do the runs on g = sign(f)|f|^(1/m*) (Table 11) end - by the bracket width, or on an +exact zero of g (which every method, A' included, accepts)? Counted per method on the +multiple/fractional problems, next to A' on the same problems. + + python -u diag_revision2.py +""" +from __future__ import annotations + +import json +import math +import random + +import scipy.optimize._tstutils as T + +from corpus_roots_jl import solved +from rescore_uniform import load_uniform +from simroot.baselines import brent_scipy, chandrupatla_lib +from simroot.pure import pure_root +from sweep_controls import k1_problems, m_star, nonsimple +from sweep_full import MAXFEV, XTOL, make_method + +APRIME = ("pure", "ratio", "derived3", "project", "x", "contra+flatanalogmempt+exp+half+close+sub", + "recent", "distance", "none", "pattern") + + +def point3(): + U = {r["label"]: r for r in load_uniform("sweep_full.json")} + O = {r["label"]: r for r in json.load(open("sweep_full.json", encoding="utf8"))} + probs = {pid: (f, a, b) for s, pid, f, a, b, _r in __import__("sweep_full").load_problems()} + for lb in ("dekker", "illinois", "regula_falsi"): + fails_u = [p for p, (n, ok) in U[lb]["APS"]["per"].items() if not ok] + fails_o = [p for p, (n, ok) in O[lb]["APS"]["per"].items() if not ok] + print(f"{lb}: APS failures uniform {len(fails_u)}, harness {len(fails_o)}, " + f"same set {sorted(fails_u) == sorted(fails_o)}; uniform total {U[lb]['APS']['total']}, " + f"harness {O[lb]['APS']['total']}") + f, a, b = probs["aps.13.00"] + x, n, conv = make_method(("base", lb))(f, a, b, XTOL, MAXFEV) + print(f" aps.13.00: returned x={float(x):.3g}, nfev={n}, converged={conv}, " + f"f(x)={f(float(x))!r}, sign-change criterion {solved(f, float(x))}") + + +def ends_on_zero(method, f, g, a, b): + """(nfev, stopped on an exact zero of the function it saw, bracket-width stop).""" + seen = [] + + def w(x): + v = g(x) + seen.append(v) + return v + T._f6_cache.clear() + random.seed(20260921) + x, n, conv = method(w, a, b, XTOL, MAXFEV) + return int(n), bool(seen and seen[-1] == 0.0), bool(conv) + + +def point4(): + for s in ("CHA", "NEW", "D"): + tally = {"chandrupatla_g": [0, 0, 0], "brent_g": [0, 0, 0], "Aprime": [0, 0, 0]} + for pid, f, a, b, r in k1_problems(s): + m = m_star(f, r) + if not nonsimple(m): + continue + inv = 1.0 / m + + def g(x, _f=f, _i=inv): + v = _f(x) + return 0.0 if v == 0 else math.copysign(abs(v) ** _i, v) + for name, meth, fun in (("chandrupatla_g", chandrupatla_lib, g), ("brent_g", brent_scipy, g), + ("Aprime", make_method(APRIME), f)): + n, zero, conv = ends_on_zero(meth, f, fun, a, b) + t = tally[name] + t[0] += 1 + t[1] += zero + t[2] += n + print(f"{s}: problems {tally['Aprime'][0]}; runs ending on an exact zero / total evaluations: " + + "; ".join(f"{k} {v[1]}/{v[2]}" for k, v in tally.items())) + + +if __name__ == "__main__": + point3() + point4() + print("revision2 diag done")