|
| 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