forked from quantumlib/Cirq
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Obs] 4.4 - Readout error mitigation (quantumlib#4323)
Add a facility to take readout calibrations by measuring the <Z> observables under the empty circuit with readout symmetrization turned on. This can be plumbed through so other observables measured with `measure_observables` can be corrected. Note that this all only works for symmetric readout error enforced through readout_symmetrization. Part of quantumlib#3647
- Loading branch information
1 parent
9173bcc
commit 8d644be
Showing
5 changed files
with
139 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import dataclasses | ||
from typing import Union, Iterable, TYPE_CHECKING | ||
|
||
from cirq import circuits, study, ops | ||
from cirq.work.observable_measurement import measure_grouped_settings, StoppingCriteria | ||
from cirq.work.observable_settings import ( | ||
InitObsSetting, | ||
zeros_state, | ||
) | ||
|
||
if TYPE_CHECKING: | ||
import cirq | ||
|
||
|
||
def calibrate_readout_error( | ||
qubits: Iterable[ops.Qid], | ||
sampler: Union['cirq.Simulator', 'cirq.Sampler'], | ||
stopping_criteria: StoppingCriteria, | ||
): | ||
# We know there won't be any fancy sweeps or observables so we can | ||
# get away with more repetitions per job | ||
stopping_criteria = dataclasses.replace(stopping_criteria, repetitions_per_chunk=100_000) | ||
|
||
# Simultaneous readout characterization: | ||
# We can measure all qubits simultaneously (i.e. _max_setting is ZZZ..ZZ | ||
# for all qubits). We will extract individual qubit quantities, so there | ||
# are `n_qubits` InitObsSetting, each responsible for one <Z>. | ||
# | ||
# Readout symmetrization means we just need to measure the "identity" | ||
# circuit. In reality, this corresponds to measuring I for half the time | ||
# and X for the other half. | ||
init_state = zeros_state(qubits) | ||
max_setting = InitObsSetting( | ||
init_state=init_state, observable=ops.PauliString({q: ops.Z for q in qubits}) | ||
) | ||
grouped_settings = { | ||
max_setting: [ | ||
InitObsSetting(init_state=init_state, observable=ops.PauliString({q: ops.Z})) | ||
for q in qubits | ||
] | ||
} | ||
|
||
results = measure_grouped_settings( | ||
circuit=circuits.Circuit(), | ||
grouped_settings=grouped_settings, | ||
sampler=sampler, | ||
stopping_criteria=stopping_criteria, | ||
circuit_sweep=study.UnitSweep, | ||
readout_symmetrization=True, | ||
) | ||
(result,) = list(results) | ||
return result |
48 changes: 48 additions & 0 deletions
48
cirq-core/cirq/work/observable_readout_calibration_test.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
from typing import Sequence | ||
|
||
import cirq | ||
import cirq.work as cw | ||
import numpy as np | ||
|
||
|
||
class DepolarizingWithDampedReadoutNoiseModel(cirq.NoiseModel): | ||
"""This simulates asymmetric readout error. | ||
The noise is structured so the T1 decay is applied, then the readout bitflip, then measurement. | ||
If a circuit contains measurements, they must be in moments that don't also contain gates. | ||
""" | ||
|
||
def __init__(self, depol_prob: float, bitflip_prob: float, decay_prob: float): | ||
self.qubit_noise_gate = cirq.DepolarizingChannel(depol_prob) | ||
self.readout_noise_gate = cirq.BitFlipChannel(bitflip_prob) | ||
self.readout_decay_gate = cirq.AmplitudeDampingChannel(decay_prob) | ||
|
||
def noisy_moment(self, moment: 'cirq.Moment', system_qubits: Sequence['cirq.Qid']): | ||
if cirq.devices.noise_model.validate_all_measurements(moment): | ||
return [ | ||
cirq.Moment(self.readout_decay_gate(q) for q in system_qubits), | ||
cirq.Moment(self.readout_noise_gate(q) for q in system_qubits), | ||
moment, | ||
] | ||
else: | ||
return [moment, cirq.Moment(self.qubit_noise_gate(q) for q in system_qubits)] | ||
|
||
|
||
def test_calibrate_readout_error(): | ||
sampler = cirq.DensityMatrixSimulator( | ||
noise=DepolarizingWithDampedReadoutNoiseModel( | ||
depol_prob=1e-3, | ||
bitflip_prob=0.03, | ||
decay_prob=0.03, | ||
), | ||
seed=10, | ||
) | ||
readout_calibration = cw.calibrate_readout_error( | ||
qubits=cirq.LineQubit.range(2), | ||
sampler=sampler, | ||
stopping_criteria=cw.RepetitionsStoppingCriteria(100_000), | ||
) | ||
means = readout_calibration.means() | ||
assert len(means) == 2, 'Two qubits' | ||
assert np.all(means > 0.89) | ||
assert np.all(means < 0.91) |