Skip to content

Commit

Permalink
Insertion noise model (quantumlib#4672)
Browse files Browse the repository at this point in the history
This PR is part of quantumlib#4640. It adds the `InsertionNoiseModel`, which injects noise based on a user-defined map.

quantumlib#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`
  • Loading branch information
95-martin-orion authored and MichaelBroughton committed Jan 22, 2022
1 parent 658c46c commit c7c0fa8
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 0 deletions.
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
68 changes: 68 additions & 0 deletions cirq-core/cirq/devices/insertion_noise_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# 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 dataclasses
from typing import TYPE_CHECKING, Dict, List, Optional, Sequence

from cirq import devices, ops
from cirq.devices import noise_utils

if TYPE_CHECKING:
import cirq


@dataclasses.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[noise_utils.OpIdentifier, 'cirq.Operation'] = dataclasses.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':
noise_ops: List['cirq.Operation'] = []
candidate_ops = [
op
for op in moment
if (not self.require_physical_tag) or noise_utils.PHYSICAL_GATE_TAG in op.tags
]
for op in candidate_ops:
match_id: Optional[noise_utils.OpIdentifier] = None
candidate_ids = [op_id for op_id in self.ops_added if op in op_id]
for op_id in candidate_ids:
if match_id is None or 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)),
]

0 comments on commit c7c0fa8

Please sign in to comment.