NextStatNextStat

Сходимость оптимизатора и философия best-NLL

NextStat использует L-BFGS-B и по умолчанию нацелен на лучший минимум NLL (best-NLL). Расхождения с pyhf по параметрам наилучшего фита на больших моделях (>100 параметров) ожидаемы и задокументированы: это поведение оптимизатора, а не баг модели.

Позиция: best-NLL по умолчанию

  • NextStat намеренно не ограничивает оптимизатор ради совпадения с конкретным внешним инструментом.
  • Если L-BFGS-B находит более глубокий минимум, чем SLSQP в pyhf, это корректный результат.
  • Паритет функции цели проверен: NextStat и pyhf вычисляют одну и ту же NLL в одной и той же точке параметров (обычно ~1e-9 ... 1e-13).
  • Расхождения дает оптимизатор, а не статистическая модель.

Типичный масштаб расхождений

МодельПараметрыΔNLL (NS − pyhf)Причина
simple_workspace20.0Оба сходятся
complex_workspace90.0Оба сходятся
tchannel184−0.01 ... −0.08преждевременная остановка SLSQP в pyhf
tHu~200−0.08преждевременная остановка SLSQP в pyhf
tttt249−0.01преждевременная остановка SLSQP в pyhf

Отрицательная ΔNLL означает, что NextStat нашел лучший (меньший) минимум.

Уровни паритета

Уровень 1: паритет функции цели (P0, обязательно)

NLL(params) совпадает между NextStat и pyhf в одной и той же точке параметров. Допуски: rtol=1e-6, atol=1e-8. Проверено golden-тестами на всех fixture-workspace.

Уровень 2: паритет фита (P1, условно)

Параметры наилучшего фита совпадают в пределах допусков: atol=2e-4 по параметрам, atol=5e-4 по неопределенностям. На малых моделях (<50 параметров) обычно полное совпадение; на больших возможны расхождения из-за разных оптимизаторов. Это не дефект, если NS NLL ≤ pyhf NLL.

Уровень 3: совместимость оптимизатора (отвергнуто)

Намеренно ухудшать оптимизатор ради совпадения с SLSQP мы отвергаем: это искусственное ограничение без научной ценности.

Как проверить

# Для пользователей
import nextstat, json

ws = json.load(open("workspace.json"))
model = nextstat.from_pyhf(json.dumps(ws))
result = nextstat.fit(model)
print(f"NLL: {result.nll}")  # чем меньше, тем лучше
# Для разработчиков (проверки паритета)
make pyhf-audit-nll   # Паритет функции цели (должно всегда проходить)
make pyhf-audit-fit   # Паритет фита (на больших моделях возможны расхождения)

# Диагностика cross-eval
python tests/diagnose_optimizer.py workspace.json

Warm-start для воспроизводимости pyhf

Если в конкретном сценарии нужно совпасть с pyhf (например, чтобы воспроизвести опубликованный результат):

import pyhf, nextstat, json

# 1. Фит в pyhf
ws = json.load(open("workspace.json"))
model = pyhf.Workspace(ws).model()
pyhf_pars, _ = pyhf.infer.mle.fit(
    model.config.suggested_init(), model, return_uncertainties=True
)

# 2. Warm-start NextStat из точки pyhf
ns_model = nextstat.from_pyhf(json.dumps(ws))
result = nextstat.fit(ns_model, init_pars=pyhf_pars.tolist())
# result.nll <= pyhf NLL (гарантировано)

L-BFGS-B vs SLSQP

АспектL-BFGS-B (NextStat)SLSQP (pyhf/scipy)
ГессианКвази-Ньютон (m=10 историй)Rank-1 update
ГраницыНативные box-ограниченияНативные box-ограничения
Сходимость||proj_grad|| < ftolпорог по ||grad||
СложностьO(m·n) на итерациюO(n²) на итерацию
Большие модели (>100p)УстойчивоЧасто останавливается преждевременно

Подтверждение по профильному скану

ФикстураNS vs pyhf |dq(μ)|NS vs ROOT |dq(μ)|Фит ROOT
xmlimport1e-70.051Сошлось
multichannel4e-73.4e-8Сошлось
coupled_histosys5e-622.5ОШИБКА (status=-1)