1# Copyright 2020 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.
14from typing import List, Dict, Sequence, Any
15
16import cirq
17import cirq_pasqal
18
19
20class PasqalNoiseModel(cirq.devices.NoiseModel):
21    """A noise model for Pasqal neutral atom device"""
22
23    def __init__(self, device: cirq.devices.Device):
24        self.noise_op_dict = self.get_default_noise_dict()
25        if not isinstance(device, cirq_pasqal.PasqalDevice):
26            raise TypeError(
27                "The noise model varies between Pasqal's devices. "
28                "Please specify the one you intend to execute the "
29                "circuit on."
30            )
31        self.device = device
32
33    def get_default_noise_dict(self) -> Dict[str, Any]:
34        """Returns the current noise parameters"""
35        default_noise_dict = {
36            str(cirq.ops.YPowGate()): cirq.ops.depolarize(1e-2),
37            str(cirq.ops.ZPowGate()): cirq.ops.depolarize(1e-2),
38            str(cirq.ops.XPowGate()): cirq.ops.depolarize(1e-2),
39            str(cirq.ops.PhasedXPowGate(phase_exponent=0)): cirq.ops.depolarize(1e-2),
40            str(cirq.ops.HPowGate(exponent=1)): cirq.ops.depolarize(1e-2),
41            str(cirq.ops.CNotPowGate(exponent=1)): cirq.ops.depolarize(3e-2),
42            str(cirq.ops.CZPowGate(exponent=1)): cirq.ops.depolarize(3e-2),
43            str(cirq.ops.CCXPowGate(exponent=1)): cirq.ops.depolarize(8e-2),
44            str(cirq.ops.CCZPowGate(exponent=1)): cirq.ops.depolarize(8e-2),
45        }
46        return default_noise_dict
47
48    def noisy_moment(
49        self, moment: cirq.ops.Moment, system_qubits: Sequence[cirq.ops.Qid]
50    ) -> List[cirq.ops.Operation]:
51        """Returns a list of noisy moments.
52        The model includes
53        - Depolarizing noise with gate-dependent strength
54        Args:
55            moment: ideal moment
56            system_qubits: List of qubits
57        Returns:
58            List of ideal and noisy moments
59        """
60        noise_list = []
61        for op in moment:
62            op_str = self.get_op_string(op)
63            noise_op = self.noise_op_dict.get(op_str, cirq.ops.depolarize(5e-2))
64            for qubit in op.qubits:
65                noise_list.append(noise_op.on(qubit))
66        return list(moment) + noise_list
67
68    # TODO(#3388) Add documentation for Raises.
69    # pylint: disable=missing-raises-doc
70    def get_op_string(self, cirq_op: cirq.ops.Operation) -> str:
71        """Find the string representation for a given operation.
72
73        Args:
74            cirq_op: one cirq operation.
75
76        Returns:
77            String representing the gate operations.
78        """
79        if not self.device.is_pasqal_device_op(cirq_op) or isinstance(
80            cirq_op.gate, cirq.ops.MeasurementGate
81        ):
82            raise ValueError('Got unknown operation:', cirq_op)
83
84        return str(cirq_op.gate)
85
86    # pylint: enable=missing-raises-doc
87