Skip to content

Commit 2fa04e4

Browse files
95-martin-oriontonybruguier
authored andcommitted
Add Google-specific variant for noise properties. (quantumlib#5082)
This PR provides an implementation of `SuperconductingQubitsNoiseProperties` for Google hardware. Now that SQNP is an abstract type, this is a prerequisite for re-enabling calibration-to-noise tools. This is part 3 of quantumlib#4666; part 2 was quantumlib#4964. Remaining steps include: - Reinstatement of calibration_to_noise_properties using new types - Reinstatement of compare_generated_noise_to_metrics using new types
1 parent c899d37 commit 2fa04e4

File tree

5 files changed

+434
-6
lines changed

5 files changed

+434
-6
lines changed

cirq-core/cirq/devices/noise_properties.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import abc
2323
from typing import Iterable, Sequence, TYPE_CHECKING, List
2424

25-
from cirq import _import, ops, protocols, devices
25+
from cirq import _compat, _import, ops, protocols, devices
2626
from cirq.devices.noise_utils import (
2727
PHYSICAL_GATE_TAG,
2828
)
@@ -54,7 +54,7 @@ def __init__(self, noise_properties: NoiseProperties) -> None:
5454
self._noise_properties = noise_properties
5555
self.noise_models = self._noise_properties.build_noise_models()
5656

57-
def virtual_predicate(self, op: 'cirq.Operation') -> bool:
57+
def is_virtual(self, op: 'cirq.Operation') -> bool:
5858
"""Returns True if an operation is virtual.
5959
6060
Device-specific subclasses should implement this method to mark any
@@ -68,6 +68,10 @@ def virtual_predicate(self, op: 'cirq.Operation') -> bool:
6868
"""
6969
return False
7070

71+
@_compat.deprecated(deadline='v0.16', fix='Use is_virtual instead.')
72+
def virtual_predicate(self, op: 'cirq.Operation') -> bool:
73+
return self.is_virtual(op)
74+
7175
def noisy_moments(
7276
self, moments: Iterable['cirq.Moment'], system_qubits: Sequence['cirq.Qid']
7377
) -> Sequence['cirq.OP_TREE']:
@@ -91,7 +95,7 @@ def noisy_moments(
9195
# using `self.virtual_predicate` to determine virtuality.
9296
new_moments = []
9397
for moment in split_measure_moments:
94-
virtual_ops = {op for op in moment if self.virtual_predicate(op)}
98+
virtual_ops = {op for op in moment if self.is_virtual(op)}
9599
physical_ops = [
96100
op.with_tags(PHYSICAL_GATE_TAG) for op in moment if op not in virtual_ops
97101
]

cirq-core/cirq/devices/noise_properties_test.py

+8
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,11 @@ def test_sample_model():
6060
cirq.Moment(cirq.H(q0), cirq.H(q1)),
6161
)
6262
assert noisy_circuit == expected_circuit
63+
64+
65+
def test_deprecated_virtual_predicate():
66+
q0, q1 = cirq.LineQubit.range(2)
67+
props = SampleNoiseProperties([q0, q1], [(q0, q1), (q1, q0)])
68+
model = NoiseModelFromNoiseProperties(props)
69+
with cirq.testing.assert_deprecated("Use is_virtual", deadline="v0.16"):
70+
_ = model.virtual_predicate(cirq.X(q0))

cirq-core/cirq/devices/superconducting_qubits_noise_properties.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,15 @@ class SuperconductingQubitsNoiseProperties(devices.NoiseProperties, abc.ABC):
3535
3636
Args:
3737
gate_times_ns: Dict[type, float] of gate types to their duration on
38-
quantum hardware.
38+
quantum hardware. Used with t(1|phi)_ns to specify thermal noise.
3939
t1_ns: Dict[cirq.Qid, float] of qubits to their T_1 time, in ns.
4040
tphi_ns: Dict[cirq.Qid, float] of qubits to their T_phi time, in ns.
4141
readout_errors: Dict[cirq.Qid, np.ndarray] of qubits to their readout
4242
errors in matrix form: [P(read |1> from |0>), P(read |0> from |1>)].
43+
Used to prepend amplitude damping errors to measurements.
4344
gate_pauli_errors: dict of noise_utils.OpIdentifiers (a gate and the qubits it
44-
targets) to the Pauli error for that operation. Keys in this dict
45-
must have defined qubits.
45+
targets) to the Pauli error for that operation. Used to construct
46+
depolarizing error. Keys in this dict must have defined qubits.
4647
validate: If True, verifies that t1 and tphi qubits sets match, and
4748
that all symmetric two-qubit gates have errors which are
4849
symmetric over the qubits they affect. Defaults to True.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# Copyright 2022 The Cirq Developers
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
"""Class for representing noise on a Google device."""
17+
18+
from dataclasses import dataclass, field
19+
from typing import Dict, List, Set, Type
20+
import numpy as np
21+
22+
import cirq, cirq_google
23+
from cirq import _compat, devices
24+
from cirq.devices import noise_utils
25+
from cirq.transformers.heuristic_decompositions import gate_tabulation_math_utils
26+
27+
28+
SINGLE_QUBIT_GATES: Set[Type['cirq.Gate']] = {
29+
cirq.ZPowGate,
30+
cirq.PhasedXZGate,
31+
cirq.MeasurementGate,
32+
cirq.ResetChannel,
33+
}
34+
SYMMETRIC_TWO_QUBIT_GATES: Set[Type['cirq.Gate']] = {
35+
cirq_google.SycamoreGate,
36+
cirq.FSimGate,
37+
cirq.PhasedFSimGate,
38+
cirq.ISwapPowGate,
39+
cirq.CZPowGate,
40+
}
41+
ASYMMETRIC_TWO_QUBIT_GATES: Set[Type['cirq.Gate']] = set()
42+
43+
44+
@dataclass
45+
class GoogleNoiseProperties(devices.SuperconductingQubitsNoiseProperties):
46+
"""Noise-defining properties for a Google device.
47+
48+
Inherited args:
49+
gate_times_ns: Dict[type, float] of gate types to their duration on
50+
quantum hardware. Used with t(1|phi)_ns to specify thermal noise.
51+
t1_ns: Dict[cirq.Qid, float] of qubits to their T_1 time, in ns.
52+
tphi_ns: Dict[cirq.Qid, float] of qubits to their T_phi time, in ns.
53+
readout_errors: Dict[cirq.Qid, np.ndarray] of qubits to their readout
54+
errors in matrix form: [P(read |1> from |0>), P(read |0> from |1>)].
55+
Used to prepend amplitude damping errors to measurements.
56+
gate_pauli_errors: dict of noise_utils.OpIdentifiers (a gate and the qubits it
57+
targets) to the Pauli error for that operation. Used to construct
58+
depolarizing error. Keys in this dict must have defined qubits.
59+
validate: If True, verifies that t1 and tphi qubits sets match, and
60+
that all symmetric two-qubit gates have errors which are
61+
symmetric over the qubits they affect. Defaults to True.
62+
63+
Additional args:
64+
fsim_errors: Dict[noise_utils.OpIdentifier, cirq.PhasedFSimGate] of gate types
65+
(potentially on specific qubits) to the PhasedFSim fix-up operation
66+
for that gate. Defaults to no-op for all gates.
67+
"""
68+
69+
fsim_errors: Dict[noise_utils.OpIdentifier, cirq.PhasedFSimGate] = field(default_factory=dict)
70+
71+
def __post_init__(self):
72+
super().__post_init__()
73+
74+
# validate two qubit gate errors.
75+
self._validate_symmetric_errors('fsim_errors')
76+
77+
@classmethod
78+
def single_qubit_gates(cls) -> Set[type]:
79+
return SINGLE_QUBIT_GATES
80+
81+
@classmethod
82+
def symmetric_two_qubit_gates(cls) -> Set[type]:
83+
return SYMMETRIC_TWO_QUBIT_GATES
84+
85+
@classmethod
86+
def asymmetric_two_qubit_gates(cls) -> Set[type]:
87+
return ASYMMETRIC_TWO_QUBIT_GATES
88+
89+
@_compat.cached_property
90+
def _depolarizing_error(self) -> Dict[noise_utils.OpIdentifier, float]:
91+
depol_errors = super()._depolarizing_error
92+
93+
def extract_entangling_error(match_id: noise_utils.OpIdentifier):
94+
"""Gets the entangling error component of depol_errors[match_id]."""
95+
unitary_err = cirq.unitary(self.fsim_errors[match_id])
96+
fid = gate_tabulation_math_utils.unitary_entanglement_fidelity(unitary_err, np.eye(4))
97+
return 1 - fid
98+
99+
# Subtract entangling angle error.
100+
for op_id in depol_errors:
101+
if op_id.gate_type not in self.two_qubit_gates():
102+
continue
103+
if op_id in self.fsim_errors:
104+
depol_errors[op_id] -= extract_entangling_error(op_id)
105+
continue
106+
# Find the closest matching supertype, if one is provided.
107+
# Gateset has similar behavior, but cannot be used here
108+
# because depol_errors is a dict, not a set.
109+
match_id = None
110+
candidate_parents = [
111+
parent_id for parent_id in self.fsim_errors if op_id.is_proper_subtype_of(parent_id)
112+
]
113+
for parent_id in candidate_parents:
114+
if match_id is None or parent_id.is_proper_subtype_of(match_id):
115+
match_id = parent_id
116+
if match_id is not None:
117+
depol_errors[op_id] -= extract_entangling_error(match_id)
118+
119+
return depol_errors
120+
121+
def build_noise_models(self) -> List['cirq.NoiseModel']:
122+
"""Construct all NoiseModels associated with NoiseProperties."""
123+
noise_models = super().build_noise_models()
124+
125+
# Insert entangling gate coherent errors after thermal error.
126+
if self.fsim_errors:
127+
fsim_ops = {op_id: gate.on(*op_id.qubits) for op_id, gate in self.fsim_errors.items()}
128+
noise_models.insert(1, devices.InsertionNoiseModel(ops_added=fsim_ops))
129+
130+
return noise_models
131+
132+
133+
class NoiseModelFromGoogleNoiseProperties(devices.NoiseModelFromNoiseProperties):
134+
"""A noise model defined from noise properties of a Google device."""
135+
136+
def is_virtual(self, op: cirq.Operation) -> bool:
137+
return isinstance(op.gate, cirq.ZPowGate) and cirq_google.PhysicalZTag not in op.tags
138+
139+
# noisy_moments is implemented by the superclass.

0 commit comments

Comments
 (0)