Контракт паритета с pyhf
NextStat валидирует численную корректность относительно pyhf (бэкенд NumPy, f64) как канонического оракула. Ожидаемое согласие задается 7-уровневой иерархией допусков: от побиновой арифметики (1e-12) до статистик toy-ансамбля (0.05).
Режимы вычислений
| Режим | Суммирование | Бэкенд | Потоки | Сценарий |
|---|
| Parity | Компенсированное (Кэхен) | SIMD (Accelerate отключен) | 1 (принудительно) | Валидация против pyhf |
| Fast | Обычное | SIMD / Accelerate / CUDA | Rayon (авто) | Продакшн-вывод |
Включение режима паритета
# 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-12 | 1e-10 |
Идентичная арифметика f64 без редукции по бинам. Те же операции и тот же порядок дают идентичный результат.
Уровень 2: вектор ожидаемых данных (с накоплением)
| Метрика | Допуск (Parity) | Допуск (Fast) |
|---|
max(abs(ns − pyhf)) all bins | 1e-8 | 1e-8 |
Уровень 3: значение NLL (скалярная редукция)
| Метрика | Допуск (Parity) | Допуск (Fast) |
|---|
abs(2·ns_nll − pyhf_twice_nll) | atol 1e-8 | rtol 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-4 | same |
NextStat использует reverse-mode AD (точно). В pyhf используется центральная схема конечных разностей (h=1e-5). Допуск отражает шум конечных разностей, а не ошибку NextStat.
Уровень 5: параметры наилучшего фита (поверхность оптимизатора)
| Метрика | Допуск (Parity) | Допуск (Fast) |
|---|
abs(ns_hat_i − pyhf_hat_i) | 2e-4 | 2e-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-4 | 5e-4 |
Уровень 7: toy-ансамбль (статистический)
| Метрика | Допуск | Обоснование |
|---|
| Δmean(pull_mu) | 0.05 | Статистический шум из-за конечного N_toys |
| Δstd(pull_mu) | 0.05 | То же |
| Δcoverage_1σ | 0.03 | Биномиальный шум |
| Δcoverage | 0.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 |
|---|
| xmlimport | 1e-7 | 0.051 | 0 (ok) |
| multichannel | 4e-7 | 3.4e-8 | 0 (ok) |
| coupled_histosys | 5e-6 | 22.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