# Error mitigation and fidelity

> How Qsolve measures fidelity, how to declare error mitigation with report_mitigation, how mitigated figures are scored and labelled, and what you may claim.

Source: https://qsolve.omlabs.co/docs/build/error-mitigation-and-fidelity
Updated: 2026-11-12

Qsolve measures the fidelity of your quantum run from what crossed the backend, and you declare any error mitigation with `ctx.report_mitigation`. The raw fidelity is always shown. Your mitigated figures appear beside it, labelled reported, and Qsolve recomputes the fidelity against the same ideal output rather than taking your number.

## What fidelity means here

Fidelity compares two probability distributions over bit strings: `p`, what the device returned, and `q`, the ideal output of the same circuit. Qsolve uses the classical (Bhattacharyya) fidelity:

```text
F = ( sum over bit strings i of sqrt(p_i * q_i) ) ^ 2
```

`F` is 1 for identical distributions and 0 for disjoint ones. With finite shots even a perfect device scores below 1, so every figure comes with a shot-noise floor: the fidelity a noise-free device would get with the same shots.

The ideal output is simulated noise-free and exactly, up to 20 qubits. A circuit with more qubits, or with mid-circuit measurement, resets or classical control, cannot be scored. The share of your quantum method's shots that could be scored is reported as `coverage`.

## What Qsolve measures without you

For the quantum method, Qsolve computes the raw fidelity of the circuits that carry the run's noise:

- A circuit whose ideal output is a single bit string, such as a mirror or calibration circuit, computes nothing the answer depends on and is left out.
- Every other circuit counts, whether or not you reported it. Reporting a flattering subset changes nothing.
- Only circuits with at least half the two-qubit operations of the largest circuit are averaged, so a trivial circuit sent alongside carries no weight.
- The average is weighted by shots. All dispatches of one circuit are pooled before scoring.

With no report, mitigated fidelity equals raw fidelity and the share kept is 1.0. The result's `source` is `ideal_simulator`, `noise_model` or `hardware`. On an ideal simulator fidelity is only the shot-noise floor, so mitigation is validated on noise-model targets; see [Benchmarks](/docs/build/benchmarks).

## Declaring mitigation

If you post-process counts to reduce the effect of noise, say so with `ctx.report_mitigation`. Readout correction, post-selection, zero-noise extrapolation and anything else all count.

```python
ctx.report_mitigation(
    circuits=[dispatch],              # indices from pub_result.metadata["qsolve"][k]["dispatch"]
    distribution={"0101": 0.62, "1010": 0.31, ...},   # the mitigated output, bit string to weight
    methods=["readout correction", "post-selection (exactly k ones)"],
    kept=0.87,                        # share of shots kept, between 0 and 1
    steps=[{"name": "raw counts", "fidelity": 0.81},
           {"name": "readout correction", "fidelity": 0.90},
           {"name": "post-selection", "fidelity": 0.96}],
)
```

| Argument | Required | Meaning |
|---|---|---|
| `circuits` | Yes | Dispatch indices of the circuits whose output this distribution mitigates, from `metadata["qsolve"][k]["dispatch"]` of the pub result. |
| `distribution` | Yes | The mitigated distribution, bit string to weight. Weights must be finite, not negative, and sum to more than zero. Qsolve normalises them. |
| `methods` | No | Names of the mitigation methods, as plain text. |
| `kept` | No | The share of shots that survived your checks, 0 to 1. Omit it when nothing was discarded. |
| `steps` | No | Your own ladder: a list of `{"name": ..., "fidelity": ...}`. Shown as you give it. |

Bit strings use Qiskit order (classical bit 0 on the right) and must match the measured register's width. This is the real call from the examples:

```python
if run.report and run.dispatch is not None:
    ctx.report_mitigation(circuits=[run.dispatch], **run.report)
```

`run.report` is a dictionary with `distribution`, `methods`, `kept` and `steps`, built after readout correction and post-selection in `max-cut`. `molecular-ground-state` makes one call per measured circuit, with the dispatch indices of its copies.

A call raises `WorkflowError` when:

- `circuits` is empty or cites a circuit that was not sent: "report_mitigation cites circuits [3], but 2 were sent so far (a dispatch id is in pub_result.metadata['qsolve'][k]['dispatch'])";
- the distribution is empty, has a negative or non-finite weight, or sums to zero;
- `kept` is outside 0 to 1.

## How a report is scored

Qsolve computes the fidelity of your distribution against the ideal output of the circuits it covers. A report is used only when every circuit it cites shares one ideal output, and only for circuits sent by the quantum method. Otherwise it is ignored.

| Number | Source |
|---|---|
| Raw fidelity and its floor | Measured by Qsolve |
| Mitigated fidelity | Recomputed by Qsolve from your distribution; labelled reported |
| Share of shots kept | The mean of your reports' `kept` values; reported |
| Methods | The union of the names you gave; reported |
| Steps | Your list, as written; reported |
| Coverage | Measured by Qsolve |

## Fidelity requirements in benchmark cases

You set the bar yourself in `benchmarks/cases.yaml`, on a noisy target:

```yaml
- name: noise-model-ring-6
  input_file: examples/ring_6.json
  target: fake:fez@1.6
  seed: 1
  expect: {max_gap: 0.0, min_fidelity: 0.97, max_two_qubit_gates: 40}
```

| `expect` key | The case fails when |
|---|---|
| `min_fidelity` | no mitigation numbers exist; fewer than 90% of the shots could be scored; or the mitigated fidelity is below the bound |
| `min_postselection` | no mitigation numbers exist, or the share of shots kept is below the bound |
| `max_two_qubit_gates` | the largest sent circuit, transpiled to `cx, rz, sx, x`, has more two-qubit gates than the bound |

The gate `fidelity_checked` fails unless at least one case sets `min_fidelity` on a `fake:` or `ibm:` target. Set bounds to what your circuit measurably reaches, with a margin.

## What you may and may not claim

You may report a distribution you computed from the counts that came back through `ctx`, with post-processing described in `methods` and `steps`, and `kept` as the share of those shots your checks retained.

You may not report a distribution that was not derived from the measured counts: no ideal output from a simulation, no answer known in advance, no classical solution dressed up as a measurement. Qsolve scores a report against the ideal output, so a distribution equal to the ideal output scores a fidelity of 1.

Qsolve can check that the numbers are consistent, not that your mitigation is legitimate. That is why raw figures are shown next to yours and yours are labelled reported. Describe each step in `methods` and `steps` so a reader can judge it.

## Practical advice

- Readout correction works on the full distribution, so it needs `2^n` entries. The examples apply it up to 16 qubits and skip it above.
- Post-selection suits problems with a conserved quantity, such as exactly k ones. Report the discarded share honestly in `kept`.
- Include a `steps` ladder when you can; it shows what each stage bought.
- Do not rely on mitigation to rescue a deep circuit. Keep circuits shallow first; see [Backends and targets](/docs/build/backends-and-targets).

For how a researcher reads these figures, see [Accuracy, fidelity and noise](/docs/use/accuracy-fidelity-and-noise).
