Export Format
prophys defines a single, neutral, versioned interchange format for a
compiled model — the ModelPackage. It carries no
dependency on any downstream consumer; a consumer (Qalibri or any other
system) implements its own import against this documented shape.
compiled = model.compile()
pkg = compiled.export() # ModelPackage
pkg.save("model.json")
loaded = prp.export.ModelPackage.load("model.json")
Structure
ModelPackage is a plain dataclass, serializable to JSON:
ModelPackage
format_version: str # "1.1"
model_name: str
params: dict[str, Any] # calibrated parameters, constrained space
correlation: dict | None # reserved for joint/copula metadata
attributes: list[AttributeExport]
manifest: RunManifest | None # 1.1: provenance, see below
diagnostics: dict | None # 1.1: attached diagnostic reports
AttributeExport
name: str
unit: str
domain: str # free-text description
representation: "quantized" | "histogram" | "sampler"
support: (float, float) # observed [min, max] over the sample
mean: float
unit_description: str | None # 1.1: unit expanded to dimension + SI scale
std_error: float | None # 1.1: Monte-Carlo error of `mean`
n_samples: int | None # 1.1: draws the representation was built from
# representation == "quantized":
grid: list[float] # bin-center support points
probs: list[float] # normalized probabilities, same length as grid
# representation == "histogram":
edges: list[float] # bin edges, length len(probs) + 1
probs: list[float]
# representation == "sampler":
samples: list[float] # raw Monte-Carlo draws, JAX-free
Choosing a representation
"quantized"— a probability mass function on \(2^n\) support points. The natural format for consumers that need a discretized distribution over a fixed grid (e.g. state-preparation for quantum amplitude estimation)."histogram"— equal-width bins with edges and probabilities; a gridding-free summary when the consumer just needs a coarse density."sampler"— raw NumPy floats, no JAX dependency at all; the simplest possible handoff for a consumer that wants to resample or refit.
pkg = compiled.export(representation="sampler", n_samples=8192)
Provenance: the run manifest
Format 1.1 attaches a RunManifest. A
ModelPackage says what the answer was; the manifest says where it came
from:
pkg = prp.export.export_model(
compiled,
params=result.raw_params,
observations={"y": data},
diagnostics=prp.report_card(compiled, "y", data, result=result),
notes="design case B, revision 3",
)
print(pkg.manifest)
RunManifest:
prophys 0.3.0 on Python 3.12.14 (Darwin-arm64)
model ProbabilityModel(y)
structure 50bab27094b6a6ea...
seed 7, mode eval, tau 1, n_samples 2000
licensed: False
created 2026-08-26T06:23:15Z
observations: {'y': {'n': 200, 'informative': 200, 'kinds': {'exact': 200}}}
notes: design case B, revision 3
The model is identified by a structure hash: a digest over its attributes, distribution families, parameter declarations and uncertain inputs. The hash covers structure, not fitted values — two runs of a fit on the same model produce the same hash, or the hash would identify the run rather than the model. Parameters are recorded separately, in both constrained form (what a reader interprets) and unconstrained form (what the engine evaluates from, and so what reproduces the run exactly).
Verifying and reproducing
loaded = prp.export.ModelPackage.load("model.json")
loaded.verify(model.compile()) # structure and parameters
compiled = prp.export.reproduce(loaded.manifest, model)
verify() raises
ReproducibilityError naming what differs — an edited
model, a drifted parameter, a differing parameter set.
reproduce() recompiles the model with the manifest’s
mode, smoothing temperature, sample count and seed, after checking the
structure hash.
This reproduces the run configuration; it does not rebuild the model graph
from the manifest. The graph is defined by the Python that built it, and the
structure hash is what certifies that this Python still describes the same
model. When two hashes disagree,
structure_description() shows which part changed.
Round-tripping
ModelPackage.save/.load round-trip through JSON losslessly for all
three representations; the symbolic graph itself is not serialized (the
package intentionally only carries the calibrated numeric artifacts a
downstream consumer needs, not the engine internals used to produce them).
A format 1.0 file loads unchanged — the 1.1 fields are optional and default
to absent, and manifest is None. A file whose format_version is
outside SUPPORTED_VERSIONS is refused with its
version named rather than read with fields silently missing; unknown
fields within a supported version are dropped, so a file from a later
prophys still loads with what this one understands.
- class prophys.export.ModelPackage(format_version: 'str', model_name: 'str', params: 'dict[str, Any]', attributes: 'list[AttributeExport]', correlation: 'dict[str, Any] | None' = None, manifest: 'RunManifest | None' = None, diagnostics: 'dict[str, Any] | None' = None)[source]
- Parameters:
- manifest: RunManifest | None = None
versions, structure hash, seed, licensing. None only for a package loaded from a 1.0 file.
- Type:
Provenance for this export
- class prophys.export.AttributeExport(name: 'str', unit: 'str', domain: 'str', representation: 'str', support: 'tuple[float, float]', grid: 'list[float] | None' = None, probs: 'list[float] | None' = None, edges: 'list[float] | None' = None, samples: 'list[float] | None' = None, mean: 'float | None' = None, unit_description: 'str | None' = None, std_error: 'float | None' = None, n_samples: 'int | None' = None)[source]
- Parameters:
- unit_description: str | None = None
The attribute’s unit expanded into its dimension and SI scale (see prophys.units.describe), so a consumer can check compatibility without reimplementing this package’s unit parser.
- prophys.export.export_model(compiled, attribute_names=None, representation='quantized', n_qubits=8, n_samples=4096, seed=0, params=None, observations=None, diagnostics=None, notes='')[source]
Build a ModelPackage from a CompiledModel.
representation controls how each attribute’s marginal distribution is serialized:
"quantized": PMF on2**n_qubitssupport points (e.g. for quantum-amplitude-estimation-style consumers)."histogram": variable-width-free histogram with2**n_qubitsequal-width bins."sampler": raw Monte-Carlo samples, JAX-free (plain floats), for consumers that just want data to resample from.
params is the unconstrained parameter dict to export at — pass a fit’s raw_params so the exported distributions and the manifest both record the fitted model rather than its declared initial values. observations and diagnostics are recorded in the manifest as provenance; notes is free text carried alongside.
- class prophys.export.RunManifest(manifest_version, prophys_version, structure_hash, model_name, params, raw_params, seed, mode, tau, n_samples, created_utc, python_version, platform, licensed, licensee='', observations=<factory>, diagnostics=<factory>, notes='')[source]
Everything needed to trace an exported result back to its run.
- Parameters:
- structure_hash: str
Digest of the model’s structure; see
structure_hash().
- raw_params: dict[str, Any]
The same parameters in unconstrained space — the values needed to reproduce the run bit for bit, since that is what the engine evaluates from.
- licensed: bool
Whether a valid PROPHYS_LICENSE was in effect. A free-tier run is limited in model size, so this records which regime produced the result.
- matches(other)[source]
Whether two manifests describe the same model structure.
Compares the structure hash only: two manifests can legitimately differ in seed, sample count, timestamp and fitted parameters while describing the same model.
- Parameters:
other (RunManifest)
- Return type:
- prophys.export.build_manifest(compiled, *, params=None, observations=None, diagnostics=None, notes='')[source]
Build a
RunManifestfor a compiled model.params is the unconstrained parameter dict the run used — a fit’s raw_params. Without it the model’s declared initial values are recorded, which is correct for an unfitted model and would be misleading for a fitted one, so pass it whenever a fit happened.
diagnostics accepts any mapping of name to
Report(or plain dict), which is serialized alongside — the natural place for the output ofprophys.engine.scoring.report_card().
- prophys.export.structure_hash(model)[source]
A stable digest of a ProbabilityModel’s structure.
Covers, per attribute: its name, unit, domain, the class name of its distribution and of every nested distribution, and every Param leaf’s name, shape, bounds and transform — plus the names and families of the upstream RandomVariable leaves.
Deliberately excludes: parameter values (they are recorded separately and are expected to change between a prior and a fitted run), sample counts, seeds, and anything else about how the model was evaluated. Two runs of a fit on the same model must produce the same structure hash, or the hash would identify the run rather than the model.
- Return type:
- prophys.export.structure_description(model)[source]
The structure the hash is taken over, as a readable dict.
Exposed rather than kept private: when two hashes disagree, this is what tells the reader which part of the model changed.
- prophys.export.verify(manifest, compiled, *, check_params=True)[source]
Check that compiled is the model manifest was produced from.
Compares the structure hash and, unless check_params is false, every recorded parameter value against the model’s current ones. Raises
ReproducibilityErrornaming what differs.Parameter comparison is to single-precision tolerance: the values were serialized from float32 arrays, and requiring exact equality of a decimal round-trip would fail on every manifest for no useful reason.
- Parameters:
manifest (RunManifest)
check_params (bool)
- Return type:
None
- prophys.export.reproduce(manifest, model)[source]
Recompile model exactly as manifest records it.
Returns a CompiledModel with the manifest’s mode, tau, sample count and seed, verified against its structure hash. The recorded parameters are not baked in — they are returned to the caller as the raw_params to pass to any evaluation — because a CompiledModel takes its parameters per call rather than holding them.
This reproduces the run configuration; it does not rebuild the model graph from the manifest. The graph is defined by the Python that built it, and the structure hash is what certifies that this Python still describes the same model.
- Parameters:
manifest (RunManifest)
- Return type: