-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add calibration-to-noise pipeline #5187
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
ebb0ca4
8cfdf1a
c15b008
a47e87b
da727b4
10c79ee
69a2ec8
47e2212
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
# Copyright 2022 The Cirq Developers | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# https://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
|
||
"""Tools for converting Calibrations to NoiseProperties. | ||
|
||
Given a Calibration "cal", a user can simulate noise approximating that | ||
calibration using the following pipeline: | ||
|
||
noise_props = cg.noise_properties_from_calibration(cal) | ||
noise_model = cg.NoiseModelFromGoogleNoiseProperties(noise_props) | ||
simulator = cirq.Simulator(noise=noise_model) | ||
simulator.simulate(circuit) | ||
""" | ||
|
||
from typing import Dict, Tuple, Type, TYPE_CHECKING | ||
import numpy as np | ||
|
||
from cirq import ops | ||
from cirq.devices import noise_utils | ||
from cirq_google import engine | ||
from cirq_google import ops as cg_ops | ||
from cirq_google.devices import google_noise_properties | ||
|
||
if TYPE_CHECKING: | ||
import cirq | ||
|
||
|
||
def _unpack_1q_from_calibration( | ||
metric_name: str, calibration: engine.Calibration | ||
) -> Dict['cirq.Qid', float]: | ||
"""Converts a single-qubit metric from Calibration to dict format.""" | ||
if metric_name not in calibration: | ||
return {} | ||
return { | ||
engine.Calibration.key_to_qubit(key): engine.Calibration.value_to_float(val) | ||
for key, val in calibration[metric_name].items() | ||
} | ||
|
||
|
||
def _unpack_2q_from_calibration( | ||
metric_name: str, calibration: engine.Calibration | ||
) -> Dict[Tuple['cirq.Qid', ...], float]: | ||
"""Converts a two-qubit metric from Calibration to dict format.""" | ||
if metric_name not in calibration: | ||
return {} | ||
return { | ||
engine.Calibration.key_to_qubits(key): engine.Calibration.value_to_float(val) | ||
for key, val in calibration[metric_name].items() | ||
} | ||
|
||
|
||
def noise_properties_from_calibration( | ||
calibration: engine.Calibration, | ||
) -> google_noise_properties.GoogleNoiseProperties: | ||
"""Translates between a Calibration object and a NoiseProperties object. | ||
|
||
The NoiseProperties object can then be used as input to the NoiseModelFromNoiseProperties | ||
class (cirq.devices.noise_properties) to create a NoiseModel that can be used with a simulator. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Backticks for docs linking . There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
|
||
To manually override noise properties, call `override` on the output: | ||
|
||
# Set all gate durations to 37ns. | ||
noise_properties_from_calibration(cal).override(gate_times_ns=37) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: >>> There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done, but see above. |
||
|
||
See GoogleNoiseProperties for details. | ||
|
||
Args: | ||
calibration: a Calibration object with hardware metrics. | ||
""" | ||
|
||
# TODO: acquire this based on the target device. | ||
# Default map of gates to their durations. | ||
default_gate_ns: Dict[Type['cirq.Gate'], float] = { | ||
ops.ZPowGate: 25.0, | ||
ops.MeasurementGate: 4000.0, | ||
ops.ResetChannel: 250.0, | ||
ops.PhasedXZGate: 25.0, | ||
ops.FSimGate: 32.0, | ||
ops.ISwapPowGate: 32.0, | ||
ops.CZPowGate: 32.0, | ||
# ops.WaitGate is a special case. | ||
} | ||
|
||
# Unpack all values from Calibration object | ||
# 1. Extract T1 for all qubits | ||
T1_micros = _unpack_1q_from_calibration('single_qubit_idle_t1_micros', calibration) | ||
t1_ns = {q: T1_micro * 1000 for q, T1_micro in T1_micros.items()} | ||
|
||
# 2. Extract Tphi for all qubits | ||
rb_incoherent_errors = _unpack_1q_from_calibration( | ||
'single_qubit_rb_incoherent_error_per_gate', calibration | ||
) | ||
tphi_ns = {} | ||
if rb_incoherent_errors: | ||
microwave_time_ns = default_gate_ns[ops.PhasedXZGate] | ||
for qubit, q_t1_ns in t1_ns.items(): | ||
tphi_err = rb_incoherent_errors[qubit] - microwave_time_ns / (3 * q_t1_ns) | ||
q_tphi_ns = 1e10 if tphi_err <= 0 else microwave_time_ns / (3 * tphi_err) | ||
tphi_ns[qubit] = q_tphi_ns | ||
|
||
# 3a. Extract Pauli error for single-qubit gates. | ||
rb_pauli_errors = _unpack_1q_from_calibration( | ||
'single_qubit_rb_pauli_error_per_gate', calibration | ||
) | ||
gate_pauli_errors = { | ||
noise_utils.OpIdentifier(gate, q): pauli_err | ||
for q, pauli_err in rb_pauli_errors.items() | ||
for gate in google_noise_properties.SINGLE_QUBIT_GATES | ||
} | ||
|
||
# 3b. Extract Pauli error for two-qubit gates. | ||
gate_prefix_pairs: Dict[Type['cirq.Gate'], str] = { | ||
cg_ops.SycamoreGate: 'two_qubit_parallel_sycamore_gate', | ||
ops.ISwapPowGate: 'two_qubit_parallel_sqrt_iswap_gate', | ||
} | ||
for gate, prefix in gate_prefix_pairs.items(): | ||
pauli_error = _unpack_2q_from_calibration( | ||
prefix + '_xeb_pauli_error_per_cycle', calibration | ||
) | ||
gate_pauli_errors.update( | ||
{ | ||
k: v | ||
for qs, pauli_err in pauli_error.items() | ||
for k, v in { | ||
noise_utils.OpIdentifier(gate, *qs): pauli_err, | ||
noise_utils.OpIdentifier(gate, *qs[::-1]): pauli_err, | ||
}.items() | ||
} | ||
) | ||
|
||
# 4. Extract readout fidelity for all qubits. | ||
p00 = _unpack_1q_from_calibration('single_qubit_p00_error', calibration) | ||
p11 = _unpack_1q_from_calibration('single_qubit_p11_error', calibration) | ||
readout_errors = { | ||
q: np.array([p00.get(q, 0), p11.get(q, 0)]) for q in set(p00.keys()) | set(p11.keys()) | ||
} | ||
|
||
# 5. Extract entangling angle errors. | ||
fsim_errors = {} | ||
for gate, prefix in gate_prefix_pairs.items(): | ||
theta_errors = _unpack_2q_from_calibration( | ||
prefix + '_xeb_entangler_theta_error_per_cycle', | ||
calibration, | ||
) | ||
phi_errors = _unpack_2q_from_calibration( | ||
prefix + '_xeb_entangler_phi_error_per_cycle', | ||
calibration, | ||
) | ||
angle_keys = set(theta_errors.keys()) | set(phi_errors.keys()) | ||
for qubits in angle_keys: | ||
theta = theta_errors.get(qubits, 0) | ||
phi = phi_errors.get(qubits, 0) | ||
op_id = noise_utils.OpIdentifier(gate, *qubits) | ||
fsim_errors[op_id] = ops.PhasedFSimGate(theta=theta, phi=phi) | ||
op_id_reverse = noise_utils.OpIdentifier(gate, *qubits[::-1]) | ||
fsim_errors[op_id_reverse] = ops.PhasedFSimGate(theta=theta, phi=phi) | ||
|
||
# Known false positive: https://github.com/PyCQA/pylint/issues/5857 | ||
return google_noise_properties.GoogleNoiseProperties( # pylint: disable=unexpected-keyword-arg | ||
gate_times_ns=default_gate_ns, | ||
t1_ns=t1_ns, | ||
tphi_ns=tphi_ns, | ||
readout_errors=readout_errors, | ||
gate_pauli_errors=gate_pauli_errors, | ||
fsim_errors=fsim_errors, | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Don't forget >>> to show command lines and put in output lines.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, but we don't have a set standard for this - these rules are applied inconsistently throughout Cirq, and our docs don't mention them.