iqm.error_reduction_tools.rem.rem_processors.ReadoutErrorMitigation#
- class iqm.error_reduction_tools.rem.rem_processors.ReadoutErrorMitigation(readout_errors, max_entropy=28.0)#
Bases:
objectApply readout error mitigation to measurement counts, with optional twirling support.
This class performs tensor-product inversion of readout confusion matrices to correct measurement errors. Supports both standard and symmetrized (twirled) characterization data.
Methods
_mitigate(counts, cl_index_to_qubit_name[, ...])Apply readout error mitigation to the given counts.
_normalize_observables(observables, ...)Normalize observables to the canonical qubit-name list format.
_symmetrize_copy(charact_data)Return a symmetrized copy of characterization data without modifying the original.
_validate_charact_data()Validate the characterization data format.
_validate_counts_and_mapping(counts, ...)Validate the counts format and qubit mapping.
_warn_high_error_rates(charact_data)Emit warnings for qubits whose readout error rate is >= 20%.
from_client(client[, qubits, ...])Create ReadoutErrorMitigation by running characterization on a client.
Get the calibration data for all qubits.
mitigate_counts(experiment_counts, ...[, ...])Mitigate a list of measurement count dictionaries for one or more observables.
Symmetrize the characterization data by averaging error rates.
- classmethod from_client(client, qubits=None, number_of_circuits=100, shots=10000, symmetrize=True, seed=None, equatorial_randomization=True, max_entropy=28.0)#
Create ReadoutErrorMitigation by running characterization on a client.
Runs the full characterization workflow on the quantum computer and then returns an initialized ReadoutErrorMitigation instance.
- Parameters:
client (Pulla) – Client for connecting to an IQM quantum computer.
qubits (list[str] | None) – List of qubit names to characterize (e.g., [“QB1”, “QB2”]). If None, characterizes all qubits available on the client.
number_of_circuits (int) – Number of calibration circuits to generate.
shots (int) – Total shots for characterization, distributed across all circuits.
symmetrize (bool) – If True, generates complementary I/X preparation pairs.
seed (int | None) – Random seed for reproducible characterization.
equatorial_randomization (bool) – If True, randomizes X gate phases.
max_entropy (float) – Maximum allowed Shannon entropy (in bits) threshold.
- Returns:
ReadoutErrorMitigation instance with characterization data.
- Return type:
Example
>>> from iqm.pulla import Pulla >>> client = Pulla(url="https://example.iqm.fi") >>> mitigator = ReadoutErrorMitigation.from_client( ... client=client, ... qubits=["QB1", "QB2"], ... number_of_circuits=30, ... shots=10000, ... ) >>> mitigated = mitigator.mitigate(counts, cl_index_to_qubit_name=["QB1", "QB2"])
- get_calibration_data()#
Get the calibration data for all qubits.
- symmetrize_charact_data()#
Symmetrize the characterization data by averaging error rates.
Converts matrices to symmetrized scalar error rates by averaging P(0|1) and P(1|0). If data is already scalar, this method has no effect.
- Return type:
None
- mitigate_counts(experiment_counts, qubit_to_bit_mapping, observables=None, twirled=False, force_mitigation=False, nearest_probability=True, client=None, auto_characterize_shots=10000, auto_characterize_circuits=20)#
Mitigate a list of measurement count dictionaries for one or more observables.
Bitstrings and bit indices follow little-endian (Qiskit) convention: bit index 0 is the rightmost character of the bitstring.
- Parameters:
experiment_counts (list[dict[str, int]]) – Measurement count dictionaries. Each dict maps bitstrings (e.g.,
"0101") to shot counts. Bitstrings follow little-endian convention (rightmost character = bit index 0).qubit_to_bit_mapping (dict[str, int] | TwirledCircuit | list[TwirledCircuit]) –
A property of the transpiled circuit: it maps every measured qubit name to its classical bit index and is fixed once the circuit has been run.
Accepts multiple formats (auto-detected):
dict (canonical):
{"QB5": 0, "QB12": 1, "QB3": 2, "QB7": 3}TwirledCircuit: extracts mapping from
measured_qubitslist[TwirledCircuit]: extracts from the first circuit
observables (list[list[str] | str | list[int]] | None) –
Observables to mitigate and compute ZZ…Z expectation values for.
observablesis a list of qubit-name subsets; for each subset the raw counts are first marginalized to the corresponding bit indices (looked up fromqubit_to_bit_mapping), then readout-error-mitigated, and finally a ZZ…Z expectation value is computed.Supports three formats per element (auto-detected):
Qubit names (current):
[["QB3", "QB5"], ["QB1"]]Pauli strings (Z/I only):
["IIIZZIZI", "ZIIIIIIII"]Bit indices:
[[2, 4], [0]]
If
None, a single observable covering all qubits inqubit_to_bit_mappingis used. Formats can be mixed within a single call.twirled (bool) – If
True, symmetrize the characterization data before mitigation by averaging P(0|1) and P(1|0) into a single error rate per qubit. This is appropriate when the counts were obtained using readout twirling. The originalcharact_dataon the instance is not modified; a temporary symmetrized copy is used. Default isFalse.force_mitigation (bool) – If True, apply mitigation even if complexity exceeds threshold.
nearest_probability (bool) – If True, project each quasi-probability distribution onto the nearest valid probability distribution using
mthree’snearest_probability_distributionmethod. Eliminates negative values at the cost of a small bias. Default is True.client (Pulla | None) – Optional client for automatic characterization of missing qubits on the quantum computer.
auto_characterize_shots (int) – Shots to use when auto-characterizing (default: 10000).
auto_characterize_circuits (int) – Number of circuits for auto-characterization (default: 20).
- Returns:
"mitigated_counts":list[list[dict[str, float]]]—result["mitigated_counts"][i][j]is the mitigated quasi-probability distribution for circuitiunder observablej."expectation_values":list[list[float]]—result["expectation_values"][i][j]is the ZZ…Z expectation value for circuitiunder observablej."characterization_performed":bool— True if auto-characterization ran.
- Return type:
Dictionary containing
- Raises:
ValueError – If
experiment_countsorqubit_to_bit_mappingare empty, if any observable qubit is not inqubit_to_bit_mapping, or characterization data is missing and noclientis provided.
Example — full-distribution mitigation:
>>> mitigator = ReadoutErrorMitigation(charact_data) >>> result = mitigator.mitigate_counts( ... experiment_counts=[{"00": 450, "11": 450}], ... qubit_to_bit_mapping={"QB1": 0, "QB2": 1}, ... ) >>> result["mitigated_counts"][0][0] # circuit 0, observable 0 >>> result["expectation_values"][0][0] # <ZZ> for circuit 0
Example — multiple observables in one call:
>>> result = mitigator.mitigate_counts( ... experiment_counts=[{"0000": 450, "1111": 450}], ... qubit_to_bit_mapping={"QB1": 0, "QB2": 1, "QB3": 2, "QB4": 3}, ... observables=[ ... ["QB1", "QB2", "QB3", "QB4"], # full ZZZZ ... ["QB1", "QB2"], # ZZ on first two qubits ... ["QB3", "QB4"], # ZZ on last two qubits ... ], ... ) >>> result["expectation_values"][0] # [<ZZZZ>, <Z0Z1>, <Z2Z3>]
Example — auto-characterization:
>>> mitigator = ReadoutErrorMitigation(charact_data={}) >>> result = mitigator.mitigate_counts( ... experiment_counts=counts_list, ... qubit_to_bit_mapping={"QB1": 0, "QB2": 1}, ... client=client, ... )
Example — twirled mitigation (same instance reusable for both):
>>> rem = ReadoutErrorMitigation(charact_data=probs["charact_data"]) >>> result_twirled = rem.mitigate_counts( ... [twirled_counts], mapping, twirled=True, ... ) >>> result_standard = rem.mitigate_counts( ... [standard_counts], mapping, twirled=False, ... )
Example — Pauli-string observables (natural for Qiskit users):
>>> result = rem.mitigate_counts( ... experiment_counts=[counts], ... qubit_to_bit_mapping={"QB1": 0, "QB2": 1, "QB3": 2, "QB4": 3}, ... observables=["IZIZ", "ZZII"], ... twirled=True, ... )
Example — bit-index observables (framework-agnostic):
>>> result = rem.mitigate_counts( ... experiment_counts=[counts], ... qubit_to_bit_mapping={"QB1": 0, "QB2": 1, "QB3": 2, "QB4": 3}, ... observables=[[0, 2], [2, 3]], ... )
Example — TwirledCircuit as mapping:
>>> result = rem.mitigate_counts( ... experiment_counts=[counts], ... qubit_to_bit_mapping=twirled_circuit, ... twirled=True, ... )