Skip to content

Commit 807a24d

Browse files
Insertion noise model (#4672)
This PR is part of #4640. It adds the `InsertionNoiseModel`, which injects noise based on a user-defined map. #4671 is a prerequisite for this PR. The only files that need to be reviewed in this PR are: - `cirq-core/cirq/devices/...` - `__init__.py` - `insertion_noise_model[_test].py`
1 parent ca09f4c commit 807a24d

File tree

3 files changed

+175
-0
lines changed

3 files changed

+175
-0
lines changed

cirq-core/cirq/devices/__init__.py

+4
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@
4848
draw_placements,
4949
)
5050

51+
from cirq.devices.insertion_noise_model import (
52+
InsertionNoiseModel,
53+
)
54+
5155
from cirq.devices.noise_utils import (
5256
OpIdentifier,
5357
decay_constant_to_xeb_fidelity,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Copyright 2021 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+
import dataclasses
16+
from typing import TYPE_CHECKING, Dict, List, Optional, Sequence
17+
18+
from cirq import devices, ops
19+
from cirq.devices import noise_utils
20+
21+
if TYPE_CHECKING:
22+
import cirq
23+
24+
25+
@dataclasses.dataclass
26+
class InsertionNoiseModel(devices.NoiseModel):
27+
"""Simple base noise model for inserting operations.
28+
29+
Operations generated by this model for a given moment are all added into a
30+
single "noise moment", which is added before or after the original moment
31+
based on `prepend`.
32+
33+
Args:
34+
ops_added: a map of gate types (and optionally, qubits they act on) to
35+
operations that should be added.
36+
prepend: whether to add the new moment before the current one.
37+
require_physical_tag: whether to only apply noise to operations tagged
38+
with PHYSICAL_GATE_TAG.
39+
"""
40+
41+
ops_added: Dict[noise_utils.OpIdentifier, 'cirq.Operation'] = dataclasses.field(
42+
default_factory=dict
43+
)
44+
prepend: bool = False
45+
require_physical_tag: bool = True
46+
47+
def noisy_moment(
48+
self, moment: 'cirq.Moment', system_qubits: Sequence['cirq.Qid']
49+
) -> 'cirq.OP_TREE':
50+
noise_ops: List['cirq.Operation'] = []
51+
candidate_ops = [
52+
op
53+
for op in moment
54+
if (not self.require_physical_tag) or noise_utils.PHYSICAL_GATE_TAG in op.tags
55+
]
56+
for op in candidate_ops:
57+
match_id: Optional[noise_utils.OpIdentifier] = None
58+
candidate_ids = [op_id for op_id in self.ops_added if op in op_id]
59+
for op_id in candidate_ids:
60+
if match_id is None or op_id.is_proper_subtype_of(match_id):
61+
match_id = op_id
62+
if match_id is not None:
63+
noise_ops.append(self.ops_added[match_id])
64+
if not noise_ops:
65+
return [moment]
66+
if self.prepend:
67+
return [ops.Moment(noise_ops), moment]
68+
return [moment, ops.Moment(noise_ops)]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# Copyright 2021 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+
import cirq
16+
from cirq.devices.insertion_noise_model import InsertionNoiseModel
17+
from cirq.devices.noise_utils import (
18+
PHYSICAL_GATE_TAG,
19+
OpIdentifier,
20+
)
21+
22+
23+
def test_insertion_noise():
24+
q0, q1 = cirq.LineQubit.range(2)
25+
op_id0 = OpIdentifier(cirq.XPowGate, q0)
26+
op_id1 = OpIdentifier(cirq.ZPowGate, q1)
27+
model = InsertionNoiseModel(
28+
{op_id0: cirq.T(q0), op_id1: cirq.H(q1)}, require_physical_tag=False
29+
)
30+
assert not model.prepend
31+
32+
moment_0 = cirq.Moment(cirq.X(q0), cirq.X(q1))
33+
assert model.noisy_moment(moment_0, system_qubits=[q0, q1]) == [
34+
moment_0,
35+
cirq.Moment(cirq.T(q0)),
36+
]
37+
38+
moment_1 = cirq.Moment(cirq.Z(q0), cirq.Z(q1))
39+
assert model.noisy_moment(moment_1, system_qubits=[q0, q1]) == [
40+
moment_1,
41+
cirq.Moment(cirq.H(q1)),
42+
]
43+
44+
moment_2 = cirq.Moment(cirq.X(q0), cirq.Z(q1))
45+
assert model.noisy_moment(moment_2, system_qubits=[q0, q1]) == [
46+
moment_2,
47+
cirq.Moment(cirq.T(q0), cirq.H(q1)),
48+
]
49+
50+
moment_3 = cirq.Moment(cirq.Z(q0), cirq.X(q1))
51+
assert model.noisy_moment(moment_3, system_qubits=[q0, q1]) == [moment_3]
52+
53+
54+
def test_prepend():
55+
q0, q1 = cirq.LineQubit.range(2)
56+
op_id0 = OpIdentifier(cirq.XPowGate, q0)
57+
op_id1 = OpIdentifier(cirq.ZPowGate, q1)
58+
model = InsertionNoiseModel(
59+
{op_id0: cirq.T(q0), op_id1: cirq.H(q1)}, prepend=True, require_physical_tag=False
60+
)
61+
62+
moment_0 = cirq.Moment(cirq.X(q0), cirq.Z(q1))
63+
assert model.noisy_moment(moment_0, system_qubits=[q0, q1]) == [
64+
cirq.Moment(cirq.T(q0), cirq.H(q1)),
65+
moment_0,
66+
]
67+
68+
69+
def test_require_physical_tag():
70+
q0, q1 = cirq.LineQubit.range(2)
71+
op_id0 = OpIdentifier(cirq.XPowGate, q0)
72+
op_id1 = OpIdentifier(cirq.ZPowGate, q1)
73+
model = InsertionNoiseModel({op_id0: cirq.T(q0), op_id1: cirq.H(q1)})
74+
assert model.require_physical_tag
75+
76+
moment_0 = cirq.Moment(cirq.X(q0).with_tags(PHYSICAL_GATE_TAG), cirq.Z(q1))
77+
assert model.noisy_moment(moment_0, system_qubits=[q0, q1]) == [
78+
moment_0,
79+
cirq.Moment(cirq.T(q0)),
80+
]
81+
82+
83+
def test_supertype_matching():
84+
# Demonstrate that the model applies the closest matching type
85+
# if multiple types match a given gate.
86+
q0 = cirq.LineQubit(0)
87+
op_id0 = OpIdentifier(cirq.Gate, q0)
88+
op_id1 = OpIdentifier(cirq.XPowGate, q0)
89+
model = InsertionNoiseModel(
90+
{op_id0: cirq.T(q0), op_id1: cirq.S(q0)}, require_physical_tag=False
91+
)
92+
93+
moment_0 = cirq.Moment(cirq.Rx(rads=1).on(q0))
94+
assert model.noisy_moment(moment_0, system_qubits=[q0]) == [
95+
moment_0,
96+
cirq.Moment(cirq.S(q0)),
97+
]
98+
99+
moment_1 = cirq.Moment(cirq.Y(q0))
100+
assert model.noisy_moment(moment_1, system_qubits=[q0]) == [
101+
moment_1,
102+
cirq.Moment(cirq.T(q0)),
103+
]

0 commit comments

Comments
 (0)