From 012bd1abd66399fe32cbc6f8507e5c78de0e8bc8 Mon Sep 17 00:00:00 2001 From: Orion Martin <40585662+95-martin-orion@users.noreply.github.com> Date: Fri, 1 Apr 2022 14:48:08 -0700 Subject: [PATCH 01/11] Noise props from calibration, first stage --- .../calibration_to_noise_properties.py | 132 +++++++++ .../calibration_to_noise_properties_test.py | 278 ++++++++++++++++++ 2 files changed, 410 insertions(+) create mode 100644 cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties.py create mode 100644 cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties_test.py diff --git a/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties.py b/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties.py new file mode 100644 index 00000000000..fde3129cf46 --- /dev/null +++ b/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties.py @@ -0,0 +1,132 @@ +# Copyright 2021 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. + +from typing import Dict, Tuple, Type +import numpy as np + +import cirq, cirq_google +from cirq.devices import noise_utils +from cirq_google.devices import google_noise_properties + + +def _unpack_1q_from_calibration( + metric_name: str, calibration: cirq_google.Calibration +) -> Dict[cirq.Qid, float]: + """Converts a single-qubit metric from Calibration to dict format.""" + if metric_name not in calibration: + return {} + return { + cirq_google.Calibration.key_to_qubit(key): cirq_google.Calibration.value_to_float(val) + for key, val in calibration[metric_name].items() + } + + +def _unpack_2q_from_calibration( + metric_name: str, calibration: cirq_google.Calibration +) -> Dict[Tuple[cirq.Qid, ...], float]: + """Converts a two-qubit metric from Calibration to dict format.""" + if metric_name not in calibration: + return {} + return { + cirq_google.Calibration.key_to_qubits(key): cirq_google.Calibration.value_to_float(val) + for key, val in calibration[metric_name].items() + } + + +def noise_properties_from_calibration( + calibration: cirq_google.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. + + 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] = { + cirq.ZPowGate: 25.0, + cirq.MeasurementGate: 4000.0, + cirq.ResetChannel: 250.0, + cirq.PhasedXZGate: 25.0, + cirq.FSimGate: 32.0, + cirq.ISwapPowGate: 32.0, + cirq.CZPowGate: 32.0, + # cirq.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[cirq.PhasedXZGate] + for qubit, q_t1_ns in t1_ns.items(): + tphi_err = rb_incoherent_errors[qubit] - microwave_time_ns / (3 * q_t1_ns) + if tphi_err > 0: + q_tphi_ns = microwave_time_ns / (3 * tphi_err) + else: + q_tphi_ns = 1e10 + 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. + tq_iswap_pauli_error = _unpack_2q_from_calibration( + 'two_qubit_parallel_sqrt_iswap_gate_xeb_pauli_error_per_cycle', calibration + ) + gate_pauli_errors.update( + { + k: v + for qs, pauli_err in tq_iswap_pauli_error.items() + for k, v in { + noise_utils.OpIdentifier(cirq.ISwapPowGate, *qs): pauli_err, + noise_utils.OpIdentifier(cirq.ISwapPowGate, *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()) + } + + # TODO: include entangling errors once provided by QCS. + # These must also be accounted for in Pauli error. + + return google_noise_properties.GoogleNoiseProperties( + gate_times_ns=DEFAULT_GATE_NS, + t1_ns=t1_ns, + tphi_ns=tphi_ns, + readout_errors=readout_errors, + gate_pauli_errors=gate_pauli_errors, + ) diff --git a/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties_test.py b/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties_test.py new file mode 100644 index 00000000000..1cb8c7322ae --- /dev/null +++ b/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties_test.py @@ -0,0 +1,278 @@ +# Copyright 2021 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. + +import cirq, cirq_google +from cirq_google.api import v2 +from cirq_google.experimental.noise_models.calibration_to_noise_properties import ( + noise_properties_from_calibration, +) +from cirq.devices.noise_utils import ( + OpIdentifier, +) +from google.protobuf.text_format import Merge +import numpy as np +import pytest + + +def test_noise_properties_from_calibration(): + qubits = [cirq.GridQubit(0, 0), cirq.GridQubit(0, 1), cirq.GridQubit(1, 0)] + pauli_error = [0.001, 0.002, 0.003] + incoherent_error = [0.0001, 0.0002, 0.0003] + p00_error = [0.004, 0.005, 0.006] + p11_error = [0.007, 0.008, 0.009] + t1_micros = [10, 20, 30] + syc_pauli = [0.01, 0.02] + iswap_pauli = [0.03, 0.04] + + _CALIBRATION_DATA = Merge( + f""" + timestamp_ms: 1579214873, + metrics: [{{ + name: 'single_qubit_rb_pauli_error_per_gate', + targets: ['0_0'], + values: [{{ + double_val: {pauli_error[0]} + }}] + }}, {{ + name: 'single_qubit_rb_pauli_error_per_gate', + targets: ['0_1'], + values: [{{ + double_val:{pauli_error[1]} + }}] + }}, {{ + name: 'single_qubit_rb_pauli_error_per_gate', + targets: ['1_0'], + values: [{{ + double_val:{pauli_error[2]} + }}] + }}, {{ + name: 'single_qubit_rb_incoherent_error_per_gate', + targets: ['0_0'], + values: [{{ + double_val: {incoherent_error[0]} + }}] + }}, {{ + name: 'single_qubit_rb_incoherent_error_per_gate', + targets: ['0_1'], + values: [{{ + double_val:{incoherent_error[1]} + }}] + }}, {{ + name: 'single_qubit_rb_incoherent_error_per_gate', + targets: ['1_0'], + values: [{{ + double_val:{incoherent_error[2]} + }}] + }}, {{ + name: 'single_qubit_p00_error', + targets: ['0_0'], + values: [{{ + double_val: {p00_error[0]} + }}] + }}, {{ + name: 'single_qubit_p00_error', + targets: ['0_1'], + values: [{{ + double_val: {p00_error[1]} + }}] + }}, {{ + name: 'single_qubit_p00_error', + targets: ['1_0'], + values: [{{ + double_val: {p00_error[2]} + }}] + }}, {{ + name: 'single_qubit_p11_error', + targets: ['0_0'], + values: [{{ + double_val: {p11_error[0]} + }}] + }}, {{ + name: 'single_qubit_p11_error', + targets: ['0_1'], + values: [{{ + double_val: {p11_error[1]} + }}] + }}, {{ + name: 'single_qubit_p11_error', + targets: ['1_0'], + values: [{{ + double_val: {p11_error[2]} + }}] + }}, {{ + name: 'single_qubit_idle_t1_micros', + targets: ['0_0'], + values: [{{ + double_val: {t1_micros[0]} + }}] + }}, {{ + name: 'single_qubit_idle_t1_micros', + targets: ['0_1'], + values: [{{ + double_val: {t1_micros[1]} + }}] + }}, {{ + name: 'single_qubit_idle_t1_micros', + targets: ['1_0'], + values: [{{ + double_val: {t1_micros[2]} + }}] + }}, {{ + name: 'two_qubit_parallel_sycamore_gate_xeb_pauli_error_per_cycle', + targets: ['0_0', '0_1'], + values: [{{ + double_val: {syc_pauli[0]} + }}] + }}, {{ + name: 'two_qubit_parallel_sycamore_gate_xeb_pauli_error_per_cycle', + targets: ['0_0', '1_0'], + values: [{{ + double_val: {syc_pauli[1]} + }}] + }}, {{ + name: 'two_qubit_parallel_sqrt_iswap_gate_xeb_pauli_error_per_cycle', + targets: ['0_0', '0_1'], + values: [{{ + double_val: {iswap_pauli[0]} + }}] + }}, {{ + name: 'two_qubit_parallel_sqrt_iswap_gate_xeb_pauli_error_per_cycle', + targets: ['0_0', '1_0'], + values: [{{ + double_val: {iswap_pauli[1]} + }}] + }}] +""", + v2.metrics_pb2.MetricsSnapshot(), + ) + + # Create NoiseProperties object from Calibration + calibration = cirq_google.Calibration(_CALIBRATION_DATA) + prop = noise_properties_from_calibration(calibration) + + for i, q in enumerate(qubits): + assert np.isclose( + prop.gate_pauli_errors[OpIdentifier(cirq.PhasedXZGate, q)], pauli_error[i] + ) + assert np.allclose(prop.readout_errors[q], np.array([p00_error[i], p11_error[i]])) + assert np.isclose(prop.t1_ns[q], t1_micros[i] * 1000) + # TODO: test Tphi + microwave_time_ns = 25.0 + tphi_err = incoherent_error[i] - microwave_time_ns / (3 * prop.t1_ns[q]) + if tphi_err > 0: + tphi_ns = microwave_time_ns / (3 * tphi_err) + else: + tphi_ns = 1e10 + assert prop.tphi_ns[q] == tphi_ns + + qubit_pairs = [(qubits[0], qubits[1]), (qubits[0], qubits[2])] + for i, qs in enumerate(qubit_pairs): + assert np.isclose( + prop.gate_pauli_errors[OpIdentifier(cirq.ISwapPowGate, *qs)], iswap_pauli[i] + ) + assert np.isclose( + prop.gate_pauli_errors[OpIdentifier(cirq.ISwapPowGate, *qs[::-1])], iswap_pauli[i] + ) + + +def test_incomplete_calibration(): + pauli_error = [0.001, 0.002, 0.003] + p00_error = [0.004, 0.005, 0.006] + p11_error = [0.007, 0.008, 0.009] + t1_micros = [10, 20, 30] + + _CALIBRATION_DATA = Merge( + f""" + timestamp_ms: 1579214873, + metrics: [{{ + name: 'single_qubit_rb_pauli_error_per_gate', + targets: ['0_0'], + values: [{{ + double_val: {pauli_error[0]} + }}] + }}, {{ + name: 'single_qubit_rb_pauli_error_per_gate', + targets: ['0_1'], + values: [{{ + double_val:{pauli_error[1]} + }}] + }}, {{ + name: 'single_qubit_rb_pauli_error_per_gate', + targets: ['1_0'], + values: [{{ + double_val:{pauli_error[2]} + }}] + }}, {{ + name: 'single_qubit_p00_error', + targets: ['0_0'], + values: [{{ + double_val: {p00_error[0]} + }}] + }}, {{ + name: 'single_qubit_p00_error', + targets: ['0_1'], + values: [{{ + double_val: {p00_error[1]} + }}] + }}, {{ + name: 'single_qubit_p00_error', + targets: ['1_0'], + values: [{{ + double_val: {p00_error[2]} + }}] + }}, {{ + name: 'single_qubit_p11_error', + targets: ['0_0'], + values: [{{ + double_val: {p11_error[0]} + }}] + }}, {{ + name: 'single_qubit_p11_error', + targets: ['0_1'], + values: [{{ + double_val: {p11_error[1]} + }}] + }}, {{ + name: 'single_qubit_p11_error', + targets: ['1_0'], + values: [{{ + double_val: {p11_error[2]} + }}] + }}, {{ + name: 'single_qubit_idle_t1_micros', + targets: ['0_0'], + values: [{{ + double_val: {t1_micros[0]} + }}] + }}, {{ + name: 'single_qubit_idle_t1_micros', + targets: ['0_1'], + values: [{{ + double_val: {t1_micros[1]} + }}] + }}, {{ + name: 'single_qubit_idle_t1_micros', + targets: ['1_0'], + values: [{{ + double_val: {t1_micros[2]} + }}] + }}] +""", + v2.metrics_pb2.MetricsSnapshot(), + ) + + # Create NoiseProperties object from Calibration + calibration = cirq_google.Calibration(_CALIBRATION_DATA) + with pytest.raises(ValueError, match='Keys specified for T1 and Tphi are not identical.'): + _ = noise_properties_from_calibration(calibration) From a644bd357c965b08d61b94ed0e68b2b2b335cd6e Mon Sep 17 00:00:00 2001 From: Orion Martin <40585662+95-martin-orion@users.noreply.github.com> Date: Mon, 4 Apr 2022 08:53:00 -0700 Subject: [PATCH 02/11] Entangling errors --- .../calibration_to_noise_properties.py | 65 +++++++++++---- .../calibration_to_noise_properties_test.py | 79 +++++++++++++++++-- 2 files changed, 122 insertions(+), 22 deletions(-) diff --git a/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties.py b/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties.py index fde3129cf46..b69fa357c87 100644 --- a/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties.py +++ b/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties.py @@ -48,11 +48,19 @@ def noise_properties_from_calibration( calibration: cirq_google.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. + 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) + + See GoogleNoiseProperties for details. + Args: - calibration: a Calibration object with hardware metrics + calibration: a Calibration object with hardware metrics. """ # TODO: acquire this based on the target device. @@ -99,19 +107,23 @@ class (cirq.devices.noise_properties) to create a NoiseModel that can be used wi } # 3b. Extract Pauli error for two-qubit gates. - tq_iswap_pauli_error = _unpack_2q_from_calibration( - 'two_qubit_parallel_sqrt_iswap_gate_xeb_pauli_error_per_cycle', calibration - ) - gate_pauli_errors.update( - { - k: v - for qs, pauli_err in tq_iswap_pauli_error.items() - for k, v in { - noise_utils.OpIdentifier(cirq.ISwapPowGate, *qs): pauli_err, - noise_utils.OpIdentifier(cirq.ISwapPowGate, *qs[::-1]): pauli_err, - }.items() - } - ) + for gate, prefix in [ + (cirq_google.SycamoreGate, 'two_qubit_parallel_sycamore_gate'), + (cirq.ISwapPowGate, 'two_qubit_parallel_sqrt_iswap_gate'), + ]: + 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) @@ -120,8 +132,28 @@ class (cirq.devices.noise_properties) to create a NoiseModel that can be used wi q: np.array([p00.get(q, 0), p11.get(q, 0)]) for q in set(p00.keys()) | set(p11.keys()) } - # TODO: include entangling errors once provided by QCS. - # These must also be accounted for in Pauli error. + # 5. Extract entangling angle errors. + fsim_errors = {} + for gate, prefix in [ + (cirq_google.SycamoreGate, 'two_qubit_parallel_sycamore_gate'), + (cirq.ISwapPowGate, 'two_qubit_parallel_sqrt_iswap_gate'), + ]: + 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] = cirq.PhasedFSimGate(theta=theta, phi=phi) + op_id_reverse = noise_utils.OpIdentifier(gate, *qubits[::-1]) + fsim_errors[op_id_reverse] = cirq.PhasedFSimGate(theta=theta, phi=phi) return google_noise_properties.GoogleNoiseProperties( gate_times_ns=DEFAULT_GATE_NS, @@ -129,4 +161,5 @@ class (cirq.devices.noise_properties) to create a NoiseModel that can be used wi tphi_ns=tphi_ns, readout_errors=readout_errors, gate_pauli_errors=gate_pauli_errors, + fsim_errors=fsim_errors, ) diff --git a/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties_test.py b/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties_test.py index 1cb8c7322ae..9a7fca399d8 100644 --- a/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties_test.py +++ b/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties_test.py @@ -34,6 +34,14 @@ def test_noise_properties_from_calibration(): t1_micros = [10, 20, 30] syc_pauli = [0.01, 0.02] iswap_pauli = [0.03, 0.04] + syc_angles = [ + cirq.PhasedFSimGate(theta=0.011, phi=-0.021), + cirq.PhasedFSimGate(theta=-0.012, phi=0.022), + ] + iswap_angles = [ + cirq.PhasedFSimGate(theta=-0.013, phi=0.023), + cirq.PhasedFSimGate(theta=0.014, phi=-0.024), + ] _CALIBRATION_DATA = Merge( f""" @@ -152,6 +160,54 @@ def test_noise_properties_from_calibration(): values: [{{ double_val: {iswap_pauli[1]} }}] + }}, {{ + name: 'two_qubit_parallel_sycamore_gate_xeb_entangler_theta_error_per_cycle', + targets: ['0_0', '0_1'], + values: [{{ + double_val: {syc_angles[0].theta} + }}] + }}, {{ + name: 'two_qubit_parallel_sycamore_gate_xeb_entangler_theta_error_per_cycle', + targets: ['0_0', '1_0'], + values: [{{ + double_val: {syc_angles[1].theta} + }}] + }}, {{ + name: 'two_qubit_parallel_sqrt_iswap_gate_xeb_entangler_theta_error_per_cycle', + targets: ['0_0', '0_1'], + values: [{{ + double_val: {iswap_angles[0].theta} + }}] + }}, {{ + name: 'two_qubit_parallel_sqrt_iswap_gate_xeb_entangler_theta_error_per_cycle', + targets: ['0_0', '1_0'], + values: [{{ + double_val: {iswap_angles[1].theta} + }}] + }}, {{ + name: 'two_qubit_parallel_sycamore_gate_xeb_entangler_phi_error_per_cycle', + targets: ['0_0', '0_1'], + values: [{{ + double_val: {syc_angles[0].phi} + }}] + }}, {{ + name: 'two_qubit_parallel_sycamore_gate_xeb_entangler_phi_error_per_cycle', + targets: ['0_0', '1_0'], + values: [{{ + double_val: {syc_angles[1].phi} + }}] + }}, {{ + name: 'two_qubit_parallel_sqrt_iswap_gate_xeb_entangler_phi_error_per_cycle', + targets: ['0_0', '0_1'], + values: [{{ + double_val: {iswap_angles[0].phi} + }}] + }}, {{ + name: 'two_qubit_parallel_sqrt_iswap_gate_xeb_entangler_phi_error_per_cycle', + targets: ['0_0', '1_0'], + values: [{{ + double_val: {iswap_angles[1].phi} + }}] }}] """, v2.metrics_pb2.MetricsSnapshot(), @@ -178,12 +234,23 @@ def test_noise_properties_from_calibration(): qubit_pairs = [(qubits[0], qubits[1]), (qubits[0], qubits[2])] for i, qs in enumerate(qubit_pairs): - assert np.isclose( - prop.gate_pauli_errors[OpIdentifier(cirq.ISwapPowGate, *qs)], iswap_pauli[i] - ) - assert np.isclose( - prop.gate_pauli_errors[OpIdentifier(cirq.ISwapPowGate, *qs[::-1])], iswap_pauli[i] - ) + for gate, values in [ + (cirq_google.SycamoreGate, syc_pauli), + (cirq.ISwapPowGate, iswap_pauli), + ]: + assert np.isclose(prop.gate_pauli_errors[OpIdentifier(gate, *qs)], values[i]) + assert np.isclose(prop.gate_pauli_errors[OpIdentifier(gate, *qs[::-1])], values[i]) + assert np.isclose(prop.gate_pauli_errors[OpIdentifier(gate, *qs)], values[i]) + assert np.isclose(prop.gate_pauli_errors[OpIdentifier(gate, *qs[::-1])], values[i]) + + for gate, values in [ + (cirq_google.SycamoreGate, syc_angles), + (cirq.ISwapPowGate, iswap_angles), + ]: + assert prop.fsim_errors[OpIdentifier(gate, *qs)] == values[i] + assert prop.fsim_errors[OpIdentifier(gate, *qs[::-1])] == values[i] + assert prop.fsim_errors[OpIdentifier(gate, *qs)] == values[i] + assert prop.fsim_errors[OpIdentifier(gate, *qs[::-1])] == values[i] def test_incomplete_calibration(): From 4b35e837d6d9977b8b952bf004443bb560a38048 Mon Sep 17 00:00:00 2001 From: Orion Martin <40585662+95-martin-orion@users.noreply.github.com> Date: Mon, 4 Apr 2022 09:22:47 -0700 Subject: [PATCH 03/11] Format and enforce typing. --- .../calibration_to_noise_properties.py | 17 ++++++++--------- .../calibration_to_noise_properties_test.py | 1 - 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties.py b/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties.py index b69fa357c87..49cc87c5634 100644 --- a/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties.py +++ b/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties.py @@ -107,10 +107,11 @@ class (cirq.devices.noise_properties) to create a NoiseModel that can be used wi } # 3b. Extract Pauli error for two-qubit gates. - for gate, prefix in [ - (cirq_google.SycamoreGate, 'two_qubit_parallel_sycamore_gate'), - (cirq.ISwapPowGate, 'two_qubit_parallel_sqrt_iswap_gate'), - ]: + gate_prefix_pairs: Dict[Type[cirq.Gate], str] = { + cirq_google.SycamoreGate: 'two_qubit_parallel_sycamore_gate', + cirq.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 ) @@ -134,10 +135,7 @@ class (cirq.devices.noise_properties) to create a NoiseModel that can be used wi # 5. Extract entangling angle errors. fsim_errors = {} - for gate, prefix in [ - (cirq_google.SycamoreGate, 'two_qubit_parallel_sycamore_gate'), - (cirq.ISwapPowGate, 'two_qubit_parallel_sqrt_iswap_gate'), - ]: + for gate, prefix in gate_prefix_pairs.items(): theta_errors = _unpack_2q_from_calibration( prefix + '_xeb_entangler_theta_error_per_cycle', calibration, @@ -155,7 +153,8 @@ class (cirq.devices.noise_properties) to create a NoiseModel that can be used wi op_id_reverse = noise_utils.OpIdentifier(gate, *qubits[::-1]) fsim_errors[op_id_reverse] = cirq.PhasedFSimGate(theta=theta, phi=phi) - return google_noise_properties.GoogleNoiseProperties( + # 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, diff --git a/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties_test.py b/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties_test.py index 9a7fca399d8..41a3d0b570a 100644 --- a/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties_test.py +++ b/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties_test.py @@ -223,7 +223,6 @@ def test_noise_properties_from_calibration(): ) assert np.allclose(prop.readout_errors[q], np.array([p00_error[i], p11_error[i]])) assert np.isclose(prop.t1_ns[q], t1_micros[i] * 1000) - # TODO: test Tphi microwave_time_ns = 25.0 tphi_err = incoherent_error[i] - microwave_time_ns / (3 * prop.t1_ns[q]) if tphi_err > 0: From 8b1333ad2c3076e3f4feb44c0cd875405cebf066 Mon Sep 17 00:00:00 2001 From: Orion Martin <40585662+95-martin-orion@users.noreply.github.com> Date: Mon, 4 Apr 2022 09:55:18 -0700 Subject: [PATCH 04/11] formatting linter comments... --- .../noise_models/calibration_to_noise_properties.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties.py b/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties.py index 49cc87c5634..3dff4cc010d 100644 --- a/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties.py +++ b/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties.py @@ -154,7 +154,7 @@ class (cirq.devices.noise_properties) to create a NoiseModel that can be used wi fsim_errors[op_id_reverse] = cirq.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 + return google_noise_properties.GoogleNoiseProperties( # pylint: disable=unexpected-keyword-arg gate_times_ns=DEFAULT_GATE_NS, t1_ns=t1_ns, tphi_ns=tphi_ns, From 62dd5b090b40f9e762035cfb03cf65c2902146cc Mon Sep 17 00:00:00 2001 From: Orion Martin <40585662+95-martin-orion@users.noreply.github.com> Date: Mon, 4 Apr 2022 15:41:56 -0700 Subject: [PATCH 05/11] Bundle of nits --- .../calibration_to_noise_properties.py | 74 +++++++++++-------- 1 file changed, 44 insertions(+), 30 deletions(-) diff --git a/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties.py b/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties.py index 3dff4cc010d..9dcc4ea3860 100644 --- a/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties.py +++ b/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties.py @@ -1,4 +1,4 @@ -# Copyright 2021 The Cirq Developers +# 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. @@ -12,40 +12,57 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Dict, Tuple, Type + +"""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 -import cirq, cirq_google +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: cirq_google.Calibration -) -> Dict[cirq.Qid, float]: + 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 { - cirq_google.Calibration.key_to_qubit(key): cirq_google.Calibration.value_to_float(val) + 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: cirq_google.Calibration -) -> Dict[Tuple[cirq.Qid, ...], float]: + 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 { - cirq_google.Calibration.key_to_qubits(key): cirq_google.Calibration.value_to_float(val) + 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: cirq_google.Calibration, + calibration: engine.Calibration, ) -> google_noise_properties.GoogleNoiseProperties: """Translates between a Calibration object and a NoiseProperties object. @@ -65,15 +82,15 @@ class (cirq.devices.noise_properties) to create a NoiseModel that can be used wi # TODO: acquire this based on the target device. # Default map of gates to their durations. - DEFAULT_GATE_NS: Dict[Type['cirq.Gate'], float] = { - cirq.ZPowGate: 25.0, - cirq.MeasurementGate: 4000.0, - cirq.ResetChannel: 250.0, - cirq.PhasedXZGate: 25.0, - cirq.FSimGate: 32.0, - cirq.ISwapPowGate: 32.0, - cirq.CZPowGate: 32.0, - # cirq.WaitGate is a special case. + 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 @@ -87,13 +104,10 @@ class (cirq.devices.noise_properties) to create a NoiseModel that can be used wi ) tphi_ns = {} if rb_incoherent_errors: - microwave_time_ns = DEFAULT_GATE_NS[cirq.PhasedXZGate] + 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) - if tphi_err > 0: - q_tphi_ns = microwave_time_ns / (3 * tphi_err) - else: - q_tphi_ns = 1e10 + 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. @@ -107,9 +121,9 @@ class (cirq.devices.noise_properties) to create a NoiseModel that can be used wi } # 3b. Extract Pauli error for two-qubit gates. - gate_prefix_pairs: Dict[Type[cirq.Gate], str] = { - cirq_google.SycamoreGate: 'two_qubit_parallel_sycamore_gate', - cirq.ISwapPowGate: 'two_qubit_parallel_sqrt_iswap_gate', + 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( @@ -149,13 +163,13 @@ class (cirq.devices.noise_properties) to create a NoiseModel that can be used wi theta = theta_errors.get(qubits, 0) phi = phi_errors.get(qubits, 0) op_id = noise_utils.OpIdentifier(gate, *qubits) - fsim_errors[op_id] = cirq.PhasedFSimGate(theta=theta, phi=phi) + fsim_errors[op_id] = ops.PhasedFSimGate(theta=theta, phi=phi) op_id_reverse = noise_utils.OpIdentifier(gate, *qubits[::-1]) - fsim_errors[op_id_reverse] = cirq.PhasedFSimGate(theta=theta, phi=phi) + 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, + gate_times_ns=default_gate_ns, t1_ns=t1_ns, tphi_ns=tphi_ns, readout_errors=readout_errors, From 7de9c6b34e5941ec80093487d5fa79f024f8a9ae Mon Sep 17 00:00:00 2001 From: Orion Martin <40585662+95-martin-orion@users.noreply.github.com> Date: Wed, 6 Apr 2022 09:32:40 -0700 Subject: [PATCH 06/11] Move to engine and clean imports. --- .../calibration_to_noise_properties.py | 178 --------- .../calibration_to_noise_properties_test.py | 344 ------------------ 2 files changed, 522 deletions(-) delete mode 100644 cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties.py delete mode 100644 cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties_test.py diff --git a/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties.py b/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties.py deleted file mode 100644 index 9dcc4ea3860..00000000000 --- a/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties.py +++ /dev/null @@ -1,178 +0,0 @@ -# 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. - - 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) - - 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, - ) diff --git a/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties_test.py b/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties_test.py deleted file mode 100644 index 41a3d0b570a..00000000000 --- a/cirq-google/cirq_google/experimental/noise_models/calibration_to_noise_properties_test.py +++ /dev/null @@ -1,344 +0,0 @@ -# Copyright 2021 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. - -import cirq, cirq_google -from cirq_google.api import v2 -from cirq_google.experimental.noise_models.calibration_to_noise_properties import ( - noise_properties_from_calibration, -) -from cirq.devices.noise_utils import ( - OpIdentifier, -) -from google.protobuf.text_format import Merge -import numpy as np -import pytest - - -def test_noise_properties_from_calibration(): - qubits = [cirq.GridQubit(0, 0), cirq.GridQubit(0, 1), cirq.GridQubit(1, 0)] - pauli_error = [0.001, 0.002, 0.003] - incoherent_error = [0.0001, 0.0002, 0.0003] - p00_error = [0.004, 0.005, 0.006] - p11_error = [0.007, 0.008, 0.009] - t1_micros = [10, 20, 30] - syc_pauli = [0.01, 0.02] - iswap_pauli = [0.03, 0.04] - syc_angles = [ - cirq.PhasedFSimGate(theta=0.011, phi=-0.021), - cirq.PhasedFSimGate(theta=-0.012, phi=0.022), - ] - iswap_angles = [ - cirq.PhasedFSimGate(theta=-0.013, phi=0.023), - cirq.PhasedFSimGate(theta=0.014, phi=-0.024), - ] - - _CALIBRATION_DATA = Merge( - f""" - timestamp_ms: 1579214873, - metrics: [{{ - name: 'single_qubit_rb_pauli_error_per_gate', - targets: ['0_0'], - values: [{{ - double_val: {pauli_error[0]} - }}] - }}, {{ - name: 'single_qubit_rb_pauli_error_per_gate', - targets: ['0_1'], - values: [{{ - double_val:{pauli_error[1]} - }}] - }}, {{ - name: 'single_qubit_rb_pauli_error_per_gate', - targets: ['1_0'], - values: [{{ - double_val:{pauli_error[2]} - }}] - }}, {{ - name: 'single_qubit_rb_incoherent_error_per_gate', - targets: ['0_0'], - values: [{{ - double_val: {incoherent_error[0]} - }}] - }}, {{ - name: 'single_qubit_rb_incoherent_error_per_gate', - targets: ['0_1'], - values: [{{ - double_val:{incoherent_error[1]} - }}] - }}, {{ - name: 'single_qubit_rb_incoherent_error_per_gate', - targets: ['1_0'], - values: [{{ - double_val:{incoherent_error[2]} - }}] - }}, {{ - name: 'single_qubit_p00_error', - targets: ['0_0'], - values: [{{ - double_val: {p00_error[0]} - }}] - }}, {{ - name: 'single_qubit_p00_error', - targets: ['0_1'], - values: [{{ - double_val: {p00_error[1]} - }}] - }}, {{ - name: 'single_qubit_p00_error', - targets: ['1_0'], - values: [{{ - double_val: {p00_error[2]} - }}] - }}, {{ - name: 'single_qubit_p11_error', - targets: ['0_0'], - values: [{{ - double_val: {p11_error[0]} - }}] - }}, {{ - name: 'single_qubit_p11_error', - targets: ['0_1'], - values: [{{ - double_val: {p11_error[1]} - }}] - }}, {{ - name: 'single_qubit_p11_error', - targets: ['1_0'], - values: [{{ - double_val: {p11_error[2]} - }}] - }}, {{ - name: 'single_qubit_idle_t1_micros', - targets: ['0_0'], - values: [{{ - double_val: {t1_micros[0]} - }}] - }}, {{ - name: 'single_qubit_idle_t1_micros', - targets: ['0_1'], - values: [{{ - double_val: {t1_micros[1]} - }}] - }}, {{ - name: 'single_qubit_idle_t1_micros', - targets: ['1_0'], - values: [{{ - double_val: {t1_micros[2]} - }}] - }}, {{ - name: 'two_qubit_parallel_sycamore_gate_xeb_pauli_error_per_cycle', - targets: ['0_0', '0_1'], - values: [{{ - double_val: {syc_pauli[0]} - }}] - }}, {{ - name: 'two_qubit_parallel_sycamore_gate_xeb_pauli_error_per_cycle', - targets: ['0_0', '1_0'], - values: [{{ - double_val: {syc_pauli[1]} - }}] - }}, {{ - name: 'two_qubit_parallel_sqrt_iswap_gate_xeb_pauli_error_per_cycle', - targets: ['0_0', '0_1'], - values: [{{ - double_val: {iswap_pauli[0]} - }}] - }}, {{ - name: 'two_qubit_parallel_sqrt_iswap_gate_xeb_pauli_error_per_cycle', - targets: ['0_0', '1_0'], - values: [{{ - double_val: {iswap_pauli[1]} - }}] - }}, {{ - name: 'two_qubit_parallel_sycamore_gate_xeb_entangler_theta_error_per_cycle', - targets: ['0_0', '0_1'], - values: [{{ - double_val: {syc_angles[0].theta} - }}] - }}, {{ - name: 'two_qubit_parallel_sycamore_gate_xeb_entangler_theta_error_per_cycle', - targets: ['0_0', '1_0'], - values: [{{ - double_val: {syc_angles[1].theta} - }}] - }}, {{ - name: 'two_qubit_parallel_sqrt_iswap_gate_xeb_entangler_theta_error_per_cycle', - targets: ['0_0', '0_1'], - values: [{{ - double_val: {iswap_angles[0].theta} - }}] - }}, {{ - name: 'two_qubit_parallel_sqrt_iswap_gate_xeb_entangler_theta_error_per_cycle', - targets: ['0_0', '1_0'], - values: [{{ - double_val: {iswap_angles[1].theta} - }}] - }}, {{ - name: 'two_qubit_parallel_sycamore_gate_xeb_entangler_phi_error_per_cycle', - targets: ['0_0', '0_1'], - values: [{{ - double_val: {syc_angles[0].phi} - }}] - }}, {{ - name: 'two_qubit_parallel_sycamore_gate_xeb_entangler_phi_error_per_cycle', - targets: ['0_0', '1_0'], - values: [{{ - double_val: {syc_angles[1].phi} - }}] - }}, {{ - name: 'two_qubit_parallel_sqrt_iswap_gate_xeb_entangler_phi_error_per_cycle', - targets: ['0_0', '0_1'], - values: [{{ - double_val: {iswap_angles[0].phi} - }}] - }}, {{ - name: 'two_qubit_parallel_sqrt_iswap_gate_xeb_entangler_phi_error_per_cycle', - targets: ['0_0', '1_0'], - values: [{{ - double_val: {iswap_angles[1].phi} - }}] - }}] -""", - v2.metrics_pb2.MetricsSnapshot(), - ) - - # Create NoiseProperties object from Calibration - calibration = cirq_google.Calibration(_CALIBRATION_DATA) - prop = noise_properties_from_calibration(calibration) - - for i, q in enumerate(qubits): - assert np.isclose( - prop.gate_pauli_errors[OpIdentifier(cirq.PhasedXZGate, q)], pauli_error[i] - ) - assert np.allclose(prop.readout_errors[q], np.array([p00_error[i], p11_error[i]])) - assert np.isclose(prop.t1_ns[q], t1_micros[i] * 1000) - microwave_time_ns = 25.0 - tphi_err = incoherent_error[i] - microwave_time_ns / (3 * prop.t1_ns[q]) - if tphi_err > 0: - tphi_ns = microwave_time_ns / (3 * tphi_err) - else: - tphi_ns = 1e10 - assert prop.tphi_ns[q] == tphi_ns - - qubit_pairs = [(qubits[0], qubits[1]), (qubits[0], qubits[2])] - for i, qs in enumerate(qubit_pairs): - for gate, values in [ - (cirq_google.SycamoreGate, syc_pauli), - (cirq.ISwapPowGate, iswap_pauli), - ]: - assert np.isclose(prop.gate_pauli_errors[OpIdentifier(gate, *qs)], values[i]) - assert np.isclose(prop.gate_pauli_errors[OpIdentifier(gate, *qs[::-1])], values[i]) - assert np.isclose(prop.gate_pauli_errors[OpIdentifier(gate, *qs)], values[i]) - assert np.isclose(prop.gate_pauli_errors[OpIdentifier(gate, *qs[::-1])], values[i]) - - for gate, values in [ - (cirq_google.SycamoreGate, syc_angles), - (cirq.ISwapPowGate, iswap_angles), - ]: - assert prop.fsim_errors[OpIdentifier(gate, *qs)] == values[i] - assert prop.fsim_errors[OpIdentifier(gate, *qs[::-1])] == values[i] - assert prop.fsim_errors[OpIdentifier(gate, *qs)] == values[i] - assert prop.fsim_errors[OpIdentifier(gate, *qs[::-1])] == values[i] - - -def test_incomplete_calibration(): - pauli_error = [0.001, 0.002, 0.003] - p00_error = [0.004, 0.005, 0.006] - p11_error = [0.007, 0.008, 0.009] - t1_micros = [10, 20, 30] - - _CALIBRATION_DATA = Merge( - f""" - timestamp_ms: 1579214873, - metrics: [{{ - name: 'single_qubit_rb_pauli_error_per_gate', - targets: ['0_0'], - values: [{{ - double_val: {pauli_error[0]} - }}] - }}, {{ - name: 'single_qubit_rb_pauli_error_per_gate', - targets: ['0_1'], - values: [{{ - double_val:{pauli_error[1]} - }}] - }}, {{ - name: 'single_qubit_rb_pauli_error_per_gate', - targets: ['1_0'], - values: [{{ - double_val:{pauli_error[2]} - }}] - }}, {{ - name: 'single_qubit_p00_error', - targets: ['0_0'], - values: [{{ - double_val: {p00_error[0]} - }}] - }}, {{ - name: 'single_qubit_p00_error', - targets: ['0_1'], - values: [{{ - double_val: {p00_error[1]} - }}] - }}, {{ - name: 'single_qubit_p00_error', - targets: ['1_0'], - values: [{{ - double_val: {p00_error[2]} - }}] - }}, {{ - name: 'single_qubit_p11_error', - targets: ['0_0'], - values: [{{ - double_val: {p11_error[0]} - }}] - }}, {{ - name: 'single_qubit_p11_error', - targets: ['0_1'], - values: [{{ - double_val: {p11_error[1]} - }}] - }}, {{ - name: 'single_qubit_p11_error', - targets: ['1_0'], - values: [{{ - double_val: {p11_error[2]} - }}] - }}, {{ - name: 'single_qubit_idle_t1_micros', - targets: ['0_0'], - values: [{{ - double_val: {t1_micros[0]} - }}] - }}, {{ - name: 'single_qubit_idle_t1_micros', - targets: ['0_1'], - values: [{{ - double_val: {t1_micros[1]} - }}] - }}, {{ - name: 'single_qubit_idle_t1_micros', - targets: ['1_0'], - values: [{{ - double_val: {t1_micros[2]} - }}] - }}] -""", - v2.metrics_pb2.MetricsSnapshot(), - ) - - # Create NoiseProperties object from Calibration - calibration = cirq_google.Calibration(_CALIBRATION_DATA) - with pytest.raises(ValueError, match='Keys specified for T1 and Tphi are not identical.'): - _ = noise_properties_from_calibration(calibration) From 51c312efc4207f6e781d65259ed698dffd0c1244 Mon Sep 17 00:00:00 2001 From: Orion Martin <40585662+95-martin-orion@users.noreply.github.com> Date: Wed, 6 Apr 2022 09:46:03 -0700 Subject: [PATCH 07/11] Surface to top level and support JSON serialization --- cirq-google/cirq_google/__init__.py | 3 + cirq-google/cirq_google/devices/__init__.py | 20 +- cirq-google/cirq_google/engine/__init__.py | 89 ++- .../calibration_to_noise_properties_test.py | 5 +- .../cirq_google/json_resolver_cache.py | 1 + .../json_test_data/GoogleNoiseProperties.json | 653 ++++++++++++++++++ .../json_test_data/GoogleNoiseProperties.repr | 59 ++ .../cirq_google/json_test_data/spec.py | 1 + 8 files changed, 804 insertions(+), 27 deletions(-) create mode 100644 cirq-google/cirq_google/json_test_data/GoogleNoiseProperties.json create mode 100644 cirq-google/cirq_google/json_test_data/GoogleNoiseProperties.repr diff --git a/cirq-google/cirq_google/__init__.py b/cirq-google/cirq_google/__init__.py index 04c0f23119f..59374012570 100644 --- a/cirq-google/cirq_google/__init__.py +++ b/cirq-google/cirq_google/__init__.py @@ -57,6 +57,8 @@ from cirq_google.devices import ( Bristlecone, Foxtail, + GoogleNoiseProperties, + NoiseModelFromGoogleNoiseProperties, SerializableDevice, Sycamore, Sycamore23, @@ -78,6 +80,7 @@ get_engine_calibration, get_engine_device, get_engine_sampler, + noise_properties_from_calibration, ) from cirq_google.line import ( diff --git a/cirq-google/cirq_google/devices/__init__.py b/cirq-google/cirq_google/devices/__init__.py index ac295331171..2ba59d16b53 100644 --- a/cirq-google/cirq_google/devices/__init__.py +++ b/cirq-google/cirq_google/devices/__init__.py @@ -12,8 +12,22 @@ # See the License for the specific language governing permissions and # limitations under the License. -from cirq_google.devices.known_devices import Bristlecone, Foxtail, Sycamore, Sycamore23 +from cirq_google.devices.google_noise_properties import ( + GoogleNoiseProperties, + NoiseModelFromGoogleNoiseProperties, +) -from cirq_google.devices.serializable_device import SerializableDevice +from cirq_google.devices.known_devices import ( + Bristlecone, + Foxtail, + Sycamore, + Sycamore23, +) -from cirq_google.devices.xmon_device import XmonDevice +from cirq_google.devices.serializable_device import ( + SerializableDevice, +) + +from cirq_google.devices.xmon_device import ( + XmonDevice, +) diff --git a/cirq-google/cirq_google/engine/__init__.py b/cirq-google/cirq_google/engine/__init__.py index 52d094f6635..ae76adb4fca 100644 --- a/cirq-google/cirq_google/engine/__init__.py +++ b/cirq-google/cirq_google/engine/__init__.py @@ -15,33 +15,67 @@ """Client for running on Google's Quantum Engine. """ -from cirq_google.engine.abstract_engine import AbstractEngine +from cirq_google.engine.abstract_engine import ( + AbstractEngine, +) -from cirq_google.engine.abstract_job import AbstractJob +from cirq_google.engine.abstract_job import ( + AbstractJob, +) -from cirq_google.engine.abstract_processor import AbstractProcessor +from cirq_google.engine.abstract_processor import ( + AbstractProcessor, +) -from cirq_google.engine.abstract_program import AbstractProgram +from cirq_google.engine.abstract_program import ( + AbstractProgram, +) -from cirq_google.engine.abstract_local_engine import AbstractLocalEngine +from cirq_google.engine.abstract_local_engine import ( + AbstractLocalEngine, +) -from cirq_google.engine.abstract_local_job import AbstractLocalJob +from cirq_google.engine.abstract_local_job import ( + AbstractLocalJob, +) -from cirq_google.engine.abstract_local_processor import AbstractLocalProcessor +from cirq_google.engine.abstract_local_processor import ( + AbstractLocalProcessor, +) -from cirq_google.engine.abstract_local_program import AbstractLocalProgram +from cirq_google.engine.abstract_local_program import ( + AbstractLocalProgram, +) -from cirq_google.engine.simulated_local_engine import SimulatedLocalEngine +from cirq_google.engine.simulated_local_engine import ( + SimulatedLocalEngine, +) -from cirq_google.engine.simulated_local_job import SimulatedLocalJob +from cirq_google.engine.simulated_local_job import ( + SimulatedLocalJob, +) -from cirq_google.engine.simulated_local_processor import SimulatedLocalProcessor +from cirq_google.engine.simulated_local_processor import ( + SimulatedLocalProcessor, +) -from cirq_google.engine.simulated_local_program import SimulatedLocalProgram -from cirq_google.engine.calibration import Calibration +from cirq_google.engine.simulated_local_program import ( + SimulatedLocalProgram, +) +from cirq_google.engine.calibration import ( + Calibration, +) + +from cirq_google.engine.calibration_layer import ( + CalibrationLayer, +) +from cirq_google.engine.calibration_result import ( + CalibrationResult, +) +from cirq_google.engine.calibration_to_noise_properties import ( + noise_properties_from_calibration, +) -from cirq_google.engine.calibration_layer import CalibrationLayer -from cirq_google.engine.calibration_result import CalibrationResult from cirq_google.engine.engine import ( Engine, get_engine, @@ -50,13 +84,21 @@ ProtoVersion, ) -from cirq_google.engine.engine_client import EngineException +from cirq_google.engine.engine_client import ( + EngineException, +) -from cirq_google.engine.engine_job import EngineJob +from cirq_google.engine.engine_job import ( + EngineJob, +) -from cirq_google.engine.engine_processor import EngineProcessor +from cirq_google.engine.engine_processor import ( + EngineProcessor, +) -from cirq_google.engine.engine_program import EngineProgram +from cirq_google.engine.engine_program import ( + EngineProgram, +) from cirq_google.engine.runtime_estimator import ( estimate_run_time, @@ -64,9 +106,14 @@ estimate_run_sweep_time, ) -from cirq_google.engine.engine_sampler import get_engine_sampler, QuantumEngineSampler +from cirq_google.engine.engine_sampler import ( + get_engine_sampler, + QuantumEngineSampler, +) -from cirq_google.engine.validating_sampler import ValidatingSampler +from cirq_google.engine.validating_sampler import ( + ValidatingSampler, +) from cirq_google.engine.virtual_engine_factory import ( create_noiseless_virtual_engine_from_device, diff --git a/cirq-google/cirq_google/engine/calibration_to_noise_properties_test.py b/cirq-google/cirq_google/engine/calibration_to_noise_properties_test.py index 460092504b2..1861b67ac29 100644 --- a/cirq-google/cirq_google/engine/calibration_to_noise_properties_test.py +++ b/cirq-google/cirq_google/engine/calibration_to_noise_properties_test.py @@ -14,7 +14,6 @@ import cirq, cirq_google from cirq.devices.noise_utils import OpIdentifier -from cirq_google.engine.calibration_to_noise_properties import noise_properties_from_calibration from google.protobuf.text_format import Merge import numpy as np @@ -211,7 +210,7 @@ def test_noise_properties_from_calibration(): # Create NoiseProperties object from Calibration calibration = cirq_google.Calibration(_CALIBRATION_DATA) - prop = noise_properties_from_calibration(calibration) + prop = cirq_google.noise_properties_from_calibration(calibration) for i, q in enumerate(qubits): assert np.isclose( @@ -337,4 +336,4 @@ def test_incomplete_calibration(): # Create NoiseProperties object from Calibration calibration = cirq_google.Calibration(_CALIBRATION_DATA) with pytest.raises(ValueError, match='Keys specified for T1 and Tphi are not identical.'): - _ = noise_properties_from_calibration(calibration) + _ = cirq_google.noise_properties_from_calibration(calibration) diff --git a/cirq-google/cirq_google/json_resolver_cache.py b/cirq-google/cirq_google/json_resolver_cache.py index db11e11eae0..6ad05ec45a0 100644 --- a/cirq-google/cirq_google/json_resolver_cache.py +++ b/cirq-google/cirq_google/json_resolver_cache.py @@ -30,6 +30,7 @@ def _class_resolver_dictionary() -> Dict[str, ObjectFactory]: 'CalibrationLayer': cirq_google.CalibrationLayer, 'CalibrationResult': cirq_google.CalibrationResult, 'CouplerPulse': cirq_google.experimental.CouplerPulse, + 'GoogleNoiseProperties': cirq_google.GoogleNoiseProperties, 'SycamoreGate': cirq_google.SycamoreGate, 'GateTabulation': cirq_google.GateTabulation, 'PhysicalZTag': cirq_google.PhysicalZTag, diff --git a/cirq-google/cirq_google/json_test_data/GoogleNoiseProperties.json b/cirq-google/cirq_google/json_test_data/GoogleNoiseProperties.json new file mode 100644 index 00000000000..47ebcc4b635 --- /dev/null +++ b/cirq-google/cirq_google/json_test_data/GoogleNoiseProperties.json @@ -0,0 +1,653 @@ +{ + "cirq_type": "GoogleNoiseProperties", + "gate_times_ns": [ + [ + "ZPowGate", + 25.0 + ], + [ + "MeasurementGate", + 4000.0 + ], + [ + "ResetChannel", + 250.0 + ], + [ + "PhasedXZGate", + 25.0 + ], + [ + "FSimGate", + 32.0 + ], + [ + "ISwapPowGate", + 32.0 + ], + [ + "CZPowGate", + 32.0 + ] + ], + "t1_ns": [ + [ + { + "cirq_type": "GridQubit", + "row": 0, + "col": 0 + }, + 10000.0 + ], + [ + { + "cirq_type": "GridQubit", + "row": 0, + "col": 1 + }, + 20000.0 + ], + [ + { + "cirq_type": "GridQubit", + "row": 1, + "col": 0 + }, + 30000.0 + ] + ], + "tphi_ns": [ + [ + { + "cirq_type": "GridQubit", + "row": 0, + "col": 0 + }, + 10000000000.0 + ], + [ + { + "cirq_type": "GridQubit", + "row": 0, + "col": 1 + }, + 10000000000.0 + ], + [ + { + "cirq_type": "GridQubit", + "row": 1, + "col": 0 + }, + 375000.00000000047 + ] + ], + "readout_errors": [ + [ + { + "cirq_type": "GridQubit", + "row": 0, + "col": 0 + }, + [ + 0.004, + 0.007 + ] + ], + [ + { + "cirq_type": "GridQubit", + "row": 0, + "col": 1 + }, + [ + 0.005, + 0.008 + ] + ], + [ + { + "cirq_type": "GridQubit", + "row": 1, + "col": 0 + }, + [ + 0.006, + 0.009 + ] + ] + ], + "gate_pauli_errors": [ + [ + { + "cirq_type": "OpIdentifier", + "gate_type": "ZPowGate", + "qubits": [ + { + "cirq_type": "GridQubit", + "row": 0, + "col": 0 + } + ] + }, + 0.001 + ], + [ + { + "cirq_type": "OpIdentifier", + "gate_type": "MeasurementGate", + "qubits": [ + { + "cirq_type": "GridQubit", + "row": 0, + "col": 0 + } + ] + }, + 0.001 + ], + [ + { + "cirq_type": "OpIdentifier", + "gate_type": "ResetChannel", + "qubits": [ + { + "cirq_type": "GridQubit", + "row": 0, + "col": 0 + } + ] + }, + 0.001 + ], + [ + { + "cirq_type": "OpIdentifier", + "gate_type": "PhasedXZGate", + "qubits": [ + { + "cirq_type": "GridQubit", + "row": 0, + "col": 0 + } + ] + }, + 0.001 + ], + [ + { + "cirq_type": "OpIdentifier", + "gate_type": "ZPowGate", + "qubits": [ + { + "cirq_type": "GridQubit", + "row": 0, + "col": 1 + } + ] + }, + 0.002 + ], + [ + { + "cirq_type": "OpIdentifier", + "gate_type": "MeasurementGate", + "qubits": [ + { + "cirq_type": "GridQubit", + "row": 0, + "col": 1 + } + ] + }, + 0.002 + ], + [ + { + "cirq_type": "OpIdentifier", + "gate_type": "ResetChannel", + "qubits": [ + { + "cirq_type": "GridQubit", + "row": 0, + "col": 1 + } + ] + }, + 0.002 + ], + [ + { + "cirq_type": "OpIdentifier", + "gate_type": "PhasedXZGate", + "qubits": [ + { + "cirq_type": "GridQubit", + "row": 0, + "col": 1 + } + ] + }, + 0.002 + ], + [ + { + "cirq_type": "OpIdentifier", + "gate_type": "ZPowGate", + "qubits": [ + { + "cirq_type": "GridQubit", + "row": 1, + "col": 0 + } + ] + }, + 0.003 + ], + [ + { + "cirq_type": "OpIdentifier", + "gate_type": "MeasurementGate", + "qubits": [ + { + "cirq_type": "GridQubit", + "row": 1, + "col": 0 + } + ] + }, + 0.003 + ], + [ + { + "cirq_type": "OpIdentifier", + "gate_type": "ResetChannel", + "qubits": [ + { + "cirq_type": "GridQubit", + "row": 1, + "col": 0 + } + ] + }, + 0.003 + ], + [ + { + "cirq_type": "OpIdentifier", + "gate_type": "PhasedXZGate", + "qubits": [ + { + "cirq_type": "GridQubit", + "row": 1, + "col": 0 + } + ] + }, + 0.003 + ], + [ + { + "cirq_type": "OpIdentifier", + "gate_type": "SycamoreGate", + "qubits": [ + { + "cirq_type": "GridQubit", + "row": 0, + "col": 0 + }, + { + "cirq_type": "GridQubit", + "row": 0, + "col": 1 + } + ] + }, + 0.01 + ], + [ + { + "cirq_type": "OpIdentifier", + "gate_type": "SycamoreGate", + "qubits": [ + { + "cirq_type": "GridQubit", + "row": 0, + "col": 1 + }, + { + "cirq_type": "GridQubit", + "row": 0, + "col": 0 + } + ] + }, + 0.01 + ], + [ + { + "cirq_type": "OpIdentifier", + "gate_type": "SycamoreGate", + "qubits": [ + { + "cirq_type": "GridQubit", + "row": 0, + "col": 0 + }, + { + "cirq_type": "GridQubit", + "row": 1, + "col": 0 + } + ] + }, + 0.02 + ], + [ + { + "cirq_type": "OpIdentifier", + "gate_type": "SycamoreGate", + "qubits": [ + { + "cirq_type": "GridQubit", + "row": 1, + "col": 0 + }, + { + "cirq_type": "GridQubit", + "row": 0, + "col": 0 + } + ] + }, + 0.02 + ], + [ + { + "cirq_type": "OpIdentifier", + "gate_type": "ISwapPowGate", + "qubits": [ + { + "cirq_type": "GridQubit", + "row": 0, + "col": 0 + }, + { + "cirq_type": "GridQubit", + "row": 0, + "col": 1 + } + ] + }, + 0.03 + ], + [ + { + "cirq_type": "OpIdentifier", + "gate_type": "ISwapPowGate", + "qubits": [ + { + "cirq_type": "GridQubit", + "row": 0, + "col": 1 + }, + { + "cirq_type": "GridQubit", + "row": 0, + "col": 0 + } + ] + }, + 0.03 + ], + [ + { + "cirq_type": "OpIdentifier", + "gate_type": "ISwapPowGate", + "qubits": [ + { + "cirq_type": "GridQubit", + "row": 0, + "col": 0 + }, + { + "cirq_type": "GridQubit", + "row": 1, + "col": 0 + } + ] + }, + 0.04 + ], + [ + { + "cirq_type": "OpIdentifier", + "gate_type": "ISwapPowGate", + "qubits": [ + { + "cirq_type": "GridQubit", + "row": 1, + "col": 0 + }, + { + "cirq_type": "GridQubit", + "row": 0, + "col": 0 + } + ] + }, + 0.04 + ] + ], + "fsim_errors": [ + [ + { + "cirq_type": "OpIdentifier", + "gate_type": "SycamoreGate", + "qubits": [ + { + "cirq_type": "GridQubit", + "row": 0, + "col": 0 + }, + { + "cirq_type": "GridQubit", + "row": 0, + "col": 1 + } + ] + }, + { + "cirq_type": "PhasedFSimGate", + "theta": 0.011, + "zeta": 0.0, + "chi": 0.0, + "gamma": 0.0, + "phi": -0.021 + } + ], + [ + { + "cirq_type": "OpIdentifier", + "gate_type": "SycamoreGate", + "qubits": [ + { + "cirq_type": "GridQubit", + "row": 0, + "col": 1 + }, + { + "cirq_type": "GridQubit", + "row": 0, + "col": 0 + } + ] + }, + { + "cirq_type": "PhasedFSimGate", + "theta": 0.011, + "zeta": 0.0, + "chi": 0.0, + "gamma": 0.0, + "phi": -0.021 + } + ], + [ + { + "cirq_type": "OpIdentifier", + "gate_type": "SycamoreGate", + "qubits": [ + { + "cirq_type": "GridQubit", + "row": 0, + "col": 0 + }, + { + "cirq_type": "GridQubit", + "row": 1, + "col": 0 + } + ] + }, + { + "cirq_type": "PhasedFSimGate", + "theta": -0.012, + "zeta": 0.0, + "chi": 0.0, + "gamma": 0.0, + "phi": 0.022 + } + ], + [ + { + "cirq_type": "OpIdentifier", + "gate_type": "SycamoreGate", + "qubits": [ + { + "cirq_type": "GridQubit", + "row": 1, + "col": 0 + }, + { + "cirq_type": "GridQubit", + "row": 0, + "col": 0 + } + ] + }, + { + "cirq_type": "PhasedFSimGate", + "theta": -0.012, + "zeta": 0.0, + "chi": 0.0, + "gamma": 0.0, + "phi": 0.022 + } + ], + [ + { + "cirq_type": "OpIdentifier", + "gate_type": "ISwapPowGate", + "qubits": [ + { + "cirq_type": "GridQubit", + "row": 0, + "col": 0 + }, + { + "cirq_type": "GridQubit", + "row": 0, + "col": 1 + } + ] + }, + { + "cirq_type": "PhasedFSimGate", + "theta": -0.013, + "zeta": 0.0, + "chi": 0.0, + "gamma": 0.0, + "phi": 0.023 + } + ], + [ + { + "cirq_type": "OpIdentifier", + "gate_type": "ISwapPowGate", + "qubits": [ + { + "cirq_type": "GridQubit", + "row": 0, + "col": 1 + }, + { + "cirq_type": "GridQubit", + "row": 0, + "col": 0 + } + ] + }, + { + "cirq_type": "PhasedFSimGate", + "theta": -0.013, + "zeta": 0.0, + "chi": 0.0, + "gamma": 0.0, + "phi": 0.023 + } + ], + [ + { + "cirq_type": "OpIdentifier", + "gate_type": "ISwapPowGate", + "qubits": [ + { + "cirq_type": "GridQubit", + "row": 0, + "col": 0 + }, + { + "cirq_type": "GridQubit", + "row": 1, + "col": 0 + } + ] + }, + { + "cirq_type": "PhasedFSimGate", + "theta": 0.014, + "zeta": 0.0, + "chi": 0.0, + "gamma": 0.0, + "phi": -0.024 + } + ], + [ + { + "cirq_type": "OpIdentifier", + "gate_type": "ISwapPowGate", + "qubits": [ + { + "cirq_type": "GridQubit", + "row": 1, + "col": 0 + }, + { + "cirq_type": "GridQubit", + "row": 0, + "col": 0 + } + ] + }, + { + "cirq_type": "PhasedFSimGate", + "theta": 0.014, + "zeta": 0.0, + "chi": 0.0, + "gamma": 0.0, + "phi": -0.024 + } + ] + ], + "validate": true +} \ No newline at end of file diff --git a/cirq-google/cirq_google/json_test_data/GoogleNoiseProperties.repr b/cirq-google/cirq_google/json_test_data/GoogleNoiseProperties.repr new file mode 100644 index 00000000000..364a42537d2 --- /dev/null +++ b/cirq-google/cirq_google/json_test_data/GoogleNoiseProperties.repr @@ -0,0 +1,59 @@ +cirq_google.GoogleNoiseProperties( + gate_times_ns={ + cirq.ops.common_gates.ZPowGate: 25.0, + cirq.ops.measurement_gate.MeasurementGate: 4000.0, + cirq.ops.common_channels.ResetChannel: 250.0, + cirq.ops.phased_x_z_gate.PhasedXZGate: 25.0, + cirq.ops.fsim_gate.FSimGate: 32.0, + cirq.ops.swap_gates.ISwapPowGate: 32.0, + cirq.ops.common_gates.CZPowGate: 32.0 + }, + t1_ns={ + cirq.GridQubit(0, 0): 10000.0, + cirq.GridQubit(0, 1): 20000.0, + cirq.GridQubit(1, 0): 30000.0 + }, + tphi_ns={ + cirq.GridQubit(0, 0): 10000000000.0, + cirq.GridQubit(0, 1): 10000000000.0, + cirq.GridQubit(1, 0): 375000.00000000047 + }, + readout_errors={ + cirq.GridQubit(0, 0): np.array([0.004, 0.007], dtype=np.float64), + cirq.GridQubit(0, 1): np.array([0.005, 0.008], dtype=np.float64), + cirq.GridQubit(1, 0): np.array([0.006, 0.009], dtype=np.float64) + }, + gate_pauli_errors={ + cirq.devices.noise_utils.OpIdentifier(cirq.ops.common_gates.ZPowGate, cirq.GridQubit(0, 0)): 0.001, + cirq.devices.noise_utils.OpIdentifier(cirq.ops.measurement_gate.MeasurementGate, cirq.GridQubit(0, 0)): 0.001, + cirq.devices.noise_utils.OpIdentifier(cirq.ops.common_channels.ResetChannel, cirq.GridQubit(0, 0)): 0.001, + cirq.devices.noise_utils.OpIdentifier(cirq.ops.phased_x_z_gate.PhasedXZGate, cirq.GridQubit(0, 0)): 0.001, + cirq.devices.noise_utils.OpIdentifier(cirq.ops.common_gates.ZPowGate, cirq.GridQubit(0, 1)): 0.002, + cirq.devices.noise_utils.OpIdentifier(cirq.ops.measurement_gate.MeasurementGate, cirq.GridQubit(0, 1)): 0.002, + cirq.devices.noise_utils.OpIdentifier(cirq.ops.common_channels.ResetChannel, cirq.GridQubit(0, 1)): 0.002, + cirq.devices.noise_utils.OpIdentifier(cirq.ops.phased_x_z_gate.PhasedXZGate, cirq.GridQubit(0, 1)): 0.002, + cirq.devices.noise_utils.OpIdentifier(cirq.ops.common_gates.ZPowGate, cirq.GridQubit(1, 0)): 0.003, + cirq.devices.noise_utils.OpIdentifier(cirq.ops.measurement_gate.MeasurementGate, cirq.GridQubit(1, 0)): 0.003, + cirq.devices.noise_utils.OpIdentifier(cirq.ops.common_channels.ResetChannel, cirq.GridQubit(1, 0)): 0.003, + cirq.devices.noise_utils.OpIdentifier(cirq.ops.phased_x_z_gate.PhasedXZGate, cirq.GridQubit(1, 0)): 0.003, + cirq.devices.noise_utils.OpIdentifier(cirq_google.ops.sycamore_gate.SycamoreGate, cirq.GridQubit(0, 0), cirq.GridQubit(0, 1)): 0.01, + cirq.devices.noise_utils.OpIdentifier(cirq_google.ops.sycamore_gate.SycamoreGate, cirq.GridQubit(0, 1), cirq.GridQubit(0, 0)): 0.01, + cirq.devices.noise_utils.OpIdentifier(cirq_google.ops.sycamore_gate.SycamoreGate, cirq.GridQubit(0, 0), cirq.GridQubit(1, 0)): 0.02, + cirq.devices.noise_utils.OpIdentifier(cirq_google.ops.sycamore_gate.SycamoreGate, cirq.GridQubit(1, 0), cirq.GridQubit(0, 0)): 0.02, + cirq.devices.noise_utils.OpIdentifier(cirq.ops.swap_gates.ISwapPowGate, cirq.GridQubit(0, 0), cirq.GridQubit(0, 1)): 0.03, + cirq.devices.noise_utils.OpIdentifier(cirq.ops.swap_gates.ISwapPowGate, cirq.GridQubit(0, 1), cirq.GridQubit(0, 0)): 0.03, + cirq.devices.noise_utils.OpIdentifier(cirq.ops.swap_gates.ISwapPowGate, cirq.GridQubit(0, 0), cirq.GridQubit(1, 0)): 0.04, + cirq.devices.noise_utils.OpIdentifier(cirq.ops.swap_gates.ISwapPowGate, cirq.GridQubit(1, 0), cirq.GridQubit(0, 0)): 0.04 + }, + fsim_errors={ + cirq.devices.noise_utils.OpIdentifier(cirq_google.ops.sycamore_gate.SycamoreGate, cirq.GridQubit(0, 0), cirq.GridQubit(0, 1)): cirq.PhasedFSimGate(theta=0.011, zeta=0.0, chi=0.0, gamma=0.0, phi=-0.021), + cirq.devices.noise_utils.OpIdentifier(cirq_google.ops.sycamore_gate.SycamoreGate, cirq.GridQubit(0, 1), cirq.GridQubit(0, 0)): cirq.PhasedFSimGate(theta=0.011, zeta=0.0, chi=0.0, gamma=0.0, phi=-0.021), + cirq.devices.noise_utils.OpIdentifier(cirq_google.ops.sycamore_gate.SycamoreGate, cirq.GridQubit(0, 0), cirq.GridQubit(1, 0)): cirq.PhasedFSimGate(theta=-0.012, zeta=0.0, chi=0.0, gamma=0.0, phi=0.022), + cirq.devices.noise_utils.OpIdentifier(cirq_google.ops.sycamore_gate.SycamoreGate, cirq.GridQubit(1, 0), cirq.GridQubit(0, 0)): cirq.PhasedFSimGate(theta=-0.012, zeta=0.0, chi=0.0, gamma=0.0, phi=0.022), + cirq.devices.noise_utils.OpIdentifier(cirq.ops.swap_gates.ISwapPowGate, cirq.GridQubit(0, 0), cirq.GridQubit(0, 1)): cirq.PhasedFSimGate(theta=-0.013, zeta=0.0, chi=0.0, gamma=0.0, phi=0.023), + cirq.devices.noise_utils.OpIdentifier(cirq.ops.swap_gates.ISwapPowGate, cirq.GridQubit(0, 1), cirq.GridQubit(0, 0)): cirq.PhasedFSimGate(theta=-0.013, zeta=0.0, chi=0.0, gamma=0.0, phi=0.023), + cirq.devices.noise_utils.OpIdentifier(cirq.ops.swap_gates.ISwapPowGate, cirq.GridQubit(0, 0), cirq.GridQubit(1, 0)): cirq.PhasedFSimGate(theta=0.014, zeta=0.0, chi=0.0, gamma=0.0, phi=-0.024), + cirq.devices.noise_utils.OpIdentifier(cirq.ops.swap_gates.ISwapPowGate, cirq.GridQubit(1, 0), cirq.GridQubit(0, 0)): cirq.PhasedFSimGate(theta=0.014, zeta=0.0, chi=0.0, gamma=0.0, phi=-0.024) + }, + validate=True +) \ No newline at end of file diff --git a/cirq-google/cirq_google/json_test_data/spec.py b/cirq-google/cirq_google/json_test_data/spec.py index 8fa96036aaf..9ef9a76678e 100644 --- a/cirq-google/cirq_google/json_test_data/spec.py +++ b/cirq-google/cirq_google/json_test_data/spec.py @@ -41,6 +41,7 @@ 'EngineProgram', 'FSimPhaseCorrections', 'NAMED_GATESETS', + 'NoiseModelFromGoogleNoiseProperties', 'ProtoVersion', 'GateOpSerializer', 'GateOpDeserializer', From ee817014dc8d9ed91f44a847d125ee374fe2180b Mon Sep 17 00:00:00 2001 From: Orion Martin <40585662+95-martin-orion@users.noreply.github.com> Date: Wed, 6 Apr 2022 10:45:44 -0700 Subject: [PATCH 08/11] Complete coverage. --- .../devices/google_noise_properties_test.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/cirq-google/cirq_google/devices/google_noise_properties_test.py b/cirq-google/cirq_google/devices/google_noise_properties_test.py index 74e0c4079e9..5cece1d4b1a 100644 --- a/cirq-google/cirq_google/devices/google_noise_properties_test.py +++ b/cirq-google/cirq_google/devices/google_noise_properties_test.py @@ -77,6 +77,22 @@ def sample_noise_properties( ) +def test_consistent_repr(): + q0, q1 = cirq.LineQubit.range(2) + test_props = sample_noise_properties([q0, q1], [(q0, q1), (q1, q0)]) + cirq.testing.assert_equivalent_repr( + test_props, setup_code="import cirq, cirq_google\nimport numpy as np" + ) + + +def test_equals(): + q0, q1, q2 = cirq.LineQubit.range(3) + test_props = sample_noise_properties([q0, q1], [(q0, q1), (q1, q0)]) + assert test_props != "mismatched_type" + test_props_v2 = test_props.override(readout_errors={q2: [0.01, 0.02]}) + assert test_props != test_props_v2 + + def test_zphase_gates(): q0 = cirq.LineQubit(0) props = sample_noise_properties([q0], []) @@ -106,6 +122,7 @@ def test_with_params_fill(): gate_pauli_errors=expected_vals['gate_pauli_errors'], fsim_errors=expected_vals['fsim_errors'], ) + assert props_v2 != props for key in props.gate_times_ns: assert key in props_v2.gate_times_ns assert props_v2.gate_times_ns[key] == expected_vals['gate_times_ns'] @@ -150,6 +167,7 @@ def test_with_params_target(): gate_pauli_errors=expected_vals['gate_pauli_errors'], fsim_errors=expected_vals['fsim_errors'], ) + assert props_v2 != props for field_name, expected in expected_vals.items(): target_dict = getattr(props_v2, field_name) for key, val in expected.items(): @@ -171,6 +189,7 @@ def test_with_params_opid_with_gate(): gate_pauli_errors={cirq.PhasedXZGate: expected_vals['gate_pauli_errors']}, fsim_errors={cirq.CZPowGate: expected_vals['fsim_errors']}, ) + assert props_v2 != props gpe_op_id_0 = cirq.OpIdentifier(cirq.PhasedXZGate, q0) gpe_op_id_1 = cirq.OpIdentifier(cirq.PhasedXZGate, q1) assert props_v2.gate_pauli_errors[gpe_op_id_0] == expected_vals['gate_pauli_errors'] From 4050e763e6145495510a559fc6f210ba6bbccb5e Mon Sep 17 00:00:00 2001 From: Orion Martin <40585662+95-martin-orion@users.noreply.github.com> Date: Mon, 18 Apr 2022 12:56:17 -0700 Subject: [PATCH 09/11] with_params --- .../engine/calibration_to_noise_properties.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cirq-google/cirq_google/engine/calibration_to_noise_properties.py b/cirq-google/cirq_google/engine/calibration_to_noise_properties.py index b369a2ea7c7..47f5e9eb1ac 100644 --- a/cirq-google/cirq_google/engine/calibration_to_noise_properties.py +++ b/cirq-google/cirq_google/engine/calibration_to_noise_properties.py @@ -68,15 +68,15 @@ def noise_properties_from_calibration( """Translates between `cirq_google.Calibration` and NoiseProperties. The NoiseProperties object can then be used as input to the - `cirq.devices.noise_propertiesNoiseModelFromNoiseProperties` class to - create a `cirq.NoiseModel` that can be used with a simulator. + `cirq.NoiseModelFromNoiseProperties` class to create a NoiseModel that can + be used with a simulator. - To manually override noise properties, call `override` on the output: + To manually override noise properties, call `with_params` on the output: - # Set all gate durations to 37ns. - >>> noise_properties_from_calibration(cal).override(gate_times_ns=37) + >>> noise_props = noise_properties_from_calibration(cal).with_params(gate_times_ns=37) + # noise_props with all gate durations set to 37ns. - See `cirq_google.GoogleNoiseProperties` for details. + See `cirq.GoogleNoiseProperties` for details. Args: calibration: a Calibration object with hardware metrics. From c8c7ce31f194d01e3990566e6d1886a78539d9e3 Mon Sep 17 00:00:00 2001 From: Orion Martin <40585662+95-martin-orion@users.noreply.github.com> Date: Mon, 18 Apr 2022 12:57:50 -0700 Subject: [PATCH 10/11] New formatter --- cirq-google/cirq_google/devices/__init__.py | 15 +--- cirq-google/cirq_google/engine/__init__.py | 89 +++++---------------- 2 files changed, 25 insertions(+), 79 deletions(-) diff --git a/cirq-google/cirq_google/devices/__init__.py b/cirq-google/cirq_google/devices/__init__.py index 2ba59d16b53..14ddf88ecb7 100644 --- a/cirq-google/cirq_google/devices/__init__.py +++ b/cirq-google/cirq_google/devices/__init__.py @@ -17,17 +17,8 @@ NoiseModelFromGoogleNoiseProperties, ) -from cirq_google.devices.known_devices import ( - Bristlecone, - Foxtail, - Sycamore, - Sycamore23, -) +from cirq_google.devices.known_devices import Bristlecone, Foxtail, Sycamore, Sycamore23 -from cirq_google.devices.serializable_device import ( - SerializableDevice, -) +from cirq_google.devices.serializable_device import SerializableDevice -from cirq_google.devices.xmon_device import ( - XmonDevice, -) +from cirq_google.devices.xmon_device import XmonDevice diff --git a/cirq-google/cirq_google/engine/__init__.py b/cirq-google/cirq_google/engine/__init__.py index ae76adb4fca..26dfd406715 100644 --- a/cirq-google/cirq_google/engine/__init__.py +++ b/cirq-google/cirq_google/engine/__init__.py @@ -15,66 +15,34 @@ """Client for running on Google's Quantum Engine. """ -from cirq_google.engine.abstract_engine import ( - AbstractEngine, -) +from cirq_google.engine.abstract_engine import AbstractEngine -from cirq_google.engine.abstract_job import ( - AbstractJob, -) +from cirq_google.engine.abstract_job import AbstractJob -from cirq_google.engine.abstract_processor import ( - AbstractProcessor, -) +from cirq_google.engine.abstract_processor import AbstractProcessor -from cirq_google.engine.abstract_program import ( - AbstractProgram, -) +from cirq_google.engine.abstract_program import AbstractProgram -from cirq_google.engine.abstract_local_engine import ( - AbstractLocalEngine, -) +from cirq_google.engine.abstract_local_engine import AbstractLocalEngine -from cirq_google.engine.abstract_local_job import ( - AbstractLocalJob, -) +from cirq_google.engine.abstract_local_job import AbstractLocalJob -from cirq_google.engine.abstract_local_processor import ( - AbstractLocalProcessor, -) +from cirq_google.engine.abstract_local_processor import AbstractLocalProcessor -from cirq_google.engine.abstract_local_program import ( - AbstractLocalProgram, -) +from cirq_google.engine.abstract_local_program import AbstractLocalProgram -from cirq_google.engine.simulated_local_engine import ( - SimulatedLocalEngine, -) +from cirq_google.engine.simulated_local_engine import SimulatedLocalEngine -from cirq_google.engine.simulated_local_job import ( - SimulatedLocalJob, -) +from cirq_google.engine.simulated_local_job import SimulatedLocalJob -from cirq_google.engine.simulated_local_processor import ( - SimulatedLocalProcessor, -) +from cirq_google.engine.simulated_local_processor import SimulatedLocalProcessor -from cirq_google.engine.simulated_local_program import ( - SimulatedLocalProgram, -) -from cirq_google.engine.calibration import ( - Calibration, -) +from cirq_google.engine.simulated_local_program import SimulatedLocalProgram +from cirq_google.engine.calibration import Calibration -from cirq_google.engine.calibration_layer import ( - CalibrationLayer, -) -from cirq_google.engine.calibration_result import ( - CalibrationResult, -) -from cirq_google.engine.calibration_to_noise_properties import ( - noise_properties_from_calibration, -) +from cirq_google.engine.calibration_layer import CalibrationLayer +from cirq_google.engine.calibration_result import CalibrationResult +from cirq_google.engine.calibration_to_noise_properties import noise_properties_from_calibration from cirq_google.engine.engine import ( Engine, @@ -84,21 +52,13 @@ ProtoVersion, ) -from cirq_google.engine.engine_client import ( - EngineException, -) +from cirq_google.engine.engine_client import EngineException -from cirq_google.engine.engine_job import ( - EngineJob, -) +from cirq_google.engine.engine_job import EngineJob -from cirq_google.engine.engine_processor import ( - EngineProcessor, -) +from cirq_google.engine.engine_processor import EngineProcessor -from cirq_google.engine.engine_program import ( - EngineProgram, -) +from cirq_google.engine.engine_program import EngineProgram from cirq_google.engine.runtime_estimator import ( estimate_run_time, @@ -106,14 +66,9 @@ estimate_run_sweep_time, ) -from cirq_google.engine.engine_sampler import ( - get_engine_sampler, - QuantumEngineSampler, -) +from cirq_google.engine.engine_sampler import get_engine_sampler, QuantumEngineSampler -from cirq_google.engine.validating_sampler import ( - ValidatingSampler, -) +from cirq_google.engine.validating_sampler import ValidatingSampler from cirq_google.engine.virtual_engine_factory import ( create_noiseless_virtual_engine_from_device, From fe93f7bbef8e1253a96cbce882f9e30c1e9850dc Mon Sep 17 00:00:00 2001 From: Orion Martin <40585662+95-martin-orion@users.noreply.github.com> Date: Thu, 21 Apr 2022 15:50:17 -0700 Subject: [PATCH 11/11] Revive JSON, repr, eq --- .../devices/google_noise_properties.py | 104 ++++++++++++++---- .../devices/google_noise_properties_test.py | 16 +-- .../engine/calibration_to_noise_properties.py | 9 +- 3 files changed, 98 insertions(+), 31 deletions(-) diff --git a/cirq-google/cirq_google/devices/google_noise_properties.py b/cirq-google/cirq_google/devices/google_noise_properties.py index d97948365cd..820f9db45fc 100644 --- a/cirq-google/cirq_google/devices/google_noise_properties.py +++ b/cirq-google/cirq_google/devices/google_noise_properties.py @@ -25,22 +25,6 @@ from cirq.transformers.heuristic_decompositions import gate_tabulation_math_utils -SINGLE_QUBIT_GATES: Set[Type['cirq.Gate']] = { - cirq.ZPowGate, - cirq.PhasedXZGate, - cirq.MeasurementGate, - cirq.ResetChannel, -} -SYMMETRIC_TWO_QUBIT_GATES: Set[Type['cirq.Gate']] = { - cirq_google.SycamoreGate, - cirq.FSimGate, - cirq.PhasedFSimGate, - cirq.ISwapPowGate, - cirq.CZPowGate, -} -ASYMMETRIC_TWO_QUBIT_GATES: Set[Type['cirq.Gate']] = set() - - T = TypeVar('T') V = TypeVar('V') @@ -94,6 +78,26 @@ def __post_init__(self): # validate two qubit gate errors. self._validate_symmetric_errors('fsim_errors') + def __eq__(self, other): + if not isinstance(other, GoogleNoiseProperties): + return NotImplemented + if set(self.readout_errors) != set(other.readout_errors): + return False + return all( + [ + self.gate_times_ns == other.gate_times_ns, + self.t1_ns == other.t1_ns, + self.tphi_ns == other.tphi_ns, + all( + np.allclose(self.readout_errors[q], other.readout_errors[q]) + for q in self.readout_errors + ), + self.gate_pauli_errors == other.gate_pauli_errors, + self.validate == other.validate, + self.fsim_errors == other.fsim_errors, + ] + ) + def with_params( self, *, @@ -181,15 +185,21 @@ def with_params( @classmethod def single_qubit_gates(cls) -> Set[type]: - return SINGLE_QUBIT_GATES + return {cirq.ZPowGate, cirq.PhasedXZGate, cirq.MeasurementGate, cirq.ResetChannel} @classmethod def symmetric_two_qubit_gates(cls) -> Set[type]: - return SYMMETRIC_TWO_QUBIT_GATES + return { + cirq_google.SycamoreGate, + cirq.FSimGate, + cirq.PhasedFSimGate, + cirq.ISwapPowGate, + cirq.CZPowGate, + } @classmethod def asymmetric_two_qubit_gates(cls) -> Set[type]: - return ASYMMETRIC_TWO_QUBIT_GATES + return set() @_compat.cached_property def _depolarizing_error(self) -> Dict[noise_utils.OpIdentifier, float]: @@ -234,6 +244,62 @@ def build_noise_models(self) -> List['cirq.NoiseModel']: return noise_models + def __repr__(self): + gate_times_repr = ', '.join( + f'{key.__module__}.{key.__qualname__}: {val}' for key, val in self.gate_times_ns.items() + ) + args = [ + f'gate_times_ns={{{gate_times_repr}}}', + f't1_ns={self.t1_ns!r}', + f'tphi_ns={self.tphi_ns!r}', + f'readout_errors={_compat.proper_repr(self.readout_errors)}', + f'gate_pauli_errors={self.gate_pauli_errors!r}', + f'fsim_errors={self.fsim_errors!r}', + f'validate={self.validate!r}', + ] + arglines = ",\n".join(args) + return f'cirq_google.GoogleNoiseProperties({arglines})' + + def _json_dict_(self): + storage_gate_times = { + cirq.json_cirq_type(key): val for key, val in self.gate_times_ns.items() + } + return { + # JSON requires mappings to have keys of basic types. + # Pairs must be sorted to ensure consistent serialization. + 'gate_times_ns': tuple(storage_gate_times.items()), + 't1_ns': tuple(self.t1_ns.items()), + 'tphi_ns': tuple(self.tphi_ns.items()), + 'readout_errors': tuple((k, v.tolist()) for k, v in self.readout_errors.items()), + 'gate_pauli_errors': tuple(self.gate_pauli_errors.items()), + 'fsim_errors': tuple(self.fsim_errors.items()), + 'validate': self.validate, + } + + @classmethod + def _from_json_dict_( + cls, + gate_times_ns, + t1_ns, + tphi_ns, + readout_errors, + gate_pauli_errors, + fsim_errors, + validate, + **kwargs, + ): + gate_type_times = {cirq.cirq_type_from_json(gate): val for gate, val in gate_times_ns} + # Known false positive: https://github.com/PyCQA/pylint/issues/5857 + return GoogleNoiseProperties( # pylint: disable=unexpected-keyword-arg + gate_times_ns=gate_type_times, + t1_ns=dict(t1_ns), + tphi_ns=dict(tphi_ns), + readout_errors={k: np.array(v) for k, v in readout_errors}, + gate_pauli_errors=dict(gate_pauli_errors), + fsim_errors=dict(fsim_errors), + validate=validate, + ) + class NoiseModelFromGoogleNoiseProperties(devices.NoiseModelFromNoiseProperties): """A noise model defined from noise properties of a Google device.""" diff --git a/cirq-google/cirq_google/devices/google_noise_properties_test.py b/cirq-google/cirq_google/devices/google_noise_properties_test.py index 5cece1d4b1a..413339ee755 100644 --- a/cirq-google/cirq_google/devices/google_noise_properties_test.py +++ b/cirq-google/cirq_google/devices/google_noise_properties_test.py @@ -18,10 +18,6 @@ import pytest import cirq, cirq_google -from cirq_google.devices.google_noise_properties import ( - SYMMETRIC_TWO_QUBIT_GATES, - SINGLE_QUBIT_GATES, -) from cirq.devices.noise_utils import OpIdentifier, PHYSICAL_GATE_TAG from cirq_google.devices.google_noise_properties import ( @@ -62,16 +58,20 @@ def sample_noise_properties( tphi_ns={q: 2e5 for q in system_qubits}, readout_errors={q: np.array([SINGLE_QUBIT_ERROR, TWO_QUBIT_ERROR]) for q in system_qubits}, gate_pauli_errors={ - **{OpIdentifier(g, q): 0.001 for g in SINGLE_QUBIT_GATES for q in system_qubits}, + **{ + OpIdentifier(g, q): 0.001 + for g in GoogleNoiseProperties.single_qubit_gates() + for q in system_qubits + }, **{ OpIdentifier(g, q0, q1): 0.01 - for g in SYMMETRIC_TWO_QUBIT_GATES + for g in GoogleNoiseProperties.symmetric_two_qubit_gates() for q0, q1 in qubit_pairs }, }, fsim_errors={ OpIdentifier(g, q0, q1): cirq.PhasedFSimGate(0.01, 0.03, 0.04, 0.05, 0.02) - for g in SYMMETRIC_TWO_QUBIT_GATES + for g in GoogleNoiseProperties.symmetric_two_qubit_gates() for q0, q1 in qubit_pairs }, ) @@ -89,7 +89,7 @@ def test_equals(): q0, q1, q2 = cirq.LineQubit.range(3) test_props = sample_noise_properties([q0, q1], [(q0, q1), (q1, q0)]) assert test_props != "mismatched_type" - test_props_v2 = test_props.override(readout_errors={q2: [0.01, 0.02]}) + test_props_v2 = test_props.with_params(readout_errors={q2: [0.01, 0.02]}) assert test_props != test_props_v2 diff --git a/cirq-google/cirq_google/engine/calibration_to_noise_properties.py b/cirq-google/cirq_google/engine/calibration_to_noise_properties.py index 47f5e9eb1ac..6d102e3b7b7 100644 --- a/cirq-google/cirq_google/engine/calibration_to_noise_properties.py +++ b/cirq-google/cirq_google/engine/calibration_to_noise_properties.py @@ -68,15 +68,15 @@ def noise_properties_from_calibration( """Translates between `cirq_google.Calibration` and NoiseProperties. The NoiseProperties object can then be used as input to the - `cirq.NoiseModelFromNoiseProperties` class to create a NoiseModel that can - be used with a simulator. + `cirq_google.NoiseModelFromGoogleNoiseProperties` class to create a + `cirq.NoiseModel` that can be used with a simulator. To manually override noise properties, call `with_params` on the output: >>> noise_props = noise_properties_from_calibration(cal).with_params(gate_times_ns=37) # noise_props with all gate durations set to 37ns. - See `cirq.GoogleNoiseProperties` for details. + See `cirq_google.GoogleNoiseProperties` for details. Args: calibration: a Calibration object with hardware metrics. @@ -96,6 +96,7 @@ def noise_properties_from_calibration( ops.FSimGate: 32.0, ops.ISwapPowGate: 32.0, ops.CZPowGate: 32.0, + cg_ops.SycamoreGate: 12.0, # ops.WaitGate is a special case. } @@ -123,7 +124,7 @@ def noise_properties_from_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 + for gate in google_noise_properties.GoogleNoiseProperties.single_qubit_gates() } # 3b. Extract Pauli error for two-qubit gates.