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

diagnostics: dict[str, Any] | None = None

Diagnostic reports attached to the export, keyed by name — typically the output of prophys.engine.scoring.report_card().

verify(compiled, *, check_params=True)[source]

Check that compiled is the model this package came from.

Raises ReproducibilityError when the structure hash or the parameters differ; see prophys.export.manifest.verify().

Parameters:

check_params (bool)

Return type:

None

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.

std_error: float | None = None

Monte-Carlo standard error of mean, from the same draw the distribution was built from. None when the export did not compute it.

n_samples: int | None = None

Draws the exported representation was built from.

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 on 2**n_qubits support points (e.g. for quantum-amplitude-estimation-style consumers).

  • "histogram": variable-width-free histogram with 2**n_qubits equal-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.

Parameters:
  • attribute_names (list[str] | None)

  • representation (str)

  • n_qubits (int)

  • n_samples (int)

  • seed (int)

  • params (Any)

  • observations (Any)

  • diagnostics (Any)

  • notes (str)

Return type:

ModelPackage

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().

params: dict[str, Any]

Fitted parameters in constrained space — the values a reader interprets.

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.

mode: str

"eval" (exact geometry) or "opt" (smoothed).

tau: float

Smoothing temperature; meaningful only in "opt" mode.

n_samples: int

Monte-Carlo marginalization sample count the model was compiled with.

created_utc: str

ISO-8601 timestamp of the export, in UTC.

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.

observations: dict[str, Any]

Per-attribute observation counts by kind, when the run was a fit.

diagnostics: dict[str, Any]

Any diagnostic reports attached to the run, keyed by name.

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:

bool

prophys.export.build_manifest(compiled, *, params=None, observations=None, diagnostics=None, notes='')[source]

Build a RunManifest for 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 of prophys.engine.scoring.report_card().

Parameters:
Return type:

RunManifest

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:

str

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.

Return type:

dict[str, Any]

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 ReproducibilityError naming 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:
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:

Any