NextStatNextStat

Быстрый старт

Это руководство проведёт вас от нуля до первого статистического фита за 5 минут. Убедитесь, что NextStat установлен — см. Инструкцию по установке.

Шаг 1: Создайте тестовый workspace

«Workspace» — это JSON-файл, описывающий вашу статистическую модель: наблюдаемые данные, ожидания сигнала и фона, и систематические неопределённости. NextStat использует тот же формат JSON, что и pyhf.

Создадим минимальный workspace с одной сигнальной областью, одним сигнальным и одним фоновым сэмплом:

# Сохраните как workspace.json (или скопируйте в файл)
cat > workspace.json << 'EOF'
{
  "channels": [
    {
      "name": "singlechannel",
      "samples": [
        {
          "name": "signal",
          "data": [5.0, 10.0],
          "modifiers": [
            { "name": "mu", "type": "normfactor", "data": null }
          ]
        },
        {
          "name": "background",
          "data": [50.0, 60.0],
          "modifiers": [
            { "name": "uncorr_bkguncrt", "type": "shapesys", "data": [5.0, 12.0] }
          ]
        }
      ]
    }
  ],
  "observations": [
    { "name": "singlechannel", "data": [55.0, 65.0] }
  ],
  "measurements": [
    { "name": "Measurement", "config": { "poi": "mu", "parameters": [] } }
  ],
  "version": "1.0.0"
}
EOF

Шаг 2: Запустите первый фит (Python)

Откройте Python-оболочку или создайте скрипт my_first_fit.py:

import json
import nextstat

# 1. Загружаем workspace JSON
with open("workspace.json") as f:
    workspace = json.load(f)

# 2. Строим модель из workspace'а
model = nextstat.from_pyhf(json.dumps(workspace))

# 3. Запускаем фит методом максимального правдоподобия
result = nextstat.fit(model)

# 4. Печатаем результаты
poi_idx = model.poi_index()
print("Сила сигнала (mu):", result.bestfit[poi_idx])
print("Неопределённость: ", result.uncertainties[poi_idx])
print("Все параметры:    ", result.bestfit)
print("Все неопред-ти:   ", result.uncertainties)

Запустите:

python3 my_first_fit.py

Ожидаемый вывод (значения могут незначительно отличаться):

Сила сигнала (mu): 0.9999...
Неопределённость:  0.3...
Все параметры:     [0.999..., 1.0..., 0.99...]
Все неопред-ти:    [0.3..., 0.08..., 0.15...]

Что произошло? NextStat нашёл значения параметров, которые лучше всего описывают наблюдаемые данные. Сила сигнала mu ≈ 1.0 означает, что данные согласуются с гипотезой сигнал+фон. Остальные параметры — это нюисанс-параметры (систематические неопределённости), которые были профилированы в ходе фита.

Шаг 3: Проверьте гипотезу

Проверка гипотезы определяет, совместимы ли данные с заданной силой сигнала. Метод CLs — стандартный подход в физике частиц:

import json
import nextstat

with open("workspace.json") as f:
    workspace = json.load(f)

model = nextstat.from_pyhf(json.dumps(workspace))

# Проверяем гипотезу mu=1.0 (сигнал существует с номинальной силой)
result = nextstat.hypotest(model, mu_test=1.0)

print("CLs:              ", result.cls)
print("CLs+b:            ", result.clsb)
print("CLb:              ", result.clb)
print("Исключён (95%)?   ", "ДА" if result.cls < 0.05 else "НЕТ")

Ожидаемый вывод:

CLs:               0.43...
CLs+b:             0.24...
CLb:               0.55...
Исключён (95%)?    НЕТ

Что это значит? CLs > 0.05 означает, что мы не можем исключить гипотезу сигнала на уровне достоверности 95%. Сигнал совместим с данными.

Шаг 4: Вычислите верхние пределы (Brazil band)

Скан верхнего предела находит максимальную силу сигнала, совместимую с данными:

import json
import nextstat

with open("workspace.json") as f:
    workspace = json.load(f)

model = nextstat.from_pyhf(json.dumps(workspace))

# Вычисляем ожидаемые и наблюдаемые верхние пределы
limits = nextstat.upper_limit(model)

print("Наблюдаемый предел:", limits.observed)
print("Ожидаемый -2σ:     ", limits.expected_minus2)
print("Ожидаемый -1σ:     ", limits.expected_minus1)
print("Ожидаемый медиана: ", limits.expected)
print("Ожидаемый +1σ:     ", limits.expected_plus1)
print("Ожидаемый +2σ:     ", limits.expected_plus2)

Шаг 5: Используйте командную строку

NextStat также поставляется с CLI-бинарником. Все те же операции доступны из терминала:

# Фит workspace'а
nextstat fit --input workspace.json

# Проверка гипотезы (асимптотический CLs)
nextstat hypotest --input workspace.json --mu 1.0

# Проверка гипотезы с ожидаемыми бандами
nextstat hypotest --input workspace.json --mu 1.0 --expected-set

# Скан верхнего предела (201 точка от mu=0 до mu=5)
nextstat upper-limit --input workspace.json \
  --expected --scan-start 0 --scan-stop 5 --scan-points 201

# Проверка гипотезы на тоях (10k тоев, все ядра CPU)
nextstat hypotest-toys --input workspace.json \
  --mu 1.0 --n-toys 10000 --seed 42 --threads 0

# GPU-ускоренные тои (NVIDIA)
nextstat hypotest-toys --input workspace.json \
  --mu 1.0 --n-toys 10000 --gpu cuda

# GPU-ускоренные тои (Apple Silicon)
nextstat hypotest-toys --input workspace.json \
  --mu 1.0 --n-toys 10000 --gpu metal

Шаг 6: Использование из Rust (опционально)

Если вы Rust-разработчик, вы можете использовать NextStat как библиотеку в своём проекте:

use ns_inference::mle::MaximumLikelihoodEstimator;
use ns_translate::pyhf::{HistFactoryModel, Workspace};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 1. Загружаем workspace
    let json = std::fs::read_to_string("workspace.json")?;
    let workspace: Workspace = serde_json::from_str(&json)?;

    // 2. Строим модель
    let model = HistFactoryModel::from_workspace(&workspace)?;

    // 3. Фит
    let mle = MaximumLikelihoodEstimator::new();
    let result = mle.fit(&model)?;

    // 4. Печатаем результаты
    println!("Параметры фита: {:?}", result.parameters);
    println!("NLL в минимуме: {}", result.nll);
    Ok(())
}

Попробуйте в браузере (без установки)

Не хотите ничего устанавливать? Попробуйте WASM-песочницу — NextStat работает целиком в вашем браузере через WebAssembly. Без сервера, без Python, без настройки.

Что изучить дальше