# SDK: results, events and circuit records

> Reference for qsolve.results, qsolve.events and qsolve.circuit_record: every field of Result, MethodResult, Metrics, Quality, Mitigation, CircuitStats, Provenance, Uncertainty and Estimate, plus emit, capture and the drawable circuit records.

Source: https://qsolve.omlabs.co/docs/build/sdk-results-and-events
Updated: 2026-11-13

What a run produces. You never build these objects in a workflow (except `Estimate`); the SDK builds them from what it measured and you read them in `qsolve run --out`, in tests and on the platform's run page. All are pydantic models: `model_dump(mode="json")` gives plain JSON and `Model.model_validate(data)` reads it back. Back to the [SDK reference](/docs/build/sdk-reference).

## qsolve.results

```python
from qsolve import Estimate, Mitigation, Provenance, Quality, Result, Uncertainty   # top level
from qsolve.results import CircuitStats, MethodResult, Metrics                       # module only
```

Who measures what: the numbers are computed by the SDK; the words (`title`, `what`, `strength`, `limit`, `note`) are declared by your manifest. See [Reading your results](/docs/use/reading-your-results) for how a user sees them.

### Result

The final, user-facing result of one run (`RunOutput.result`).

| Field | Type | Default | Meaning |
|---|---|---|---|
| `answer` | `dict[str, Any]` | required | The quantum method's `answer()`, validated against the output schema. |
| `objective` | `float` | required | The quantum method's objective, recomputed by `evaluate` when it gave a solution. |
| `baseline_objective` | `float` | required | The reference method's objective. |
| `baseline_solver` | `str` | required | The reference method's manifest `title`. |
| `gap` | `float` | required | `(objective - baseline) / max(abs(baseline), eps)`; 0 means it matched the baseline; negative means better. |
| `uncertainty` | `Uncertainty` | required | See below. |
| `summary` | `str` | `""` | One sentence, at most 500 characters. |
| `artifacts` | `dict[str, Any]` | `{}` | Reserved for extra output; the SDK leaves it empty. |
| `quality` | `Quality \| None` | `None` | Fidelity and circuit size of the quantum run. |
| `methods` | `list[MethodResult]` | `[]` | Every declared method, in manifest order. |
| `metrics` | `Metrics \| None` | `None` | The headline accuracy. |

### Uncertainty

| Field | Type | Default | Meaning |
|---|---|---|---|
| `kind` | `str` | required | `"sampling"`, `"exact"` or `"heuristic"`. `run_workflow` writes `sampling` when the quantum method gave a `stderr`, otherwise `exact`. |
| `stderr` | `float \| None` | `None` | One standard deviation on the quantum value. |
| `note` | `str` | `""` | Text, for example `one standard deviation on the quantum value`. |

### MethodResult

One method, quantum or classical, run on this input.

| Field | Type | Default | Meaning |
|---|---|---|---|
| `id` | `str` | required | The manifest method id. |
| `role` | `str` | required | `quantum`, `reference` or `classical`. |
| `title`, `what`, `strength`, `limit` | `str` | `""` except `title` | Declared in the manifest. |
| `value` | `float \| None` | `None` | The objective this method reached (lower is better); `None` when unavailable. |
| `stderr` | `float \| None` | `None` | The method's own error bar. |
| `vs_reference` | `float \| None` | `None` | `(value - reference) / max(abs(reference), eps)`; 0 means the same. |
| `seconds` | `float \| None` | `None` | Wall-clock seconds for the method. The only field that differs run to run; it is excluded from the reproducibility hash. |
| `verified` | `str` | `"reported"` | `recomputed` when the SDK evaluated the method's solution with your `evaluate`; `reported` when it is the workflow's claim with no solution to re-evaluate. |
| `dispatches` | `int` | `0` | Circuits this method sent to the backend (the quantum method must send at least one). |
| `claimed` | `float \| None` | `None` | What the method itself reported, set only when it disagreed with the recomputed value. A set `claimed` fails validation. |
| `unavailable` | `str` | `""` | Why the method could not run on this input. |
| `p_reference` | `float \| None` | `None` | For sampling methods: the share of samples on the reference answer. |
| `valid_fraction` | `float \| None` | `None` | For sampling methods: the share of samples that are valid answers. |
| `top_outcomes` | `list[dict]` | `[]` | The five most probable outcomes: `{"outcome": str, "p": float}`. |
| `answer` | `dict \| None` | `None` | This method's `answer()`, when it succeeded. |

### Metrics

The headline numbers, computed by the SDK ([`headline`](/docs/build/sdk-runner-and-manifest#headline)).

| Field | Type | Default | Meaning |
|---|---|---|---|
| `kind` | `str` | required | `relative_gap`, `relative_error` or `error_rate` (from the manifest). |
| `accuracy` | `float` | required | 0 to 1: how close the quantum result is to the reference. |
| `accuracy_pm` | `float \| None` | `None` | Its plus or minus (one sigma, or a 95% interval half-width for an error rate). |
| `note` | `str` | `""` | How it was computed, in your words (manifest `metric.note`). |
| `tolerance` | `float` | `0.0` | The manifest's `metric.tolerance`. |
| `matches` | `bool` | `False` | The platform's verdict: equal to the reference, within a declared tolerance, or statistically indistinguishable from it. |

### Quality

| Field | Type | Default | Meaning |
|---|---|---|---|
| `mitigation` | `Mitigation \| None` | `None` | Fidelity of the quantum run. |
| `circuit` | `CircuitStats \| None` | `None` | Size of what was sent. |

### Mitigation

Fidelity of the quantum run. The raw figure is measured by the SDK; the mitigated figure is what your own post-processing reports, recomputed against the ideal output. See [Error mitigation and fidelity](/docs/build/error-mitigation-and-fidelity) and [Metering and cost](/docs/build/sdk-metering-and-cost#mitigation).

| Field | Type | Default | Meaning |
|---|---|---|---|
| `fidelity_raw` | `float` | required | Measured against the ideal distribution, before any mitigation. |
| `fidelity_mitigated` | `float` | required | After the listed methods; equals the raw figure when you reported nothing. |
| `reported` | `bool` | `False` | True when the mitigated figure comes from your report. |
| `fidelity_floor` | `float` | required | The best a noise-free device scores at this shot count. |
| `postselection_rate` | `float` | required | Share of shots that pass your checks (1.0 means nothing discarded). |
| `methods` | `list[str]` | `[]` | Mitigation methods you named. |
| `source` | `str` | required | `hardware`, `noise_model` or `ideal_simulator`. |
| `coverage` | `float` | `1.0` | Share of the quantum method's shots that could be scored (not circuits over 20 qubits or with mid-circuit measurement). |
| `details` | `dict[str, Any]` | `{}` | Holds `steps`, your fidelity ladder, when reported. |

### CircuitStats

Size of what the quantum method actually sent, measured by the SDK.

| Field | Type | Default | Meaning |
|---|---|---|---|
| `qubits` | `int` | required | Width of the widest circuit (active qubits). |
| `depth` | `int` | required | Greatest depth after transpiling to `cx, rz, sx, x`. |
| `two_qubit_gates` | `int` | required | Greatest `cx` count after transpiling. |
| `circuits` | `int` | `0` | Circuits sent in total. |
| `shots` | `int` | `0` | Shots over all of them. |

### Provenance

Immutable record of how a result was produced (`RunOutput.provenance`).

| Field | Type | Default | Meaning |
|---|---|---|---|
| `run_id` | `str` | required | A new UUID for each run. |
| `workflow`, `version` | `str` | required | Manifest `name` and `version`. |
| `commit_sha`, `image_digest` | `str \| None` | `None` | As passed to `run_workflow`. |
| `sdk_version` | `str` | required | `qsolve.__version__` at run time. |
| `input_hash` | `str` | required | SHA-256 of the input's canonical JSON, floats rounded to 1e-6. |
| `params` | `dict` | required | The resolved parameters. |
| `seed` | `int` | required | The seed. |
| `target` | `str` | required | The backend's target class: `sim-cpu`, `sim-gpu` or `qpu`. |
| `provider`, `device` | `str` | required | For example `aer` and `aer-cpu`; `fake` and `fake_fez@1.6`; `ibm` and `ibm_fez`. |
| `calibration_snapshot` | `dict` | `{}` | The backend's `calibration_snapshot()`. |
| `shots` | `int` | `0` | Total shots the quantum method sent. |
| `exec_time_s` | `float` | `0.0` | Wall-clock seconds of the whole run. |
| `started_at`, `finished_at` | `str` | required | ISO 8601 UTC timestamps. |
| `result_hash` | `str` | required | SHA-256 of the result with each method's `seconds` set to null. The reproducibility gate compares it. |
| `solver` | `dict` | `{}` | `{"name": <quantum method id>, "details": <Outcome.details as plain JSON>}`. Not hashed. |

### Estimate

The pre-run resource estimate, and the one result type you construct yourself. Its fields, and how the platform uses it, are in [Metering and cost](/docs/build/sdk-metering-and-cost#estimate).

## qsolve.events

```python
from qsolve.events import Sink, capture, emit
```

Structured run events. Solvers and workflows call `emit`; the runner (or the platform) installs a sink that streams them to the run page and log store. With no sink installed `emit` costs nothing.

| Name | Signature | Meaning |
|---|---|---|
| `Sink` | `Callable[[dict[str, Any]], None]` | The type of an event receiver. |
| `emit` | `emit(type: str, **data: Any) -> None` | Sends `{"type": type, "t": time.time(), **data}` to the installed sink, if any. Never raises: an exception in the sink is swallowed because observability must never break a run. In a workflow use `ctx.emit`, which is this function. |
| `capture` | `capture(sink: Sink \| None) -> ContextManager[None]` | Installs `sink` for the duration of a `with` block (a `ContextVar`, so it is per thread and per async task) and restores the previous one afterwards. `None` turns events off. `run_workflow(..., on_event=f)` is `capture(f)` around the run. |

```python
from qsolve.events import capture, emit

seen = []
with capture(seen.append):
    emit("train_step", step=1, value=-1.2)
print(seen[0]["type"], seen[0]["step"])     # train_step 1
```

Events the runner emits itself: `stage` with `stage` set to `build`, `run` (also carrying `device` and `target`) and `compare`. Event payloads must be JSON-serialisable; the sandbox converts non-finite floats to `null`.

## qsolve.circuit_record

```python
from qsolve.circuit_record import DRAWABLE, active_qubits, circuit_ops, circuit_summary, lowered
```

Circuits as compact, drawable records: what the platform shows on a run page. A viewer can draw or export them without Qiskit.

| Name | Signature | Meaning |
|---|---|---|
| `DRAWABLE` | `set[str]` | Gate names a viewer can draw and OpenQASM 2 can express (`h`, `cx`, `rz`, `measure`, `barrier`, ... 38 names), plus non-unitary operations. |
| `lowered` | `lowered(circuit) -> QuantumCircuit` | Expands every operation outside `DRAWABLE` by decomposing, for at most 8 rounds. Opaque operations without a definition stay as they are and are drawn as labelled boxes. |
| `circuit_ops` | `circuit_ops(circuit, max_ops=20_000) -> dict` | `{"qubits": int, "clbits": int, "ops": [[name, [qubit indices], [params], [clbit indices]], ...], "truncated": bool}` of the lowered circuit. Parameters are rounded to 6 places; an unbound parameter is its name; `delay` ops have no params. Qubit indices are positions in the circuit (physical qubits for a transpiled circuit). Stops at `max_ops` and sets `truncated`. |
| `circuit_summary` | `circuit_summary(circuit) -> dict` | `{"qubits", "clbits", "size", "depth", "two_qubit_gates", "ops": {name: count}}` without lowering: cheap enough for every circuit a run sends. Two-qubit gates exclude barriers. |
| `active_qubits` | `active_qubits(circuit) -> list[int]` | Sorted qubit indices that some operation touches (barriers included). Not to be confused with `qsolve.metering.active_qubits`, which returns a count. |

```python
from qsolve.circuit_record import circuit_ops
rec = circuit_ops(qc)
print(rec["ops"][:3])      # [["h", [0], [], []], ["cx", [0, 1], [], []], ...]
```
