| title | CLI Reference (nextstat) |
|---|---|
| status | stable |
The nextstat CLI is implemented in crates/ns-cli and focuses on:
- deterministic parity mode (
--threads 1) - JSON in / JSON out contracts for reproducible workflows
Boundary note:
- ads variance-reduction helpers (
CUPED/CURE) are currently shipped as shared Rust/Python/tool-runtime capabilities, not as standalonenextstatCLI commands. Usens_inference::{cuped_adjust,cure_adjust},nextstat.ads.{cuped_adjust,cure_adjust}, or the tool-runtime namesnextstat_ads_cuped_adjust/nextstat_ads_cure_adjust.
For the end-to-end stable-first GVM workflow, plus the wider advanced layers, start with:
docs/tutorials/hep-gvm-measurement-combinations.md
--interp-defaults {root|pyhf}(pyhf JSON only)root(default): NormSys=Code4, HistoSys=Code4p (smooth, TREx/ROOT-style)pyhf: NormSys=Code1, HistoSys=Code0 (strict pyhf defaults)- Note: GPU backends currently support only
rootinterpolation defaults. HS3 inputs always use ROOT defaults (Code1/Code0) and are not GPU-accelerated yet.
--log-level {error|warn|info|debug|trace}— log verbosity (default:warn)--bundle <DIR>— optional output directory for structured run artifacts (fit JSON, logs, diagnostics)
For the HistFactory configuration format, see docs/references/analysis-config.md.
For event-level fits, use the unbinned_spec_v0 schema (nextstat config schema --name unbinned_spec_v0).
HEP / HistFactory (HS3 auto-detected for model-based commands; some utilities remain pyhf-only):
nextstat validate --config analysis.yamlnextstat config schema [--name analysis_spec_v0|validation_report_v1|m15_config_v1|m15_assessment_table_v1|m15_map_v1|m15_mar_v1|m15_profile_diff_report_v1|m15_bundle_manifest_v1|hepdata_import_v1|hepdata_lock_v1|beta_binomial_design_v0|beta_binomial_design_analysis_v0|beta_binomial_operating_characteristics_v0|beta_binomial_posterior_predictive_v0|beta_binomial_prior_sensitivity_campaign_v0|beta_binomial_prior_sensitivity_report_v0|beta_binomial_design_report_v0|normal_normal_design_v0|normal_normal_design_analysis_v0|normal_normal_operating_characteristics_v0|normal_normal_posterior_predictive_v0|normal_normal_prior_sensitivity_campaign_v0|normal_normal_prior_sensitivity_report_v0|normal_normal_design_report_v0|bayesian_design_report_bundle_v0|bayesian_design_regulatory_appendix_v0|bayesian_prior_conflict_diagnostic_v0|bayesian_historical_control_borrowing_policy_v0|bayesian_historical_control_borrowing_review_v0|bayesian_robust_mixture_prior_policy_v0|bayesian_robust_mixture_prior_review_v0|simplified_likelihood_v0|simplified_likelihood_audit_v0|simplified_likelihood_derive_v0|simplified_likelihood_export_report_v0|simplified_likelihood_promotion_evidence_bundle_v0|simplified_likelihood_export_benchmark_snapshot_report_v0|simplified_likelihood_export_public_validation_report_v0|simplified_likelihood_exporter_stable_review_assessment_v0|simplified_likelihood_exporter_stable_source_semantics_boundary_v0|simplified_likelihood_exporter_stable_candidate_blocker_matrix_v0|simplified_likelihood_exporter_stable_candidate_review_packet_v0|simplified_likelihood_exporter_stable_promotion_decision_v0]nextstat import histfactory --xml combination.xml --output workspace.jsonnextstat import trex-config --config trex.txt --output workspace.json [--analysis-yaml analysis.yaml] [--coverage-json coverage.json] [--expr-coverage-json expr_coverage.json]nextstat import hepdata --listnextstat import hepdata --list-patches --doi https://doi.org/... --dataset-id custom.hepdata.123.v1.r1 [--bkgonly-filename ...] [--patchset-filename ...] [--cache-dir ...] [--offline]nextstat import hepdata --dataset hepdata.116034.v1.r34 --out-dir hepdata-workspaces/ [--cache-dir ...] [--lock ...] [--offline]nextstat import hepdata --doi https://doi.org/... --dataset-id custom.hepdata.123.v1.r1 [--display-name ...] [--bkgonly-filename ...] [--patchset-filename ...] [--patch ...] --out-dir hepdata-workspaces/ [--cache-dir ...] [--lock ...] [--offline]nextstat import patchset --workspace BkgOnly.json --patchset patchset.json [--patch-name ...]nextstat export histfactory --input workspace.json --out-dir export/ [--prefix meas] [--overwrite] [--python]nextstat build-hists --config trex.config --out-dir out/ [--base-dir ...] [--coverage-json coverage.json] [--expr-coverage-json expr_coverage.json]nextstat trex import-config --config trex.config --out analysis.yaml [--report analysis.mapping.json]nextstat audit --input workspace.json [--format text|json] [--output audit.json]nextstat fit --input workspace.json [--gpu cuda|metal] [--fit-regions SR1,SR2] [--validation-regions VR1] [--asimov] [--parity] [--json-metrics metrics.json] [--threads 1]nextstat hypotest --input workspace.json --mu 1.0 [--expected-set] [--json-metrics metrics.json] [--threads 1]nextstat hypotest-toys --input workspace.json --mu 1.0 [--n-toys 1000 --seed 42] [--expected-set] [--threads 0] [--gpu cuda|metal] [--json-metrics metrics.json]nextstat significance --input workspace.json [--json-metrics metrics.json] [--threads 1]nextstat goodness-of-fit --input workspace.json [--json-metrics metrics.json] [--threads 1]nextstat upper-limit --input workspace.json [--expected] [--scan-start ... --scan-stop ... --scan-points ...] [--json-metrics metrics.json] [--threads 1]nextstat scan --input workspace.json --start 0 --stop 5 --points 21 [--gpu cuda|metal] [--json-metrics metrics.json] [--threads 1]nextstat simplify workspace --input workspace.json --fit fit.json --derive-config derive.json --experiment ATLAS --analysis-id analysis.sl.v0 --reference internal-note [--description "..."] [--output simplified.json] [--report export_report.json] [--threads 1]nextstat combine <ws1.json> <ws2.json> [ws3.json ...] [--output combined.json] [--prefix-channels]nextstat combine-measurements-build-spec --manifest manifest.yaml [--output spec.json]nextstat combine-measurements-build-spec [--poi mu] --measurements measurements.csv --stat-covariance stat_cov.csv [--systematics systematics.csv] [--correlations correlations.csv] [--output spec.json]nextstat combine-measurements --input spec.json --output result.json [--ci-level 0.68] [--solver numerical|numerical-paper|analytic-perturbative|auto] [--threads 1] [--json-metrics metrics.json]nextstat combine-measurements-calibrate --input spec.json --output calibration.json [--ci-level 0.68] [--solver numerical|numerical-paper|analytic-perturbative|auto] [--n-toys 128] [--seed 42] [--threads 1] [--json-metrics metrics.json]nextstat combine-measurements-calibrate-study --input spec.json --output study.json [--ci-level 0.68] [--solver numerical|numerical-paper|analytic-perturbative|auto] [--n-toys 128] [--seeds 42,43,44] [--threads 1] [--json-metrics metrics.json]nextstat combine-measurements-scenario-study --input spec.json --scenarios scenarios.json --output study.json [--ci-level 0.68] [--solver numerical|numerical-paper|analytic-perturbative|auto] [--threads 1] [--json-metrics metrics.json]nextstat combine-measurements-calibration-campaign --input spec.json --scenarios scenarios.json --output campaign.json [--ci-level 0.68] [--solver numerical|numerical-paper|analytic-perturbative|auto] [--n-toys 128] [--seeds 42,43,44] [--threads 1] [--json-metrics metrics.json]nextstat combine-measurements-solver-parity-scenario-study --input spec.json --scenarios scenarios.json --output parity.json [--ci-level 0.68] [--lhs-solver numerical-paper] [--rhs-solver analytic-perturbative] [--threads 1] [--json-metrics metrics.json]nextstat combine-measurements-solver-parity-calibration-campaign --input spec.json --scenarios scenarios.json --output parity.json [--ci-level 0.68] [--lhs-solver numerical-paper] [--rhs-solver analytic-perturbative] [--n-toys 128] [--seeds 42,43,44] [--threads 1] [--json-metrics metrics.json]nextstat combine-measurements-solver-parity-scenario-study-from-reports --lhs lhs-study.json --rhs rhs-study.json [--lhs-solver numerical-paper] [--rhs-solver analytic-perturbative] [--format json|markdown] [--output parity.json] [--json-metrics metrics.json]nextstat combine-measurements-solver-parity-calibration-campaign-from-reports --lhs lhs-campaign.json --rhs rhs-campaign.json [--lhs-solver numerical-paper] [--rhs-solver analytic-perturbative] [--format json|markdown] [--output parity.json] [--json-metrics metrics.json]nextstat combine-measurements-solver-parity-scenario-study-summarize --input parity.json --output digest.json [--format json|markdown] [--json-metrics metrics.json]nextstat combine-measurements-solver-parity-calibration-campaign-summarize --input parity.json --output digest.json [--format json|markdown] [--json-metrics metrics.json]nextstat combine-measurements-calibration-campaign-summarize --input campaign.json --output summary.json [--format json|markdown] [--json-metrics metrics.json]nextstat combine-measurements-calibration-campaign-brief --input summary_a.json --input summary_b.json [--label outlier --label topmass_full] [--format json|markdown] [--output brief.json] [--json-metrics metrics.json]nextstat combine-measurements-calibration-campaign-family-report --input brief_a.json --input brief_b.json [--label cross_fixture --label topmass_only] [--format json|markdown] [--output report.json] [--json-metrics metrics.json]nextstat combine-measurements-calibration-campaign-family-matrix --input family_report.json [--format json|markdown] [--output matrix.json] [--json-metrics metrics.json]nextstat combine-measurements-calibration-campaign-portfolio --input matrix_a.json --input matrix_b.json [--label cross_portfolio --label topmass_only_portfolio] [--format json|markdown] [--output portfolio.json] [--json-metrics metrics.json]nextstat combine-measurements-calibration-campaign-portfolio-stability --input portfolio_a.json --input portfolio_b.json [--label seedgrid_a --label seedgrid_b] [--format json|markdown] [--output stability.json] [--json-metrics metrics.json]nextstat viz profile --input workspace.json --start 0 --stop 5 --points 21 [--output profile.json] [--threads 1]nextstat viz cls --input workspace.json [--alpha 0.05] [--scan-start 0 --scan-stop 5 --scan-points 201] [--output cls.json] [--threads 1]nextstat viz ranking --input workspace.json [--fit fit.json] [--output ranking.json] [--threads 1](for simplified-likelihood this ranks reduced nuisance coordinates, not source-level systematics)nextstat viz pulls --input workspace.json --fit fit.json [--output pulls.json] [--threads 1](outputs JSON artifact, not an image — useviz renderto produce PNG/PDF)nextstat viz corr --input workspace.json --fit fit.json [--include-covariance] [--output corr.json] [--threads 1]nextstat viz distributions --input workspace.json --histfactory-xml combination.xml [--fit fit.json] [--output distributions.json] [--threads 1]nextstat viz gammas --input workspace.json --fit fit.json [--output gammas.json] [--threads 1]nextstat viz separation --input workspace.json [--signal-samples signal] [--histfactory-xml combination.xml] [--output separation.json] [--threads 1]nextstat viz summary fit1.json fit2.json [--labels "Analysis A,Analysis B"] [--output summary.json]nextstat viz pie --input workspace.json [--fit fit.json] [--output pie.json] [--threads 1]nextstat viz render --kind pulls|corr|ranking --input artifact.json --output figure.png [--python python3] [--title "..."] [--dpi 220] [--corr-include "..."] [--corr-exclude "..."] [--corr-top-n 40]nextstat mass-scan --workspaces-dir mass_workspaces/ [--alpha 0.05] [--scan-start 0 --scan-stop 5 --scan-points 41] [--labels m100,m200] [--json-metrics metrics.json] [--threads 1]nextstat preprocess smooth --input workspace.json [--output smoothed.json] [--max-variation 0.0]nextstat preprocess prune --input workspace.json [--output pruned.json] [--threshold 0.005]nextstat report --input workspace.json --histfactory-xml combination.xml --out-dir report/ [--fit fit.json] [--render] [--deterministic] [--blind-regions SR1,SR2] [--include-covariance] [--uncertainty-grouping prefix_1] [--skip-uncertainty] [--overwrite] [--pdf report.pdf] [--svg-dir svg/] [--python python3] [--label-status Internal] [--sqrt-s-tev 13] [--show-mc-band true] [--show-stat-band true] [--band-hatch ////] [--palette hep2026|tableau10]nextstat validation-report --apex2 master_report.json --workspace workspace.json --out validation_report.json [--pdf validation_report.pdf] [--deterministic]nextstat m15 assessment-table --config m15_config.json --validation-report validation_report.json --pharma-validation pharma_validation.json [--format json|markdown] [--output m15_assessment_table.json] [--deterministic]nextstat m15 map --config m15_config.json --assessment-table m15_assessment_table.json [--format json|markdown] [--output m15_map.json] [--deterministic]nextstat m15 mar --map m15_map.json --assessment-table m15_assessment_table.json --validation-report validation_report.json --pharma-validation pharma_validation.json [--format json|markdown] [--output m15_mar.json] [--deterministic]nextstat m15 profile-diff --config m15_config.json [--format json|markdown] [--output m15_profile_diff_report.json] [--deterministic]nextstat m15 bundle --config m15_config.json --assessment-table m15_assessment_table.json --map m15_map.json --mar m15_mar.json --validation-report validation_report.json --pharma-validation pharma_validation.json [--output m15_bundle_manifest.json] [--deterministic]nextstat version
pyhf-only workspace commands:
nextstat export histfactorynextstat preprocess smoothnextstat preprocess prunenextstat reportnextstat viz distributions
Notes on combination commands:
nextstat combineremains the workspace-merge command for pyhf/HS3 JSON workspaces.nextstat combine-measurements-build-specis the stable-first tabular ingress for scalar measurement combinations; it turns spreadsheet-friendly CSV/TSV bundles into the canonical JSON spec consumed by the fit/calibration commands.nextstat combine-measurements-build-spec --manifest manifest.yamlis the stable-first manifest ingress helper; it resolves a YAML/JSON wrapper around the same CSV/TSV bundle and is the shortest supported path for first-run adoption.- The tabular helper accepts:
--measurementswithname,value--stat-covarianceas a named square matrix- optional
--systematicsrows withsystematic,measurement,magnitude,error_on_error,aux_mean - optional
--correlationsrows withsystematic,row_measurement,col_measurement,corr
- The manifest helper expects:
schema_version: nextstat_measurement_combination_manifest_v0poimeasurements_tablestat_covariance_table- optional
systematics_table - optional
correlations_table
- If
--correlationsis omitted, each systematic defaults to identity correlation. nextstat combine-measurementsis the stable-first CLI entry point for scalar HEP measurement combinations with optionalerror_on_errorsupport.nextstat combine-measurementsis JSON in / JSON out and supports deterministic execution with--threads 1.nextstat combine-measurements --solver autois the default stable path; it tries the perturbative path first and falls back to the paper-faithful numerical path when the perturbative validity gate fails.- When that runtime dispatch differs from the requested solver, the result JSON records it through
diagnostics.requested_solveranddiagnostics.effective_solver. nextstat combine-measurements --solver numericalkeeps the existing reduced-basis numerical GVM path as the explicit compatibility mode.nextstat combine-measurements --solver numerical-paperruns the paper-faithful numerical GVM path in the original correlatedtheta_s^ibasis.nextstat combine-measurements --solver analytic-perturbativeruns the Eq. (21)-(28) / Appendix B perturbative profile approximation and rejects cases that fall outside the Eq. (29)/(60) validity radius.nextstat combine-measurements-calibrate,...-calibrate-study,...-scenario-study, and...-calibration-campaignaccept the same--solvercontract.- For calibration-style commands, requested
solvercontrols the fit path used for the reference/scenario results; toy generation usesnumerical-paperas the deterministic paper-faithful reference fornumerical-paper,analytic-perturbative, andauto. nextstat combine-measurementssurfaces Lawley/Bartlett diagnostics for both trivial and non-trivial correlated GVM cases throughdiagnostics.bartlett.nextstat combine-measurementsalso surfaces perturbative-validity diagnostics throughdiagnostics.perturbative_validity, exposing the Eq. (29)/(60)-style convergence indicator for each uncertain systematic.nextstat combine-measurements-calibrateis the stable-first toy-calibration companion command; it returns a separate calibration report with the fitted reference result, empiricalq/q_starmoments, and a deterministicseed/n_toysrecord.nextstat combine-measurements-calibrate-studyis the stable-first repeated-seed companion command; it returnsper_seedsummaries plus aggregate stability diagnostics for CI inflation and Bartlett behavior across deterministic seed sweeps.nextstat combine-measurements-scenario-studyremains research-grade; it applies multipleerror_on_errorconfigurations to the same base spec and returns baseline-relative comparisons for each scenario plus aggregate ordering/stability diagnostics.nextstat combine-measurements-calibration-campaignremains research-grade; it composes both layers, runs the named scenario study and a repeated-seed calibration study for each scenario, then emits one advanced artifact with fit-side and calibration-side envelopes.nextstat combine-measurements-solver-parity-scenario-studyis the direct paper-facing parity command for Fig. 5 style workflow checks at the scenario-study level; it runs the same scenarios with two solver modes and reports per-scenario baseline/fit differences.nextstat combine-measurements-solver-parity-calibration-campaignextends that parity workflow to the full repeated-seed calibration campaign, comparing fit-side and calibration-side envelopes for two solver modes on the same seed grid.nextstat combine-measurements-solver-parity-scenario-study-from-reportsis the cached post-processing variant; it builds the same scenario-study parity artifact from two precomputed study reports and does not rerun fits.nextstat combine-measurements-solver-parity-calibration-campaign-from-reportsis the cached post-processing variant for repeated-seed campaigns; it builds the same calibration-campaign parity artifact from two precomputed campaign reports and does not rerun fits or toys.- Both solver-parity commands support
--format markdownfor a compact human-readable review note in addition to the canonical JSON artifact. nextstat combine-measurements-solver-parity-scenario-study-summarizeis the cheap post-processing companion for a scenario-study parity artifact; it emits a ranked digest with dominant gap scenarios, supported-systematics consistency, and perturbative-overlap flags.nextstat combine-measurements-solver-parity-calibration-campaign-summarizeis the cheap post-processing companion for a calibration-campaign parity artifact; it emits a ranked digest with dominant fit/calibration gap scenarios plus toy-generation-method consistency.nextstat combine-measurements-calibration-campaign-summarizeis the cheap post-processing companion command; it reads an existing campaign artifact and emits either a JSON digest or a Markdown review note with dominant scenarios, ranks, and near-neutral calibration cases.nextstat combine-measurements-calibration-campaign-briefis the cheap multi-artifact post-processing companion command; it reads multiple existing campaign digests and emits either a comparative JSON brief or a Markdown research note spanning several scenario families or fixtures.nextstat combine-measurements-calibration-campaign-family-reportis the cheap post-processing layer above...-brief; it reads multiple existing brief artifacts and emits either a family-level JSON report or a Markdown research note for cross-family review.nextstat combine-measurements-calibration-campaign-family-matrixis the machine-readable dominance layer above...-family-report; it reads one existing family report and emits either a JSON ranking/pairwise-comparison artifact or a Markdown matrix for deterministic cross-family review.nextstat combine-measurements-calibration-campaign-portfoliois the portfolio layer above...-family-matrix; it reads multiple existing family-matrix artifacts and emits either a JSON portfolio-comparison artifact or a Markdown note for cross-campaign review.nextstat combine-measurements-calibration-campaign-portfolio-stabilityis the stability layer above...-portfolio; it reads multiple existing portfolio artifacts and emits either a JSON stability report or a Markdown note for deterministic cross-run ordering checks.
Recommended operator flow:
combine-measurements-build-specwhen your source of truth is tabularcombine-measurementscombine-measurements-calibratecombine-measurements-calibrate-studycombine-measurements-scenario-studycombine-measurements-calibration-campaign...-summarize/...-brief/...-family-*/...-portfolio*...-solver-parity-*only when you need paper-facing numerical-vs-perturbative comparison
HEP / Unbinned (event-level) (Phase 1, experimental):
-
nextstat convert --input data.root --tree MyTree --output events.parquet --observable mass:100:180 [--selection "..."] [--weight "..."] [--max-events 1000000]- Writes the unbinned Parquet schema v1 (
docs/references/unbinned-parquet-schema.md) and embeds observable bounds in Parquet metadata.
- Writes the unbinned Parquet schema v1 (
-
nextstat unbinned-fit --config unbinned.json [--threads 0] [--gpu cuda|metal] [--opt-max-iter N] [--opt-tol X] [--opt-m M] [--opt-smooth-bounds]- Optimizer overrides tune L-BFGS-B for this fit invocation only.
--opt-smooth-boundsenables smooth parameter transforms (often reduces iterations on constrained fits).- Supported PDFs (v0):
gaussian,crystal_ball,double_crystal_ball,exponential,chebyshev,histogram,histogram_from_tree,kde,kde_from_tree - Supported yield modifiers (v0):
normsys(Code1:hi^alpha/lo^{-alpha}),weightsys(code0/code4p interpolation) - Tree-driven non-parametrics (
histogram_from_tree,kde_from_tree) supportweight_systematics(per-event weight ratios) andhorizontal_systematics(up/down observable expressions). --gpuis currently supported only for a conservative subset (multi-channel supported; each channel must be 1D):- Observables:
n_obs=1per channel - PDFs:
gaussian,exponential,crystal_ball,double_crystal_ball,chebyshev(Chebyshev order<= 16)histogramhistogram_from_treeonly when used as a pre-materialized histogram shape (no shape morphing):horizontal_systematicsmust be empty andweight_systematicsmay be used only withapply_to_shape: false(yield-only).- Yields: fixed / parameter / scaled
- Yield modifiers:
normsys,weightsys(ROOT interpolation defaults only) - Gaussian constraints: supported
- Observed-data weights: supported (finite and
>= 0)
- Observed-data weights can be provided via:
- ROOT:
channels[].data.weight: "<expr>"(e.g.weight_mc) - Parquet: use
nextstat convert --weight "<expr>" ...to embed aweightscolumn Unsupported specs error out (CPU path remains available without--gpu). HEP / Hybrid (binned + unbinned) (Phase 4):
- ROOT:
- Observables:
-
nextstat hybrid-fit --binned workspace.json --unbinned unbinned.yaml [--output result.json] [--json-metrics metrics.json] [--threads 1]- Combines a binned (pyhf/HS3 JSON) and unbinned (YAML/JSON spec) model into a single
HybridLikelihoodwith shared parameters matched by name. Shared parameters get intersected bounds. - POI resolution: prefers binned model's POI, falls back to unbinned spec's
model.poi. - Output JSON includes
hybrid: true,n_shared,n_binned_params,n_unbinned_params, bestfit, uncertainties, covariance.
- Combines a binned (pyhf/HS3 JSON) and unbinned (YAML/JSON spec) model into a single
-
nextstat unbinned-scan --config unbinned.json --start 0 --stop 5 --points 21 [--threads 0] [--gpu cuda|metal]- Requires
model.poiin the spec (POI is scanned).
- Requires
-
nextstat unbinned-fit-toys --config unbinned.json --n-toys 100 --seed 42 [--gen init|mle] [--set name=value ...] [--threads 0] [--gpu cuda|metal|auto] [--gpu-devices 0,1,...] [--gpu-sample-toys] [--gpu-native] [--gpu-shards N] [--shard INDEX/TOTAL] [--summary-ci-method percentile|bca --summary-ci-level 0.68 --summary-ci-bootstrap 1000]- Requires
model.poiin the spec (POI is summarized across toys). - Output includes per-toy convergence flags (
results.converged[]) and pull summaries (from converged toys only). - CPU toy fitting uses warm-start (MLE θ̂), retry with jitter (up to 3 attempts), smooth bounds escalation on last retry, and Rayon parallel iteration. Hessian is skipped by default — only computed when pull guardrails are active (
--max-abs-poi-pull-mean/--poi-pull-std-range). --gpu autois a policy-gated mode: it selects CPU vs CUDA based on model topology and estimated events/toy and prints the decision reason. It must not be combined with--gpu-devices/--gpu-shards/--gpu-sample-toys/--gpu-native(use--gpu cuda|metalfor explicit control).- Hessian skip policy: Hessian adds ~40-50% compute per toy. For HEP CLs, only q̃(μ) = 2·ΔNLL is needed (no Hessian). For pharma, pulls are optional diagnostics. Hessian is auto-enabled by the CLI when pull guardrails are specified; otherwise it is skipped for throughput.
- Error metrics are split:
results.n_validation_error(PDF/spec constraint violations),results.n_computation_error(numeric failures),results.n_nonconverged(optimizer did not converge).n_error = n_validation_error + n_computation_error. - Optional summary CI (
summary.mean_ci, opt-in):--summary-ci-method percentile|bcaenables an additional CI block forsummary.meancomputed from converged finitepoi_hat.--summary-ci-levelsets confidence level (must be in(0,1)), default0.68.--summary-ci-bootstrapsets bootstrap resample count, default1000.- If
bcais requested but BCa prerequisites fail, output falls back to percentile and recordssummary.mean_ci.diagnostics.fallback_reason.
- CPU farm mode (
--shard INDEX/TOTAL): runs only a deterministic slice of the toy range. Each shardkofMtotal runs toys[start_k .. start_k + count_k)withseed + start_kas the base seed. This enables linear scale-out across cluster nodes. Combine results withunbinned-merge-toys. Example (4-node farm, 10000 toys):nextstat unbinned-fit-toys --config spec.json --n-toys 10000 --shard 0/4 -o shard0.json nextstat unbinned-fit-toys --config spec.json --n-toys 10000 --shard 1/4 -o shard1.json nextstat unbinned-fit-toys --config spec.json --n-toys 10000 --shard 2/4 -o shard2.json nextstat unbinned-fit-toys --config spec.json --n-toys 10000 --shard 3/4 -o shard3.json nextstat unbinned-merge-toys shard0.json shard1.json shard2.json shard3.json -o merged.json
- Requires
-
nextstat unbinned-merge-toys <shard1.json> <shard2.json> [<shard3.json> ...] [-o merged.json]- Merges shard outputs from
unbinned-fit-toys --shardinto a single result. - Validates consistent full
genconfig across shards (point, params, overrides, seed, n_toys). - Merged output includes
overall_convergence,fittable_convergence, per-shard detail, and all per-toy arrays concatenated in shard order.
- Merges shard outputs from
-
In
--gpumode, multi-channel specs are supported (each included channel must be 1D; total NLL is the sum across channels). -
--gpu-devicesselects CUDA devices for host-toy sharding (example:--gpu cuda --gpu-devices 0,1). If omitted, defaults to device0. -
--gpu-sample-toysenables experimental GPU toy sampling (default remains CPU toy sampling). GPU toy sampling supports Gaussian/Exponential/CrystalBall/DoubleCrystalBall/Chebyshev/Histogram PDFs (per included channel; yields and yield modifiers are supported). -
--gpu-native(CUDA only, experimental) enables persistent on-device L-BFGS (pipeline = "cuda_gpu_native"or"cuda_gpu_native_sharded"). This flag is explicit opt-in and is never auto-enabled.- Metal does not support
--gpu-shardsyet. For large workloads, CLI now auto-splits toys into contiguous internal batches to keep 32-bit toy offsets safe (timing.breakdown.toys.n_batches,max_toys_per_batch). It fails fast only when a single toy already exceeds the 32-bit offset budget. --gpu-shards N(CUDA only) enables logical toy sharding (round-robin over--gpu-devices):- with
--gpu-sample-toys: sharded device-resident path (pipeline = "cuda_device_sharded"), - without
--gpu-sample-toys: sharded host-toy path (pipeline = "cuda_host_sharded"). - with
--gpu-sample-toys, CLI may auto-increase requested shard count to satisfy 32-bit toy-offset and VRAM safety budgets; effective mapping is reported intiming.breakdown.toys.device_shard_plan. - Practical single-GPU validation commands (no 2+ GPU stand required):
nextstat unbinned-fit-toys --config unbinned.json --n-toys 2000 --gpu cuda --gpu-sample-toys --gpu-devices 0 --gpu-shards 4 --json-metrics metrics.jsonnextstat unbinned-fit-toys --config unbinned.json --n-toys 2000 --gpu cuda --gpu-devices 0 --gpu-shards 4 --json-metrics metrics.json- Expect
timing.breakdown.toys.device_shard_plan = [0,0,0,0].
- with
histogram_from_treeis also supported under--gpu-sample-toyswhen it falls into the GPU subset (materialized histogram shape, i.e. no shape morphing: nohorizontal_systematics, andweight_systematicsonly withapply_to_shape: false).- Integration coverage includes this
histogram_from_treesubset on both CUDA and Metal.- CUDA:
cuda_device(single shard) /cuda_device_sharded(multi-shard) pipelines keepobs_flatdevice-resident for batch fits (eliminates D2H+H2D round-trip). - Metal:
metal_devicepipeline samples toys on Metal and passes device-residentobs_flatdirectly into the Metal batch fitter.
- CUDA:
- If
--json-metrics <path>is provided,timing.breakdown.toysincludes a coarse timing breakdown:pipeline:cpu(CPU fit) |host(CPU sample + single-GPU CUDA batch fit) |cuda_host_sharded(CPU sample + sharded CUDA host-toy batch fit) |cuda_host_multi_gpu(CPU sample + multi-GPU CUDA batch fit) |cuda_device(GPU sample + single-shard CUDA batch fit) |cuda_device_sharded(GPU sample + sharded CUDA batch fit) |cuda_gpu_native(persistent on-device L-BFGS) |cuda_gpu_native_sharded(sharded persistent on-device L-BFGS) |metal_host(CPU sample + Metal batch fit) |metal_device(Metal sample + Metal batch fit)device_ids: CUDA device ids used for sharding (only for CUDA runs)device_shard_plan: CUDA shard→device mapping when--gpu-shardsis active (only for CUDA runs)warm_start,sample_s,batch_build_s,batch_fit_s,poi_sigma_enabled,poi_sigma_s- Metal-specific:
n_batches,max_toys_per_batch: internal Metal chunk plan used to stay within 32-bit toy-offset capacitysampler_init_s: sampler construction time formetal_devicerunssample_phase_detail: aggregated sampler phase timings (prepare_s,counts_kernel_s,counts_readback_s,prefix_sum_s,sample_kernel_s) and per-channel breakdown
- CI guardrails (optional):
--require-all-converged,--max-abs-poi-pull-mean <x>,--poi-pull-std-range <low> <high>
- Metal does not support
-
nextstat unbinned-ranking --config unbinned.json [--threads 0]- Requires
model.poiin the spec (impacts are computed on POI).
- Requires
-
nextstat unbinned-hypotest --config unbinned.json --mu 1.0 [--threads 0] [--gpu cuda|metal]- Computes
q_mu(andq0ifmu=0is within bounds).
- Computes
-
nextstat unbinned-hypotest-toys --config unbinned.json --mu 1.0 [--n-toys 1000 --seed 42] [--expected-set] [--threads 0] [--gpu cuda|metal] [--gpu-devices 0,1,...] [--gpu-sample-toys] [--gpu-shards N] -
Toy-based CLs
hypotest(qtilde) for unbinned models. -
In
--gpumode: per-toy fits are accelerated via GPU batch/lockstep fitting (conservative GPU subset applies). Multi-channel specs are supported (each included channel must be 1D). -
--gpu-devicesselects CUDA devices for host-toy sharding (default0if omitted). -
Toys are sampled on CPU by default;
--gpu-sample-toysenables experimental GPU toy sampling (Gaussian/Exponential/CrystalBall/DoubleCrystalBall/Chebyshev/Histogram PDFs, per included channel).- Metal does not support
--gpu-shardsyet. For large workloads, CLI now auto-splits toys into contiguous internal batches to keep 32-bit toy offsets safe (timing.breakdown.toys.n_batches,max_toys_per_batch). It fails fast only when a single toy already exceeds the 32-bit offset budget. --gpu-shards N(CUDA only) enables logical toy sharding (round-robin over--gpu-devices):- with
--gpu-sample-toys: sharded device-resident path (pipeline = "cuda_device_sharded"), - without
--gpu-sample-toys: sharded host-toy path (pipeline = "cuda_host_sharded"). - with
--gpu-sample-toys, CLI may auto-increase requested shard count to satisfy 32-bit toy-offset and VRAM safety budgets; effective mapping is reported intiming.breakdown.toys.device_shard_plan. - Practical single-GPU validation commands:
nextstat unbinned-hypotest-toys --config unbinned.json --mu 1.0 --n-toys 2000 --gpu cuda --gpu-sample-toys --gpu-devices 0 --gpu-shards 4 --json-metrics metrics.jsonnextstat unbinned-hypotest-toys --config unbinned.json --mu 1.0 --n-toys 2000 --gpu cuda --gpu-devices 0 --gpu-shards 4 --json-metrics metrics.json- Expect
timing.breakdown.toys.device_shard_plan = [0,0,0,0].
- with
histogram_from_treeis also supported under--gpu-sample-toyswhen it falls into the GPU subset (materialized histogram shape, i.e. no shape morphing: nohorizontal_systematics, andweight_systematicsonly withapply_to_shape: false).- Integration coverage includes this
histogram_from_treesubset on both CUDA and Metal.
- Metal does not support
-
In
--gpu cudamode,--json-metrics <path>includestiming.breakdownwith:obs_fits_s: observed-data baseline fits time (free + fixed generation points)toys: per-ensemble timings forbandsb(sampling + batch build + free/fixed fits)
-
In
--gpu metalmode,--json-metrics <path>includes the same per-ensemble timing structure undertiming.breakdown.toys:n_batches,max_toys_per_batchb.sample_s,b.ensemble_s,b.build_s,b.free_fit_s,b.fixed_fit_ssb.sample_s,sb.ensemble_s,sb.build_s,sb.free_fit_s,sb.fixed_fit_s
Time series (Phase 8):
nextstat timeseries kalman-filter --input kalman_1d.jsonnextstat timeseries kalman-smooth --input kalman_1d.jsonnextstat timeseries kalman-em --input kalman_1d.json ...nextstat timeseries kalman-fit --input kalman_1d.json ...nextstat timeseries kalman-viz --input kalman_1d.json [--max-iter 50] [--level 0.95] [--forecast-steps ...]nextstat timeseries kalman-forecast --input kalman_1d.json ...nextstat timeseries kalman-simulate --input kalman_1d.json ...nextstat timeseries garch11-fit --input returns.jsonnextstat timeseries sv-logchi2-fit --input returns.json- Kalman JSON input accepts fixed weekly aliases
local_level_weeklyandlocal_linear_trend_weeklyin addition to the generic seasonal builders.
Survival analysis (Phase 9):
nextstat survival cox-ph-fit --input cox.json [--ties efron|breslow] [--no-robust] [--no-cluster-correction] [--no-baseline]nextstat survival km --input km.json [--conf-level 0.95] [--output km.json]nextstat survival log-rank-test --input lr.json [--output lr_result.json]
Churn / Subscription (Phase 7):
nextstat churn generate-data [--n-customers 2000] [--n-cohorts 6] [--max-time 24] [--seed 42]nextstat churn retention --input churn.json [--conf-level 0.95]nextstat churn risk-model --input churn.jsonnextstat churn bootstrap-hr --input churn.json [--n-bootstrap 1000] [--seed 42] [--conf-level 0.95] [--ci-method percentile|bca] [--n-jackknife 200]nextstat churn uplift --input churn.json [--horizon 12]nextstat churn ingest --input raw.json [--observation-end 24]nextstat churn cohort-matrix --input churn.json --periods 1,3,6,12,24nextstat churn compare --input churn.json [--correction bh|bonferroni] [--alpha 0.05]nextstat churn diagnostics --input churn.json [--trim 0.01] [--covariate-names name1,name2] [--out-dir artifacts/] [--output diag.json]nextstat churn uplift-survival --input churn.json [--horizon 12] [--eval-horizons 3,6,12,24]churn bootstrap-hrnow supports:--ci-method percentile|bca(default:percentile)--n-jackknife Nfor BCa acceleration estimation (used only forbca)
- Output includes method metadata (
ci_method_requested, per-coefficientci_method) and per-coefficient diagnostics undercoefficients[].ci_diagnosticswith fallback reason when BCa falls back to percentile.
The --gpu <device> flag enables GPU acceleration, where <device> is one of:
cuda— NVIDIA GPU (f64 precision). Requirescudafeature and an NVIDIA GPU at runtime.metal— Apple Silicon GPU (f32 precision). Requiresmetalfeature and Apple Silicon (M1+) at runtime.
Without --gpu, the standard CPU (SIMD/Rayon + Accelerate) path is used. If the requested GPU is not available at runtime, the command exits with an error.
# CUDA (NVIDIA)
cargo build -p ns-cli --features cuda --release
# Metal (Apple Silicon)
cargo build -p ns-cli --features metal --release
# Both
cargo build -p ns-cli --features "cuda,metal" --releasefit --gpu cuda|metal — Single-model MLE fit on GPU. Uses GpuSession for fused NLL+gradient (1 kernel launch per L-BFGS iteration). Hessian computed via finite differences of GPU gradient at the end.
nextstat fit --input workspace.json --gpu cuda
nextstat fit --input workspace.json --gpu metalscan --gpu cuda|metal — GPU-accelerated profile likelihood scan. A single GpuSession is shared across all scan points with warm-start between mu values.
nextstat scan --input workspace.json --start 0 --stop 5 --points 21 --gpu cuda
nextstat scan --input workspace.json --start 0 --stop 5 --points 21 --gpu metalhypotest-toys --gpu cuda|metal — Batch toy fitting on GPU. The lockstep GPU batch optimizer computes NLL + analytical gradient for all toys in a single kernel launch per iteration. Both CUDA (f64) and Metal (f32) backends are supported.
# CUDA (NVIDIA, f64)
nextstat hypotest-toys --input workspace.json --mu 1.0 --n-toys 10000 --gpu cuda
# Metal (Apple Silicon, f32 — tolerance relaxed to 1e-3)
nextstat hypotest-toys --input workspace.json --mu 1.0 --n-toys 10000 --gpu metal"error: CUDA not available" — The binary was not built with --features cuda, or no NVIDIA GPU/driver is detected at runtime. Verify: nvidia-smi must show a GPU. Rebuild with cargo build -p ns-cli --features cuda --release.
"error: Metal not available" — The binary was not built with --features metal, or the machine is not Apple Silicon (M1+). Metal requires macOS 13+ and Apple GPU family 7+.
Metal f32 precision — Metal uses f32 (Apple Silicon has no hardware f64). Convergence tolerance is automatically clamped to max(tol, 1e-3). Expect NLL relative differences of ~1e-6 vs CPU f64. This is sufficient for toy-based hypothesis testing but not for strict parity validation — use CPU with --parity for that.
GPU interpolation defaults — GPU backends require --interp-defaults root (Code4/Code4p polynomial interpolation). If your model uses Code1 (exponential) NormSys with non-positive hi/lo factors, the GPU kernel cannot represent the CPU piecewise-linear fallback and will error. Use CPU path for such models.
Out-of-memory on large models — Each toy in batch mode uses shared memory proportional to n_params + n_bins. For very large models (>500 params), reduce --n-toys or use CPU path. CUDA: check nvidia-smi for VRAM usage. Metal: unified memory is shared with the system.
Slow first run — Metal compiles MSL kernels at runtime on first invocation (~1-2s). Subsequent runs reuse the compiled pipeline. CUDA uses pre-compiled PTX (no first-run overhead).
NextStat separates "specification correctness" (pyhf parity) from "speed" via two evaluation modes:
nextstat fit --input workspace.json --parityWhen --parity is active:
- EvalMode::Parity is set process-wide
- Kahan compensated summation replaces naive
+=in Poisson NLL - Apple Accelerate is automatically disabled
- Thread count forced to 1 (sequential Rayon)
- Results are bit-exact reproducible across runs
nextstat fit --input workspace.json --threads 1Same as --parity but without Kahan summation. Use --parity instead for full determinism.
Interpolation note:
- Use
--interp-defaults {root|pyhf}to choose interpolation defaults for pyhf JSON inputs. - GPU backends currently require
--interp-defaults root(Code4/Code4p). - HS3 inputs use ROOT HistFactory defaults (Code1 for NormSys, Code0 for HistoSys) and are CPU-only for now.
NEXTSTAT_DISABLE_ACCELERATE=1 nextstat fit --input workspace.jsonDisables Apple Accelerate only, without forcing single-thread or Kahan.
| Tier | Metric | Parity Tolerance | Fast Tolerance |
|---|---|---|---|
| 1 | Per-bin expected data | 1e-12 | 1e-10 |
| 2 | Expected data vector | 1e-8 | 1e-8 |
| 3 | NLL value | 1e-8 atol | 1e-6 rtol |
| 4 | Gradient (AD vs FD) | 1e-6 atol | 1e-6 atol |
| 5 | Best-fit params | 2e-4 | 2e-4 |
| 6 | Uncertainties | 5e-4 | 5e-4 |
| 7 | Toy ensemble | 0.03–0.05 | 0.03–0.05 |
Full tolerance values: tests/python/_tolerances.py.
import nextstat
nextstat.set_eval_mode("parity") # or "fast" (default)
print(nextstat.get_eval_mode())upper-limit supports two modes:
- Bisection (root finding): default.
- Scan mode: provide
--scan-start,--scan-stop,--scan-pointsto compute limits from a dense CLs curve.
Scan mode is useful for:
- storing a full curve for plotting
- avoiding repeated root-finding (including expected-set curves)
nextstat significance tests the background-only hypothesis (mu=0) and reports the observed discovery p-value (p0) and significance (Z = inverse_normal(1-p0) ~ sqrt(q0)). Comparable to TRExFitter GetSignificance.
nextstat significance --input workspace.json
nextstat significance --input workspace.json --json-metrics metrics.json --threads 4Flags:
--input, -i— input workspace (pyhf/HS3 JSON)--output, -o— output file (JSON). Defaults to stdout--json-metrics— standardized metrics JSON (schemanextstat_metrics_v0)--threads— thread count (default: 1)
nextstat goodness-of-fit fits the model, then computes the Poisson deviance chi-squared between the best-fit expected yields and observed data. Reports chi-squared, ndof, and p-value. Comparable to TRExFitter saturated-model GoF.
nextstat goodness-of-fit --input workspace.json
nextstat goodness-of-fit --input workspace.json --json-metrics metrics.jsonFlags:
--input, -i— input workspace (pyhf/HS3 JSON)--output, -o— output file (JSON). Defaults to stdout--json-metrics— standardized metrics JSON (schemanextstat_metrics_v0)--threads— thread count (default: 1)
nextstat combine merges multiple pyhf JSON workspaces into a single workspace. Channels, observations, and measurement parameter configs are merged. Systematics with the same name are automatically shared (correlated). Comparable to TRExFitter MultiFit combination and pyhf combine.
nextstat combine ws1.json ws2.json --output combined.json
nextstat combine ws1.json ws2.json ws3.json --prefix-channelsFlags:
inputs— positional, at least 2 input workspace files (pyhf JSON)--output, -o— output file (pyhf JSON). Defaults to stdout--prefix-channels— prefix channel names with workspace index to avoid conflicts (e.g. "SR" becomes "ws0_SR", "ws1_SR"). If omitted, channel names must be unique across all inputs.
nextstat mass-scan runs asymptotic CLs upper limits across multiple workspaces (one per mass/signal hypothesis) and outputs observed and expected (+-1sigma/+-2sigma) limits — the data for an ATLAS/CMS-style exclusion plot (mu_up vs mass).
nextstat mass-scan --workspaces-dir mass_workspaces/
nextstat mass-scan --workspaces-dir mass_workspaces/ --alpha 0.05 --scan-start 0 --scan-stop 10 --scan-points 101 --labels "100,200,300"Flags:
--workspaces-dir— directory containing workspace JSON files (one per mass point). Files are sorted lexicographically; use zero-padded names (e.g.mass_100.json,mass_200.json)--alpha— target CLs level (default: 0.05)--scan-start— CLs scan start mu (default: 0.0)--scan-stop— CLs scan stop mu (default: 5.0)--scan-points— CLs scan points per mass point (default: 41)--labels— optional labels for each mass point (comma-separated). If omitted, filenames (without extension) are used--output, -o— output file (JSON). Defaults to stdout--json-metrics— standardized metrics JSON (schemanextstat_metrics_v0)--threads— thread count (default: 1)
Native Rust preprocessing passes (no Python dependency). Both commands read a pyhf JSON workspace and write a modified workspace.
Smooth histosys templates (353QH,twice — ROOT TH1::Smooth equivalent). Applies running-median smoothing to HistoSys up/down deltas (variation - nominal), preserving the nominal shape.
nextstat preprocess smooth --input workspace.json --output smoothed.json
nextstat preprocess smooth --input workspace.json --max-variation 0.5Flags:
--input, -i— input workspace (pyhf JSON)--output, -o— output workspace (pyhf JSON). Defaults to stdout--max-variation— max relative variation cap (default: 0.0 = disabled). Bins where |delta/nominal| > cap are clamped
Prune negligible systematics from a workspace. Removes HistoSys/NormSys modifiers whose max relative effect is below a threshold.
nextstat preprocess prune --input workspace.json --output pruned.json
nextstat preprocess prune --input workspace.json --threshold 0.01Flags:
--input, -i— input workspace (pyhf JSON)--output, -o— output workspace (pyhf JSON). Defaults to stdout--threshold— pruning threshold: modifiers with max |delta/nominal| < threshold are removed (default: 0.005)
Most statistical commands support --json-metrics <path> to write a standardized metrics JSON (schema nextstat_metrics_v0) for experiment tracking and CI pipelines. Pass - to write to stdout.
Supported commands: fit, hypotest, hypotest-toys, significance, goodness-of-fit, upper-limit, scan, mass-scan, unbinned-fit, hybrid-fit.
The CLI outputs pretty JSON to stdout by default, or to --output.
nextstat import hepdata uses a versioned JSON contract:
schema_version = "nextstat.hepdata_import.v1"mode = "catalog"for--listand direct DOI--list-patchesmode = "materialize"for download/cache/materialization flow- additive
source_mode = "curated" | "direct_doi"on catalog/materialize outputs - published JSON Schema:
nextstat config schema --name hepdata_import_v1 - canonical examples:
docs/specs/hepdata_import_v1.catalog.example.jsondocs/specs/hepdata_import_v1.list_patches.example.jsondocs/specs/hepdata_import_v1.materialize.example.json
- regen/check:
python scripts/generate_hepdata_schema_examples.py [--check] - standalone reproducibility gate:
python scripts/check_io_contracts.py --family hepdata [--dry-run] [--report-json ...] - aggregate runner report schema:
docs/schemas/io/nextstat_io_contract_runner_report_v1.schema.json - acceptance criteria:
docs/specs/hep/hepdata_import_acceptance_v1.md - runtime benchmark gate for runtime-affecting changes:
docs/benchmarks/hepdata-import-runtime-gate.md - published frozen benchmark evidence:
docs/benchmarks/hepdata-import-benchmark-snapshot-2026-03-08.md - stable-surface support matrix:
docs/benchmarks/hepdata-import-support-matrix-2026-03-08.md - stable-surface release notes:
docs/benchmarks/hepdata-import-release-notes-2026-03-08.md - promotion runbook:
docs/benchmarks/hepdata-import-promotion-runbook-2026-03-08.md - release PR checklist:
docs/benchmarks/hepdata-import-release-pr-checklist-2026-03-08.md
Materialization also writes a lockfile with:
schema_version = "nextstat.hepdata_lock.v1"- additive
source_mode = "curated" | "direct_doi" - per-dataset download provenance and output SHA-256 hashes
- published JSON Schema:
nextstat config schema --name hepdata_lock_v1 - canonical example:
docs/specs/hepdata_lock_v1.example.json - two explicit source modes:
- curated catalog mode:
--manifest+--dataset ...or embedded catalog - direct DOI mode:
--doi+ required--dataset-id
- curated catalog mode:
Direct DOI mode is intentionally explicit:
--doiand--dataset-idare required together--manifest,--dataset, and--listcannot be combined with direct DOI flags--list-patchesis direct DOI only and performs a read-only archive inspection--patch <id>materializes the first PatchSet entry using a stable output id--patch <id>=<patch_name>materializes an explicit PatchSet patch name using a stable output id--bkgonly-filename/--patchset-filenameoverride archive filenames when the bundle does not use the defaultBkgOnly.json/patchset.json- materialize summaries now include per-dataset
downloadandinputs.available_patch_namesprovenance; lockfiles preserve the same input provenance alongside the existingdownloadblock - direct DOI patch-discovery and materialize outputs also expose additive per-dataset
timingsfor archive preparation / inspection / materialization diagnostics
Example catalog command:
nextstat import hepdata --listExample direct DOI patch discovery command:
nextstat import hepdata \
--list-patches \
--doi https://doi.org/10.17182/hepdata.90607.v3/r3 \
--dataset-id custom.hepdata.90607.v3.r3 \
--bkgonly-filename BkgOnly.json \
--patchset-filename patchset.json \
--cache-dir hepdata-workspaces/_cache \
--offlineExample materialization command:
nextstat import hepdata \
--dataset hepdata.116034.v1.r34 \
--out-dir hepdata-workspaces \
--cache-dir hepdata-workspaces/_cache \
--lock hepdata-workspaces/workspaces.lock.jsonExample direct DOI command:
nextstat import hepdata \
--doi https://doi.org/10.17182/hepdata.90607.v3/r3 \
--dataset-id custom.hepdata.90607.v3.r3 \
--display-name "Custom 90607" \
--bkgonly-filename BkgOnly.json \
--patchset-filename patchset.json \
--patch first_patch \
--out-dir hepdata-workspaces \
--cache-dir hepdata-workspaces/_cache \
--lock hepdata-workspaces/workspaces.lock.jsonInput JSON:
{
"times": [2.0, 1.0, 1.0, 0.5, 0.5, 0.2],
"events": [true, true, false, true, false, false],
"x": [[1.0, 0.0], [0.0, 1.0], [1.0, 1.0], [1.0, -1.0], [0.0, -1.0], [0.5, 0.5]],
"groups": [1, 1, 2, 2, 3, 3]
}Command:
nextstat survival cox-ph-fit --input cox.json --ties efronNotes:
groupsis optional. If present, robust SE are cluster-robust by default (robust_kind="cluster").- Robust SE are enabled by default; disable with
--no-robust. - Cluster small-sample correction is enabled by default; disable with
--no-cluster-correction. - Baseline cumulative hazard output is enabled by default; disable with
--no-baseline.
Output JSON keys (subset):
coef: fitted beta coefficients (no intercept)se,cov: observed-information SE/covariancerobust_se,robust_cov,robust_kind,robust_meta: sandwich SE/covariance (HC0 or cluster)baseline_times,baseline_cumhaz: baseline cumulative hazard estimate at event times
nextstat validate --config ... validates either:
- legacy
nextstat runconfig (run.yaml/run.json), or - analysis spec v0 (
schema_version: trex_analysis_spec_v0)
It prints a small JSON summary (config_type: run_config_legacy|analysis_spec_v0) and exits non-zero on validation errors.
For analysis spec v0:
nextstat validate --config ...fail-fast checksgates.baseline_comparemanifest presence when the gate is enabled.nextstat run --config ...executesgates.baseline_compareand fails the run if the baseline gate fails.
nextstat report writes multiple artifacts into --out-dir (currently: distributions.json, pulls.json, corr.json, yields.json, uncertainty.json, plus yields.csv and yields.tex). uncertainty.json is ranking-based and can be skipped via --skip-uncertainty. When --render is enabled it calls python -m nextstat.report render ... to produce a multi-page PDF and per-plot SVGs/PNGs (requires matplotlib, see nextstat[viz] extra).
For single-artifact rendering (without generating a full report pack), use:
nextstat viz render --kind pulls --input pulls.json --output pulls.png
nextstat viz render --kind corr --input corr.json --output corr.svg --corr-top-n 40
nextstat viz render --kind ranking --input ranking.json --output ranking.pdfRender style controls:
--label-status(header label)--sqrt-s-tev(header energy, TeV)--show-mc-band(total postfit uncertainty band)--show-stat-band(stat-only uncertainty band)--band-hatch(total-band hatch pattern)--palette(hep2026ortableau10)
If --fit is omitted, nextstat report runs an MLE fit itself and writes fit.json into --out-dir before producing the report artifacts.
For time series input formats, see:
docs/tutorials/phase-8-timeseries.mddocs/tutorials/phase-8-volatility.md
For the frequentist (CLs) workflow, see:
docs/tutorials/phase-3.1-frequentist.md
For compact post-fit parameter tables, see:
docs/references/fit-summary.md
Commands that build a HistFactory model from --input workspace.json auto-detect the JSON format (for example: fit, hypotest, scan, upper-limit, and most viz subcommands):
- pyhf JSON — standard HistFactory workspace with
"channels"+"measurements"at top level. - HS3 JSON — HEP Statistics Serialization Standard (v0.2) with
"distributions"+"metadata"(containing"hs3_version"), as produced by ROOT 6.37+. - simplified-likelihood JSON — reduced reinterpretation contract with
schema_version = "nextstat_simplified_likelihood_v0"and explicit basis-form or covariance-form reduced nuisance information.
Detection is instant (prefix scan of the first ~2 KB). No --format flag is needed.
# pyhf workspace (auto-detected)
nextstat fit --input workspace.json
# HS3 workspace from ROOT (auto-detected)
nextstat fit --input workspace-postFit_PTV.json
# Simplified-likelihood workspace (auto-detected)
nextstat fit --input simplified.json
# Both produce the same HistFactoryModel internallyWhen HS3 is detected, the CLI uses ROOT HistFactory default interpolation codes (Code1 for NormSys, Code0 for HistoSys) and selects the first analysis and "default_values" parameter point set.
For simplified-likelihood inputs in March 2026, the promoted stable subset is:
nextstat auditnextstat fitnextstat hypotestnextstat upper-limitnextstat scan
Other commands may accept simplified-likelihood JSON for compatibility-tested workflows, but discovery-style outputs, toy CLs, and ranking/impact surfaces remain outside the promoted stable subset.
For simplified-likelihood specifically, ranking acts on reduced nuisance coordinates from the compiled model; covariance-form and derived_from_workspace artifacts do not preserve source-level nuisance identities.
The promoted narrow exporter runtime for this surface is:
nextstat simplify workspace \
--input workspace.json \
--fit fit.json \
--derive-config derive.json \
--experiment ATLAS \
--analysis-id analysis.sl.v0 \
--reference internal-note \
--report export_report.json \
--output simplified.jsonCurrent boundary for that runtime path in March 2026:
- source workspaces are
pyhf-only - the published stable exporter claim is explicitly narrower:
pyhfsource, single-POI only, andconstraint_covariance_source = "source_model_constraints"for Gaussian-constrained source nuisances - the command emits
metadata.source_format = "derived_from_workspace" - partial per-channel bin selections are rejected explicitly instead of silently dropping semantics
derive.jsonmust choosereduction.constraint_covariance_sourceexplicitlysource_model_constraintsis the preferred bench-backed mode for Gaussian-constrained nuisance sources and the only source-side path inside the published stable boundaryaligned_fit_covarianceremains a research-grade fallback when the source nuisance surface is not Gaussian-constrained- derived reduced artifacts stay reduced-coordinate models and do not preserve original nuisance identities through reduction
- the runtime path is versioned, tested, and promoted for the narrow stable boundary above
- broader exporter fallback modes remain
research-grade
The following workspace utilities are currently pyhf-only:
nextstat export histfactorynextstat preprocess smoothnextstat preprocess prunenextstat reportnextstat viz distributions
nextstat viz separation also supports HS3 input, but for HS3 you must pass --signal-samples explicitly (auto-detection of signal samples uses pyhf channel/sample metadata).
nextstat import histfactory parses combination.xml + channel XMLs and reads ROOT histograms to produce a pyhf-style workspace.json.
It follows HistFactory conventions used by pyhf's readxml validation fixtures:
ShapeSyshistograms (<ShapeSys HistoName="...">) are treated as relative per-bin uncertainties and converted to absolutesigma_abs = rel * nominal.StatErrorfollows channel<StatErrorConfig ConstraintType=...>:ConstraintType="Poisson"=> preservesstaterror(per-channel, namestaterror_<channel>) and attaches per-binGammaconstraint metadata (non-standard extension) tomeasurements[].config.parameters[]entries namedstaterror_<channel>[i].ConstraintType="Gaussian"=> preservesstaterror(per-channel, namestaterror_<channel>) with Gaussian penalty (pyhf-style).
StatErrorhistograms (<StatError Activate="True" HistoName="...">) are treated as relative per-bin uncertainties and converted to absolutesigma_abs = rel * nominal.- ROOT/HistFactory defaults when
<StatErrorConfig>is omitted:ConstraintType="Poisson"andRelErrorThreshold=0.05(bins with relative stat error below threshold are pruned, i.e. the correspondingstaterror_<channel>[i]is fixed at 1.0). - Samples with
NormalizeByTheory="True"receive alumimodifier namedLumi. LumiRelErrandParamSetting Const="True"are surfaced viameasurements[].config.parameters(auxdata=[1],sigmas=[LumiRelErr],fixed=true).<NormFactor Val/Low/High>is surfaced viameasurements[].config.parametersasinitsandbounds.- HistFactory
<ConstraintTerm>(ROOT extension) is preserved as a non-standard fieldmeasurements[].config.parameters[].constraintand is interpreted by NextStat when building the model:Type="LogNormal": applies ROOT'salphaOfBetatransform fornormsysevaluation (keeps Gaussian constraint onalpha).Type="Gamma": applies a Gamma constraint term and interprets the parameter as a positivebetawithalpha=(beta-1)/rel.Type="Uniform": removes the Gaussian penalty (flat within bounds).Type="NoConstraint"/"NoSyst": fixes the parameter at nominal.
nextstat import trex-config currently supports only a small subset of TRExFitter configs:
ReadFrom: NTUP(or omitted).Region:blocks withVariable,Binning, optionalSelection.Sample:blocks withFile, optionalWeight, and optional simple modifiers (NormFactor,NormSys,StatError).Systematic:blocks forType: norm|weight|treeapplied bySamples:and optionalRegions:.
ReadFrom: HIST is supported only as a wrapper over an existing HistFactory export:
- Provide
HistoPath: /path/to/export_dir(must contain exactly onecombination.xmlunder it), or - Provide
CombinationXml: /path/to/combination.xmlexplicitly.
Partial TREx semantics in ReadFrom: HIST:
- If the config includes
Region:blocks, they act as an include-list for channels (in config order). - If the config includes
Sample:blocks, they act as an include-list for samples. Masking rules:Sample: XwithRegions: ...masksXonly in those channels.Sample: Xnested under aRegion: Yblock and withoutFile/Pathis treated as a region-scoped filter entry: it selects sampleXonly in channelY. Repeating the sameSample: Xunder multiple regions is allowed.- If a channel has any region-scoped filter entries, they take precedence over any global sample include-list for that channel.
Empty channels are dropped unless channels were explicitly selected via
Region:blocks (then it is an error).
- In HIST mode,
Region:blocks do not requireVariable/Binning, andSample:blocks do not requireFile(they can be used as pure filters). - When
HistoPathis provided, it is used as the base directory for resolving relative paths inside the HistFactory XML (common whencombination.xmllives underconfig/but ROOT inputs are referenced from the export root).
Optional outputs:
--analysis-yamlwrites an analysis spec v0 wrapper (inputs.mode=trex_config_txt) to drivenextstat run.--coverage-jsonwrites a best-effort report of unknown keys/attrs to help parity work against legacy configs.--expr-coverage-jsonwrites a report of expression-bearing keys (selection/weight/variable + weight systematics) and whether they compile under NextStat's ROOT-dialect expression engine.
nextstat trex import-config is a best-effort migration helper for TRExFitter .config files:
- it emits an analysis spec v0 YAML using
inputs.mode=trex_config_yaml - it also writes a mapping report listing mapped and unmapped keys
Example config: docs/examples/trex_config_ntup_minimal.txt.
Example config (HIST wrapper): docs/examples/trex_config_hist_minimal.txt.
nextstat audit inspects a pyhf or simplified-likelihood workspace.
- For pyhf JSON, it reports channel/sample/modifier counts plus unsupported features.
- For simplified-likelihood JSON, it reports channel/bin counts, reduced nuisance basis size, yield summaries, and factorization diagnostics for covariance-form inputs.
- For simplified-likelihood in March 2026,
auditis part of the promoted stable subset together withfit,hypotest,upper-limit, andscan. - Discovery-style outputs, toy CLs, and ranking/impact surfaces remain compatibility-tested rather than promoted on the simplified-likelihood path.
nextstat audit --input workspace.json
nextstat audit --input workspace.json --format json --output audit.jsonPublished JSON Schemas for the simplified-likelihood path:
nextstat config schema --name simplified_likelihood_v0
nextstat config schema --name simplified_likelihood_audit_v0
nextstat config schema --name simplified_likelihood_derive_v0
nextstat config schema --name simplified_likelihood_export_report_v0
nextstat config schema --name simplified_likelihood_export_benchmark_snapshot_report_v0
nextstat config schema --name simplified_likelihood_export_public_validation_report_v0
nextstat config schema --name simplified_likelihood_exporter_stable_review_assessment_v0
nextstat config schema --name simplified_likelihood_exporter_stable_source_semantics_boundary_v0
nextstat config schema --name simplified_likelihood_exporter_stable_candidate_blocker_matrix_v0
nextstat config schema --name simplified_likelihood_exporter_stable_candidate_review_packet_v0
nextstat config schema --name simplified_likelihood_exporter_stable_promotion_decision_v0nextstat export histfactory converts a pyhf workspace back to HistFactory XML + ROOT histogram files:
nextstat export histfactory --input workspace.json --out-dir export/
nextstat export histfactory --input workspace.json --out-dir export/ --prefix meas --overwrite --python--python generates a Python driver script alongside the XML/ROOT artifacts.
nextstat timeseries kalman-viz produces a plot-friendly JSON artifact with smoothed states, observations, marginal normal bands, and optional forecast:
nextstat timeseries kalman-viz --input kalman_1d.json
nextstat timeseries kalman-viz --input kalman_1d.json --level 0.99 --forecast-steps 20Internally runs EM → Kalman smooth → computes ±z_{α/2} × √diag(P) bands at the requested --level (default 0.95).
Input JSON for volatility commands:
{ "returns": [0.01, -0.02, 0.005, 0.0] }nextstat timeseries garch11-fit --input returns.jsonnextstat timeseries sv-logchi2-fit --input returns.jsonnextstat versionPrints the NextStat version string and exits.
A self-hosted REST API for shared GPU inference. Built as a separate binary in crates/ns-server.
# CPU only
cargo build -p ns-server --release
# With GPU support
cargo build -p ns-server --features cuda --release
cargo build -p ns-server --features metal --releasenextstat-server --port 3742 --gpu cuda
nextstat-server --host 0.0.0.0 --port 3742 --gpu metal --threads 8Arguments:
--port <PORT>— listening port (default: 3742)--host <HOST>— bind address (default: 0.0.0.0)--gpu <DEVICE>— GPU device:cudaormetal(omit for CPU-only). If the binary was built without the corresponding feature,nextstat-serverexits with an error.--threads <N>— Rayon thread pool size (default: 0 = auto)--max-body-mb <MiB>— maximum request body size in MiB (default: 64). Requests exceeding the limit return HTTP 413.
| Method | Path | Description |
|---|---|---|
POST |
/v1/fit |
MLE fit (workspace → FitResult) |
POST |
/v1/ranking |
Nuisance parameter ranking |
POST |
/v1/batch/fit |
Batch fit (multiple workspaces, max 100) |
POST |
/v1/batch/toys |
Batch toy fitting |
POST |
/v1/models |
Upload and cache a model |
GET |
/v1/models |
List cached models |
DELETE |
/v1/models/{id} |
Remove cached model |
GET |
/v1/health |
Server health check |
All endpoints accept/return JSON. Errors return {"error": "<message>"} with appropriate HTTP status codes.
Notes:
POST /v1/ranking: hybrid CPU+GPU. Nominal fit is CPU (f64, Hessian), per-nuisance refits use the configured GPU (CUDA f64 or Metal f32) whengpu=true.
Models are cached by SHA-256 hash of the workspace JSON. Pass model_id instead of workspace in fit/ranking requests to skip re-parsing. LRU eviction at 64 models by default.
See nextstat.remote in the Python API reference (docs/references/python-api.md).
For pharma (FDA/EMA-regulated) and production-grade statistical analyses, CPU-only execution is the validated path:
- Deterministic f64 arithmetic, bit-reproducible across runs with
--threads 1 - CPU farm mode (
--shard INDEX/TOTAL) provides linear scale-out across cluster nodes - Warm-start + retry + smooth bounds achieves >= 99.5% fittable convergence on validated models
- GPU paths (CUDA/Metal) are R&D-only; not part of the validated production pipeline
GPU acceleration is useful for:
- Batch toys on large models (>= 150 parameters): GPU 6x faster than CPU
- Profile scans on large models: GPU marginally faster (1.07x at 184 params)
- Differentiable NLL for PyTorch integration (training, gradient-based optimization)
CPU wins for:
- Single-model MLE fits (GPU kernel launch overhead dominates)
- Small models (< 150 parameters) in any mode
- Batch toys on small models (< 50 parameters)
By default, unbinned-fit-toys skips Hessian computation on toys (uncertainties = 0). This saves ~40-50% compute per toy.
Rationale: toy-based hypothesis testing needs only q̃(μ) = 2·(NLL(μ) − NLL(μ̂)), which requires two MLE fits but no Hessian. This applies to both HEP CLs and pharma toy studies.
Hessian IS needed for:
- Parameter pulls:
(θ̂ − θ₀) / σ̂requires σ̂ from the Hessian diagonal - Ranking / impact plots: computed on the nominal fit, not on toys
- Wald approximation: asymptotic CLs uses Hessian for σ (one fit, not toy loop)
The CLI auto-enables Hessian when pull guardrails are active (--max-abs-poi-pull-mean, --poi-pull-std-range). Python users set compute_hessian=True explicitly.
Before each release, run the convergence benchmark matrix:
| Model | Events | Toys | Target fittable conv | Target wall-time |
|---|---|---|---|---|
| Gauss+Exp 10k | 10k | 10000 | >= 99.9% | baseline |
| CB 10k | 10k | 10000 | >= 99.5% | <= +10% vs prev |
| CB 100k | 100k | 10000 | >= 99.9% | <= +10% vs prev |
Run on CPU (--threads 0, 16+ cores recommended). Use --shard for cluster runs.
Metrics to track per model:
results.n_converged / (results.n_toys - results.n_validation_error)= fittable convergenceresults.n_validation_error= spec/PDF errors (should be 0 after spec fix)results.n_computation_error= numeric failures- Wall-clock time (from
--json-metrics)