Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Insertion noise model #4672

Merged
merged 10 commits into from
Dec 14, 2021
4 changes: 4 additions & 0 deletions cirq-core/cirq/devices/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@
draw_placements,
)

from cirq.devices.insertion_noise_model import (
InsertionNoiseModel,
)

from cirq.devices.noise_utils import (
OpIdentifier,
decay_constant_to_xeb_fidelity,
Expand Down
71 changes: 71 additions & 0 deletions cirq-core/cirq/devices/insertion_noise_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# 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 dataclasses import dataclass, field
95-martin-orion marked this conversation as resolved.
Show resolved Hide resolved
from typing import TYPE_CHECKING, Dict, List, Optional, Sequence

from cirq import devices, ops
from cirq.devices.noise_utils import (
OpIdentifier,
PHYSICAL_GATE_TAG,
)
95-martin-orion marked this conversation as resolved.
Show resolved Hide resolved

if TYPE_CHECKING:
import cirq


@dataclass
class InsertionNoiseModel(devices.NoiseModel):
"""Simple base noise model for inserting operations.

Operations generated by this model for a given moment are all added into a
single "noise moment", which is added before or after the original moment
based on `prepend`.

Args:
ops_added: a map of gate types (and optionally, qubits they act on) to
operations that should be added.
prepend: whether to add the new moment before the current one.
require_physical_tag: whether to only apply noise to operations tagged
with PHYSICAL_GATE_TAG.
"""

ops_added: Dict[OpIdentifier, 'cirq.Operation'] = field(default_factory=dict)
prepend: bool = False
require_physical_tag: bool = True

def noisy_moment(
self, moment: 'cirq.Moment', system_qubits: Sequence['cirq.Qid']
) -> 'cirq.OP_TREE':
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Docstring.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This defines the noisy_moment method of the parent class and inherits its docstring. Compare with noisy_moments in ConstantQubitNoiseModel.

noise_ops: List['cirq.Operation'] = []
for op in moment:
if self.require_physical_tag and PHYSICAL_GATE_TAG not in op.tags:
# Only non-virtual gates get noise applied.
continue
95-martin-orion marked this conversation as resolved.
Show resolved Hide resolved
match_id: Optional[OpIdentifier] = None
for op_id in self.ops_added:
if op not in op_id:
continue
elif match_id is None:
match_id = op_id
continue
elif op_id.is_proper_subtype_of(match_id):
match_id = op_id
if match_id is not None:
noise_ops.append(self.ops_added[match_id])
if not noise_ops:
return [moment]
if self.prepend:
return [ops.Moment(noise_ops), moment]
return [moment, ops.Moment(noise_ops)]
103 changes: 103 additions & 0 deletions cirq-core/cirq/devices/insertion_noise_model_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# 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
from cirq.devices.insertion_noise_model import InsertionNoiseModel
from cirq.devices.noise_utils import (
PHYSICAL_GATE_TAG,
OpIdentifier,
)


def test_insertion_noise():
q0, q1 = cirq.LineQubit.range(2)
op_id0 = OpIdentifier(cirq.XPowGate, q0)
op_id1 = OpIdentifier(cirq.ZPowGate, q1)
model = InsertionNoiseModel(
{op_id0: cirq.T(q0), op_id1: cirq.H(q1)}, require_physical_tag=False
)
assert not model.prepend

moment_0 = cirq.Moment(cirq.X(q0), cirq.X(q1))
assert model.noisy_moment(moment_0, system_qubits=[q0, q1]) == [
moment_0,
cirq.Moment(cirq.T(q0)),
]

moment_1 = cirq.Moment(cirq.Z(q0), cirq.Z(q1))
assert model.noisy_moment(moment_1, system_qubits=[q0, q1]) == [
moment_1,
cirq.Moment(cirq.H(q1)),
]

moment_2 = cirq.Moment(cirq.X(q0), cirq.Z(q1))
assert model.noisy_moment(moment_2, system_qubits=[q0, q1]) == [
moment_2,
cirq.Moment(cirq.T(q0), cirq.H(q1)),
]

moment_3 = cirq.Moment(cirq.Z(q0), cirq.X(q1))
assert model.noisy_moment(moment_3, system_qubits=[q0, q1]) == [moment_3]


def test_prepend():
q0, q1 = cirq.LineQubit.range(2)
op_id0 = OpIdentifier(cirq.XPowGate, q0)
op_id1 = OpIdentifier(cirq.ZPowGate, q1)
model = InsertionNoiseModel(
{op_id0: cirq.T(q0), op_id1: cirq.H(q1)}, prepend=True, require_physical_tag=False
)

moment_0 = cirq.Moment(cirq.X(q0), cirq.Z(q1))
assert model.noisy_moment(moment_0, system_qubits=[q0, q1]) == [
cirq.Moment(cirq.T(q0), cirq.H(q1)),
moment_0,
]


def test_require_physical_tag():
q0, q1 = cirq.LineQubit.range(2)
op_id0 = OpIdentifier(cirq.XPowGate, q0)
op_id1 = OpIdentifier(cirq.ZPowGate, q1)
model = InsertionNoiseModel({op_id0: cirq.T(q0), op_id1: cirq.H(q1)})
assert model.require_physical_tag

moment_0 = cirq.Moment(cirq.X(q0).with_tags(PHYSICAL_GATE_TAG), cirq.Z(q1))
assert model.noisy_moment(moment_0, system_qubits=[q0, q1]) == [
moment_0,
cirq.Moment(cirq.T(q0)),
]


def test_supertype_matching():
# Demonstrate that the model applies the closest matching type
# if multiple types match a given gate.
q0 = cirq.LineQubit(0)
op_id0 = OpIdentifier(cirq.Gate, q0)
op_id1 = OpIdentifier(cirq.XPowGate, q0)
model = InsertionNoiseModel(
{op_id0: cirq.T(q0), op_id1: cirq.S(q0)}, require_physical_tag=False
)

moment_0 = cirq.Moment(cirq.Rx(rads=1).on(q0))
assert model.noisy_moment(moment_0, system_qubits=[q0]) == [
moment_0,
cirq.Moment(cirq.S(q0)),
]

moment_1 = cirq.Moment(cirq.Y(q0))
assert model.noisy_moment(moment_1, system_qubits=[q0]) == [
moment_1,
cirq.Moment(cirq.T(q0)),
]