Controls K1/K2: code and two PREREG amendments, committed before the full run.
simroot/pure.py: flatlagmempt / flatbandmempt / flatflagmempt - A' with only the flat detector swapped (Lagouanelle without declining outside (0,1), with memory; Chandrupatla's band; none). A' itself is untouched: bit-identical to the compiled simbracket on all 868 problems (x2). sweep_controls.py: K2 through sweep_fourth's harness, K1 with the oracle order m*. Amendment 1: m* <= 0 is undefined (aps.15.22-30, C0.04 crashed the first full attempt). Amendment 2 (after the smokes; only corpus-level K1 sums and corpus-4 K2 sums were seen): each side's slope rounded to 0.01, m* defined only when both sides show a zero (jumps C0.03, aps.15.21), group 'multiple/fractional' = m* >= 2 or m* <= 2/3, the branches of section 8.1 (simple roots measured at 0.99999 fell under the registered 'm* < 1'). tests/test_article_numbers.py: the verifier test runs on Linux only (the paper's numbers are a Linux run; libm's last digit differs elsewhere, e.g. 4461 vs 4462). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -88,6 +88,35 @@ m_L — оценка Лагуанеля по трём последним точ
|
|||||||
любая новая идея — отдельная предрегистрация. A′ заморожен: его код и результаты не трогаются, в
|
любая новая идея — отдельная предрегистрация. A′ заморожен: его код и результаты не трогаются, в
|
||||||
прогоне он воспроизводится бит в бит (проверяется: суммы A′ = таблица 10).
|
прогоне он воспроизводится бит в бит (проверяется: суммы A′ = таблица 10).
|
||||||
|
|
||||||
|
## Поправка 1 (после смоука, до полного прогона; результатов К1 и К2 на полных корпусах нет)
|
||||||
|
|
||||||
|
Полный прогон остановился делением на ноль: на десяти задачах оракул дал m\* ≤ 0 — `aps.15.22`–
|
||||||
|
`aps.15.30` (кусочно-линейная функция APS 15, линейный участок у корня уже h, дальше |f| постоянна:
|
||||||
|
m\* = 0) и `C0.04` нового корпуса 1 (|f| растёт к корню: m\* = −1). Степенной модели нуля в масштабе h
|
||||||
|
там нет, так что это случай «не определено»: **m\* ≤ 0 → m\* = 1**, преобразование не применяется.
|
||||||
|
Определение h и всё остальное не меняются.
|
||||||
|
|
||||||
|
## Поправка 2 (после второго смоука, до полного прогона)
|
||||||
|
|
||||||
|
Видено к этому моменту: смоук К2 только на корпусе 4 (суммы и w/t/l всех трёх вариантов) и смоук К1 —
|
||||||
|
**суммы по корпусам целиком**, без разбиения на группы. Проверка правдоподобия оракула нашла два
|
||||||
|
изъяна определения, оба — не в пользу какой-либо стороны:
|
||||||
|
|
||||||
|
1. **Скачки.** `C0.03` (f прыгает от −0.9 к 1.1 в корне) даёт m\* = 1.5·10⁻⁶; `aps.15.21` — наклон 0 с
|
||||||
|
одной стороны и 0.22 с другой, среднее 0.11. Нуля там нет, «порядок» — шум у нуля.
|
||||||
|
2. **Граница группы в точке шума.** Простой корень измеряется как 0.99999…, и правило «m\* < 1»
|
||||||
|
относило его к дробным; порядок 2 (например, log(1+q²) в D5) измеряется как 1.99999… или 2.00001.
|
||||||
|
|
||||||
|
Исправленное определение:
|
||||||
|
|
||||||
|
- наклон с каждой стороны **округляется до 0.01** (разрешение объявлено: у двухточечного наклона при
|
||||||
|
h = 10⁻⁵ ошибка порядка 10⁻⁵, так что 0.01 его не задевает); m\* **определён, только если обе
|
||||||
|
стороны положительны** (нуль виден с обеих сторон), и равен среднему двух округлённых наклонов;
|
||||||
|
иначе m\* = 1. Поправка 1 (m\* ≤ 0 → 1) входит сюда как частный случай;
|
||||||
|
- группа «кратные/дробные» — **m\* ≥ 2 или m\* ≤ ⅔**: ровно ветви §8.1, где касательный шаг проигрывает
|
||||||
|
делению пополам (те же константы, что в A′ и в К2; нового числа нет). Дробные порядки из (⅔, 1)
|
||||||
|
попадают в «остальные» — по теории §8.1 там касательный шаг сам бьёт деление.
|
||||||
|
|
||||||
## Реализация (план, до кода)
|
## Реализация (план, до кода)
|
||||||
|
|
||||||
- `simroot/pure.py`: три новых варианта плоского режима — `flatlagmempt`, `flatbandmempt`,
|
- `simroot/pure.py`: три новых варианта плоского режима — `flatlagmempt`, `flatbandmempt`,
|
||||||
|
|||||||
+60
-7
@@ -634,6 +634,31 @@ def _analogy_order(cur, past, translation=False):
|
|||||||
return math.log(lf) / math.log(lx)
|
return math.log(lf) / math.log(lx)
|
||||||
|
|
||||||
|
|
||||||
|
def _lagouanelle_raw(x2, f2, x1, f1, x0, f0):
|
||||||
|
"""PREREG_controls_2026-09-22 (control K2): Lagouanelle's order from the parabola through the
|
||||||
|
last three points WITHOUT declining outside (0, 1): theta = f f''/f'^2, m = 1/(1 - theta) for
|
||||||
|
theta < 1 (so 0 < m < 1 when theta < 0, the fractional branch), m = inf for theta >= 1. None
|
||||||
|
when f' is zero, not finite, or its square underflows. A control, not part of A'."""
|
||||||
|
if x0 is None or len({x0, x1, x2}) < 3:
|
||||||
|
return None
|
||||||
|
d01 = (f1 - f0) / (x1 - x0)
|
||||||
|
d12 = (f2 - f1) / (x2 - x1)
|
||||||
|
d012 = (d12 - d01) / (x2 - x0)
|
||||||
|
fp = d12 + d012 * (x2 - x1)
|
||||||
|
fpp = 2.0 * d012
|
||||||
|
if fp == 0.0 or not math.isfinite(fp) or not math.isfinite(fpp):
|
||||||
|
return None
|
||||||
|
den = fp * fp
|
||||||
|
if den == 0.0:
|
||||||
|
return None
|
||||||
|
theta = f2 * fpp / den
|
||||||
|
if not math.isfinite(theta):
|
||||||
|
return None
|
||||||
|
if theta >= 1.0:
|
||||||
|
return math.inf
|
||||||
|
return 1.0 / (1.0 - theta)
|
||||||
|
|
||||||
|
|
||||||
def _analogous_and_steep(cur, past):
|
def _analogous_and_steep(cur, past):
|
||||||
"""Is the configuration nearer to its own past than to the line phi = xi, and does the
|
"""Is the configuration nearer to its own past than to the line phi = xi, and does the
|
||||||
analogous pair scale like a power of order m >= 2? Two ordinal judgments and the
|
analogous pair scale like a power of order m >= 2? Two ordinal judgments and the
|
||||||
@@ -772,15 +797,18 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
|||||||
parts = prestep.split("+")
|
parts = prestep.split("+")
|
||||||
cats = ({"defect"}, {"contra"}, {"exp"}, {"half"}, {"close"}, {"sub"},
|
cats = ({"defect"}, {"contra"}, {"exp"}, {"half"}, {"close"}, {"sub"},
|
||||||
{"flatanalog", "flatanalogmem", "flatanalogpt",
|
{"flatanalog", "flatanalogmem", "flatanalogpt",
|
||||||
"flatanalogmempt"})
|
"flatanalogmempt", "flatlagmempt", "flatbandmempt",
|
||||||
|
"flatflagmempt"})
|
||||||
if (len(parts) != len(set(parts)) or any(not any(p in c for c in cats) for p in parts)
|
if (len(parts) != len(set(parts)) or any(not any(p in c for c in cats) for p in parts)
|
||||||
or any(sum(p in c for p in parts) > 1 for c in cats)):
|
or any(sum(p in c for p in parts) > 1 for c in cats)):
|
||||||
raise ValueError(f"unknown composite prestep {prestep!r}")
|
raise ValueError(f"unknown composite prestep {prestep!r}")
|
||||||
if (("exp" in parts or "sub" in parts)
|
if "exp" in parts and not any(p.startswith("flatanalog") for p in parts):
|
||||||
and not any(p.startswith("flatanalog") for p in parts)):
|
|
||||||
# the translation limit changes how the analogy reads its order; without the
|
# the translation limit changes how the analogy reads its order; without the
|
||||||
# flat regime there is no analogy to read (PREREG_flat_exp_limit)
|
# flat regime there is no analogy to read (PREREG_flat_exp_limit)
|
||||||
raise ValueError("prestep component 'exp' needs a flatanalog* component")
|
raise ValueError("prestep component 'exp' needs a flatanalog* component")
|
||||||
|
if "sub" in parts and not any(p.startswith("flat") for p in parts):
|
||||||
|
# the overshoot branch enters the flat regime; it needs one (PREREG_close_sub)
|
||||||
|
raise ValueError("prestep component 'sub' needs a flat* regime component")
|
||||||
elif prestep not in ("none", "progress", "slope", "band", "defect", "simpair", "flat",
|
elif prestep not in ("none", "progress", "slope", "band", "defect", "simpair", "flat",
|
||||||
"flat2", "flatcreep", "flatstick", "flatstickmid", "flatstickexit",
|
"flat2", "flatcreep", "flatstick", "flatstickmid", "flatstickexit",
|
||||||
"flatjudge", "flatanalog", "flatanalogmem", "contra",
|
"flatjudge", "flatanalog", "flatanalogmem", "contra",
|
||||||
@@ -789,10 +817,17 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
|||||||
parts = prestep.split("+")
|
parts = prestep.split("+")
|
||||||
pre_dec = next((p for p in parts if p in ("band", "defect", "simpair")), "none")
|
pre_dec = next((p for p in parts if p in ("band", "defect", "simpair")), "none")
|
||||||
pre_flat = next((p for p in parts if p in ("flatanalog", "flatanalogmem", "flatanalogpt",
|
pre_flat = next((p for p in parts if p in ("flatanalog", "flatanalogmem", "flatanalogpt",
|
||||||
"flatanalogmempt")), "none")
|
"flatanalogmempt", "flatlagmempt",
|
||||||
|
"flatbandmempt", "flatflagmempt")), "none")
|
||||||
# PREREG_flat_order_point: the "pt" twins split at the point from the pair's order
|
# PREREG_flat_order_point: the "pt" twins split at the point from the pair's order
|
||||||
flat_mem = pre_flat in ("flatanalogmem", "flatanalogmempt")
|
# PREREG_controls_2026-09-22: flatlag/flatband/flatflag are A' with ONLY the detector swapped
|
||||||
flat_pt = pre_flat in ("flatanalogpt", "flatanalogmempt")
|
# (Lagouanelle with memory, Chandrupatla's band, no detector); memory, split, exit unchanged
|
||||||
|
detector = {"flatlagmempt": "lag", "flatbandmempt": "band",
|
||||||
|
"flatflagmempt": "flag"}.get(pre_flat, "analogy")
|
||||||
|
flat_mem = pre_flat in ("flatanalogmem", "flatanalogmempt", "flatlagmempt", "flatbandmempt",
|
||||||
|
"flatflagmempt")
|
||||||
|
flat_pt = pre_flat in ("flatanalogpt", "flatanalogmempt", "flatlagmempt", "flatbandmempt",
|
||||||
|
"flatflagmempt")
|
||||||
pre_contra = "contra" in parts
|
pre_contra = "contra" in parts
|
||||||
pre_exp = "exp" in parts # PREREG_flat_exp_limit: translation similarity, m = inf
|
pre_exp = "exp" in parts # PREREG_flat_exp_limit: translation similarity, m = inf
|
||||||
pre_half = "half" in parts # PREREG_half_contra: the half promised vs the bracket
|
pre_half = "half" in parts # PREREG_half_contra: the half promised vs the bracket
|
||||||
@@ -1089,7 +1124,11 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
|||||||
cur = _config3(x, fx, x_prev, f_prev, x_prev2, f_prev2)
|
cur = _config3(x, fx, x_prev, f_prev, x_prev2, f_prev2)
|
||||||
flag = (sticky_m is None and last_model and f_prev != 0.0
|
flag = (sticky_m is None and last_model and f_prev != 0.0
|
||||||
and fx / f_prev >= 0.25)
|
and fx / f_prev >= 0.25)
|
||||||
|
m_now = None # controls band/flag: the split reads the current m_L
|
||||||
|
if detector == "analogy":
|
||||||
m_pair = _analogy_order(cur, configs, translation=pre_exp)
|
m_pair = _analogy_order(cur, configs, translation=pre_exp)
|
||||||
|
else:
|
||||||
|
m_pair = _lagouanelle_raw(x, fx, x_prev, f_prev, x_prev2, f_prev2)
|
||||||
steep = m_pair is not None and m_pair >= 2.0
|
steep = m_pair is not None and m_pair >= 2.0
|
||||||
# PREREG_close_sub 2026-09-21 (sub): the tangent step loses to a halving
|
# PREREG_close_sub 2026-09-21 (sub): the tangent step loses to a halving
|
||||||
# when |1 - 1/m| > 1/2 - for m > 2 (undershoot, the branch above) AND
|
# when |1 - 1/m| > 1/2 - for m > 2 (undershoot, the branch above) AND
|
||||||
@@ -1097,6 +1136,19 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
|||||||
# a sign change; the pair's order says which branch it is.
|
# a sign change; the pair's order says which branch it is.
|
||||||
shallow = (pre_sub and m_pair is not None
|
shallow = (pre_sub and m_pair is not None
|
||||||
and 0.0 < m_pair <= 2.0 / 3.0)
|
and 0.0 < m_pair <= 2.0 / 3.0)
|
||||||
|
if detector in ("band", "flag"):
|
||||||
|
# PREREG_controls K2-P / K2-F: the regime is recognised by
|
||||||
|
# Chandrupatla's band (band rejects the triple) or not at all;
|
||||||
|
# the split uses m_L of the current state, inf if undefined
|
||||||
|
m_now = math.inf if m_pair is None else m_pair
|
||||||
|
if detector == "band":
|
||||||
|
rej = _decide_refuse("band", S, z_now, x, fx, x_prev, f_prev,
|
||||||
|
x_prev2, f_prev2)
|
||||||
|
else:
|
||||||
|
rej = True
|
||||||
|
steep = rej
|
||||||
|
shallow = pre_sub and rej
|
||||||
|
m_pair = m_now
|
||||||
flag_over = (pre_sub and sticky_m is None and last_model
|
flag_over = (pre_sub and sticky_m is None and last_model
|
||||||
and f_prev != 0.0 and fx / f_prev <= -0.25)
|
and f_prev != 0.0 and fx / f_prev <= -0.25)
|
||||||
if flat_mem:
|
if flat_mem:
|
||||||
@@ -1129,7 +1181,8 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
|||||||
if flat_pt:
|
if flat_pt:
|
||||||
# PREREG_flat_order_point 2026-09-21: split where |f| ~
|
# PREREG_flat_order_point 2026-09-21: split where |f| ~
|
||||||
# |x - r|^m puts the root, m read from the analogous pair
|
# |x - r|^m puts the root, m read from the analogous pair
|
||||||
regime_target = _order_split(A, FA, B, FB, analogy_m)
|
regime_target = _order_split(
|
||||||
|
A, FA, B, FB, analogy_m if m_now is None else m_now)
|
||||||
step = regime_target - x
|
step = regime_target - x
|
||||||
else:
|
else:
|
||||||
step = 0.5 * (A + B) - x
|
step = 0.5 * (A + B) - x
|
||||||
|
|||||||
@@ -0,0 +1,225 @@
|
|||||||
|
"""PREREG_controls_2026-09-22: the controls, as registered (commit 77312be).
|
||||||
|
|
||||||
|
K2 (point 6): A' with only the flat detector swapped - Lagouanelle with memory (K2L),
|
||||||
|
Chandrupatla's band (K2P), no detector (K2F) - on the six corpora of section 9, harness of
|
||||||
|
sweep_fourth.py (same problems, seeding, charging, uniform criterion).
|
||||||
|
K1 (point 5): library Chandrupatla and Brent on g = sign(f)|f|^(1/m*), m* the true order read at
|
||||||
|
the known root, on the four corpora whose roots are known.
|
||||||
|
|
||||||
|
python -u sweep_controls.py run -> sweep_controls.json, then the report
|
||||||
|
python -u sweep_controls.py --smoke K2 on corpus 4 and K1 on all four corpora, no file
|
||||||
|
python -u sweep_controls.py --report
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import math
|
||||||
|
import random
|
||||||
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
|
from multiprocessing import Pool
|
||||||
|
|
||||||
|
import sweep_fourth
|
||||||
|
from corpus_roots_jl import solved
|
||||||
|
from sweep_full import MAXFEV, XTOL
|
||||||
|
from sweep_oos import sign_p
|
||||||
|
|
||||||
|
TAIL = "+half+close+sub"
|
||||||
|
K2 = {"K2L": "contra+flatlagmempt" + TAIL, "K2P": "contra+flatbandmempt" + TAIL,
|
||||||
|
"K2F": "contra+flatflagmempt" + TAIL}
|
||||||
|
for _k, _pre in K2.items():
|
||||||
|
sweep_fourth.CONFIGS[_k] = sweep_fourth.spec(_pre)
|
||||||
|
SETS = ("APS", "CHA", "NEW", "RJL", "APP", "D")
|
||||||
|
K1_SETS = ("APS", "CHA", "NEW", "D")
|
||||||
|
|
||||||
|
|
||||||
|
def m_star(f, r):
|
||||||
|
"""The order at the known root: slope of ln|f| against ln|x - r|, both sides averaged,
|
||||||
|
h = 1e-5 max(1, |r|) (registered). 1 when undefined."""
|
||||||
|
# amendment 2 of the PREREG (after the smokes, before any full result): each side's slope
|
||||||
|
# is rounded to 0.01 and m* is defined only when BOTH sides show a zero (slope > 0) -
|
||||||
|
# a jump (C0.03), a flat side (aps.15.21-30) or a pole-like side (C0.04) is undefined
|
||||||
|
h = 1e-5 * max(1.0, abs(r))
|
||||||
|
vals = []
|
||||||
|
for s in (1.0, -1.0):
|
||||||
|
try:
|
||||||
|
f1, f2 = abs(f(r + s * h)), abs(f(r + s * 2 * h))
|
||||||
|
m = (math.log(f2) - math.log(f1)) / math.log(2.0)
|
||||||
|
except (ValueError, OverflowError, ZeroDivisionError):
|
||||||
|
return 1.0
|
||||||
|
if not math.isfinite(m) or round(m, 2) <= 0.0:
|
||||||
|
return 1.0
|
||||||
|
vals.append(round(m, 2))
|
||||||
|
return (vals[0] + vals[1]) / 2.0
|
||||||
|
|
||||||
|
|
||||||
|
def nonsimple(m):
|
||||||
|
"""The group 'multiple/fractional': the branches of section 8.1 where a tangent step loses to
|
||||||
|
a halving, m >= 2 or m <= 2/3 (amendment 2)."""
|
||||||
|
return m >= 2.0 or m <= 2.0 / 3.0
|
||||||
|
|
||||||
|
|
||||||
|
def k1_problems(which):
|
||||||
|
from corpus_fourth import build as b4
|
||||||
|
from corpus_new import build as b1
|
||||||
|
from sweep_full import load_problems
|
||||||
|
out = []
|
||||||
|
if which in ("APS", "CHA"):
|
||||||
|
out = [(pid, f, a, b, r) for s, pid, f, a, b, r in load_problems() if s == which]
|
||||||
|
elif which == "NEW":
|
||||||
|
out = [(pid, f, a, b, r) for _c, pid, f, a, b, r in b1()[0]]
|
||||||
|
else:
|
||||||
|
out = [(pid, f, a, b, r) for _c, pid, f, a, b, r in b4()[0]]
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def k1_job(which):
|
||||||
|
import scipy.optimize._tstutils as T
|
||||||
|
|
||||||
|
from simroot.baselines import brent_scipy, chandrupatla_lib
|
||||||
|
res = {"chandrupatla_g": {}, "brent_g": {}, "mstar": {}}
|
||||||
|
for pid, f, a, b, r in k1_problems(which):
|
||||||
|
T._f6_cache.clear()
|
||||||
|
random.seed(20260921)
|
||||||
|
m = m_star(f, r)
|
||||||
|
res["mstar"][pid] = m
|
||||||
|
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 in (("chandrupatla_g", chandrupatla_lib), ("brent_g", brent_scipy)):
|
||||||
|
T._f6_cache.clear()
|
||||||
|
random.seed(20260921)
|
||||||
|
try:
|
||||||
|
x, n, conv = meth(g, a, b, XTOL, MAXFEV)
|
||||||
|
x = float(x)
|
||||||
|
except Exception:
|
||||||
|
x, n, conv = float("nan"), MAXFEV, False
|
||||||
|
ok = bool(conv and solved(f, x))
|
||||||
|
over = ok and int(n) > 2 * math.ceil(math.log2(abs(b - a) / XTOL)) + 4
|
||||||
|
res[name][pid] = [int(n), ok, bool(over)]
|
||||||
|
return which, res
|
||||||
|
|
||||||
|
|
||||||
|
def k2_job(j):
|
||||||
|
name, which = j
|
||||||
|
return name, which, sweep_fourth.evaluate((name, which))[1]
|
||||||
|
|
||||||
|
|
||||||
|
def cost(v):
|
||||||
|
return v[0] if v[1] else MAXFEV
|
||||||
|
|
||||||
|
|
||||||
|
def h2h(a, b, keep=None):
|
||||||
|
ps = [p for p in a if keep is None or keep(p)]
|
||||||
|
w = sum(cost(a[p]) < cost(b[p]) for p in ps)
|
||||||
|
l = sum(cost(a[p]) > cost(b[p]) for p in ps)
|
||||||
|
return w, len(ps) - w - l, l
|
||||||
|
|
||||||
|
|
||||||
|
def tot(r, keep=None):
|
||||||
|
return sum(cost(v) for p, v in r.items() if keep is None or keep(p))
|
||||||
|
|
||||||
|
|
||||||
|
def reference():
|
||||||
|
seen = json.load(open("sweep_fourth_seen.json", encoding="utf8"))
|
||||||
|
four = json.load(open("sweep_fourth_fourth.json", encoding="utf8"))
|
||||||
|
ref = {}
|
||||||
|
for k in ("Aprime", "chandrupatla"):
|
||||||
|
ref[k] = dict(seen[k])
|
||||||
|
ref[k].update(four[k])
|
||||||
|
return ref
|
||||||
|
|
||||||
|
|
||||||
|
def report(R):
|
||||||
|
ref = reference()
|
||||||
|
ap = ref["Aprime"]
|
||||||
|
print("C2 - six corpora, A' against each detector (total; A' w/t/l, p); violations in ()")
|
||||||
|
for k in K2:
|
||||||
|
line = [f" {k}:"]
|
||||||
|
for s in SETS:
|
||||||
|
w, t, l = h2h(ap[s], R["K2"][k][s])
|
||||||
|
line.append(f"{s} {tot(R['K2'][k][s])}({sum(v[2] for v in R['K2'][k][s].values())})"
|
||||||
|
f" {w}/{t}/{l}")
|
||||||
|
allk = sum(tot(R["K2"][k][s]) for s in SETS)
|
||||||
|
print(" ".join(line) + f" | all {allk} vs A' {sum(tot(ap[s]) for s in SETS)}")
|
||||||
|
print("\nC1 - corpus 4 (decisive): A' total, control total, A' w/t/l, p -> verdict")
|
||||||
|
passed = {}
|
||||||
|
for k in K2:
|
||||||
|
a, c = ap["D"], R["K2"][k]["D"]
|
||||||
|
w, t, l = h2h(a, c)
|
||||||
|
passed[k] = tot(a) < tot(c) and w > l and sign_p(w, l) < 0.05
|
||||||
|
print(f" {k}: A' {tot(a)} {k} {tot(c)} {w}/{t}/{l} p={sign_p(w, l):.2g} -> "
|
||||||
|
+ ("PASSED" if passed[k] else "NOT passed"))
|
||||||
|
print(" C1 overall: " + ("PASSED for every control" if all(passed.values())
|
||||||
|
else "NOT passed for " + ", ".join(k for k in K2 if not passed[k])))
|
||||||
|
print("\nC4 - violations of the 2x bound, all K2 variants, six corpora:",
|
||||||
|
sum(v[2] for k in K2 for s in SETS for v in R["K2"][k][s].values()))
|
||||||
|
best = min(K2, key=lambda k: tot(R["K2"][k]["D"]))
|
||||||
|
print(f"\nC5 - corpus 4 by class, A' against the best control ({best}): totals, w/t/l")
|
||||||
|
for cls in ("D1", "D2", "D3", "D4", "D5", "D6"):
|
||||||
|
keep = lambda p, c=cls: p.startswith(c) # noqa: E731
|
||||||
|
w, t, l = h2h(ap["D"], R["K2"][best]["D"], keep)
|
||||||
|
print(f" {cls}: A' {tot(ap['D'], keep)} {best} {tot(R['K2'][best]['D'], keep)} {w}/{t}/{l}")
|
||||||
|
print("\nC3 - K1: transform with the true order (m* >= 2 or m* <= 2/3 = 'multiple/fractional')")
|
||||||
|
for s in K1_SETS:
|
||||||
|
k1 = R["K1"][s]
|
||||||
|
mult = lambda p, _m=k1["mstar"]: nonsimple(_m[p]) # noqa: E731
|
||||||
|
rest = lambda p, _m=k1["mstar"]: not nonsimple(_m[p]) # noqa: E731
|
||||||
|
for title, keep in (("multiple/fractional", mult), ("rest", rest)):
|
||||||
|
n = sum(1 for p in k1["mstar"] if keep(p))
|
||||||
|
if n == 0:
|
||||||
|
continue
|
||||||
|
w, t, l = h2h({p: v for p, v in ap[s].items() if keep(p)}, k1["chandrupatla_g"])
|
||||||
|
print(f" {s:<4} {title:<20} n={n:3d} A' {tot(ap[s], keep):5d} "
|
||||||
|
f"Chandrupatla(g) {tot(k1['chandrupatla_g'], keep):5d} "
|
||||||
|
f"Brent(g) {tot(k1['brent_g'], keep):5d} "
|
||||||
|
f"Chandrupatla(f) {tot(ref['chandrupatla'][s], keep):5d} "
|
||||||
|
f"A' vs Ch(g) {w}/{t}/{l} p={sign_p(w, l):.2g}")
|
||||||
|
fam = defaultdict(list)
|
||||||
|
for s in K1_SETS:
|
||||||
|
for p, m in R["K1"][s]["mstar"].items():
|
||||||
|
if nonsimple(m):
|
||||||
|
fam[s].append(round(m, 2))
|
||||||
|
print(" m* on the multiple/fractional problems (first 8 per corpus):",
|
||||||
|
{s: v[:8] for s, v in fam.items()})
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
smoke = "--smoke" in sys.argv
|
||||||
|
if "--report" not in sys.argv:
|
||||||
|
k2_jobs = [(k, "fourth") for k in K2] + ([] if smoke else [(k, "seen") for k in K2])
|
||||||
|
k1_sets = K1_SETS # the oracle must meet every corpus, also in the smoke
|
||||||
|
with Pool(min(6, len(k2_jobs) + len(k1_sets))) as pool:
|
||||||
|
k2 = pool.map_async(k2_job, k2_jobs)
|
||||||
|
k1 = pool.map_async(k1_job, k1_sets)
|
||||||
|
k2, k1 = k2.get(), k1.get()
|
||||||
|
R = {"K2": {}, "K1": {}}
|
||||||
|
for name, _which, res in k2:
|
||||||
|
R["K2"].setdefault(name, {}).update(res)
|
||||||
|
for which, res in k1:
|
||||||
|
R["K1"][which] = res
|
||||||
|
if smoke:
|
||||||
|
ap = reference()["Aprime"]
|
||||||
|
for k in K2:
|
||||||
|
a, c = ap["D"], R["K2"][k]["D"]
|
||||||
|
print(f"smoke {k}: corpus 4 total {tot(c)} (A' {tot(a)}), A' w/t/l {h2h(a, c)}, "
|
||||||
|
f"violations {sum(v[2] for v in c.values())}")
|
||||||
|
for s in K1_SETS:
|
||||||
|
k1c = R["K1"][s]
|
||||||
|
print(f"smoke K1 {s}: Chandrupatla(g) {tot(k1c['chandrupatla_g'])} Brent(g) "
|
||||||
|
f"{tot(k1c['brent_g'])} m* range {min(k1c['mstar'].values()):.3g}.."
|
||||||
|
f"{max(k1c['mstar'].values()):.3g}")
|
||||||
|
print("controls smoke done")
|
||||||
|
return
|
||||||
|
with open("sweep_controls.json", "w", encoding="utf8") as fh:
|
||||||
|
json.dump(R, fh)
|
||||||
|
with open("sweep_controls.json", encoding="utf8") as fh:
|
||||||
|
R = json.load(fh)
|
||||||
|
report(R)
|
||||||
|
print("controls done")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -22,6 +22,10 @@ CREDIT_LABEL = "pure.ratio.derived3.credit"
|
|||||||
|
|
||||||
@pytest.mark.skipif(not os.path.exists(os.path.join(ROOT, "sweep_full.json")),
|
@pytest.mark.skipif(not os.path.exists(os.path.join(ROOT, "sweep_full.json")),
|
||||||
reason="sweep_full.json not present; run sweep_full.py")
|
reason="sweep_full.json not present; run sweep_full.py")
|
||||||
|
@pytest.mark.skipif(not sys.platform.startswith("linux"),
|
||||||
|
reason="the paper's numbers come from a Linux run (section 4, Reproducibility); "
|
||||||
|
"verify_article.py recomputes some of them, and libm's last digit "
|
||||||
|
"differs on other platforms (e.g. 4461 vs 4462 in Table 1b)")
|
||||||
def test_every_number_in_the_paper_matches_the_sweep():
|
def test_every_number_in_the_paper_matches_the_sweep():
|
||||||
r = subprocess.run([sys.executable, "verify_article.py"], cwd=ROOT,
|
r = subprocess.run([sys.executable, "verify_article.py"], cwd=ROOT,
|
||||||
capture_output=True, text=True, timeout=600)
|
capture_output=True, text=True, timeout=600)
|
||||||
|
|||||||
Reference in New Issue
Block a user