Skip to content

Commit

Permalink
Support readout errors modeling in noise-from-metrics utility. (#2715)
Browse files Browse the repository at this point in the history
* Add a bare-bones utility for generating noise models from calibration data.

* Formatting.

* Additional formatting.

* Appease mypy.

* More mypy wrangling.

* Pylint.

* Use Calibration object to generate noise model.

* Clean up debug logs.

* Simplify tests.

* Formatting.

* Update test to match calibration_test.

* Support readout error in noise-from-metrics utility.

* Add tests for failure cases.

* Documentation fixes.

* Keep 'exp' import.

* Avoid empty moments from noise.

* Distinguish between readout and decay errors.

* Use Pauli error for depolarization.

* Respect virtual moments.

* Remove old model, fix virtual moment handling.

* Fix T1 values.

* Fix name swap.

* Fall back to supported metric.

* Link TODOs to issues.

* Style and doc fixes.

* Add TODOs to move to cirq.google

Co-authored-by: Victory Omole <vtomole2@gmail.com>
  • Loading branch information
95-martin-orion and vtomole committed Mar 19, 2020
1 parent e151dc7 commit 76a07c7
Show file tree
Hide file tree
Showing 2 changed files with 309 additions and 38 deletions.
158 changes: 123 additions & 35 deletions cirq/contrib/noise_models/noise_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from math import exp
from typing import Dict, Sequence, TYPE_CHECKING

from cirq import devices, value, ops, protocols
Expand Down Expand Up @@ -211,54 +212,141 @@ def noisy_moment(self, moment: 'cirq.Moment',
]


class PerQubitDepolarizingNoiseModel(devices.NoiseModel):
"""DepolarizingNoiseModel which allows depolarization probabilities to be
specified separately for each qubit.
# TODO: move this to cirq.google with simple_noise_from_calibration_metrics.
# Related issue: https://github.com/quantumlib/Cirq/issues/2832
class PerQubitDepolarizingWithDampedReadoutNoiseModel(devices.NoiseModel):
"""NoiseModel with T1 decay on gates and damping/bitflip on measurement.
Similar to depol_prob in DepolarizingNoiseModel, depol_prob_map should map
Qids in the device to their depolarization probability.
With this model, T1 decay is added after all non-measurement gates, then
amplitude damping followed by bitflip error is added before all measurement
gates. Idle qubits are unaffected by this model.
As with the DepolarizingWithDampedReadoutNoiseModel, this model does not
allow a moment to contain both measurement and non-measurement gates.
"""

def __init__(
self,
depol_prob_map: Dict['cirq.Qid', float],
depol_probs: Dict['cirq.Qid', float] = None,
bitflip_probs: Dict['cirq.Qid', float] = None,
decay_probs: Dict['cirq.Qid', float] = None,
):
"""A depolarizing noise model with variable per-qubit noise.
"""A depolarizing noise model with damped readout error.
All error modes are specified on a per-qubit basis. To omit a given
error mode from the noise model, leave its dict blank when initializing
this object.
Args:
depol_prob_map: Map of depolarizing probabilities for each qubit.
depol_probs: Dict of depolarizing probabilities for each qubit.
bitflip_probs: Dict of bit-flip probabilities during measurement.
decay_probs: Dict of T1 decay probabilities during measurement.
Bitflip noise is applied first, then amplitude decay.
"""
for qubit, depol_prob in depol_prob_map.items():
value.validate_probability(depol_prob, f'depol prob of {qubit}')
self.depol_prob_map = depol_prob_map
for probs, desc in [(depol_probs, "depolarization prob"),
(bitflip_probs, "readout error prob"),
(decay_probs, "readout decay prob")]:
if probs:
for qubit, prob in probs.items():
value.validate_probability(prob, f'{desc} of {qubit}')
self.depol_probs = depol_probs
self.bitflip_probs = bitflip_probs
self.decay_probs = decay_probs

def noisy_moment(self, moment: 'cirq.Moment',
system_qubits: Sequence['cirq.Qid']):
if (_homogeneous_moment_is_measurements(moment) or
self.is_virtual_moment(moment)):
system_qubits: Sequence['cirq.Qid']) -> 'cirq.OP_TREE':
if self.is_virtual_moment(moment):
return moment
moments = []
if _homogeneous_moment_is_measurements(moment):
if self.decay_probs:
moments.append(
ops.Moment(
ops.AmplitudeDampingChannel(self.decay_probs[q])(q)
for q in system_qubits))
if self.bitflip_probs:
moments.append(
ops.Moment(
ops.BitFlipChannel(self.bitflip_probs[q])(q)
for q in system_qubits))
moments.append(moment)
return moments
else:
gated_qubits = [
q for q in system_qubits if moment.operates_on_single_qubit(q)
]
return [
moment,
ops.Moment(
ops.DepolarizingChannel(self.depol_prob_map[q])(q)
for q in gated_qubits)
]


def simple_noise_from_calibration_metrics(calibration: engine.Calibration
moments.append(moment)
if self.depol_probs:
gated_qubits = [
q for q in system_qubits
if moment.operates_on_single_qubit(q)
]
if gated_qubits:
moments.append(
ops.Moment(
ops.DepolarizingChannel(self.depol_probs[q])(q)
for q in gated_qubits))
return moments


# TODO: move this to cirq.google since it's Google-specific code.
# Related issue: https://github.com/quantumlib/Cirq/issues/2832
def simple_noise_from_calibration_metrics(calibration: engine.Calibration,
depol_noise: bool = False,
damping_noise: bool = False,
readout_decay_noise: bool = False,
readout_error_noise: bool = False
) -> devices.NoiseModel:
"""Creates a reasonable PerQubitDepolarizingNoiseModel using the provided
calibration data. This object can be retrived from the engine by calling
'get_latest_calibration()' or 'get_calibration()' using the ID of the
target processor.
"""Creates a reasonable PerQubitDepolarizingWithDampedReadoutNoiseModel
using the provided calibration data.
Args:
calibration: a Calibration object (cirq/google/engine/calibration.py).
This object can be retrived from the engine by calling
'get_latest_calibration()' or 'get_calibration()' using the ID of
the target processor.
depol_noise: Enables per-gate depolarization if True.
damping_noise: Enables per-gate amplitude damping if True.
Currently unimplemented.
readout_decay_noise: Enables pre-readout amplitude damping if True.
readout_error_noise: Enables pre-readout bitflip errors if True.
Returns:
A PerQubitDepolarizingWithDampedReadoutNoiseModel with error
probabilities generated from the provided calibration data.
"""
if not any(
[depol_noise, damping_noise, readout_decay_noise, readout_error_noise]):
raise ValueError('At least one error type must be specified.')
assert calibration is not None
rb_data: Dict['cirq.Qid', float] = {
qubit[0]: depol_prob[0] for qubit, depol_prob in
calibration['single_qubit_rb_total_error'].items()
}
return PerQubitDepolarizingNoiseModel(rb_data)
depol_probs: Dict['cirq.Qid', float] = {}
readout_decay_probs: Dict['cirq.Qid', float] = {}
readout_error_probs: Dict['cirq.Qid', float] = {}

if depol_noise:
# TODO: replace with Pauli error once it's available.
# Github issue: https://github.com/quantumlib/Cirq/issues/2832
depol_probs = {
qubit[0]: depol_prob[0] for qubit, depol_prob in
calibration['single_qubit_rb_total_error'].items()
}
if damping_noise:
# TODO: implement per-gate amplitude damping noise.
# Github issue: https://github.com/quantumlib/Cirq/issues/2807
raise NotImplementedError('Gate damping is not yet supported.')

if readout_decay_noise:
# Copied from Sycamore readout duration in known_devices.py
# TODO: replace with polling from DeviceSpecification.
# Github issue: https://github.com/quantumlib/Cirq/issues/2832
readout_micros = 1
readout_decay_probs = {
qubit[0]: 1 - exp(-1 * readout_micros / t1[0])
for qubit, t1 in calibration['single_qubit_idle_t1_micros'].items()
}
if readout_error_noise:
readout_error_probs = {
qubit[0]: err[0] for qubit, err in
calibration['single_qubit_readout_separation_error'].items()
}
return PerQubitDepolarizingWithDampedReadoutNoiseModel(
depol_probs=depol_probs,
decay_probs=readout_decay_probs,
bitflip_probs=readout_error_probs)

0 comments on commit 76a07c7

Please sign in to comment.