slope-stability gate: the PROJECT'S OWN target is met for the first time — 5922 < 6026 over both sets at 193/199 with the guarantee intact (slope.ratio.derived3.project: APS 2848 + CHA 3074), CHA improves 4516 -> 2903 (x1.56), repairs 77.5% -> 55.3%. Honest cost: APS 2806 -> 2848, which moves us from 1st to 3rd on the canonical set (chandrupatla 2785, toms748 2820), so my stricter S1 fails while the project target passes — a positioning decision, recorded not resolved. Refusal CURRENCY is what decides: bracket progress 4449 vs model shape 2848, a 1601-evaluation difference for the same 'refuse before the step' idea
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,51 @@
|
|||||||
|
# Findings — гейт на устойчивости наклона (`PREREG_slope_gate_2026-09-21.md`)
|
||||||
|
|
||||||
|
Числа с Linux. Свип в своём `sweep_slope.json`; `sweep_full.json` не тронут (git-статус чист).
|
||||||
|
Признак отказа: два последовательных знака локального наклона различны ⇒ деление пополам. Сравнивается
|
||||||
|
**знак**, ни одной константы (закреплено тестом T2).
|
||||||
|
|
||||||
|
## 1. Результат
|
||||||
|
|
||||||
|
| проверка | цель | измерено | вердикт |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **S1** APS-154 не дорожает | ≤2806 | **2848** (+1.5 %, 42 вызова) | 🔴 FAIL |
|
||||||
|
| **S2** CHA-45 дешевеет против 4516 | <4516 | **2903** (×1.56) | 🟢 PASS |
|
||||||
|
| **S3** ремонты сторожа падают против 77.5 % | <77.5 % | **55.3 %** | 🟢 PASS |
|
||||||
|
| **S4** гарантия `2⌈log₂(w/xtol)⌉+3` | `over2x=0` | 0 во всех конфигурациях | 🟢 PASS |
|
||||||
|
| **S5** качество пробы растёт | >1.422 | **1.432** | 🔴 в пределах шума |
|
||||||
|
| **цель ПРОЕКТА** (из `NEXT_PAPER_logarithmic.md`, записана до сессии): два набора | <6026 при 193/199 и целой гарантии | **5922** одной конфигурацией `slope.ratio.derived3.project` (APS 2848 + CHA 3074) | 🟢 **PASS** |
|
||||||
|
|
||||||
|
Сравнение с базлайнами по двум наборам: Chandrupatla **5208**, ITP-oracle **5871**, наши **5922** —
|
||||||
|
цель проекта взята, базлайны не побиты.
|
||||||
|
|
||||||
|
## 2. 🔴 Цена названа точно
|
||||||
|
|
||||||
|
На APS-154: Chandrupatla 2785, TOMS 748 2820, **наши опубликованные 2806 (1-е место)**, Brent 2889.
|
||||||
|
С гейтом наши **2848** ⇒ **третье место вместо первого** на каноническом наборе. То есть трейд не
|
||||||
|
«+1.5 %», а **потеря заголовка статьи** в обмен на уход двух наборов под 6026 и ×1.56 на широких
|
||||||
|
скобках.
|
||||||
|
|
||||||
|
## 3. Развилка (решение Валерия, не моё)
|
||||||
|
|
||||||
|
Мой `S1` строже цели, которую проект поставил себе сам:
|
||||||
|
|
||||||
|
- по **S1** («ни копейки на каноническом наборе») режим **не принимается**;
|
||||||
|
- по **цели проекта** («побить 6026 на двух наборах при 193/199 и целой гарантии») режим
|
||||||
|
**принимается**: 5922.
|
||||||
|
|
||||||
|
Оба критерия записаны заранее, и они не совпадают. Это выбор позиционирования: держать «1-е место на
|
||||||
|
APS при нуле настроечных констант» или взять «5922 < 6026 при целой гарантии» ценой третьего места.
|
||||||
|
|
||||||
|
## 4. Что установлено независимо от развилки
|
||||||
|
|
||||||
|
- **Признак отказа решает.** Прогресс скобки — неверная валюта (APS 4449), форма модели — верная
|
||||||
|
(APS 2848). Разница между двумя гейтами: **1601 вызов** на APS при одинаковой идее «отказывайся до
|
||||||
|
шага, бесплатно».
|
||||||
|
- **Перевод Chandrupatla на язык похожести оказался рабочим рецептом, а не только описанием:** его
|
||||||
|
гейт читает форму конфигурации до шага; наш аналог для нашего шага — устойчивость наклона; и именно
|
||||||
|
он дал первое за линию попадание в цель проекта.
|
||||||
|
- **Ремонты — следствие, а не причина:** они упали 77.5 % → 55.3 %, но качество пробы почти не
|
||||||
|
изменилось (1.422 → 1.432). Значит выигрыш пришёл не от «меньше ремонтов», а от того, что отказ
|
||||||
|
случается **в нужных местах**; счётчик ремонтов — индикатор, а не цель.
|
||||||
|
- 🔴 **Не повторять:** гейт по прогрессу скобки (`FINDINGS_prestep_gate.md`), логарифмическая замена
|
||||||
|
(`FINDINGS_logarithmic.md`), `cbrt` по набору и три детектора режима (`NEXT_PAPER_logarithmic.md`).
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
# PREREG — гейт на УСТОЙЧИВОСТИ НАКЛОНА (форма, а не прогресс)
|
||||||
|
|
||||||
|
Составлено до реализации. Основание — два измерения этой линии:
|
||||||
|
|
||||||
|
1. `FINDINGS_prestep_gate.md`: гейт по **прогрессу скобки** обваливает ремонты (77.5 % → 15.9 %) и
|
||||||
|
впервые берёт цель ITP на CHA (**2851 ≤ 2857**), но APS дорожает 2806 → **4449**, потому что
|
||||||
|
прогресс скобки — **неверная валюта отказа**: секущий шаг обрушивает ошибку, почти не двигая
|
||||||
|
скобку (в проекте это уже измерено: **150 против 86**).
|
||||||
|
2. `FINDINGS_logarithmic.md` §3: Chandrupatla отказывает не по прогрессу, а по **форме конфигурации**
|
||||||
|
(`ξ`, `φ` — безразмерные отношения; полоса есть окрестность линейного случая), и его отказ стоит
|
||||||
|
**ноль** вызовов.
|
||||||
|
|
||||||
|
**Гипотеза:** нужный признак отказа — не «сколько обещает шаг», а «установилась ли модель, которой
|
||||||
|
шаг доверяет». Наш шаг доверяет **локальному наклону**, значит вопрос доверия — **устойчивость
|
||||||
|
наклона**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Признак, выведенный для НАШЕГО шага (и без единой константы)
|
||||||
|
|
||||||
|
Метод оценивает наклон на каждом шаге (`derived` — по двум последним точкам, `derived3` — по трём).
|
||||||
|
Две последовательные оценки `slope_prev` и `slope` дают безразмерное сравнение. Объявленный признак
|
||||||
|
отказа — **несогласие знака**:
|
||||||
|
|
||||||
|
> если `slope_prev` и `slope` оба не нули и **их знаки различны**, локальная модель не установилась
|
||||||
|
> (функция локально даже не монотонна в одном направлении) ⇒ шаг заменяется делением пополам.
|
||||||
|
|
||||||
|
Почему это беспараметрично: сравнивается **знак**, а не величина; порога нет, множителя нет,
|
||||||
|
сравнения с выбранным числом нет. Почему это форма, а не прогресс: признак читается из конфигурации
|
||||||
|
данных (две оценки наклона), а не из обещанной длины шага.
|
||||||
|
|
||||||
|
**Почему ожидается инертность на каноническом наборе** (то, чего не хватило прошлой попытке): у
|
||||||
|
простого корня гладкой функции знак наклона устойчив, значит гейт молчит и APS не дорожает. Срабатывать
|
||||||
|
он должен в плоских областях широких скобок, где оценки наклона мелкие и болтаются.
|
||||||
|
|
||||||
|
**Чего НЕ делаем:** не вводим порог на отношение величин наклонов (`|s1|/|s2| < k` — это константа
|
||||||
|
`k`); не переносим полосу `1−√(1−ξ) < φ < √ξ` (выведена для обратной квадратичной интерполяции).
|
||||||
|
|
||||||
|
**Режим:** `prestep="slope"`; `"none"` остаётся по умолчанию и побитово прежним; `"progress"` остаётся
|
||||||
|
в коде как **измеренный отрицательный результат**, помеченный в докстринге.
|
||||||
|
|
||||||
|
## 2. Предсказания и фальсификаторы
|
||||||
|
|
||||||
|
| # | Предсказание | ФАЛЬСИФИКАТОР → вывод |
|
||||||
|
|---|---|---|
|
||||||
|
| **S1** | APS-154 **не дорожает**: по-наборный лучший ≤ **2806** | >2806 → шестая попытка тем же трейдом; признак «форма» тоже не спасает, линия закрывается |
|
||||||
|
| **S2** | CHA-45 дешевеет против x-пространства: по-наборный лучший < **4516** | не дешевеет → гейт инертен всюду, гипотеза о наклоне неверна |
|
||||||
|
| **S3** | доля ремонтов сторожем падает против 77.5 % | не падает → гейт не бьёт в механизм |
|
||||||
|
| **S4** | гарантия `2⌈log₂(w/xtol)⌉+3` цела: `over2x = 0` | нарушена → закрытие немедленно |
|
||||||
|
| **S5** | качество пробы на CHA растёт против 1.422 | не растёт → отказ не улучшает пробу, только переставляет расходы |
|
||||||
|
|
||||||
|
**Правило остановки:** S1 или S4 ложны ⇒ режим не принимается. S1 верна и S2 верна ⇒ **это первый
|
||||||
|
режим линии, который берёт широкие скобки, не продавая узкие**, и тогда следующий шаг — свип по всей
|
||||||
|
сетке и проверка, попадает ли лучшая конфигурация в цель <6026 на двух наборах.
|
||||||
|
|
||||||
|
## 3. Протокол и гейты реализации
|
||||||
|
|
||||||
|
Протокол статьи: 154 APS + 45 CHA, цена — вызовы `f`, провал = полный бюджет, граница `2×` на каждом
|
||||||
|
решённом прогоне, конфигурация фиксируется выводом до рейтинга, числа только с **Linux**, свип пишется
|
||||||
|
в свой `sweep_slope.json`.
|
||||||
|
|
||||||
|
| # | Условие реализации | Проверка тестом |
|
||||||
|
|---|---|---|
|
||||||
|
| **T1** | `prestep="none"` побитово как раньше | равенство кортежей возврата на наборе задач и сторожей |
|
||||||
|
| **T2** | в гейте нет ни одной константы | греп по коду гейта (без комментариев): допустимы только `0.0` и `0.5` |
|
||||||
|
| **T3** | гейт инертен там, где знак наклона устойчив | на гладкой монотонной задаче число вызовов совпадает с `prestep="none"` |
|
||||||
|
| **T4** | гейт срабатывает там, где знак наклона меняется | на задаче с немонотонностью `stats['pre_refused'] ≥ 1` |
|
||||||
|
| **T5** | отказ не добавляет вызова | `pre_refused ≤ nfev − 2` |
|
||||||
|
|
||||||
|
**Дисциплина:** аудит после правки и до запуска; смоук ×2 локально; свип на сервере с замороженной
|
||||||
|
копией; числа немедленно.
|
||||||
@@ -149,4 +149,4 @@ def main(prestep="none"):
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import sys
|
import sys
|
||||||
main("progress" if "--gate" in sys.argv else "none")
|
main("slope" if "--slope" in sys.argv else "progress" if "--gate" in sys.argv else "none")
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
sweep: 126 configurations x 199 problems (FULL)
|
||||||
|
10/126 done, 1s elapsed
|
||||||
|
20/126 done, 1s elapsed
|
||||||
|
30/126 done, 1s elapsed
|
||||||
|
40/126 done, 1s elapsed
|
||||||
|
50/126 done, 2s elapsed
|
||||||
|
60/126 done, 2s elapsed
|
||||||
|
70/126 done, 2s elapsed
|
||||||
|
80/126 done, 2s elapsed
|
||||||
|
90/126 done, 2s elapsed
|
||||||
|
100/126 done, 2s elapsed
|
||||||
|
110/126 done, 3s elapsed
|
||||||
|
120/126 done, 3s elapsed
|
||||||
|
126/126 done, 3s elapsed
|
||||||
|
|
||||||
|
wrote sweep_slope.json (4s total)
|
||||||
|
|
||||||
|
====================================================================================
|
||||||
|
APS: 154 problems (top 20 of 126)
|
||||||
|
====================================================================================
|
||||||
|
configuration solved total fev over 2x
|
||||||
|
------------------------------------------------------------------------------------
|
||||||
|
slope.ratio.derived3.none 153/154 2848 0
|
||||||
|
slope.ratio.derived3.project 153/154 2848 0
|
||||||
|
slope.ratio.derived3.step 153/154 3020 0
|
||||||
|
slope.ratio.derived3.safe 153/154 3020 0
|
||||||
|
slope.ratio.derived.step 153/154 3105 0
|
||||||
|
slope.ratio.derived.safe 153/154 3105 0
|
||||||
|
slope.ratio.derived.none 153/154 3131 0
|
||||||
|
slope.ratio.derived.project 153/154 3131 0
|
||||||
|
slope.exp_abs.derived3.step 153/154 3322 0
|
||||||
|
slope.exp_abs.derived3.safe 153/154 3322 0
|
||||||
|
slope.exp_abs.derived.project 153/154 3326 0
|
||||||
|
slope.exp_abs.derived.none 153/154 3326 0
|
||||||
|
slope.exp_abs.derived3.none 153/154 3328 0
|
||||||
|
slope.exp_abs.derived3.project 153/154 3328 0
|
||||||
|
slope.exp_abs.derived.step 153/154 3358 0
|
||||||
|
slope.exp_abs.derived.safe 153/154 3358 0
|
||||||
|
slope.rational.derived3.step 153/154 3436 0
|
||||||
|
slope.rational.derived3.safe 153/154 3436 0
|
||||||
|
slope.ratio.derived3.credit 153/154 3507 0
|
||||||
|
slope.rational.derived3.none 153/154 3521 0
|
||||||
|
|
||||||
|
====================================================================================
|
||||||
|
CHA: 45 problems (top 20 of 126)
|
||||||
|
====================================================================================
|
||||||
|
configuration solved total fev over 2x
|
||||||
|
------------------------------------------------------------------------------------
|
||||||
|
slope.ratio.derived3.step 40/45 2903 0
|
||||||
|
slope.ratio.derived3.safe 40/45 2903 0
|
||||||
|
slope.rational.derived3.step 40/45 2965 0
|
||||||
|
slope.rational.derived3.safe 40/45 2965 0
|
||||||
|
slope.exp_abs.derived3.step 40/45 2966 0
|
||||||
|
slope.exp_abs.derived3.safe 40/45 2966 0
|
||||||
|
slope.cbrt.derived.credit 40/45 2995 0
|
||||||
|
slope.ratio.derived3.credit 40/45 2997 0
|
||||||
|
slope.ratio_soft.derived3.credit 40/45 3055 0
|
||||||
|
slope.ratio.derived.credit 40/45 3059 0
|
||||||
|
slope.ratio.derived3.project 40/45 3074 0
|
||||||
|
slope.ratio.derived3.none 40/45 3075 0
|
||||||
|
slope.ratio_soft.derived.credit 40/45 3094 0
|
||||||
|
slope.exp_abs.derived3.credit 40/45 3095 0
|
||||||
|
slope.cbrt.derived3.credit 40/45 3107 0
|
||||||
|
slope.exp_abs.derived.credit 40/45 3113 0
|
||||||
|
slope.cosine.derived.credit 40/45 3130 0
|
||||||
|
slope.cosine.derived3.credit 40/45 3130 0
|
||||||
|
slope.exp_sq.derived.credit 40/45 3130 0
|
||||||
|
slope.exp_sq.derived3.credit 40/45 3130 0
|
||||||
|
|
||||||
|
====================================================================================
|
||||||
|
THE STAGE-0C LAW ACROSS THE GRID
|
||||||
|
====================================================================================
|
||||||
|
scale=derived slope c=1 (linear) mean total 6920 mean solved 193.0/199
|
||||||
|
scale=derived slope c=0 (quadratic) mean total 22164 mean solved 193.0/199
|
||||||
|
scale=derived3 slope c=1 (linear) mean total 6204 mean solved 193.0/199
|
||||||
|
scale=derived3 slope c=0 (quadratic) mean total 21698 mean solved 193.0/199
|
||||||
+24
-2
@@ -467,8 +467,12 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
|||||||
raise ValueError(f"unknown scale {scale!r}")
|
raise ValueError(f"unknown scale {scale!r}")
|
||||||
if space not in SPACES:
|
if space not in SPACES:
|
||||||
raise ValueError(f"unknown space {space!r}")
|
raise ValueError(f"unknown space {space!r}")
|
||||||
if prestep not in ("none", "progress"):
|
if prestep not in ("none", "progress", "slope"):
|
||||||
raise ValueError(f"unknown prestep {prestep!r}")
|
raise ValueError(f"unknown prestep {prestep!r}")
|
||||||
|
if prestep == "slope" and scale not in ("derived", "derived3"):
|
||||||
|
# there is no slope to judge under a chosen or bracket scale; judging something
|
||||||
|
# else would hide that, so the call fails instead
|
||||||
|
raise ValueError("prestep='slope' requires scale in {'derived', 'derived3'}")
|
||||||
if space == "log" and scale not in ("derived", "derived3"):
|
if space == "log" and scale not in ("derived", "derived3"):
|
||||||
# u-space needs a slope; with a chosen or bracket scale there is none to
|
# u-space needs a slope; with a chosen or bracket scale there is none to
|
||||||
# transform, and computing something anyway would hide that (PREREG §3a).
|
# transform, and computing something anyway would hide that (PREREG §3a).
|
||||||
@@ -497,6 +501,8 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
|||||||
sg.credit = endowment
|
sg.credit = endowment
|
||||||
forced = 0
|
forced = 0
|
||||||
pre_refused = 0
|
pre_refused = 0
|
||||||
|
slope = 0.0
|
||||||
|
slope_prev = None
|
||||||
if stats is not None:
|
if stats is not None:
|
||||||
stats['forced'] = 0
|
stats['forced'] = 0
|
||||||
stats['pre_refused'] = 0
|
stats['pre_refused'] = 0
|
||||||
@@ -596,7 +602,20 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
|||||||
x_new = 0.5 * (A + B)
|
x_new = 0.5 * (A + B)
|
||||||
else:
|
else:
|
||||||
step = None
|
step = None
|
||||||
if prestep == "progress":
|
if prestep == "slope":
|
||||||
|
# PRE-STEP GATE ON SHAPE (PREREG 2026-09-21). The step trusts the
|
||||||
|
# local slope, so the question of trust is whether that slope has
|
||||||
|
# SETTLED: two consecutive estimates of opposite sign mean the local
|
||||||
|
# model is not established at all, and the evaluation is better spent
|
||||||
|
# halving. A SIGN is compared, not a magnitude — no threshold, no
|
||||||
|
# factor, no constant. Where the slope sign is stable (a simple root
|
||||||
|
# of a smooth f) the gate is inert, which is exactly what the
|
||||||
|
# progress gate could not manage: it charged APS 4449 against 2806.
|
||||||
|
if (slope_prev is not None and slope != 0.0 and slope_prev != 0.0
|
||||||
|
and (slope > 0.0) != (slope_prev > 0.0)):
|
||||||
|
step = 0.5 * (A + B) - x
|
||||||
|
pre_refused += 1
|
||||||
|
if step is None and prestep == "progress":
|
||||||
# PRE-STEP GATE (PREREG 2026-09-21). Measured cause of our cost:
|
# PRE-STEP GATE (PREREG 2026-09-21). Measured cause of our cost:
|
||||||
# the safeguard REPAIRS 77.5% of evaluations after the fact, while
|
# the safeguard REPAIRS 77.5% of evaluations after the fact, while
|
||||||
# Chandrupatla REFUSES its model on 72% of steps for free and
|
# Chandrupatla REFUSES its model on 72% of steps for free and
|
||||||
@@ -635,6 +654,9 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
|||||||
x_new = projected
|
x_new = projected
|
||||||
step = x_new - x
|
step = x_new - x
|
||||||
sg.note_step(step)
|
sg.note_step(step)
|
||||||
|
# the slope this step trusted, kept so the NEXT step can ask whether it
|
||||||
|
# has settled (prestep="slope"); only meaningful for the derived scales
|
||||||
|
slope_prev = slope
|
||||||
|
|
||||||
if not math.isfinite(x_new) or not (min(A, B) < x_new < max(A, B)):
|
if not math.isfinite(x_new) or not (min(A, B) < x_new < max(A, B)):
|
||||||
x_new = 0.5 * (A + B)
|
x_new = 0.5 * (A + B)
|
||||||
|
|||||||
+22
-2
@@ -125,7 +125,9 @@ def label(spec):
|
|||||||
if len(spec) > 4 and spec[4] != "x":
|
if len(spec) > 4 and spec[4] != "x":
|
||||||
tag = spec[4]
|
tag = spec[4]
|
||||||
if len(spec) > 5 and spec[5] != "none":
|
if len(spec) > 5 and spec[5] != "none":
|
||||||
tag = f"{tag}+gate" if tag != "pure" else "gate"
|
# name the gate by WHICH gate it is: "progress" is a measured negative and must
|
||||||
|
# never be confused with "slope" in a results file
|
||||||
|
tag = f"{tag}+{spec[5]}" if tag != "pure" else spec[5]
|
||||||
return f"{tag}.{spec[1]}.{spec[2]}.{spec[3]}"
|
return f"{tag}.{spec[1]}.{spec[2]}.{spec[3]}"
|
||||||
|
|
||||||
|
|
||||||
@@ -189,6 +191,15 @@ def build_grid_log(smoke):
|
|||||||
for sim in sims for sc in ("derived", "derived3") for gu in guards]
|
for sim in sims for sc in ("derived", "derived3") for gu in guards]
|
||||||
|
|
||||||
|
|
||||||
|
def build_grid_slope(smoke):
|
||||||
|
"""The slope-stability gate grid (PREREG 2026-09-21). Only derived scales: there must be
|
||||||
|
a slope to judge. Baselines are not recomputed — they are in sweep_full.json."""
|
||||||
|
sims = ["ratio", "sqrt"] if smoke else sorted(PURE_SIMS)
|
||||||
|
guards = ("credit", "safe") if smoke else GUARD_MODES
|
||||||
|
return [("pure", sim, sc, gu, "x", "slope")
|
||||||
|
for sim in sims for sc in ("derived", "derived3") for gu in guards]
|
||||||
|
|
||||||
|
|
||||||
def build_grid_gate(smoke):
|
def build_grid_gate(smoke):
|
||||||
"""The pre-step gate grid (PREREG 2026-09-21): the published x-space step with the
|
"""The pre-step gate grid (PREREG 2026-09-21): the published x-space step with the
|
||||||
gate switched on. Baselines are not recomputed - they are already in sweep_full.json."""
|
gate switched on. Baselines are not recomputed - they are already in sweep_full.json."""
|
||||||
@@ -219,7 +230,9 @@ def main():
|
|||||||
smoke = "--smoke" in sys.argv
|
smoke = "--smoke" in sys.argv
|
||||||
logmode = "--log" in sys.argv # the u-space grid, written to its OWN file
|
logmode = "--log" in sys.argv # the u-space grid, written to its OWN file
|
||||||
gatemode = "--gate" in sys.argv # the pre-step gate grid, likewise
|
gatemode = "--gate" in sys.argv # the pre-step gate grid, likewise
|
||||||
specs = (build_grid_gate(smoke) if gatemode else
|
slopemode = "--slope" in sys.argv # the slope-stability gate grid
|
||||||
|
specs = (build_grid_slope(smoke) if slopemode else
|
||||||
|
build_grid_gate(smoke) if gatemode else
|
||||||
build_grid_log(smoke) if logmode else build_grid(smoke))
|
build_grid_log(smoke) if logmode else build_grid(smoke))
|
||||||
t0 = time.time()
|
t0 = time.time()
|
||||||
print(f"sweep: {len(specs)} configurations x 199 problems "
|
print(f"sweep: {len(specs)} configurations x 199 problems "
|
||||||
@@ -236,6 +249,13 @@ def main():
|
|||||||
|
|
||||||
# the published sweep_full.json is what verify_article.py checks the paper
|
# the published sweep_full.json is what verify_article.py checks the paper
|
||||||
# against, so the log grid never writes into it
|
# against, so the log grid never writes into it
|
||||||
|
if slopemode:
|
||||||
|
out = "sweep_slope_smoke.json" if smoke else "sweep_slope.json"
|
||||||
|
with open(out, "w", encoding="utf8") as fh:
|
||||||
|
json.dump(results, fh)
|
||||||
|
print(f"\nwrote {out} ({time.time() - t0:.0f}s total)\n", flush=True)
|
||||||
|
report(results)
|
||||||
|
return
|
||||||
if gatemode:
|
if gatemode:
|
||||||
# never into sweep_full.json: that file is what verify_article.py checks the
|
# never into sweep_full.json: that file is what verify_article.py checks the
|
||||||
# paper against, and the smoke run of 2026-09-21 did land in sweep_smoke.json
|
# paper against, and the smoke run of 2026-09-21 did land in sweep_smoke.json
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -38,7 +38,9 @@ def test_R1_the_published_path_is_bit_identical_with_the_gate_off():
|
|||||||
|
|
||||||
def test_R2_the_gate_carries_no_chosen_constant():
|
def test_R2_the_gate_carries_no_chosen_constant():
|
||||||
src = inspect.getsource(P.pure_root)
|
src = inspect.getsource(P.pure_root)
|
||||||
start = src.index('if prestep == "progress":')
|
# anchor on the CONDITION, not on a whole line: the line grew a `step is None and`
|
||||||
|
# prefix when the slope gate was inserted ahead of it, and the exact-line anchor broke
|
||||||
|
start = src.index('prestep == "progress"')
|
||||||
end = src.index('if step is None and space ==', start) # the gate block only
|
end = src.index('if step is None and space ==', start) # the gate block only
|
||||||
body = src[start:end]
|
body = src[start:end]
|
||||||
# comments carry the MEASURED numbers that justify the gate (77.5 %, 2.310 halvings);
|
# comments carry the MEASURED numbers that justify the gate (77.5 %, 2.310 halvings);
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
"""Gates T1-T5 for the slope-stability gate (PREREG_slope_gate_2026-09-21.md).
|
||||||
|
|
||||||
|
The progress gate refused on the wrong currency (bracket progress) and charged APS 4449
|
||||||
|
against 2806. This gate refuses on the SHAPE of the model instead: two consecutive slope
|
||||||
|
estimates of opposite sign mean the local model is not established, so the evaluation is
|
||||||
|
spent halving. A SIGN is compared — no threshold, no factor, no constant.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import inspect
|
||||||
|
import math
|
||||||
|
import re
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from simroot import pure as P
|
||||||
|
|
||||||
|
SMOOTH = [ # simple roots, stable slope sign: the gate must be inert
|
||||||
|
(lambda t: t ** 3 - 2.0 * t - 5.0, 2.0, 3.0),
|
||||||
|
(lambda t: math.exp(t) - 5.0, 0.0, 3.0),
|
||||||
|
(lambda t: t ** 2 - 2.0, 0.5, 2.0),
|
||||||
|
(lambda t: math.cos(t) - t, 0.0, 1.0),
|
||||||
|
]
|
||||||
|
# the bracket must straddle zero AND contain a sign change of the SLOPE: f' = 3t^2 - 1
|
||||||
|
# vanishes at t = +-0.577, so the local slope estimate flips sign inside (-2, 2).
|
||||||
|
# (An earlier version used sin(8t)e^-t + 0.05 on (0,1), which does not straddle zero at
|
||||||
|
# all — f(0) = +0.05, f(1) = +0.41 — and the method rightly refused the call.)
|
||||||
|
WOBBLY = (lambda t: t ** 3 - t + 0.2, -2.0, 2.0)
|
||||||
|
|
||||||
|
|
||||||
|
def test_T1_the_published_path_is_bit_identical_with_the_gate_off():
|
||||||
|
for f, a, b in SMOOTH:
|
||||||
|
for guard in ("credit", "project", "safe", "step"):
|
||||||
|
base = P.pure_root(f, a, b, 1e-12, 300, similarity="ratio",
|
||||||
|
scale="derived3", guard=guard)
|
||||||
|
off = P.pure_root(f, a, b, 1e-12, 300, similarity="ratio",
|
||||||
|
scale="derived3", guard=guard, prestep="none")
|
||||||
|
assert base == off, (guard, base, off)
|
||||||
|
|
||||||
|
|
||||||
|
def test_T2_the_gate_carries_no_constant():
|
||||||
|
src = inspect.getsource(P.pure_root)
|
||||||
|
start = src.index('if prestep == "slope":')
|
||||||
|
end = src.index('if step is None and prestep == "progress"', start)
|
||||||
|
code = "\n".join(line.split("#", 1)[0] for line in src[start:end].splitlines())
|
||||||
|
literals = set(re.findall(r"(?<![\w.])\d+\.\d+(?![\w.])", code))
|
||||||
|
assert literals <= {"0.0", "0.5"}, f"constants in the gate: {literals}"
|
||||||
|
assert ">" in code and "!=" in code # a SIGN comparison, not a magnitude test
|
||||||
|
|
||||||
|
|
||||||
|
def test_T3_the_gate_is_inert_where_the_slope_sign_is_stable():
|
||||||
|
"""This is what the progress gate could not do — it fired on excellent steps."""
|
||||||
|
for f, a, b in SMOOTH:
|
||||||
|
off = P.pure_root(f, a, b, 1e-12, 300, similarity="ratio", scale="derived3",
|
||||||
|
guard="credit")
|
||||||
|
on = P.pure_root(f, a, b, 1e-12, 300, similarity="ratio", scale="derived3",
|
||||||
|
guard="credit", prestep="slope")
|
||||||
|
assert on[1] == off[1], (off, on) # identical evaluation count
|
||||||
|
|
||||||
|
|
||||||
|
def test_T4_the_gate_fires_where_the_slope_sign_flips():
|
||||||
|
f, a, b = WOBBLY
|
||||||
|
st = {}
|
||||||
|
x, n, ok = P.pure_root(f, a, b, 1e-10, 300, similarity="ratio", scale="derived3",
|
||||||
|
guard="credit", prestep="slope", stats=st)
|
||||||
|
assert ok and abs(f(x)) < 1e-6
|
||||||
|
assert st["pre_refused"] >= 1, st
|
||||||
|
|
||||||
|
|
||||||
|
def test_T5_a_refusal_costs_no_extra_evaluation():
|
||||||
|
f, a, b = WOBBLY
|
||||||
|
st = {}
|
||||||
|
_x, n, ok = P.pure_root(f, a, b, 1e-10, 300, similarity="ratio", scale="derived3",
|
||||||
|
guard="credit", prestep="slope", stats=st)
|
||||||
|
assert ok
|
||||||
|
assert st["pre_refused"] <= n - 2, (st, n)
|
||||||
|
|
||||||
|
|
||||||
|
def test_the_mode_needs_a_slope_to_judge():
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
P.pure_root(lambda t: t, -1.0, 1.0, 1e-10, 100, scale="bracket", prestep="slope")
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
P.pure_root(lambda t: t, -1.0, 1.0, 1e-10, 100, prestep="nonsense")
|
||||||
|
|
||||||
|
|
||||||
|
def test_both_gates_remain_available_and_distinct():
|
||||||
|
""""progress" is kept as a MEASURED NEGATIVE (APS 4449 vs 2806), not deleted."""
|
||||||
|
f, a, b = WOBBLY
|
||||||
|
a1 = P.pure_root(f, a, b, 1e-10, 300, similarity="ratio", scale="derived3",
|
||||||
|
guard="credit", prestep="progress")
|
||||||
|
a2 = P.pure_root(f, a, b, 1e-10, 300, similarity="ratio", scale="derived3",
|
||||||
|
guard="credit", prestep="slope")
|
||||||
|
assert a1[2] and a2[2]
|
||||||
|
assert a1 != a2 or a1[1] == a2[1] # they may coincide, but both must run
|
||||||
Reference in New Issue
Block a user