NextStatNextStat

Контракт паритета с pyhf

NextStat валидирует численную корректность относительно pyhf (бэкенд NumPy, f64) как канонического оракула. Ожидаемое согласие задается 7-уровневой иерархией допусков: от побиновой арифметики (1e-12) до статистик toy-ансамбля (0.05).

Режимы вычислений

РежимСуммированиеБэкендПотокиСценарий
ParityКомпенсированное (Кэхен)SIMD (Accelerate отключен)1 (принудительно)Валидация против pyhf
FastОбычноеSIMD / Accelerate / CUDARayon (авто)Продакшн-вывод

Включение режима паритета

# CLI
nextstat fit --input workspace.json --parity

# Python
import nextstat
nextstat.set_eval_mode("parity")

# Rust
ns_compute::set_eval_mode(ns_compute::EvalMode::Parity);

Режим паритета выставляет атомарный флаг на уровне процесса: Apple Accelerate отключается, число потоков принудительно выставляется в 1, а для пуассоновского NLL используется компенсированное суммирование Кэхена.

7-уровневая иерархия допусков

Уровень 1: ожидаемые данные по бинам (чистая арифметика)

МетрикаДопуск (Parity)Допуск (Fast)
abs(ns_bin − pyhf_bin)1e-121e-10

Идентичная арифметика f64 без редукции по бинам. Те же операции и тот же порядок дают идентичный результат.

Уровень 2: вектор ожидаемых данных (с накоплением)

МетрикаДопуск (Parity)Допуск (Fast)
max(abs(ns − pyhf)) all bins1e-81e-8

Уровень 3: значение NLL (скалярная редукция)

МетрикаДопуск (Parity)Допуск (Fast)
abs(2·ns_nll − pyhf_twice_nll)atol 1e-8rtol 1e-6

Суммирование λ − n·ln(λ) + ln(n!) по ~1000 бинам накапливает округления порядка ~1e-10. Суммирование Кэхена в режиме Parity снижает это до ~1e-14.

Уровень 4: градиент (AD против конечных разностей)

МетрикаДопуск (Parity)Допуск (Fast)
abs(ns_grad_i − pyhf_fd_i)atol 1e-6, rtol 1e-4same

NextStat использует reverse-mode AD (точно). В pyhf используется центральная схема конечных разностей (h=1e-5). Допуск отражает шум конечных разностей, а не ошибку NextStat.

Уровень 5: параметры наилучшего фита (поверхность оптимизатора)

МетрикаДопуск (Parity)Допуск (Fast)
abs(ns_hat_i − pyhf_hat_i)2e-42e-4

Вблизи минимума NLL поверхность «плоская». Небольшие различия в точности градиента или критериях остановки могут сдвигать минимум на O(1e-4) в пространстве параметров при изменении NLL на уровне O(1e-10). На больших моделях (>100 параметров) L-BFGS-B часто находит более низкий NLL, чем SLSQP.

Уровень 6: неопределенности параметров (чувствительность гессиана)

МетрикаДопуск (Parity)Допуск (Fast)
abs(ns_unc_i − pyhf_unc_i)5e-45e-4

Уровень 7: toy-ансамбль (статистический)

МетрикаДопускОбоснование
Δmean(pull_mu)0.05Статистический шум из-за конечного N_toys
Δstd(pull_mu)0.05То же
Δcoverage_1σ0.03Биномиальный шум
Δcoverage0.05Покрытие

Визуальная шкала

Строже ◄──────────────────────────────────────── Мягче

1e-12     1e-10     1e-8      1e-6      1e-4      1e-2
  │         │         │         │         │         │
  ├─ Уровень 1: expected_data по бинам (1e-12)
  │         │         │         │
  │         ├─ Уровень 2: вектор expected_data (1e-8)
  │         │         │         │
  │         │         ├─ Уровень 3: значение NLL (atol 1e-8, rtol 1e-6)
  │         │         │         │
  │         │         │         ├─ Уровень 4: градиент (atol 1e-6, rtol 1e-4)
  │         │         │         │         │
  │         │         │         │         ├─ Уровень 5: best-fit параметры (2e-4)
  │         │         │         │         ├─ Уровень 6: неопределенности (5e-4)
  │         │         │         │         │         │
  │         │         │         │         │         ├─ Уровень 7: toys (0.03–0.05)

Архитектура: спецификация vs скорость

┌──────────────────────────────────────────────────────┐
│  pyhf NumPy = SPEC (оракул для CI/регрессий)          │
│  expected_data, nll, grad_nll в фиксированных точках  │
└───────────────────┬──────────────────────────────────┘
                    │ контракт допусков (7 уровней)
┌───────────────────┴──────────────────────────────────┐
│  PreparedModel = СКОМПИЛИРОВАННАЯ МОДЕЛЬ              │
│  workspace JSON → плоская раскладка: observed_flat,   │
│  ln_factorials, obs_mask, CSR-индексы модификаторов   │
├──────────────────┬───────────────────────────────────┤
│  Parity mode     │  Fast mode                         │
│  Кэхен, 1 поток  │  Accelerate/CUDA, Rayon            │
│  CI-гейт         │  продакшн                          │
└──────────────────┴───────────────────────────────────┘

Производительность

ОперацияПропускная способность
PreparedModel: вычисление NLL (простое)~200K eval/сек
Накладные расходы Кэхена<5%
Батч-тои (1000, CPU Rayon)~50× против последовательного
Батч-тои (1000, CUDA)~200× против последовательного
Парсинг TTree и заполнение гистограмм~8.5× против uproot+numpy
Ранкинг (16 NP, autodiff)~4× против конечных разностей в pyhf

Кросс-валидация с ROOT

ФикстураNS vs pyhf: max |dq(μ)|NS vs ROOT: max |dq(μ)|Статус ROOT
xmlimport1e-70.0510 (ok)
multichannel4e-73.4e-80 (ok)
coupled_histosys5e-622.5-1 (FAILED)

pyhf это спецификация. Отклонения ROOT носят информационный характер и не являются блокирующим гейтом. Полный разбор: сравнение ROOT/HistFactory.

Интеграция с CI

# Полный набор тестов паритета (детерминированно, один поток)
pytest tests/python/test_pyhf_validation.py \
       tests/python/test_expected_data_parity.py -v

# Генерация эталонного отчета
NEXTSTAT_GOLDEN_REPORT_DIR=reports/ \
  pytest tests/python/test_expected_data_parity.py -v