Native tuning and conformal calibration
This page documents the current native Python surface for combining model tuning
with split conformal intervals. It is intentionally narrower than the historical
finetune_params path: unsupported shapes fail closed instead of silently
falling back to a different engine.
Use this path when you already have explicit arrays for development, scoring, calibration, and prediction cohorts, and you want the tuning trace plus conformal calibration artifact persisted in a nirs4all workspace.
For a runnable smoke-tested script that chains the current public subset end to
end, see examples/user/04_models/U09_native_tuning_conformal.py. It runs
native tuning, final conformal calibration, calibrated prediction, robustness
reporting, workspace reload, and robustness artifact publication on small
in-memory arrays.
For the same lane with a spectroscopy-standard PLS model, see
examples/user/04_models/U10_native_pls_conformal_robustness.py; it tunes
PLSRegression.n_components through the public model.n_components search
space path and then runs the same conformal and robustness audit stages.
What is native today
The supported public subset is:
nirs4all.run(..., engine="dag-ml", tuning={...})one final sklearn-like estimator, optionally preceded by a linear chain of sklearn-like transformers;
explicit array datasets:
(X, y),[X, y],(X, y, sample_ids, groups, metadata),[X, y, sample_ids, groups, metadata], or{"X": X, "y": y};explicit dataset-backed mappings with a selector for fit, scoring, winner and
calibration_data.dataset, usingSpectroDataset,DatasetConfigs, dataset config mappings, or config/path strings;linear pipeline wrappers
{"steps": [...]}and the legacy public alias{"pipeline": [...]}for the same supported step sequence; do not provide both keys in one wrapper;explicit
tuning.score_datafor objective scoring, as a mapping or tuple/list;Optuna/n4m adapter execution through the native tuning seam;
optional
tuning.winnerto project a terminal prediction entry;optional top-level
calibrationor nestedtuning.calibrationto calibrate conformal intervals from that projected winner;workspace persistence for both
TuningResultandCalibratedRunResult.
The following remain intentionally closed until their gates are implemented: bare/implicit dataset loaders for native tuning, implicit splitting, branch/merge graphs, model aliases, non-linear preprocessing graphs, and bit-exact resume from an interrupted optimizer checkpoint.
Native keyword/effect quick map
Use this table when wiring notebooks, CI, bindings, Studio forms, or generated configuration into the native Python runtime. It separates the public syntax from the effect of that syntax and from the evidence that downstream tools can inspect without parsing private metadata.
Public syntax |
Runtime effect |
Published evidence |
Fail-closed boundary |
|---|---|---|---|
|
Selects the DAG-ML execution backend for the current run. |
|
It is not an optimizer selector; |
|
Runs the fixed-topology native tuning subset over one estimator or a linear sklearn-like chain. |
|
Branch/merge graphs, implicit splits, aliases, non-linear graphs, and unsupported datasets fail before execution. |
|
Defines the ordered candidate parameter contract. |
|
Values must stay TCV1 JSON-native and fingerprintable; Python objects, bytes, NaN/Infinity and duplicate canonical paths are rejected. |
|
Enqueues a caller-provided warm-start assignment as the first optimizer trial. |
First |
Keys must exist in |
|
Scores candidate trials on an explicit HPO objective cohort. |
Trial scores, trial diagnostics, and |
It is never reused as final conformal calibration evidence. |
|
Fits a temporary conformal calibrator per candidate only for conformal-aware objective metrics. |
Trial diagnostics include |
The temporary calibrator is discarded after scoring and cannot renew the final guarantee. |
|
Supplies or selects the cohort used to project the terminal winner. |
|
Missing row-aligned identities or mixed dataset/array payloads fail before calibration. |
|
Fits final split-conformal intervals from the projected winner and applies them to provided prediction values. |
|
|
|
Selects already materialized conformal intervals from a calibrated result or conformal bundle sidecar. |
|
It does not recalibrate; unsupported coverages, invalid sidecars, and conformal |
|
Produces an audit-only robustness/generalization report for frozen predictions. |
|
It does not refit, structurally retrain, or renew conformal guarantees. |
|
Replays explicit-X spectral/OOD perturbations through an in-memory predictor or saved bundle. |
|
|
|
Reports whether a stored prediction can safely run spectral/OOD replay. |
|
|
|
Publishes a |
Stored prediction id plus reloadable |
It publishes prediction evidence only; conformal artifacts/interval guarantees remain owned by |
|
Prediction-time shortcut for the same workspace prediction publisher. |
Returned |
It does not persist conformal interval artifacts; use |
|
Converts workspace/store rows loaded with arrays into native |
Preserves intervals, |
Records loaded without arrays cannot become executable prediction results. |
|
Loads one workspace prediction, delegates to |
Consumes stored executable |
Does not synthesize missing spectra or bypass |
Minimal pattern
import numpy as np
import nirs4all
from sklearn.linear_model import Ridge
from sklearn.preprocessing import StandardScaler
X_dev = np.asarray([[0.0], [1.0], [2.0], [3.0]], dtype=float)
y_dev = np.asarray([0.0, 1.0, 2.0, 3.0], dtype=float)
X_score = np.asarray([[4.0], [5.0], [6.0]], dtype=float)
y_score = np.asarray([4.0, 5.0, 6.0], dtype=float)
X_cal = np.asarray([[7.0], [8.0], [9.0], [10.0]], dtype=float)
y_cal = np.asarray([7.2, 7.8, 9.3, 9.7], dtype=float)
result = nirs4all.run(
pipeline=[
{
"name": "scale",
"class": "sklearn.preprocessing.StandardScaler",
"params": {"with_mean": False},
},
{
"name": "ridge",
"class": "sklearn.linear_model.Ridge",
"params": {"fit_intercept": False},
},
],
dataset=(
X_dev,
y_dev,
["train-001", "train-002", "train-003", "train-004"],
None,
None,
),
engine="dag-ml",
workspace_path="workspace/",
tuning={
"engine": "optuna",
"space": {"model.alpha": [0.1, 1.0]},
"force_params": {"model.alpha": 0.1},
"metric": "rmse",
"direction": "minimize",
"sampler": "grid",
"n_trials": 2,
"score_data": {
"X": X_score,
"y": y_score,
"sample_ids": ["score-001", "score-002", "score-003"],
},
"workspace_tuning_id": "ridge-native-tuning",
"winner": {
"X": X_cal,
"y_true": y_cal,
"score": 0.3,
"metric": "rmse",
"sample_ids": ["cal-001", "cal-002", "cal-003", "cal-004"],
},
},
calibration={
"y_pred": [11.0, 12.0],
"prediction_sample_ids": ["pred-001", "pred-002"],
"coverage": 0.8,
"workspace_conformal_id": "ridge-conformal",
},
)
When top-level calibration or tuning.calibration is present, the return
value is a
TunedSingleEstimatorConformalResult with:
result.run: the tunedRunResult;result.calibrated: the conformalCalibratedRunResultorPredictResult.
The top-level form is an alias for tuning.calibration in the current DAG-ML
tuning subset, so do not provide both in the same call.
Without either calibration form, the same call returns a RunResult.
DAG-ML cache namespace proofs
The Python surface does not reinterpret DAG-ML D10 cache namespace proofs. When
native training, replay, bundles or prediction-cache payloads contain
cache_namespace_fingerprints, nirs4all treats them as signed DAG-ML control
plane data and forwards them unchanged through
nirs4all.pipeline.dagml.native_client to dag_ml. DAG-ML owns validation,
namespace-aware handle derivation, file-store payload naming and columnar
manifest exposure.
There is no nirs4all keyword for mutating this proof after signing. Change the candidate, data identity, fold, trial or seed before creating the DAG-ML contract.
Inspecting tuning and conformal outputs
The tuned run keeps the optimizer evidence on RunResult without pretending
that development objective values are test scores:
print(result.tuning_id)
print(result.tuning_best_params)
print(result.tuning_best_value)
tuning_result = result.tuning_result
if tuning_result is not None:
print(tuning_result.optimizer)
print(tuning_result.n_trials)
tuning_result.save_summary("tuning-summary.json")
TuningResult.summary_artifact(), to_summary_json() and save_summary()
produce a lightweight deterministic summary.json-style payload for CI,
bindings and Studio cards. It includes the result fingerprint, engine, metric,
direction, optimizer, best params/value, trial count, trial state counts and
compact trial rows. Compact diagnostic fields are scalar-only; whitelisted
diagnostics carrying arrays or mappings fail closed instead of being
stringified into ambiguous card text. It also includes a safe-to-publish
persistence block with resume, storage_configured, study_name, and
optimizer_state_resume_supported, but deliberately omits the raw storage
URI so public cards do not leak local paths or credentials. The summary does not
replace the full TuningResult.to_json() tape.
The matching JSON Schema is public through get_tuning_summary_schema() and
tuning_summary_schema_json(). The pre-execution ordered tuning-space artifact
has its own public schema through get_tuning_space_schema() and
tuning_space_schema_json().
When workspace_path is supplied, the completed TuningResult is written at
the optimizer-to-refit boundary, before terminal winner refit, winner
projection, or final conformal calibration. If a later refit or calibration
step fails, the workspace still preserves the optimizer evidence tape for
inspection and resume checks. If every candidate trial fails, no terminal refit
is attempted, but the all-failed TuningResult remains persistable with its
trial states and summary artifact.
When calibration is requested, intervals live on the calibrated prediction
container. In that case result is a TunedSingleEstimatorConformalResult:
result.run is the underlying RunResult, result.calibrated is the
calibrated prediction container, the tuning_* accessors above proxy to
result.run, and the common conformal accessors proxy to result.calibrated.
Use the accessor methods rather than reaching into metadata:
prediction = result.calibrated if hasattr(result, "calibrated") else None
if prediction is not None:
print(result.interval_coverages)
intervals_80 = result.interval(0.8)
status = result.conformal_guarantee_status
print(status["status"])
metrics = result.metrics(y_true_prediction)
robustness_report = result.robustness(y_true=y_true_prediction)
These accessors are part of the public Python contract. Studio, bindings and scripts should depend on them, not on private fields or serialized JSON layout.
Public Python result types
The native conformal/tuning surface exposes stable top-level containers from
nirs4all:
RunResult: returned byrun(tuning=...)when no final conformal calibration is requested. Itstuning_result,tuning_id,tuning_best_params, andtuning_best_valueaccessors expose optimizer evidence.TuningResultandTrialResult: public optimizer-evidence containers returned by tuning helpers and workspace loaders.TuningResultprovides deterministic JSON persistence plussummary_artifact(),to_summary_json()andsave_summary()for dashboards and release bundles. Direct construction is fail-closed:SearchSpaceParameter,ParameterPatch,OrderedSearchSpaceSpec,DagMLTuningSpec,TrialResultandTuningResultcanonicalize tuning paths, reject non-TCV1 values, non-finite scores, boolean integers, duplicate trial ids and params outsidetuning.spacebefore fingerprinting or summary publication. Reload throughTuningResult.from_dict()orload_json()uses the same strict numeric boundary forbest_value: booleans and numeric strings are rejected, not coerced.TunedSingleEstimatorConformalResult: returned byrun(tuning=..., calibration=...)andtune_single_estimator(..., calibration=...). It keeps the tuned run inrun, the calibrated prediction result incalibrated, and proxies the commontuning_*accessors.CalibratedRunResult: returned bycalibrate(..., as_predict_result=False), conformal workspace/bundle loaders, and the tuning+calibration path when a rich calibrated artifact is requested. It carries point predictions, intervals, sample ids, the conformal artifact fingerprint, guarantee metadata, JSON persistence helpers, andmetrics(y_true). Reload recomputes storedqhatvalues from retained non-negative residual scores, then verifies that every materialized interval is exactly derived from the embedded artifact asy_pred ± qhat; grouped conformal results use a row-alignedqhatvector selected from the calibrated group for each prediction row. Self-consistent JSON with edited intervals, quantiles, group keys or vectorqhatvalues is rejected.CalibratedPredictionBlockandConformalIntervalBlock: internal/public substrate objects used by calibrated results and prediction helpers. Direct construction is fail-closed: interval coverage must match its mapping key, interval arrays must matchy_predshape, lower bounds must be<=upper bounds,qhatmust be scalar or row-aligned and non-negative, and only the supported conformal method/unit vocabulary is accepted. Positive infinity remains valid for unbounded intervals.ConformalCalibrationData: typed input helper forcalibrate(calibration_data=...). It serializes either replayed-array evidence or a selected dataset-backed calibration cohort to the same mapping syntax used by the runtime. Dataset-backed selectors reject non-string or whitespace-padded keys plus non-JSON-native values, andinclude_augmentedmust be a boolean so the helper cannot publish a coerced calibration cohort.ConformalMetricSet: returned byconformal_metrics(...)andCalibratedRunResult.metrics(y_true). It is empirical post-hoc evidence on an observed prediction cohort; it does not change the conformal guarantee. The container fails closed ifobserved_coverageis non-finite, outside[0, 1], or inconsistent withn_covered / n_samples; ifcoverage_gapis non-finite or not exactlyobserved_coverage - coverage; or if width/interval-score diagnostics are NaN or negative. Positive infinity remains valid for unbounded intervals.TUNING_ENGINES,TUNING_DIRECTIONS,TUNING_CONTRACT_KEYS,TUNING_OPTIMIZER_PERSISTENCE_KEYS,TUNING_RUNTIME_KEYS,CONFORMAL_TUNING_SCORE_METRICS,TuningEngine, andTuningDirection: public discovery and typing helpers for the nativerun(tuning=...)contract. Use them for forms, bindings, CLI validation and Studio controls instead of hard-coding the currently executable optimizer engines, objective directions, strict contract keys, optimizer persistence keys, runtime wrapper keys and conformal-aware objective metrics.inspect_tuning_space(...),OrderedSearchSpaceSpec,SearchSpaceParameter, andParameterPatch: public inspection helpers for canonical orderedtuning.spacepatches. Use the JSON-nativenirs4all.tuning.ordered_search_spaceartifact in forms, bindings and Studio previews when you need the exact patch order and search-space fingerprint before execution. UseTUNING_SPACE_SCHEMA_ID,get_tuning_space_schema()andtuning_space_schema_json()to validate this artifact without executing a pipeline.FINETUNE_ENGINES,FINETUNE_APPROACHES,FINETUNE_EVAL_MODES,FINETUNE_OPTUNA_SAMPLERS,FINETUNE_OPTUNA_PRUNERS,FINETUNE_N4M_SAMPLERS,FINETUNE_N4M_PRUNERS,FINETUNE_DAGML_DETERMINISTIC_ENGINES,FINETUNE_DAGML_META_KEYS,FINETUNE_DAGML_SELECTION_METRICS,FINETUNE_DAGML_APPROACHES,FINETUNE_DAGML_EVAL_MODES,FINETUNE_ENGINE_ALIASES,FINETUNE_SAMPLER_KEY_ALIASES,FINETUNE_EVAL_MODE_ALIASES,FinetuneEngine,FinetuneSampler,FinetunePruner,FinetuneApproach, andFinetuneEvalMode: public discovery and typing helpers for historical model-localfinetune_params. These constants separate the registry-level vocabulary from the Optuna, n4m and deterministic DAG-ML executable subsets so downstream tooling does not overclaim support.CONFORMAL_CALIBRATION_METHODS,CONFORMAL_CALIBRATION_UNITS,CONFORMAL_MULTI_TARGET_POLICIES,CONFORMAL_EXECUTABLE_MULTI_TARGET_POLICIES,ConformalMethod,ConformalUnit, andConformalMultiTarget: public discovery and typing helpers for the current conformal calibration method, exchangeability unit, and reserved/executable multi-target policy vocabulary. The executable V1 policies aremarginalfor one-dimensional targets andjoint_maxfor simultaneous multi-target regions on two-dimensional replayed arrays.ROBUSTNESS_MODES,ROBUSTNESS_EXECUTABLE_MODES, andRobustnessMode: public discovery and typing helpers for the reserved robustness reuse policies and the subset executable by the current runtime.RobustnessScenarioSpec: typed input helper forrobustness(scenarios=...). It serializes the supported audit scenario keywords to the same mapping syntax consumed by the runtime and rejects unsupported kinds, non-finite severities, negative noise severities, and unsupported distributions before execution.ROBUSTNESS_SCENARIO_KINDS/RobustnessScenarioKind: public discovery and typing helpers for the exact scenariokindvocabulary exposed by the runtime, report metadata, and keyword registry.ROBUSTNESS_STOCHASTIC_SCENARIO_KINDS,ROBUSTNESS_SCENARIO_DISTRIBUTIONS, andRobustnessScenarioDistribution: public discovery and typing helpers for the subset of scenario kinds that acceptsdistribution, and for the currently supported distribution vocabulary.RobustnessReport: returned byrobustness(...). It is an audit artifact for frozen prediction cohorts and provides deterministic JSON, Markdown, HTML and Parquet-directory exports.
These types are intended for Python users, CI automation, Studio integration and bindings. Prefer their accessors and export helpers over private implementation modules.
The keyword/effect registry is also exposed from the top-level package for generated docs, forms, Studio and bindings:
registry = nirs4all.get_keyword_registry()
registry_json = nirs4all.keyword_registry_json(indent=2)
registry_schema = nirs4all.get_keyword_registry_schema()
registry_schema_json = nirs4all.keyword_registry_schema_json(indent=2)
robustness_summary_schema = nirs4all.get_robustness_summary_schema()
robustness_summary_schema_json = nirs4all.robustness_summary_schema_json(indent=2)
scenario_keywords = [
entry for entry in registry["entries"] if entry["path"].startswith("robustness.scenarios")
]
The registry is descriptive. It reports keyword paths, value schemas, effects,
engine support and UI hints, but it does not execute or validate a run by
itself. For static consumers, nirs4all keyword-registry --output keyword-registry.json and nirs4all keyword-registry --schema --output keyword-registry.schema.json emit the same artifacts as the published docs
_static/keyword-registry*.json files.
get_robustness_summary_schema() and robustness_summary_schema_json() expose
the JSON Schema for RobustnessReport.summary_artifact() / summary.json, so
bindings and Studio can validate card/dashboard payloads without loading the
full report schema. Docs builds also publish this contract as
_static/robustness-summary.schema.json; CI can emit the same schema with
nirs4all robustness-summary-schema --output robustness-summary.schema.json.
The mapping form above is the lowest-level public syntax. The same current subset also has typed helpers:
tuning = nirs4all.NativeTuning(
engine="optuna",
space={"scale": [nirs4all.TuningPassthrough()], "model.alpha": [0.1, 1.0]},
force_params={"scale": nirs4all.TuningPassthrough(), "model.alpha": 0.1},
metric="rmse",
direction="minimize",
sampler="grid",
n_trials=2,
score_data=nirs4all.TuningScoreData(
X=X_score,
y=y_score,
sample_ids=["score-001", "score-002", "score-003"],
),
winner=nirs4all.TuningWinner(
X=X_cal,
y_true=y_cal,
score=0.3,
metric="rmse",
sample_ids=["cal-001", "cal-002", "cal-003", "cal-004"],
),
workspace_tuning_id="ridge-native-tuning",
)
result = nirs4all.run(
pipeline=[
{"name": "scale", "transform": StandardScaler()},
{"model": Ridge()},
],
dataset=(X_dev, y_dev, ["train-001", "train-002", "train-003", "train-004"]),
engine="dag-ml",
workspace_path="workspace/",
tuning=tuning,
calibration=nirs4all.TuningCalibration(
y_pred=[11.0, 12.0],
prediction_sample_ids=["pred-001", "pred-002"],
coverage=0.8,
workspace_conformal_id="ridge-conformal",
),
)
Typed helpers normalize to the same dictionaries consumed by the runtime. They
do not expand the supported subset. NativeTuning.to_tuning_spec() returns the
deterministic optimizer contract; runtime-only blocks such as score_data,
winner, calibration, and workspace ids stay outside that fingerprint.
force_params is inside that deterministic contract because it changes the
optimizer trajectory: it warm-starts the first Optuna/n4m trial with explicit
public parameter values, fails closed when a key is not present in
run.tuning.space, and can invalidate calibration when it changes the selected
predictor.
Both optimizer adapters publish that warm-start through the same public tape:
the first TrialResult.params, best_params when the forced trial wins, and
the trial search_space_fingerprint are expressed in decoded nirs4all syntax.
Backend-specific labels or encoded categorical tokens are never part of the
public result.
The tuning.space mapping is lowered to an ordered search-space contract before
any optimizer sees it. Public dotted paths such as ridge.alpha and
sklearn double-underscore paths such as ridge__alpha are canonicalized to the
same dotted path; duplicate canonical paths are rejected instead of relying on
dictionary overwrite order.
Candidate assignments and force_params become ordered ParameterPatch records
with canonical string paths and JSON-native values, and trial diagnostics include a
search_space_fingerprint so a score tape can be tied back to the exact ordered
space that produced it.
In short, sklearn double-underscore paths are canonicalized to the same dotted
spelling as the public patch syntax.
The typed helper applies the same validation as mapping payloads: storage must
be an explicit URI such as sqlite:///study.db, and study_name is trimmed and
cannot contain NUL characters.
TuningCalibration(...) validates coverage as a scalar in (0, 1) or a
non-empty unique list of such values, normalizes method and unit to the
supported lower-case spellings, and refuses values outside
CONFORMAL_CALIBRATION_METHODS and CONFORMAL_CALIBRATION_UNITS before runtime
execution. Its extra={...} field is reserved for additional calibration
options only; it cannot override typed keys such as coverage, method, unit,
prediction_sample_ids, y_pred, workspace metadata, or inject
calibration_data. TuningCalibration.as_predict_result must be a boolean,
and TuningCalibration.extra, TuningCalibration.workspace_metadata and
NativeTuning.workspace_metadata require canonical non-empty string keys.
Their values must stay strict JSON-native: finite integers/floats, strings,
booleans, null, lists, or nested mappings with canonical string keys. Non-finite
numbers, bytes, tuples, sets and arbitrary Python objects fail before
publication. The same strict boundary is enforced again when workspace tuning
or conformal rows are persisted through save_workspace_tuning_result(...),
run(tuning=..., workspace_path=...), TuningCalibration.workspace_metadata,
or save_workspace_calibrated_result(...); the workspace store does not
stringify Python objects into metadata. Prediction workspace publication applies
the same rule to predict(..., save_to_workspace=True, workspace_metadata=..., workspace_result_metadata=...) and to
save_workspace_predict_result(..., metadata=..., result_metadata=...) before
writing the prediction sidecar.
NativeTuning.space and force_params canonicalize string patch keys before
serializing. Raw NativeTuning(score_data={...}), winner={...} and
calibration={...} mappings use the same fail-closed boundary before
to_dict() publishes them: dataset selectors require canonical string keys,
include_augmented and as_predict_result are strict booleans, winner scores
are finite numbers, and nested calibration_data is rejected. Raw calibration
coverage, method, unit, workspace_metadata and
workspace_conformal_id are validated and canonicalized like the typed
TuningCalibration(...) helper. Public calibrate(..., result_metadata=...)
and predict_calibrated(..., result_metadata=...) also require strict
JSON-native mappings before generated guarantee metadata is merged. Top-level
NativeTuning core fields are
validated through DagMLTuningSpec before publication: engine, metric and
direction are canonicalized, n_trials/seed/resume reject coercive values,
and storage, study and workspace tuning ids must be canonical strings.
TuningScoreData.metric/score_metric and raw
NativeTuning.score_data.metric/score_metric must be real non-empty strings;
non-string values, blank strings and NUL-containing strings fail closed, and the
published score metric is lowercase canonical metric.
TuningWinner.metric, dataset_name, model_name and task_type, plus the
same raw NativeTuning.winner fields and aliases, use the same no-stringify
rule; winner metric and task_type publish lowercase values, while
dataset_name and model_name publish trimmed labels.
TuningPassthrough() is the typed form of the structured
{"kind": "passthrough"} marker for optional non-final preprocessing steps;
forms, bindings and Studio should use it instead of inventing a separate
placeholder. The structured marker is exact: kind must be the literal string
"passthrough" and host objects that only stringify to that value are rejected
before native tuning can run.
For named categorical choices, tuning.space also accepts
{"type": "categorical", "options": {"label": value}}: Optuna/n4m see the
stable label, while best_params, trial rows and terminal refit receive the
public JSON-native value.
When force_params targets such a named categorical choice, callers still pass
the public decoded value; Optuna and n4m may encode it to a stable backend
label internally, including for optimizer.enqueue(...), but both adapters
publish the decoded value back in TrialResult.params and best_params. They
reject internal labels or unknown keys instead of silently changing the
warm-start trial. The internal categorical codec is also fail-closed on direct
construction: choices must be non-empty, unique, optimizer-native without a
decoder, and TCV1 JSON-native; decoder keys must match encoded choices and
decoded public values must be unique before Optuna/n4m can use them.
The pruner key is transported to the selected optimizer for the supported
backend names (none, median, successive_halving, hyperband, plus native
n4m aliases such as asha/racing where available). The current shared
objective reports one final score per trial; it does not invent intermediate
fold/resource signals, so pruning is limited to what that backend can infer from
the available objective calls. If an Optuna objective raises
optuna.exceptions.TrialPruned, the adapter preserves that optimizer state as
TrialResult(state="PRUNED", value=None) with compact diagnostics
score_extractor="pruned" instead of collapsing it into FAIL; in the
reference contract, the adapter records a distinct
TrialResult(state="PRUNED", value=None, diagnostics={...}). The n4m adapter
uses the same portable row when the shared objective raises a prune exception,
but only if the installed native binding can terminalize the trial through
optimizer.tell_result(..., TrialStatus.PRUNED). Older n4m bindings that cannot
record a pruned terminal state fail closed instead of converting the candidate
to FAIL or to a worst completed score.
sampler and pruner strings are normalized case-insensitively before adapter
dispatch, so forms such as " TPE " or " HyperBand " resolve to their
canonical lower-case values. The legacy Optuna and n4m managers apply the same
canonicalization to approach, eval_mode, and explicit direction, including
the sample alias for sampler and the eval_mode="avg" alias for
eval_mode="mean"; Optuna also canonicalizes per-phase sampler values before
multi-phase dispatch. Logs, traces and optimizer dispatch therefore use stable
values after validation. The shared Optuna and n4m adapters both accept
sampler="grid" for the portable categorical/grid subset; n4m fails closed with
an explicit enum error if the installed native optimizer bindings do not expose
their GRID sampler yet. Unknown sampler or pruner spellings are rejected
before optimizer construction; they do not silently fall back to TPE or to a
no-pruner study.
For Optuna persistence, storage must be an explicit URI string such as
sqlite:///study.db; a bare path like study.db is rejected. study_name is
trimmed and cannot contain NUL characters. When resume=True, the Optuna
adapter requires both storage and study_name; anonymous or in-memory resume
is rejected before optimizer execution. If force_params is also supplied while
resuming a non-empty study, it must match the already materialized warm-start
trial; the adapter does not enqueue a duplicate trial and fails closed if the
caller changes the warm-start assignment under the same study_name/storage.
Existing materialized trial params must also match the current tuning.space
keys exactly; changing the search-space keys under the same persisted study
fails closed before optimizer execution. Existing categorical values must also
still be present in the current choices for their key; removing or renaming a
choice under the same persisted study fails closed before optimizer execution.
Existing numeric values must also remain inside the current range for their key;
narrowing a range so that a stored trial falls outside it fails closed before
optimizer execution. If the current numeric space declares a step, restored
values must also lie on that grid. During Optuna storage resume, n_trials is
treated as the target total trial count: an already completed one-trial study
with n_trials=1 runs no extra trial, while n_trials=2 runs one remaining
trial. New Optuna storage-backed studies also persist nirs4all study
fingerprints in study.user_attrs: a format marker, schema version, optimizer
contract fingerprint and search-space fingerprint. Resume fails closed if those
fingerprints are missing from a non-empty study or no longer match.
Trials restored from Optuna storage keep compact summary diagnostics even when
they were produced by a previous process: completed rows use
score_extractor="optuna_storage", failed rows use score_extractor="failed",
and pruned rows use score_extractor="pruned". Restored Optuna COMPLETE
rows must carry a finite numeric value; missing or non-finite storage values
fail closed instead of becoming completed trials with no usable objective value.
Restored non-COMPLETE rows must not carry a final storage value; failed,
pruned or in-flight rows with final values are rejected as corrupted optimizer
history. Restored RUNNING rows fail closed during resume because interrupted
active trials cannot be safely recovered into a terminal HPO tape. Restored terminal Optuna rows must keep exactly the current
tuning.space parameter keys when the search space is non-empty; rows whose
stored parameter table was removed fail closed instead of becoming completed
trials with empty public params. Restored queued Optuna WAITING rows that
already carry materialized params or fixed_params must also satisfy the current
tuning.space; incompatible values are rejected before Optuna can consume them.
Restored Optuna trial numbers must be canonical unique integers; corrupt storage
that yields non-integer or duplicate trial numbers fails closed before the HPO
tape is projected.
n4m optimizer-state persistence uses native N4MOPT checkpoints in this shared
objective seam: set storage="file:///absolute/checkpoint-dir" and
study_name="filename-safe-name". After each terminal trial, nirs4all writes a
JSON manifest containing the native checkpoint bytes plus tuning/search-space
fingerprints. With resume=True, the adapter reloads that checkpoint only when
the optimizer contract still matches; changing the search space, sampler,
pruner, seed, direction, metric or warm-start assignment fails closed. The
n_trials value is treated as the target total trial count, so a checkpoint
with one completed trial and n_trials=2 runs one remaining trial.
Restored n4m trial rows are decoded back to the public parameter syntax before
they enter TuningResult.trials, and are ordered canonically by numeric trial id
even if a native binding returns checkpoint records in another order. Duplicate
restored trial ids, or restored ids that are not canonical integers, fail closed
before TuningResult construction;
optimizer-only categorical labels used for JSON-native values or named options are never exposed as resumed
TrialResult.params. The restored checkpoint row params must still match the
current tuning.space keys and value domains; edited or incompatible checkpoint
keys, categorical choices, numeric ranges or numeric steps fail closed before
they can contribute optimizer history. Restored COMPLETE rows must carry a
finite numeric score; missing, boolean or non-finite scores fail closed instead
of becoming complete trials with no usable value. Restored non-success rows also keep compact diagnostics
aligned with their state: score_extractor="failed" for failed candidates and
score_extractor="pruned" for pruned candidates. If a native checkpoint
contains a terminal cancelled row, it is restored as CANCELLED with
score_extractor="cancelled", so resumed summary cards do not collapse these
states into a generic checkpoint row. Non-terminal or unsupported checkpoint
rows such as RUNNING fail closed during resume instead of being counted as
completed optimizer history. Restored n4m non-COMPLETE rows must not carry a
final score; failed, pruned or cancelled checkpoint rows with scores are
rejected as corrupted optimizer history.
When an individual candidate fails during fit or scoring, the tuning tape keeps a
FAIL trial with value=None and diagnostic error metadata. Other candidates
can still complete; if all candidates fail, nirs4all returns the failed tuning
tape, uses a finite direction-worst best_value for fingerprint/summary
compatibility, and skips terminal refit because there is no winner.
nirs4all distinguishes this from Optuna- or n4m-pruned trials, which stay
visible as PRUNED rows in TuningResult.trials, trial_states and compact
summary rows.
n4m bindings must expose either optimizer.tell_result(...) with a failed trial
status or optimizer.tell(...) so the adapter can advance the native optimizer
after a candidate failure. Older bindings that cannot record failed candidates
are rejected fail-closed instead of silently losing the trial tape.
n4m prune-aware tapes additionally require
optimizer.tell_result(..., TrialStatus.PRUNED).
Linear preprocessing steps may also use explicit sklearn.* class-path
mappings with params, or direct sklearn.* string steps when no constructor
parameters are needed. String imports work as direct model steps, named tuple
steps, and transform/model mapping values. When constructor parameters are
required inside a linear chain, preprocessing mappings may use
{"transform": "sklearn.preprocessing.StandardScaler", "params": {"with_mean": False}} and final model mappings may use
{"model": "sklearn.linear_model.Ridge", "params": {"fit_intercept": False}}.
Constructor params must use canonical string keys and TCV1-compatible
JSON-native values; Python objects, tuples, bytes and non-finite numbers are
rejected before import or instantiation.
The {"class": ..., "params": ...} form remains available for preprocessing
steps and the direct single-model form.
Short aliases and arbitrary imports remain unsupported in the native tuning
compiler.
A single model can use the same declarative form directly, for example
pipeline={"name": "ridge", "class": "sklearn.linear_model.Ridge", "params": {"fit_intercept": False}} or pipeline={"name": "ridge", "model": "sklearn.linear_model.Ridge", "params": {"fit_intercept": False}} with
space={"ridge.alpha": [...]}.
The lower-level nirs4all.tune_single_estimator() helper also accepts
NativeTuning(score_data=..., winner=..., calibration=...) for explicit
array/tuple/list scoring and winner cohorts. It uses the same single-estimator
and linear compiler forms described above, including direct sklearn.* string
imports when no constructor parameters are needed and {"class": ..., "params": ...} steps when they are, plus the {"steps": [...]} and {"pipeline": [...]}
wrappers. That includes
TuningConformalScoreCalibration for temporary conformal-aware objective scoring,
TuningWinner(...) for the projected final winner entry, and
TuningCalibration(...) for the final integrated tune → calibrate call.
Dataset-backed score_data and winner still belong to run(tuning=...), which
owns dataset loading and selector resolution.
Important keywords and effects
Keyword |
Required now |
Effect |
|---|---|---|
|
yes |
Selects the native public subset. Other engines reject |
|
yes |
Selects the optimizer adapter, currently |
|
yes |
Parameter search space. Linear chains use paths such as |
|
yes |
Explicit scoring cohort used by the objective. nirs4all does not derive it implicitly. Only mapping and tuple/list forms are accepted. Mapping form accepts |
|
optional |
Explicit development calibration cohort for conformal-aware objective scoring. It fits a temporary calibrator per candidate and never replaces the final |
|
required for calibration |
Explicit terminal prediction cohort used to create |
|
optional |
Applies split conformal calibration after tuning and terminal winner projection. |
|
optional |
Top-level alias for |
|
optional |
Enables workspace persistence. The same path is inherited by |
|
optional |
Stable id for the persisted |
|
optional |
Stable id for the persisted conformal result; caller-provided ids must be canonical non-empty strings without surrounding whitespace or NULs. |
|
optional |
Reuses an already completed workspace tuning result for this subset; it is not interrupted optimizer-state resume. |
Caller-provided conformal ids must be canonical non-empty strings without surrounding whitespace or NULs. Caller-provided robustness ids must also be canonical non-empty strings without surrounding whitespace or NULs. Omitting these ids asks the workspace store to generate ids that remain outside the scientific result fingerprints.
Registry coverage reference
The public keyword registry is the canonical machine-readable contract for
forms, bindings, Studio and docs generation. The table below mirrors the native
tuning/conformal/robustness entries that this guide covers. Changes names the
state affected by the keyword; Invalidates calibration explains whether a
previous conformal result must be treated as stale.
Registry path |
Lifecycle |
Changes |
Invalidates calibration |
Summary |
|---|---|---|---|---|
|
|
|
|
Selects the pipeline execution backend; native is the fail-closed Archive V2 Methods subset, while dual is a bounded native-first, no-fallback oracle only for explicit array regression with KFold(shuffle=False) and PLSRegression; neither selects the HPO algorithm. |
|
|
|
|
Fixed-topology full-DAG HPO argument. The public subset currently executes only |
|
|
|
|
Optimizer selector inside full-DAG tuning; it is distinct from |
|
|
|
|
Fixed-topology parameter search space. The public subset supports value patches on a single estimator; structural axes and full pipeline recompilation remain planned. |
|
|
|
|
Enqueues a caller-provided parameter assignment as the first optimizer trial. Keys must be a subset of |
|
|
|
|
Seeds the HPO trajectory. A different seed invalidates calibration only when it changes the selected predictor. |
|
|
|
|
Optuna storage URI for storage-backed studies, or n4m |
|
|
|
|
Optuna study name or n4m checkpoint filename stem. Whitespace is trimmed, NUL characters are rejected, and n4m also requires a filename-safe value. |
|
|
|
|
Supplies the explicit scoring cohort used by |
|
|
|
|
Provides an explicit development calibration cohort used only for conformal-aware objective scoring. It fits a temporary calibrator per candidate and never replaces the final |
|
|
|
|
Selects the nominal coverage for the temporary conformal scorer inside |
|
|
|
|
Projects the refit winner to an explicit terminal prediction entry. It is required before |
|
|
|
|
Runs conformal calibration immediately after the explicit winner projection. |
|
|
|
|
Top-level alias for |
|
|
|
|
Names the workspace |
|
|
|
|
Persists caller-provided strict JSON-native metadata alongside the workspace |
|
|
|
|
Reuses an already completed matching workspace |
|
|
|
|
Names the conformal workspace record produced by |
|
|
|
|
Exchangeable calibration cohort. The public API supports replayed-array mappings and explicit dataset-backed selectors with replayed predictions, an in-memory predictor, a saved predictor bundle, a |
|
|
|
|
Selects explicit dataset-backed calibration evidence from a |
|
|
|
|
Filters the explicit dataset-backed calibration cohort. It is mandatory so nirs4all never guesses a calibration partition. |
|
|
|
|
Provides already replayed point predictions for the calibration cohort. It does not trigger model replay. |
|
|
|
|
Replays an in-memory sklearn-like predictor on the selected dataset-backed calibration cohort. |
|
|
|
|
Replays a saved predictor path through |
|
|
|
|
Replays a |
|
|
|
|
Replays a stored workspace chain through |
|
|
|
|
Provides the explicit workspace root used by |
|
|
|
|
Supplies explicit row-aligned physical sample ids for calibration. |
|
|
|
|
Supplies optional row-aligned group labels for raw replayed-array calibration mappings. |
|
|
|
|
Supplies optional row-aligned metadata for raw replayed-array calibration mappings. Metadata keys used by |
|
|
|
|
Extracts physical sample ids from the selected |
|
|
|
|
Extracts optional row-aligned group labels into the calibration cohort manifest; it does not enable grouped conformal guarantees by itself. |
|
|
|
|
Copies selected |
|
|
|
|
Conformal score and interval construction method. V1 currently supports split absolute residuals on replayed regression predictions. |
|
|
|
|
Nominal coverage used to select the finite-sample conformal quantile; retained scores allow deterministic interval materialization for replayed-array calibration. |
|
|
|
|
V1 exchangeability unit. The current public surface requires explicit physical sample ids for replayed-array calibration. |
|
|
|
|
Grouped split conformal is executable for replayed-array calibration when calibration and prediction rows provide matching group evidence. Report slices alone never create a grouped conformal guarantee. |
|
|
|
|
Row-aligned prediction labels for |
|
|
|
|
Row-aligned prediction metadata for non- |
|
|
|
|
|
|
|
|
|
Stores strict JSON-native result metadata before nirs4all merges generated guarantee metadata. It changes the calibrated result fingerprint, not the fitted conformal artifact. |
|
|
|
|
Stores strict JSON-native result metadata when applying an existing calibrated result to new replayed predictions; nirs4all appends guarantee/source metadata after validation. |
|
|
|
|
Selects already materialized calibrated coverages at prediction time for calibrated replayed-array results/stores/bundles and model |
|
|
|
|
Requests all model prediction entries. With a conformal sidecar and |
|
|
|
|
Publishes the returned |
|
|
|
|
Stores caller-provided strict JSON-native sample-level metadata alongside the workspace prediction sidecar when |
|
|
|
|
Stores strict JSON-native result-level metadata for the workspace prediction row, including |
Workspace prediction ids are canonical non-empty strings without surrounding
whitespace or NULs. The public prediction publisher generates the id it returns
as metadata["workspace_prediction_id"]; lower-level explicit prediction_id
writes fail closed on invalid ids instead of silently generating or stringifying
a replacement.
Workspace link ids stored beside tuning, conformal and robustness rows follow
the same rule when supplied: run_id, pipeline_id, chain_id, and source
prediction_id/conformal_id must be canonical non-empty strings without
surrounding whitespace or NULs.
| robustness.mode | robustness | robustness_report | mode_dependent | Selects the robustness reuse policy. The current public API supports clean_frozen audit-only reports on already replayed predictions; matched recalibration and structural refit remain planned. |
| robustness.scenarios | robustness | robustness_results | mode_dependent | Defines report cells for robustness diagnostics. The current public API accepts RobustnessScenarioSpec helpers or equivalent mappings for observed, prediction-space and explicit-X spectral scenarios. |
| robustness.scenarios[].kind | robustness | robustness_results | mode_dependent | Selects the audit-only scenario cell. observed leaves predictions unchanged; prediction-space scenarios alter already materialized predictions; spectral scenarios replay an explicit frozen predictor on perturbed X. |
| robustness.scenarios[].severity | robustness | robustness_results | mode_dependent | Controls scenario magnitude: offset, noise standard deviation, scale/ramp amplitude or fractional feature-axis shift depending on the scenario kind. |
| robustness.scenarios[].distribution | robustness | robustness_results | mode_dependent | Selects the stochastic scenario distribution. The current executable subset accepts distribution only for prediction_noise and spectral_noise, with normal and centered uniform noise supported fail-closed. |
| robustness.X | robustness | robustness_results | mode_dependent | Supplies the explicit input matrix used by spectral scenarios. It is never fitted on; it is perturbed for evaluation-only frozen predictor replay. |
| robustness.predictor | robustness | robustness_results | mode_dependent | Frozen predictor replay hook for spectral scenarios. The current Python surface accepts predictor.predict(X) or a callable and does not refit or recalibrate it. |
| robustness.predictor_bundle | robustness | robustness_results | mode_dependent | Saved predictor path replay hook for spectral scenarios. The current Python surface routes perturbed X through nirs4all.predict(model=predictor_bundle, data={"X": X, "sample_ids": ...}) and does not refit or recalibrate it. |
| robustness.slice_by | reporting | reported_metrics | not_applicable | Adds diagnostic report slices over supplied metadata. A sliced empirical coverage is not a conditional conformal guarantee unless a separate valid grouped calibrator supports it. |
| robustness.seed | robustness | robustness_results | mode_dependent | Controls reproducible audit-only stochastic scenarios such as prediction_noise. It does not change a frozen predictor or calibrator; omitted seeds use effective_seed=0. |
| robustness.workspace_path | reporting | workspace_robustness_results | not_applicable | Persists the verified RobustnessReport in the workspace after the audit is computed. This does not change metrics, fingerprints, predictions, or conformal guarantees. |
| robustness.workspace_robustness_id | reporting | workspace_robustness_results | not_applicable | Stable id for the workspace robustness row produced by robustness(workspace_path=...). If omitted, the store generates an id that is not injected into the report fingerprint; if provided, it must be a canonical non-empty string without surrounding whitespace or NULs. |
| robustness.workspace_name | reporting | workspace_robustness_results | not_applicable | Human-readable workspace label for a persisted robustness report. It affects only workspace inventory and does not enter the report fingerprint. |
| robustness.workspace_metadata | reporting | workspace_robustness_results | not_applicable | Strict JSON-native workspace metadata stored alongside the robustness report row for CI/Studio indexing. It does not enter the report fingerprint. |
For tuning, workspace_metadata is constrained by the published keyword
registry schema to strict JSON-native values with canonical string keys; it is
display/storage metadata only and never changes optimizer behavior.
Robustness workspace metadata follows the same strict JSON-native published
schema and the workspace store rejects invalid values before inserting the
robustness_results row.
The calibration payload cannot provide its own calibration_data inside
run(tuning=...), whether calibration is nested under tuning.calibration or
passed as top-level run(..., calibration=...). Calibration evidence is derived
from tuning.winner so the order remains auditable: tune, terminal
refit/projection, then conformal calibration. The HPO scoring cohort from
tuning.score_data is never reused as final calibration evidence; if the final
intervals differ from score-data residuals, the tuning.winner cohort is
authoritative. In the typed helper, TuningCalibration.extra follows the same
rule and also refuses overrides of typed calibration keys.
When workspace_conformal_id is supplied, the workspace row stores the
winner-derived calibrated result, so reloading it preserves those intervals and
does not recompute them from tuning.score_data.
Reloaded CalibratedRunResult objects can be converted back to the public
prediction surface with calibrated.to_predict_result(), preserving intervals,
calibrated_result_fingerprint, calibration_replay_source, and
tuning_calibration_source for notebooks, bindings, CLI views and Studio.
The conversion and reload path validates conformal_guarantee_status against
the embedded conformal artifact; stale fingerprints, method/unit mismatches or
non-materialized coverage selections fail closed rather than displaying a false
guarantee.
The calibrated prediction metadata also includes
tuning_calibration_source={"source": "tuning.winner", "score_data_role": "hpo_objective_only", "score_data_used": false}. Studio,
Python bindings and audit tools can use this field to show that
tuning.score_data influenced optimizer ranking only, not the final conformal
evidence. If the calibration payload supplies result_metadata, nirs4all keeps
those user fields and adds tuning_calibration_source only when it is absent.
Use prediction.tuning_calibration_source or
calibrated.tuning_calibration_source for the direct accessor instead of
parsing metadata manually.
The tuple/list dataset order is fixed: (X, y, sample_ids, groups, metadata).
Use None for an omitted middle field. Tuple/list forms with extra fields are
rejected instead of guessed, and all identity fields must align with y.
The score_data tuple/list follows the same rule for the scoring cohort:
(X_score, y_score, sample_ids, groups, metadata). Use mapping form when you
need to override the scoring metric for that cohort. NativeTuning.to_dict()
validates tuple/list score_data before publication: it must contain between
two and five fields, metadata must use canonical non-empty string keys in either
column-style mapping or row-style sequence form, metadata values must stay
strict JSON-native and finite, and tuple inputs publish as JSON-native lists.
Any other score_data type, including scalar strings, bytes, numbers and
arbitrary objects, is rejected before publication.
The replayed-array calibration_data tuple is similarly explicit:
(y_true, y_pred, sample_ids, groups, metadata). sample_ids are still
required for conformal calibration evidence; use mapping form when you need
named fields or dataset-backed selectors. Raw replayed-array mappings accept the
same canonical payload plus the public aliases y_pred_calibration or
calibration_predictions for y_pred, calibration_sample_ids or
physical_sample_ids for sample_ids, calibration_groups for groups, and
calibration_metadata for metadata. Providing more than one alias for the same
field is rejected as ambiguous before calibration.
You can also use the typed helper when building forms or bindings:
calibration_data = nirs4all.ConformalCalibrationData(
y_true=y_cal,
y_pred=y_cal_pred,
sample_ids=calibration_sample_ids,
groups=calibration_groups,
metadata=calibration_metadata,
)
calibrated = nirs4all.calibrate(
calibration_data=calibration_data,
y_pred=y_pred,
prediction_sample_ids=prediction_sample_ids,
coverage=0.9,
)
Grouped split conformal, also called Mondrian conformal, is executable for the
same replayed-array substrate when both calibration and prediction rows carry
the declared grouping evidence. Use group_by="group" to consume
calibration_groups and prediction_groups; use metadata keys in group_by
when the row-aligned calibration_metadata and prediction_metadata mappings
contain those keys. Missing prediction groups, null metadata values, or groups
not seen during calibration fail closed; nirs4all never falls back to the global
calibrator for a grouped request. Group labels are strict strings too:
whitespace-padded labels, NUL-containing labels and non-string labels fail before
group quantiles are fitted or selected.
calibrated = nirs4all.calibrate(
y_true=y_cal,
y_pred_calibration=y_cal_pred,
y_pred=y_pred,
calibration_sample_ids=calibration_sample_ids,
prediction_sample_ids=prediction_sample_ids,
calibration_groups=calibration_groups,
prediction_groups=prediction_groups,
group_by="group",
coverage=0.9,
)
status = calibrated.conformal_guarantee_status
print(status["guarantee"]) # split_conformal_group_marginal_coverage
calibrate.group_by is therefore partial: it changes
calibration_partitions and coverage_claim_scope, and replaces any existing
calibrator. calibrate.prediction_groups and calibrate.prediction_metadata
are partial, not_applicable to calibration invalidation, and change
selected_prediction_bounds plus coverage_claim_scope because they route
prediction rows to already fitted group quantiles.
For multi-target regression, multi_target="joint_max" is executable on
two-dimensional replayed arrays. nirs4all stores one nonconformity score per
physical sample, max(abs(y_true - y_pred)) across target columns, and applies
the resulting scalar qhat to every target column of the prediction row. The
guarantee is simultaneous for the full target vector, not a separate conditional
claim per target.
calibrated = nirs4all.calibrate(
y_true=y_cal_2d,
y_pred_calibration=y_cal_pred_2d,
y_pred=y_pred_2d,
calibration_sample_ids=calibration_sample_ids,
prediction_sample_ids=prediction_sample_ids,
multi_target="joint_max",
coverage=0.9,
)
print(calibrated.conformal_guarantee_status["guarantee"])
# split_conformal_joint_max_simultaneous_coverage
For conformal-aware development scoring, use mapping form with
conformal_calibration and a conformal metric:
"score_data": {
"X": X_score,
"y": y_score,
"metric": "conformal_mean_width",
"conformal_coverage": 0.9,
"conformal_calibration": {
"X": X_dev_calibration,
"y_true": y_dev_calibration,
"sample_ids": dev_calibration_ids,
},
}
Or use the typed helper when building public configs in Python:
tuning = nirs4all.NativeTuning(
engine="optuna",
space={"alpha": [0.2, 0.9]},
metric="conformal_mean_width",
direction="minimize",
score_data=nirs4all.TuningScoreData(
X=X_score,
y=y_score,
conformal_coverage=0.9,
conformal_calibration=nirs4all.TuningConformalScoreCalibration(
X=X_dev_calibration,
y_true=y_dev_calibration,
physical_sample_ids=dev_calibration_ids,
),
),
)
The typed form is checked early: conformal_coverage is valid only with
conformal_calibration, must be a finite numeric scalar in (0, 1), and
conformal score metrics require that temporary calibration cohort.
Numeric strings are rejected; coverage values must be real numeric scalars, not
host values that can be converted with float(...).
TuningScoreData(...) accepts either X/y or the score-cohort aliases
X_score/y_score, plus either metric or its input alias score_metric, then
emits canonical X/y and metric; it also accepts groups/score_groups and
metadata/score_metadata, emitted canonically as groups/metadata.
The score metric must be a real non-empty string, not an integer, boolean,
blank string, or NUL-containing value; it is published as lowercase canonical
metric before objective scoring.
Metadata keys must be canonical non-empty strings for both column-style mappings
and row-style sequences of mappings; raw NativeTuning.score_data.metadata
follows the same rule. Metadata values must be strict JSON-native and finite:
lists and nested mappings are accepted, but non-finite numbers, bytes, tuples
and Python objects are rejected before runtime. Providing multiple aliases in
any group is rejected as ambiguous.
TuningConformalScoreCalibration(...) accepts exactly one feature alias among
X, X_calibration, and features, plus exactly one target alias among
y_true, y, y_calibration, target, and targets; it emits canonical
X/y_true. It also accepts sample_ids, calibration_sample_ids, or
physical_sample_ids, groups/calibration_groups, and
metadata/calibration_metadata, then emits canonical sample_ids, groups,
and metadata for the runtime. Metadata keys must be canonical non-empty
strings for both column-style mappings and row-style sequences of mappings;
raw score_data.conformal_calibration.metadata follows the same rule before
NativeTuning.to_dict() can publish it, including the same strict JSON-native
value boundary. The same one-alias rule is used by raw replayed-array
calibrate(calibration_data={...}) mappings for y_pred_calibration /
calibration_predictions, calibration_sample_ids / physical_sample_ids,
calibration_groups, and calibration_metadata. Mapping payloads may use the
read-only alias
coverage, but conformal_coverage is the canonical spelling used by the
registry. Multi-coverage lists belong to final calibration/prediction, not to the
temporary tuning scorer. Final TuningCalibration.coverage accepts one numeric
coverage or a non-empty unique list of numeric coverages; list elements such as
"0.8" fail closed instead of being coerced. Raw mapping payloads follow the
same alias exclusivity rules as the typed helpers for both standard and
conformal-aware score data; nirs4all rejects ambiguous mappings before building
the scorer.
This calibrator is temporary and candidate-local. It is used only to rank HPO
trials; final intervals still come from tuning.winner plus top-level
calibration or nested tuning.calibration.
The completed tuning_result.trials[*].diagnostics also records this boundary
for audit: score_family="conformal",
score_extractor="conformal_temporary_calibration", and
final_calibration_scope="unmodified_by_score_data".
The lightweight TuningResult.summary_artifact() mirrors a compact subset of
those diagnostics under trials[*].diagnostics so bindings, Studio and CI cards
can explain FAIL or conformal-aware trials without loading the full HPO tape.
It keeps scalar audit keys such as error_type, score_family,
score_extractor, search_space_fingerprint and tuning_fingerprint, but
does not include candidate params or raw exception messages.
score_data can also use an explicit dataset-backed selector:
"score_data": {
"dataset": spectro_dataset, # or DatasetConfigs / config mapping / "dataset.json"
"selector": {"partition": "val"},
"sample_id_column": "Sample_ID",
"group_column": "Batch",
"metadata_columns": ["Site"],
}
The selector is mandatory here too. This keeps objective scoring auditable and
prevents nirs4all from silently deriving a validation cohort. If the source is a
DatasetConfigs, config mapping, or path, it must resolve to exactly one
dataset. The Python helper form TuningScoreData(dataset=..., selector=...)
executes this same selected cohort and transports sample_id_column,
group_column, and metadata_columns to compatible predict() methods.
Those dataset-backed column selectors are strict identifiers:
sample_id_column and group_column must be canonical non-empty strings
without surrounding whitespace or NULs, and metadata_columns must be either one
canonical string or a duplicate-free sequence of canonical strings. Raw
NativeTuning.score_data and NativeTuning.winner dataset-backed mappings use
the same column-selector rule before publication.
It also accepts sample_ids, score_sample_ids, prediction_sample_ids, or
physical_sample_ids, serializing exactly one provided alias to canonical
sample_ids. It rejects mixed dataset + explicit X/y arrays, matching the
mapping runtime boundary.
Dataset-backed inputs must be explicit mappings. nirs4all does not guess a training partition:
dataset = {
"dataset": spectro_dataset, # or DatasetConfigs / config mapping / "dataset.json"
"selector": {"partition": "train"},
"sample_id_column": "Sample_ID",
"group_column": "Batch",
"metadata_columns": ["Site"],
}
include_augmented defaults to False for this native tuning subset. Set
"include_augmented": True only when augmented samples are intended to be part
of the fit cohort. Dataset-backed tuning selectors reject non-string or
whitespace-padded keys plus non-JSON-native values, and include_augmented
must be a boolean before TuningScoreData or TuningWinner can publish a
runtime mapping. Config/path sources must resolve to exactly one dataset.
The same mapping form is available for tuning.winner, which is the cohort used
for RunResult.best and conformal calibration:
"winner": {
"dataset": spectro_dataset, # or DatasetConfigs / config mapping / "dataset.json"
"selector": {"partition": "test"},
"sample_id_column": "Sample_ID",
"group_column": "Batch",
"metadata_columns": ["Site"],
"score": 0.42,
"metric": "rmse",
}
Do not combine this with explicit X/y_true arrays in the same winner
payload. The typed helper form TuningWinner(dataset=..., selector=...)
projects the same selected cohort into RunResult.best, preserving physical
sample ids, optional groups, and selected metadata columns for calibration and
audit, and rejects the same mixed dataset + explicit X/y_true payloads.
Use one representation per cohort. TuningWinner.score must be a finite number,
not a boolean or numeric string. TuningWinner.metadata and raw
NativeTuning.winner.metadata use the same strict metadata key rule as
TuningScoreData; their values must also stay strict JSON-native and finite.
TuningWinner.metric, dataset_name, model_name and task_type are strict
string fields: nirs4all rejects non-string values, blanks and NULs instead of
calling str(...). metric and task_type are published lowercase; dataset and
model labels are published trimmed.
For the winner cohort, sample_ids, prediction_sample_ids, and
physical_sample_ids are accepted aliases. The typed TuningWinner(...) helper
also accepts these aliases plus winner_sample_ids, and serializes exactly one
provided alias to canonical sample_ids. Raw winner mappings follow the same
fail-closed rule for X/winner_x, y_true/winner_y_true, score, metric,
sample id, dataset name, model name, task type, and metadata aliases; provide one
spelling per field. They are stored as physical sample ids in the prediction
metadata and are required before the winner can be used as conformal calibration
evidence. When the selected SpectroDataset target is a single-column matrix,
the native tuning adapter normalizes it to a one-dimensional target vector before
conformal calibration; multi-target matrices remain outside
this public subset.
Calibrating from an explicit dataset cohort
nirs4all.calibrate() can also use a selected dataset cohort directly when you
already have replayed point predictions for that exact cohort. The dataset
value may be a SpectroDataset, a DatasetConfigs object, a dataset config
mapping, or a config/path string accepted by DatasetConfigs. The dataset
supplies the calibration targets and identities; either y_pred inside the
mapping or top-level y_pred_calibration= supplies the replayed calibration
predictions. Alternatively, predictor may provide an in-memory sklearn-like
object; nirs4all extracts X from the same selected cohort and calls
predict(X), forwarding sample_ids, groups and metadata when accepted by
the predictor signature. For saved predictors, use predictor_bundle to route
through nirs4all.predict() on the extracted X. Mapping aliases
model_bundle, predictor_path and model_path are accepted for this same
source; the typed helper emits canonical predictor_bundle. For previous
nirs4all training outputs, use predictor_result=train_result.best; for a
stored workspace chain, use predictor_chain_id together with an explicit
nested workspace_path. The typed helper also accepts workspace_chain_id as
a read alias and serializes it as canonical predictor_chain_id. Both spellings
must carry a canonical non-empty chain id without surrounding whitespace or NULs;
nirs4all rejects invalid values before calling predict(chain_id=...) or
publishing replay provenance.
calibrated = nirs4all.calibrate(
calibration_data={
"dataset": spectro_dataset, # or "calibration_dataset.json"
"selector": {"partition": "calibration"},
"sample_id_column": "Sample_ID",
"group_column": "Batch",
"metadata_columns": ["Site"],
"y_pred": y_cal_pred,
},
y_pred=y_pred_new,
prediction_sample_ids=prediction_sample_ids,
coverage=0.9,
)
The typed helper serializes to the same payload and is the preferred shape for forms, bindings and Studio payload builders:
calibrated = nirs4all.calibrate(
calibration_data=nirs4all.ConformalCalibrationData(
dataset=spectro_dataset,
selector={"partition": "calibration"},
sample_id_column="Sample_ID",
group_column="Batch",
metadata_columns=["Site"],
y_pred=y_cal_pred,
),
y_pred=y_pred_new,
prediction_sample_ids=prediction_sample_ids,
coverage=0.9,
)
For in-memory replay, replace y_pred= with predictor=fitted_model. The
selected calibration X, sample ids, groups and metadata are forwarded to
predict() when the predictor accepts them. For saved model bundles, use
predictor_bundle="model.n4a" in the same typed helper. For nirs4all replay
sources, use predictor_result=train_result.best or
predictor_chain_id="chain-id" with workspace_path="workspace/".
If you prefer to keep all prediction arrays at the top level:
calibrated = nirs4all.calibrate(
calibration_data={
"dataset": spectro_dataset,
"selector": {"partition": "calibration"},
"sample_id_column": "Sample_ID",
},
y_pred_calibration=y_cal_pred,
y_pred=y_pred_new,
prediction_sample_ids=prediction_sample_ids,
coverage=0.9,
)
In-memory predictor replay:
calibrated = nirs4all.calibrate(
calibration_data={
"dataset": "calibration_dataset.json",
"selector": {"partition": "calibration"},
"sample_id_column": "Sample_ID",
"group_column": "Batch",
"metadata_columns": ["Site"],
"predictor": fitted_model,
},
y_pred=y_pred_new,
prediction_sample_ids=prediction_sample_ids,
coverage=0.9,
)
Saved predictor replay:
calibrated = nirs4all.calibrate(
calibration_data={
"dataset": "calibration_dataset.json",
"selector": {"partition": "calibration"},
"sample_id_column": "Sample_ID",
"predictor_bundle": "model.n4a",
},
y_pred=y_pred_new,
prediction_sample_ids=prediction_sample_ids,
coverage=0.9,
)
Previous-result replay:
calibrated = nirs4all.calibrate(
calibration_data={
"dataset": "calibration_dataset.json",
"selector": {"partition": "calibration"},
"sample_id_column": "Sample_ID",
"predictor_result": train_result.best,
"workspace_path": "workspace",
},
y_pred=y_pred_new,
prediction_sample_ids=prediction_sample_ids,
coverage=0.9,
)
Workspace-chain replay:
calibrated = nirs4all.calibrate(
calibration_data={
"dataset": "calibration_dataset.json",
"selector": {"partition": "calibration"},
"sample_id_column": "Sample_ID",
"predictor_chain_id": "chain-abc123",
"workspace_path": "workspace",
},
y_pred=y_pred_new,
prediction_sample_ids=prediction_sample_ids,
coverage=0.9,
)
Provide exactly one calibration replay source inside the mapping: y_pred,
predictor, predictor_bundle, predictor_result, or predictor_chain_id.
The selector is mandatory, physical sample ids must be explicit through
sample_ids, calibration_sample_ids, physical_sample_ids, or
sample_id_column, and mixed dataset + y_true/X payloads are rejected.
Physical sample id values for calibration and prediction cohorts must be
canonical non-empty strings without surrounding whitespace or NULs.
Supplying more than one explicit sample-id alias for a dataset-backed calibration
mapping is rejected as ambiguous; use sample_id_column only when the ids should
be read from dataset metadata.
Optional row-aligned groups and metadata can be supplied explicitly with
groups/calibration_groups and metadata/calibration_metadata, or derived
from group_column and metadata_columns.
Dataset-backed selectors reject non-string or whitespace-padded
keys plus non-JSON-native values, and raw mapping include_augmented must be a
boolean. Dataset-backed predictor_chain_id/workspace_chain_id values are
strict workspace chain ids, not labels; blanks, whitespace-padded strings, NULs
and non-strings fail before replay. Dataset-backed calibration column selectors
are canonical too:
sample_id_column and group_column must be non-empty strings without
surrounding whitespace or NULs, and metadata_columns must be one canonical
string or a duplicate-free sequence of canonical strings. The same rule applies
to ConformalCalibrationData(...) and raw calibration_data={...} mappings
before cohort extraction. The nested workspace_path is used for predictor
replay only; it is separate from the top-level workspace path used to save
conformal results.
Applying a stored calibrator
Reload the conformal artifact from the workspace and apply it to new point predictions:
calibrated = nirs4all.load_workspace_calibrated_result(
"workspace/",
"ridge-conformal",
)
prediction = nirs4all.predict_calibrated(
calibrated,
y_pred=[13.0, 14.0],
prediction_sample_ids=["pred-003", "pred-004"],
)
interval = prediction.interval(0.8)
print(interval.lower, interval.upper)
The direct predict_calibrated() example above does not replay a model. It
applies the stored conformal calibrator to already computed point predictions.
The prediction_sample_ids are required for every non-empty conformal prediction
cohort and must align with y_pred. They must be non-empty canonical strings,
unique physical sample identifiers, and disjoint from the calibration cohort
physical sample ids embedded in the conformal artifact; empty ids, surrounding
whitespace, NUL-containing ids, duplicating a prediction id, or reusing a
calibration sample as a prediction row fail closed instead of displaying a leaked guarantee. The
returned result preserves the original calibration_replay_source from the
stored calibrated result; caller-supplied result_metadata cannot replace that
source or the generated conformal_guarantee_status. The top-level
source_calibrated_result_fingerprint must match the same value inside
conformal_guarantee_status, so corrupted or mixed application metadata fails
closed on reload. If you pass
coverage=..., the value selects one or more already materialized coverages:
it must be finite, unique when passed as a list, and strictly between 0 and 1.
It does not recalibrate the conformal artifact. If the requested coverage was
not materialized during calibration, predict() fails closed and reports the
available coverages.
The same identity contract is rechecked when loading filesystem stores,
workspace conformal rows, or .n4a conformal sidecars. A stored
calibrated_result.json whose non-empty prediction cohort lacks canonical
physical sample_ids fails reload instead of becoming a partial
PredictResult. A corrupted conformal_results workspace row with the same
missing prediction identity is rejected by both
load_workspace_calibrated_result(...) and
load_workspace_calibrated_predict_result(...).
Workspace conformal metadata is also validated before insertion, so invalid
metadata cannot be hidden in the row and then trusted on reload.
Result-level metadata must also be strict JSON-compatible before construction,
reload, fingerprinting or persistence: non-finite floats, Python objects,
non-string keys, whitespace-padded keys, nested non-string mapping keys and
tuple values that would otherwise be silently coerced fail closed.
Grouped conformal results keep the same contract through these persistence
paths: filesystem stores, workspace conformal_results rows, conformal-only
.n4a bundles and model .n4a sidecars preserve group_keys,
group_calibrators and row-aligned qhat vectors, then revalidate them and
strict non-boolean integer grouped n_samples summaries against the embedded
artifact before exposing a CalibratedRunResult or PredictResult.
Version fields on conformal cohort manifests, calibration artifacts and
calibrated results are strict integer contract tags: boolean true/false and
numeric strings are rejected on direct construction and reload instead of being
coerced to schema version 1. Optional conformal artifact identity strings
(target_name, predictor_fingerprint, calibration_data_fingerprint) must be
either None or non-empty strings without surrounding whitespace or NULs; reload
rejects the same invalid values instead of publishing ambiguous provenance.
ConformalCalibrationSpec validates direct construction the same way as
parse_conformal_calibration_spec(): coverage values must be real numeric
scalars, not booleans or numeric strings, and method, unit, group keys and
multi-target mode are canonicalized before fingerprinting.
Reloaded cohort manifest unit and calibrated prediction method/unit
fields must already be strings; objects with a helpful __str__ are rejected
instead of being stringified into valid contract values.
Conformal calibration cohort rows built directly or reloaded from JSON now
require a strict non-boolean integer row_index, canonical non-whitespace
sample_id, role and optional group, plus strict JSON-native metadata with
string keys, before the cohort manifest can be fingerprinted. The optional
serialized n_samples summary must also be a strict non-boolean integer matching
the row count. Row-aligned calibration metadata supplied as either column
mappings or per-row mappings uses the same strict key rule: non-string or
whitespace-padded metadata keys fail before they can be coerced into manifest
JSON.
Conformal numeric arrays (y_true, y_pred, interval bounds and qhat) must
contain real numeric values; boolean payloads fail closed instead of being
coerced to 0.0/1.0, and numeric strings such as "1.0" are rejected instead
of being parsed as floats. The same bool rejection applies to Python-side
from_dict(...) payloads carrying NumPy boolean scalars in serialized scores or
quantiles, and serialized numeric fields reject NumPy ndarray scalars instead of
coercing them to JSON numbers. Direct ConformalIntervalBlock /
CalibratedPredictionBlock construction is also fail-closed for mismatched
coverage keys, mismatched interval shapes, inverted bounds, invalid method/unit
values, invalid group-key lengths, and negative or non-row-aligned qhat values.
Direct SplitConformalCalibrator construction validates retained residual
scores, coverage keys, recomputed quantiles, method and unit before apply();
negative scores, edited qhat values or unsupported vocabulary fail closed
instead of materializing invalid intervals.
Empirical ConformalMetricSet diagnostics are checked the same way at
construction: observed_coverage must be finite in [0, 1] and match
n_covered / n_samples, coverage_gap must equal observed_coverage - coverage, and widths/interval scores must be non-negative or positive infinity
for unbounded intervals.
For model .n4a bundles carrying a conformal sidecar, predict(..., coverage=...) replays the model bundle first, then applies the attached
calibrator. This currently applies intervals only to the single selected
prediction entry (all_predictions=False). all_predictions=True remains
fail-closed until each returned prediction entry can carry its own calibrated
identity mapping. If the bundle contains a conformal/ sidecar but that
sidecar is incomplete, duplicated, or contains unexpected members, predict()
fails sidecar validation instead of falling back to an uncalibrated prediction
path. The same fail-closed path applies when the sidecar looks structurally
complete but its calibrated_result.json has a non-empty prediction cohort
without canonical physical sample_ids: predict() rejects the sidecar before
running the raw model prediction.
Audit-only robustness report
When observed targets are available for an external prediction cohort,
nirs4all.robustness() can produce the current audit-only robustness report:
report = nirs4all.robustness(
prediction,
y_true=[13.2, 13.7],
X=X_external,
predictor=frozen_predictor,
mode="clean_frozen",
scenarios=[
nirs4all.RobustnessScenarioSpec(kind="observed"),
nirs4all.RobustnessScenarioSpec(kind="prediction_bias", severity=0.2),
nirs4all.RobustnessScenarioSpec(kind="prediction_noise", severity=0.05),
nirs4all.RobustnessScenarioSpec(kind="spectral_noise", severity=0.001),
],
metadata={"instrument": ["inst-a", "inst-b"], "batch": ["b1", "b2"]},
slice_by=["instrument"],
seed=123,
workspace_path="workspace/",
workspace_robustness_id="external-audit",
workspace_name="External robustness audit",
)
print(report.scenarios[0].metrics.rmse)
print(report.scenarios[0].conformal_metrics[0.8].observed_coverage)
print(report.scenarios[0].slices[0].metrics.mae)
print(report.summary_rows())
print(report.degradation_rows())
print(report.worst_slices(metric="rmse", top_k=3))
# Convenience syntax when you already hold a result object:
same_report_from_prediction = prediction.robustness(y_true=[13.2, 13.7])
report.save_json("robustness-report.json")
report.save_summary("robustness-summary.json")
report.save_markdown("robustness-report.md")
report.save_html("robustness-report.html")
report.save_parquet("robustness-report.parquet")
report.save_artifacts("robustness-artifacts")
same_report = nirs4all.RobustnessReport.load_json("robustness-report.json")
same_report_from_tables = nirs4all.RobustnessReport.load_parquet("robustness-report.parquet")
same_report_from_bundle = nirs4all.RobustnessReport.load_artifacts("robustness-artifacts")
robustness_id = nirs4all.save_workspace_robustness_report(
"workspace/",
report,
robustness_id="external-audit-copy",
name="External robustness audit",
)
same_report_from_workspace = nirs4all.load_workspace_robustness_report(
"workspace/",
"external-audit",
)
The same pattern composes directly with calibrated predictions produced by the native tuning example:
prediction = nirs4all.predict_calibrated(
restored_conformal,
y_pred=[13.0, 14.0],
prediction_sample_ids=["pred-003", "pred-004"],
)
report = prediction.robustness(
y_true=[13.2, 13.7],
scenarios=[
nirs4all.RobustnessScenarioSpec(kind="observed"),
nirs4all.RobustnessScenarioSpec(kind="prediction_bias", severity=0.1),
nirs4all.RobustnessScenarioSpec(kind="prediction_noise", severity=0.05),
],
metadata={"batch": ["external-a", "external-b"]},
slice_by=["batch"],
seed=7,
workspace_path="workspace/",
workspace_robustness_id="u09-robustness",
workspace_name="U09 robustness audit",
)
report.save_artifacts("workspace/u09-robustness-artifacts")
same_report = nirs4all.load_workspace_robustness_report("workspace/", "u09-robustness")
same_bundle = nirs4all.RobustnessReport.load_artifacts("workspace/u09-robustness-artifacts")
This is the current public Python E2E lane:
run(tuning=...) → calibrate/predict_calibrated() → PredictResult.robustness()
with explicit external y_true. It remains audit-only: robustness scenarios do
not refit, recalibrate, or renew the conformal guarantee.
For CI/release publication, an already verified JSON report can also be republished without re-running the audit:
nirs4all robustness-report robustness-report.json --format markdown --output robustness-report.md
nirs4all robustness-report robustness-report.json --format summary --output robustness-summary.json
nirs4all robustness-report robustness-report.json --format html --output robustness-report.html
nirs4all robustness-report robustness-report.json --format parquet --output robustness-report.parquet
nirs4all robustness-report robustness-report.json --format artifacts --output robustness-artifacts/
nirs4all robustness-report robustness-artifacts/ --format markdown --output reviewed-report.md
nirs4all workspace robustness list --workspace workspace --json
nirs4all workspace robustness show external-audit --workspace workspace --json
nirs4all workspace robustness from-prediction --workspace workspace --prediction-id pred-001 --y-true "1.0,2.0,3.0" --scenarios-json '[{"kind":"spectral_offset","severity":0.01}]' --save-to-workspace --workspace-robustness-id pred-001-spectral-audit --format summary --output robustness-summary.json
nirs4all workspace robustness export external-audit --workspace workspace --format summary --output robustness-summary.json
nirs4all workspace robustness export external-audit --workspace workspace --format artifacts --output robustness-artifacts/
nirs4all workspace robustness export external-audit --workspace workspace --format html --output robustness-report.html
workspace robustness from-prediction is the CLI form of
nirs4all.robustness_from_workspace_prediction(...). It keeps the same
fail-closed boundary: stored provenance markers do not become executable
spectra, but actual row-aligned X/spectra plus predictor_bundle evidence
are consumed as defaults for spectral/OOD scenarios.
This does not recalibrate or refit. Post-prediction scenarios compute point
metrics and conformal diagnostics on already materialized predictions:
prediction_bias adds severity to y_pred, and prediction_noise draws
seeded normal or centered uniform noise. normal uses severity as standard
deviation; uniform samples from [-severity, +severity]. In both cases existing
conformal interval bounds are shifted with the prediction delta. If seed is
omitted, nirs4all records and uses effective_seed=0, so repeated reports are
still deterministic. The explicit-X spectral cells require X and exactly one
frozen replay source: either an in-memory predictor or a saved
predictor_bundle. The in-memory form accepts predictor.predict(X) or a
callable. The saved-bundle form routes the perturbed matrix through public
nirs4all.predict(model=predictor_bundle, data={"X": X, "sample_ids": ...}, all_predictions=False), preserving row identity when the prediction result
carries sample ids. Both forms replay without fit or recalibration and recenter
existing conformal intervals by the prediction delta. predictor and
predictor_bundle are mutually exclusive. Spectral reports include
metadata["spectral_replay"] so auditors can distinguish in-memory predictor
replay from saved-bundle replay, see the saved bundle path, confirm the
nirs4all.predict route, and verify whether sample ids were forwarded.
If a PredictResult already carries actual row-aligned metadata["X"] or
metadata["spectra"] plus published robustness_evidence.predictor_bundle or
model_path, nirs4all.robustness(result, ...) and
result.robustness(...) use those values as the spectral replay defaults.
This only consumes real arrays stored on the result; strings such as
"prediction_arrays.X" are provenance markers and remain fail-closed unless
the array itself is present.
For workspace-loaded predictions, convert the stored record explicitly:
from nirs4all.data.predictions import Predictions
predictions = Predictions.from_workspace("workspace/", load_arrays=True)
result = predictions.get_predict_result_by_id("pred-001")
record = predictions.get_prediction_by_id("pred-001", load_arrays=True)
# Direct public one-record shortcut:
result = nirs4all.load_workspace_predict_result("workspace/", "pred-001")
# Direct public bulk shortcut:
results = nirs4all.load_workspace_predict_results("workspace/")
# Direct workspace prediction -> robustness report shortcut:
report = nirs4all.robustness_from_workspace_prediction(
"workspace/",
"pred-001",
y_true=record["y_true"],
scenarios=[{"kind": "spectral_offset", "severity": 0.01}],
save_to_workspace=True,
workspace_robustness_id="pred-001-spectral-audit",
)
if result.spectral_replay_evidence_status["status"] == "ready_for_spectral_replay":
report = result.robustness(
y_true=record["y_true"],
scenarios=[{"kind": "spectral_noise", "severity": 0.01}],
)
nirs4all.load_workspace_predict_result() is the direct public one-record
workspace path. nirs4all.load_workspace_predict_results() is the direct
public bulk workspace path, with optional dataset_name=... filtering.
Predictions.get_predict_result_by_id() is the lower-level buffer equivalent;
Predictions.to_predict_results() performs the same conversion for the whole
buffer. All four call
PredictResult.from_prediction_record() internally and
preserve result_metadata, robustness_evidence, executable X/spectra,
sample_indices, model name, preprocessing metadata,
calibration_replay_source and tuning_calibration_source. They intentionally
require load_arrays=True; a metadata-only record is not enough to replay
spectral/OOD scenarios.
nirs4all.robustness_from_workspace_prediction() is the direct report path for
one persisted prediction: it performs the same conversion and then calls
robustness(), optionally saving the report back into the workspace with a
prediction_id link.
Python publishers can make the evidence executable by writing it through
Predictions.add_prediction(..., X=..., spectra=..., result_metadata=...)
before flush(). X/spectra are stored in the array sidecar,
result_metadata.robustness_evidence records the predictor_bundle or
model_path, and Predictions.merge_stores() preserves those fields during
workspace consolidation. This is the supported Python publisher path for
spectral/OOD replay evidence; provenance-only strings remain non-executable.
spectral_noise adds seeded noise to X; normal uses severity as standard
deviation and uniform samples from [-severity, +severity].
spectral_offset applies a deterministic additive offset; spectral_scale multiplies every feature by
1.0 + severity; spectral_slope adds a centered linear ramp across the
feature axis; and spectral_shift shifts spectra by fractional feature units
with interpolation.
These stress cells are diagnostic only and do not renew a conformal coverage
guarantee. Matched recalibration and structural refit remain planned gates.
scenarios accepts either the typed RobustnessScenarioSpec helper shown
above or the equivalent mapping form, for example
{"kind": "prediction_noise", "severity": 0.05}. The supported kind values
are currently observed, prediction_bias, prediction_noise,
spectral_noise, spectral_offset, spectral_scale, spectral_slope, and
spectral_shift. severity is an offset for prediction_bias, a standard
deviation for normal noise, a centered half-width for uniform noise, and the
deterministic magnitude for the other spectral cells. distribution is optional
and currently accepts "normal" or "uniform" for the two noise cells.
extra={...} on
RobustnessScenarioSpec can carry stable labels or audit metadata into the
scenario payload, but it cannot override kind, severity, or
distribution. Direct RobustnessScenarioSpec construction is fail-closed:
kind and distribution must be real strings, not host objects that stringify
to supported values;
severity must be a real numeric scalar, not a boolean or numeric string, and
extra keys must be canonical non-empty strings without NULs before
to_dict() can publish the scenario. Scenario mappings, including
extra={...} on the typed helper, use the same strict boundary: keys must be
canonical non-empty strings without NULs,
kind and distribution must be real strings, severity must be a real finite
numeric scalar, and the payload must stay TCV1 JSON-native and fingerprintable.
Plain strings, finite numbers, booleans,
null, arrays, and objects with text keys are accepted; Python objects,
NaN/Infinity, bytes, and other opaque values are rejected before report
execution.
The same no-stringify rule is applied again to published result payloads:
RobustnessScenarioResult.scenario and RobustnessSliceResult.slice_key keys
must be canonical non-empty strings without NULs, their values must stay TCV1
JSON-native, and RobustnessScenarioResult.severity must be a real numeric
scalar. These mappings enter report JSON, fingerprints, summary_rows(),
degradation_rows(), worst_slices(), tabular_records() and Studio/CI
summary cards, so corrupted reloads fail closed instead of silently converting
Python keys with str(...).
The JSON export is deterministic and verified on reload through the report
fingerprint, which makes it suitable for CI artifacts and release audits.
save_markdown() writes the same report as a deterministic human-readable
summary for release notes, audit packets, or CI job summaries. The Markdown
summary includes conformal guarantee status details when present, including
requested/effective engine, method, unit, selected and calibrated coverages,
scope, invalidation reasons and limitations; this is descriptive metadata and
does not renew the guarantee. It also includes degradation versus the reference
scenario and the worst diagnostic slices when slices are available.
summary_rows(reference=0) returns one compact row per scenario for CI
dashboards and Studio cards: point metrics, deltas/ratios versus the reference
scenario, conformal coverage/width summary, the worst diagnostic slice, and an
execution-scope hint. execution_scope is baseline, prediction_replay or
spectral_replay; requires_spectral_replay=true marks explicit-X
spectral/OOD replay rows and is metadata for orchestration, not permission for
bindings or Studio to replay perturbations locally.
Markdown and HTML exports render this same compact scenario summary, including
scope and replay-evidence columns, before the detailed tables. save_html()
writes a standalone escaped HTML artifact with the same report tables. save_parquet()
writes a portable directory containing manifest.json and one Parquet file per
non-empty flat report table, such as compact scenario summary, scenario metrics,
degradation rows, conformal diagnostics and slices. It also stores report.json;
manifest.json records table filenames, row counts, and fingerprints, so
RobustnessReport.load_parquet() can reload the full report and verify that the
exported table files still match the manifest. save_artifacts() writes a
publication directory with manifest.json plus selected report.json,
summary.json, report.md, report.html, and report.parquet/ artifacts.
Use it when CI, release automation, bindings, or Studio need one stable
directory to attach or inspect. summary.json is the lightweight UI/CI payload:
fingerprint, mode, report version, slice keys, optional
conformal_guarantee_status, optional spectral_replay, and compact
summary_rows() with execution-scope hints. The guarantee and spectral replay blocks are copied from report
metadata when present so cards and dashboards can show engine, coverage,
invalidation details, replay source, bundle path and sample-id forwarding status
without parsing the full report.
Because report metadata enters RobustnessReport.fingerprint, its keys must be
canonical non-empty strings without NULs and its values must stay TCV1
JSON-native; arbitrary Python objects or non-string keys fail before JSON
publication.
The same payload is available directly with summary_artifact(),
to_summary_json() and save_summary(), and from CLI publication with
--format summary. Its JSON Schema is available from
get_robustness_summary_schema() and robustness_summary_schema_json(). The
full bundle can be emitted from existing report/workspace CLI entry points with
--format artifacts --output <directory>.
RobustnessReport.load_artifacts() reloads a bundle from its manifest and
verifies the embedded JSON or Parquet report fingerprint. When Markdown, HTML or
Parquet artifacts are present, it compares them against the deterministic report
rendering or table-level Parquet manifest. When summary.json is present, it is
also compared against the deterministic summary payload. Missing or edited files
fail closed before downstream CI, bindings or Studio consume the directory. The
nirs4all robustness-report CLI accepts either a report JSON file or one of
these verified artifact directories as input.
Workspace CLI
The workspace commands are read-only and useful in CI or release audits:
nirs4all workspace tuning list --workspace workspace/ --json
nirs4all workspace tuning show ridge-native-tuning --workspace workspace/ --json
nirs4all workspace conformal list --workspace workspace/ --json
nirs4all workspace conformal show ridge-conformal --workspace workspace/ --json
nirs4all workspace conformal predict ridge-conformal \
--workspace workspace/ \
--y-pred "13.0,14.0" \
--sample-ids "pred-003,pred-004" \
--json
show reloads and verifies the typed artifact before printing it. conformal predict emits calibrated intervals and guarantee metadata without modifying the
workspace.
Executable example
See examples/user/04_models/U09_native_tuning_conformal.py for a complete
smoke-tested script. It covers typed conformal-aware objective scoring with
NativeTuning, TuningScoreData, and TuningConformalScoreCalibration,
then keeps final conformal calibration on the projected winner, reloads the
workspace artifacts, applies the stored calibrator to new point predictions,
and includes a standalone ConformalCalibrationData calibration smoke path.
Reading the guarantee metadata
Both calibrated result containers and prediction results expose
conformal_guarantee_status:
status = prediction.conformal_guarantee_status
print(status["status"])
print(status["coverage"])
print(status["limitations"])
print(prediction.calibration_replay_source["kind"])
The metadata records the method, unit, requested/effective coverage, fingerprints
and known limitations. It also includes calibration_replay_source, a compact
provenance block that tells whether calibration predictions were provided as
arrays, a PredictResult, dataset-backed y_pred, an in-memory predictor, a
saved predictor_bundle, a predictor_result, or a workspace
predictor_chain_id; the block records the route (provided_arrays,
predictor.predict, or nirs4all.predict) and bundle/chain details when
applicable. Guarantee metadata string fields are not coerced: effective_engine,
requested_engine, source_calibrated_result_fingerprint, and each
invalidation_reasons entry must be non-empty strings without surrounding
whitespace, and persisted conformal_guarantee_status.version must be the strict
integer 1. Status predictor_fingerprint, calibration_data_fingerprint,
guarantee, and scope are also rechecked against the embedded artifact on
construction and reload. A persisted status must include the complete generated
field set, and status must be active exactly when invalidation_reasons is
empty, otherwise invalidated. The generated limitations list must also match
the embedded artifact’s guarantee mode exactly; edited, shortened, empty or
non-string limitation payloads fail closed before publication. Use
prediction.calibration_replay_source or
calibrated.calibration_replay_source for the direct accessor; it reads the
nested status block first and falls back to the top-level result metadata for
older or partially projected payloads. New CalibratedRunResult payloads fail
closed if both locations carry calibration_replay_source but disagree, so
notebooks, Studio and bindings cannot observe contradictory replay provenance.
Empirical diagnostics such as observed coverage and interval width can be
computed separately with nirs4all.conformal_metrics() when observed y_true
values are available for the prediction cohort. Invalid or inconsistent metric
payloads fail closed before fingerprinting, persistence or UI publication.