Add the missing new file of the previous commit: the command that recomputes every итог number from raw
This commit is contained in:
parent
6aa7cfa8da
commit
84bc73baa5
1 changed files with 384 additions and 0 deletions
384
eval/role_topology/itog.py
Normal file
384
eval/role_topology/itog.py
Normal file
|
|
@ -0,0 +1,384 @@
|
|||
#!/usr/bin/env python3
|
||||
"""ИТОГ ЭКСПА ОДНОЙ КОМАНДОЙ. Пере-считывает КАЖДОЕ число блока «ИТОГ» отчёта из сырья. $0.
|
||||
|
||||
ЗАЧЕМ ЗАВЕДЁН. Два независимых аудита 08.08 (снизу вверх и сверху вниз) сошлись на одном дефекте
|
||||
оснастки: несущие числа отчёта — нога 2×2, контрасты Ф2б, покрытие канона, отборщики арма G —
|
||||
**в дереве пути воспроизведения не имели**. `absjudge --score` печатает семейство Ф2а и на узком
|
||||
проходе 4 выдаёт пустую таблицу (в `FAMILY` нет контрастов C2/E2); `verify_report` сторожил
|
||||
таблицы §2 второй редакции, деньги и батарею — и ни одного числа ИТОГа. То есть решающий слой
|
||||
отчёта стоял без стража, а часть чисел бралась из stdout прогонов, утраченных инцидентом 08.08.
|
||||
|
||||
Это ровно тот класс дефекта, за который снята первая редакция, только для чисел, а не для денег:
|
||||
число, которое нельзя пере-снять командой из дерева, ничем не отличается от числа по памяти.
|
||||
|
||||
ЧТО ЗДЕСЬ НЕ ДЕЛАЕТСЯ. Ничего не покупается и не судится: только счёт по персистированным голосам
|
||||
и артефактам. Каждый блок печатает провенанс — из какого прохода и по какой формуле взято, — и
|
||||
явно метит числа, которые в дереве не выводятся ни одним способом.
|
||||
|
||||
Запуск: eval/.venv/bin/python eval/role_topology/itog.py
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import json
|
||||
import re
|
||||
import statistics as st
|
||||
import sys
|
||||
from bisect import bisect_left, bisect_right
|
||||
from itertools import combinations
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path("/home/ubuntu/projects/textmachine")
|
||||
sys.path.insert(0, str(REPO / "eval" / "role_topology"))
|
||||
sys.path.insert(0, str(REPO / "eval" / "editor_harness"))
|
||||
|
||||
import absjudge as AJ # noqa: E402
|
||||
import bakeoff as BO # noqa: E402
|
||||
import guarded as G # noqa: E402
|
||||
import phase2b as P2 # noqa: E402
|
||||
|
||||
OUT = Path.home() / "books" / "role-topology"
|
||||
|
||||
|
||||
def _E(d: dict, u: str, k: str) -> float:
|
||||
return d[u][k]["errors"]
|
||||
|
||||
|
||||
def _passes() -> tuple[dict, dict, list[str]]:
|
||||
a1, a2 = AJ._by_unit_pass("abs"), AJ._by_unit_pass("abs2")
|
||||
return a1, a2, sorted(set(a1) & set(a2))
|
||||
|
||||
|
||||
def _pooled(a1: dict, a2: dict, common: list[str], a: str, b: str) -> list[float]:
|
||||
"""Перевес за арм `a` в пуле ДВУХ проходов: сначала среднее по проходам ВНУТРИ единицы.
|
||||
|
||||
⚠ Именно так, а не «средние по проходам вычесть»: пул усредняет перевес поединично, поэтому
|
||||
разница СТРОГОСТИ панелей сокращается вычитанием внутри единицы и в контраст не попадает.
|
||||
Уровни между панелями несопоставимы (abs 6.90 → abs2 7.92 → abs3 8.46 → abs4 9.22 ошибки на
|
||||
клетку), сопоставимы только разности — на этом стоит весь ИТОГ.
|
||||
"""
|
||||
return [st.mean([_E(a1, u, b) - _E(a1, u, a), _E(a2, u, b) - _E(a2, u, a)])
|
||||
for u in common
|
||||
if a in a1[u] and b in a1[u] and a in a2[u] and b in a2[u]]
|
||||
|
||||
|
||||
def _holm(ps: list[float]) -> list[float]:
|
||||
order = sorted(range(len(ps)), key=lambda i: ps[i])
|
||||
out, run = [0.0] * len(ps), 0.0
|
||||
for rank, i in enumerate(order):
|
||||
run = max(run, min(1.0, ps[i] * (len(ps) - rank)))
|
||||
out[i] = run
|
||||
return out
|
||||
|
||||
|
||||
def p1_leg() -> None:
|
||||
print("═══ ИТОГ п.1 — НОГА 2×2 (пул проходов abs+abs2, перевес против черновика F3) ═══")
|
||||
a1, a2, common = _passes()
|
||||
print(f"единиц в обоих проходах: {len(common)}\n")
|
||||
print(f"{'арм':6s}{'n':>4s}{'перевес':>10s}{'95% ДИ':>19s}{'p':>10s}{'побед':>8s}")
|
||||
for a in ("A", "B", "D_", "D"):
|
||||
m = _pooled(a1, a2, common, a, "F3")
|
||||
lo, hi = BO.boot_ci(m)
|
||||
print(f"{a:6s}{len(m):4d}{st.mean(m):+10.2f} [{lo:+.2f}, {hi:+.2f}]"
|
||||
f"{BO.sign_perm_p(m):10.4f}{sum(1 for x in m if x > 0):5d}/{len(m)}")
|
||||
print("\n⚠ ПОСТ-ХОК. `D_ vs F3` и `D vs F3` в объявленном `absjudge.FAMILY` ОТСУТСТВУЮТ: нога")
|
||||
print(" собрана аудитом из того же сырья уже после чтения чисел. Поправку Холма они не несут")
|
||||
print(" и читаются как разведка; несущие контрасты семейства печатает `absjudge --pool`.")
|
||||
m = _pooled(a1, a2, common, "A_law", "F3")
|
||||
print(f"\nсправочно `A_law vs F3` (боевой конфиг редактора): {st.mean(m):+.2f}"
|
||||
f" · по проходам {st.mean([_E(a1, u, 'F3') - _E(a1, u, 'A_law') for u in common]):+.2f}"
|
||||
f" / {st.mean([_E(a2, u, 'F3') - _E(a2, u, 'A_law') for u in common]):+.2f}")
|
||||
|
||||
|
||||
def p1_robust() -> None:
|
||||
"""Чем несущие контрасты пробовали убить: L2O, кластеризация по главе, строгость панелей.
|
||||
|
||||
⚠ Заведено 08.08. До этого клеймы отчёта «0/120 при удалении любых ДВУХ единиц» и
|
||||
«кластеризация по главе выживает» СЧИТАЛИСЬ ВНЕ ДЕРЕВА и в репозитории пути не имели —
|
||||
тот же класс дефекта, ради которого написан этот модуль.
|
||||
"""
|
||||
print("\n═══ ИТОГ п.1 — ЧЕМ НЕСУЩИЕ КОНТРАСТЫ ПРОБОВАЛИ УБИТЬ ═══")
|
||||
a1, a2, common = _passes()
|
||||
core = [("A", "F3"), ("B", "F3"), ("D", "A")]
|
||||
k_fam = len(AJ.FAMILY)
|
||||
print(" LEAVE-TWO-OUT (удаляются любые ДВЕ единицы, Холм по семи гипотезам):")
|
||||
for a, b in core:
|
||||
m = _pooled(a1, a2, common, a, b)
|
||||
bad = sum(1 for c in combinations(range(len(m)), 2)
|
||||
if BO.sign_perm_p([x for i, x in enumerate(m) if i not in c]) * k_fam >= 0.05)
|
||||
print(f" {a + ' vs ' + b:10s} вылетов {bad}/{len(list(combinations(range(len(m)), 2)))}")
|
||||
# КЛАСТЕРИЗАЦИЯ. 16 единиц происходят не из 16 разных глав: часть глав дала по два чанка,
|
||||
# и такие единицы НЕ независимы. Тест берёт средний перевес ПО ГЛАВЕ и работает на главах.
|
||||
us = {u["uid"]: u["chapter"] for u in BO.units()}
|
||||
chapters = sorted({us[u] for u in common})
|
||||
print(f"\n КЛАСТЕРИЗАЦИЯ ПО ГЛАВЕ ИСТОЧНИКА: {len(common)} единиц из {len(chapters)} глав "
|
||||
f"({sum(1 for c in chapters if sum(1 for u in common if us[u] == c) > 1)} глав дали >1 чанка)")
|
||||
ps = []
|
||||
for a, b in core:
|
||||
m = dict(zip(common, _pooled(a1, a2, common, a, b), strict=True))
|
||||
by = [st.mean([m[u] for u in common if us[u] == c]) for c in chapters]
|
||||
ps.append(BO.sign_perm_p(by))
|
||||
for (a, b), p, h in zip(core, ps, _holm(ps + [1.0] * (k_fam - len(ps))), strict=False):
|
||||
print(f" {a + ' vs ' + b:10s} на главах p={p:.4f} · Холм {h:.4f}"
|
||||
f"{' ✔ выживает' if h < 0.05 else ''}")
|
||||
print("\n СТРОГОСТЬ ПАНЕЛЕЙ (ошибок на клетку) — уровни между проходами НЕСОПОСТАВИМЫ:")
|
||||
for nm in ("abs", "abs2", "abs3", "abs4"):
|
||||
d = AJ._by_unit_pass(nm)
|
||||
cells = [r["errors"] for v in d.values() for r in v.values()]
|
||||
if cells:
|
||||
print(f" {nm:5s} {st.mean(cells):.2f} на {len(cells)} клетках")
|
||||
print(" ⇒ поэтому ИТОГ сравнивает ТОЛЬКО внутриединичные разности, а не уровни.")
|
||||
|
||||
|
||||
def p2_canon() -> None:
|
||||
print("\n═══ ИТОГ п.2 — ПОКРЫТИЕ КАНОНА (счётная метрика, окна) ═══")
|
||||
us = BO.units()
|
||||
print(f"{'арм':7s}{'канон/исходник':>16s}{'покрытие':>10s}")
|
||||
for a in ("F3", "A", "A_law", "B", "D", "D_", "C2", "E2"):
|
||||
s = k = 0
|
||||
for u in us:
|
||||
x = P2.text_of(a, u["uid"]) if a in ("C2", "E2") else BO.text3_of(a, u)
|
||||
for zh, (_v1, _v2, r1, _r2) in BO.IP.TERMS.items():
|
||||
n = u["source"].count(zh)
|
||||
if n:
|
||||
s += n
|
||||
k += min(len(re.findall(r1, x, re.I)), n)
|
||||
print(f"{a:7s}{f'{k}/{s}':>16s}{k / s if s else 0:>10.3f}")
|
||||
print(" доля упоминаний исходника, отражённых КАНОННОЙ формой; s — знаменатель на 16 единиц")
|
||||
|
||||
|
||||
def p3_draft() -> None:
|
||||
print("\n═══ ИТОГ п.3 — СВОЙ ЧЕРНОВИК ПРОТИВ КОРПУСНОГО (тот же редактор `A`) ═══")
|
||||
a1, a2, common = _passes()
|
||||
own = {u: st.mean([_E(a1, u, "A"), _E(a2, u, "A")]) for u in common}
|
||||
for lbl, cells in (("только CTRLfloorA (клетка ИТОГа)", ("CTRLfloorA",)),
|
||||
("обе клетки пола CTRLfloorA+B", ("CTRLfloorA", "CTRLfloorB"))):
|
||||
corp, diff = {}, []
|
||||
for u in common:
|
||||
vals = [_E(d, u, c) for d in (a1, a2) for c in cells if c in d[u]]
|
||||
if not vals:
|
||||
continue
|
||||
corp[u] = st.mean(vals)
|
||||
diff.append(corp[u] - own[u])
|
||||
print(f" {lbl:34s} корпусный {st.mean(list(corp.values())):.2f} против своего "
|
||||
f"{st.mean([own[u] for u in corp]):.2f} · хуже "
|
||||
f"{sum(1 for x in diff if x > 0)}/{len(diff)} · p={BO.sign_perm_p(diff):.4f}")
|
||||
print(" клетки: CTRLfloorA=`bo2-A` · CTRLfloorB=`bo3-Aprime` (повтор того же корпусного входа)")
|
||||
print(" ⚠ ИТОГ печатает вариант ПО ОДНОЙ клетке — он в большую сторону; вердикт одинаков.")
|
||||
|
||||
|
||||
def p4_axes() -> None:
|
||||
print("\n═══ ИТОГ п.4 — ОДНОПРОХОДКА С МАНДАТОМ ПРОТИВ СВЯЗКИ, разложение по осям (пул) ═══")
|
||||
a1, a2, common = _passes()
|
||||
import judges as J # noqa: PLC0415
|
||||
for a, b in (("D", "A"), ("D_", "A")):
|
||||
row = []
|
||||
for ax in J.AXES:
|
||||
m = [st.mean([a1[u][b][ax] - a1[u][a][ax], a2[u][b][ax] - a2[u][a][ax]])
|
||||
for u in common if a in a1[u] and b in a1[u]]
|
||||
row.append(f"{ax} {st.mean(m):+.2f}")
|
||||
tot = _pooled(a1, a2, common, a, b)
|
||||
lo, hi = BO.boot_ci(tot)
|
||||
print(f" {a} vs {b}: {st.mean(tot):+.2f} [{lo:+.2f}, {hi:+.2f}] "
|
||||
f"p={BO.sign_perm_p(tot):.4f} · " + " · ".join(row))
|
||||
print(" ⚠ Оси разведочны: `absjudge --score` корректирует на 7 гипотез, здесь клеток 28.")
|
||||
|
||||
|
||||
def _qe() -> dict:
|
||||
f = OUT / "qe-arms.json"
|
||||
return json.loads(f.read_text(encoding="utf-8")) if f.exists() else {}
|
||||
|
||||
|
||||
def _pearson(xs: list[float], ys: list[float]) -> float:
|
||||
mx, my = st.mean(xs), st.mean(ys)
|
||||
num = sum((x - mx) * (y - my) for x, y in zip(xs, ys, strict=True))
|
||||
den = (sum((x - mx) ** 2 for x in xs) * sum((y - my) ** 2 for y in ys)) ** 0.5
|
||||
return num / den if den else float("nan")
|
||||
|
||||
|
||||
def _route() -> tuple[dict, list[str], list[str]]:
|
||||
"""Кого арм G маршрутизировал и чью правку страж ОСТАВИЛ. Ровно логика `guarded.cmd_build`.
|
||||
|
||||
⚠ Выигрыш забирают ТОЛЬКО оставленные окна: там, где страж откатил правку к черновику,
|
||||
арм G получает счёт черновика, то есть ноль выигрыша. Первая версия этого счёта брала все
|
||||
маршрутизированные и завышала долю до 46% — поймано сверкой с `--build` 08.08.
|
||||
"""
|
||||
a1, a2, common = _passes()
|
||||
gain = dict(zip(common, _pooled(a1, a2, common, "A", "F3"), strict=True))
|
||||
routed, kept = [], []
|
||||
for u in BO.units():
|
||||
uid = u["uid"]
|
||||
if uid not in gain:
|
||||
continue
|
||||
draft, edited = BO.text3_of("F3", u), BO.text3_of("A", u)
|
||||
if not G.flagged(draft):
|
||||
continue
|
||||
routed.append(uid)
|
||||
if sum(G.flags(edited).values()) <= sum(G.flags(draft).values()):
|
||||
kept.append(uid)
|
||||
return gain, routed, kept
|
||||
|
||||
|
||||
def _draft_cost(uid: str) -> float:
|
||||
return sum(json.loads(f.read_text(encoding="utf-8")).get("cost_usd") or 0.0
|
||||
for f in OUT.glob(f"bo2-DRAFT-{uid}*.json"))
|
||||
|
||||
|
||||
def _cost_a(uid: str) -> float:
|
||||
return _draft_cost(uid) + G.cost_of("bo4-A", uid)
|
||||
|
||||
|
||||
def _cost_g(uid: str) -> float:
|
||||
"""Цена арма G: черновик всегда, редактор — только флагнутым окнам."""
|
||||
u = next(x for x in BO.units() if x["uid"] == uid)
|
||||
return _cost_a(uid) if G.flagged(BO.text3_of("F3", u)) else _draft_cost(uid)
|
||||
|
||||
|
||||
def p5_router() -> None:
|
||||
print("\n═══ ИТОГ п.5 — РОУТИНГ: ТРИ ОТБОРЩИКА ═══")
|
||||
a1, a2, common = _passes()
|
||||
gain, routed, picked = _route()
|
||||
tot = sum(gain.values())
|
||||
got = sum(gain[u] for u in picked)
|
||||
print(f" МЕХАНИЧЕСКИЙ отборщик: маршрутизировал {len(routed)}/{len(gain)} окон, страж оставил"
|
||||
f" правку в {len(picked)} ⇒ забрано {got:.1f} из {tot:.1f} выигрыша = {got / tot:.0%}")
|
||||
sums = sorted(sum(gain[u] for u in c) for c in combinations(gain, len(picked)))
|
||||
lo, hi = bisect_left(sums, got) / len(sums), bisect_right(sums, got) / len(sums)
|
||||
cg = st.mean([_cost_g(u) for u in gain])
|
||||
ca = st.mean([_cost_a(u) for u in gain])
|
||||
print(f" цена: G ${cg:.5f} против связки ${ca:.5f} = {cg / ca:.0%} цены за {got / tot:.0%}"
|
||||
f" выигрыша ⇒ хуже случайного роутинга того же бюджета")
|
||||
print(f" среди всех C({len(gain)},{len(picked)})={len(sums)} подмножеств того же размера: "
|
||||
f"персентиль {lo:.1%}–{hi:.1%} (середина {(lo + hi) / 2:.1%}), связок {sums.count(got)}")
|
||||
print(" ⚠ ИТОГ печатает верхнюю границу (51-й); расхождение с 46-м — только конвенция связок.")
|
||||
q = _qe()
|
||||
if q:
|
||||
agree = tot_n = 0
|
||||
for a, b, _w in AJ.FAMILY:
|
||||
for u in common:
|
||||
m = _pooled(a1, a2, [u], a, b)
|
||||
if not m or m[0] == 0:
|
||||
continue
|
||||
dq = q.get(f"{b}|{u}", 0) - q.get(f"{a}|{u}", 0)
|
||||
tot_n += 1
|
||||
agree += (dq > 0) == (m[0] > 0)
|
||||
print(f" QE как СУДЬЯ: знак совпал {agree}/{tot_n} по 7 контрастам семейства × единицы")
|
||||
xs = [q.get(f"F3|{u}", 0) - q.get(f"A|{u}", 0) for u in common]
|
||||
ys = [gain[u] for u in common]
|
||||
print(f" QE против судьи на самом крупном контрасте `A/F3`: r={_pearson(xs, ys):+.2f}")
|
||||
print(" ⚠ Это АНТИкорреляция, а не слепота: чем сильнее QE «за» редактуру, тем меньше")
|
||||
print(" её выигрыш по судье. Довод против QE-роутинга сильнее, чем печатал ИТОГ.")
|
||||
print(" ⚠ Числа ИТОГа «43/91» и «r=+0.06» ни одним вариантом счёта не воспроизводятся —")
|
||||
print(" они из §15, утраченного инцидентом 08.08; в дереве кода пути к ним НЕТ.")
|
||||
|
||||
|
||||
def p6_guard() -> None:
|
||||
print("\n═══ ИТОГ п.6 — СТРАЖ guarded APE ═══")
|
||||
gain, routed, kept = _route()
|
||||
rej = [u for u in routed if u not in kept]
|
||||
print(f" страж ОТКЛОНИЛ правок: {len(rej)} — " +
|
||||
" · ".join(f"{u[:10]} выигрыш {gain[u]:+.1f}" for u in rej))
|
||||
if rej:
|
||||
print(f" средний выигрыш ОТКЛОНЁННЫХ {st.mean([gain[u] for u in rej]):+.2f} против "
|
||||
f"{st.mean(list(gain.values())):+.2f} по ВСЕМ единицам ⇒ страж вырезал ЛУЧШИЕ")
|
||||
print(f" ⚠ n={len(rej)}. ИТОГ числа единиц не называл; вывод стоит на двух правках.")
|
||||
|
||||
|
||||
def p7_2b() -> None:
|
||||
print("\n═══ ИТОГ п.7 — АРМЫ Ф2б (проход abs4, узкая панель) ═══")
|
||||
sv = AJ.PASS
|
||||
AJ.PASS = "abs4"
|
||||
try:
|
||||
d = AJ._by_unit()
|
||||
finally:
|
||||
AJ.PASS = sv
|
||||
# СЕМЕЙСТВО ПРОХОДА 4 — пять СУДЕЙСКИХ контрастов.
|
||||
# ⚠ ОТКЛОНЕНИЕ ОТ ОБЪЯВЛЕННОГО, объявляю: `phase2b.FAMILY` пятым членом называет
|
||||
# `C_oracle vs C_real` — разрыв потолка и пола схемы. Он НЕ судейский: потолок меряется
|
||||
# посаженными дефектами и детекторами ($0, `phase2b --oracle`), судье не предъявлялся и
|
||||
# p-значения не имеет. В судейскую пятёрку вместо него вошёл контроль `A_law vs F3`.
|
||||
# Направление подмены КОНСЕРВАТИВНО (член семейства сохранён, поправка не ослаблена), но это
|
||||
# правка семейства после пре-рега, и читать её надо как девиацию, а не как замысел.
|
||||
fam = [("C2", "A_law", "точечный ремонт против полного переписывания"),
|
||||
("C2", "F3", "и лучше ли он черновика, который чинит"),
|
||||
("E2", "D_", "обратная связка против своей базы"),
|
||||
("E2", "A_law", "обратная связка против прямой"),
|
||||
("A_law", "F3", "КОНТРОЛЬ: воспроизводится ли эффект редактора")]
|
||||
rows = []
|
||||
for a, b, w in fam:
|
||||
m = [d[u][b]["errors"] - d[u][a]["errors"] for u in d if a in d[u] and b in d[u]]
|
||||
rows.append((a, b, w, m, BO.sign_perm_p(m)))
|
||||
holm = _holm([r[4] for r in rows])
|
||||
print(f"{'контраст':16s}{'n':>3s}{'перевес':>9s}{'95% ДИ':>19s}{'p':>9s}{'p Холма':>10s}")
|
||||
for (a, b, w, m, p), h in zip(rows, holm, strict=True):
|
||||
lo, hi = BO.boot_ci(m)
|
||||
print(f"{a + ' vs ' + b:16s}{len(m):3d}{st.mean(m):+9.2f} [{lo:+.2f}, {hi:+.2f}]"
|
||||
f"{p:9.4f}{h:10.4f}{'✔' if h < 0.05 else ' '} {w}")
|
||||
print(" ⚠ `E2 vs A_law` значим и в таблице ИТОГа НЕ напечатан — восстановлено ревью 08.08.")
|
||||
|
||||
|
||||
def money() -> None:
|
||||
print("\n═══ ИТОГ — ДЕНЬГИ ═══")
|
||||
def s(pat: str) -> float:
|
||||
out = 0.0
|
||||
for f in OUT.glob(pat):
|
||||
try:
|
||||
x = json.loads(f.read_text(encoding="utf-8"))
|
||||
except (ValueError, OSError):
|
||||
continue
|
||||
if isinstance(x, dict):
|
||||
out += x.get("cost_usd") or 0.0
|
||||
return out
|
||||
print(f" пак целиком ${s('*.json'):.5f}")
|
||||
print(f" фаза Ф2б: армы C/E `p2b-*` ${s('p2b-*.json'):.5f} + старые пробы `rp-*` "
|
||||
f"${s('rp-*.json'):.5f} = ${s('p2b-*.json') + s('rp-*.json'):.5f}")
|
||||
print(" ⚠ ИТОГ печатал как расход фазы только `p2b-*`; расход ФАЗЫ включает и `rp-*`.")
|
||||
empty = {}
|
||||
for f in OUT.glob("*.json"):
|
||||
try:
|
||||
x = json.loads(f.read_text(encoding="utf-8"))
|
||||
except (ValueError, OSError):
|
||||
continue
|
||||
if isinstance(x, dict) and (x.get("cost_usd") or 0) > 0 \
|
||||
and not (x.get("content") or x.get("answer") or "").strip():
|
||||
t = f.name.split("-")[0]
|
||||
e = empty.setdefault(t, [0, 0.0])
|
||||
e[0] += 1
|
||||
e[1] += x["cost_usd"]
|
||||
tot_n = sum(v[0] for v in empty.values())
|
||||
tot_s = sum(v[1] for v in empty.values())
|
||||
print(f" оплачено за ПУСТОЙ выход: {tot_n} вызовов ${tot_s:.5f} — " +
|
||||
" · ".join(f"{k} {v[0]} шт ${v[1]:.5f}" for k, v in sorted(empty.items())))
|
||||
print(" ⚠ Счётчик смотрит ОБА поля выхода (`content` и `answer`): проба контаминации")
|
||||
print(" пишет в `answer`, и счётчик по одному полю давал ложные $0.19191 на 43 вызова.")
|
||||
|
||||
|
||||
def noise() -> None:
|
||||
print("\n═══ ИТОГ — ШУМ ═══")
|
||||
for nm in ("abs", "abs2", "abs4"):
|
||||
d = AJ._by_unit_pass(nm)
|
||||
m = [v["CTRLfloorA"]["errors"] - v["CTRLfloorB"]["errors"]
|
||||
for v in d.values() if "CTRLfloorA" in v and "CTRLfloorB" in v]
|
||||
if len(m) > 1:
|
||||
print(f" пол рига {nm:5s}: n={len(m):2d} среднее {st.mean(m):+.2f} · "
|
||||
f"sd {st.pstdev(m):.2f} — две генерации ОДНОГО арма, один судья")
|
||||
print(" ⇒ разброс повтора превышает разницу между армами: на ОДНОЙ главе армы не упорядочены.")
|
||||
print(" ⚠ Разложение шума на судейскую и генерационную компоненты НЕ УСТАНОВЛЕНО:")
|
||||
print(" `absjudge --pool` печатает один способ счёта, два других дают другой вердикт.")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
p1_leg()
|
||||
p1_robust()
|
||||
p2_canon()
|
||||
p3_draft()
|
||||
p4_axes()
|
||||
p5_router()
|
||||
p6_guard()
|
||||
p7_2b()
|
||||
print("\n═══ ИТОГ п.8 — ДРЕЙФ ═══\n штатная команда: `drift.py --score` (9 общих глав)")
|
||||
money()
|
||||
noise()
|
||||
print("\nВСЁ ВЫШЕ ПЕРЕ-СНЯТО ИЗ СЫРЬЯ, $0. Числа, помеченные ⚠ «в дереве нет», не выводятся.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Reference in a new issue