1# Copyright 2018 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.
14import abc
15from typing import Any, cast, Tuple, TYPE_CHECKING, Union, Dict
16
17from cirq._doc import document
18from cirq.ops import common_gates, raw_types, identity
19from cirq.type_workarounds import NotImplementedType
20
21
22if TYPE_CHECKING:
23    import cirq
24    from cirq.ops.pauli_string import SingleQubitPauliStringGateOperation
25    from cirq.value.product_state import (
26        _XEigenState,
27        _YEigenState,
28        _ZEigenState,
29    )  # coverage: ignore
30
31
32class Pauli(raw_types.Gate, metaclass=abc.ABCMeta):
33    """Represents the Pauli gates.
34
35    This is an abstract class with no public subclasses. The only instances
36    of private subclasses are the X, Y, or Z Pauli gates defined below.
37    """
38
39    _XYZ = None  # type: Tuple[Pauli, Pauli, Pauli]
40
41    @staticmethod
42    def by_index(index: int) -> 'Pauli':
43        return Pauli._XYZ[index % 3]
44
45    @staticmethod
46    def by_relative_index(p: 'Pauli', relative_index: int) -> 'Pauli':
47        return Pauli._XYZ[(p._index + relative_index) % 3]
48
49    def __init__(self, index: int, name: str) -> None:
50        self._index = index
51        self._name = name
52
53    def num_qubits(self):
54        return 1
55
56    def _commutes_(self, other: Any, atol: float) -> Union[bool, NotImplementedType, None]:
57        if not isinstance(other, Pauli):
58            return NotImplemented
59        return self is other
60
61    def third(self, second: 'Pauli') -> 'Pauli':
62        return Pauli._XYZ[(-self._index - second._index) % 3]
63
64    def relative_index(self, second: 'Pauli') -> int:
65        """Relative index of self w.r.t. second in the (X, Y, Z) cycle."""
66        return (self._index - second._index + 1) % 3 - 1
67
68    def phased_pauli_product(
69        self, other: Union['cirq.Pauli', 'identity.IdentityGate']
70    ) -> Tuple[complex, Union['cirq.Pauli', 'identity.IdentityGate']]:
71        if self == other:
72            return 1, identity.I
73        if other is identity.I:
74            return 1, self
75        return 1j ** cast(Pauli, other).relative_index(self), self.third(cast(Pauli, other))
76
77    def __gt__(self, other):
78        if not isinstance(other, Pauli):
79            return NotImplemented
80        return (self._index - other._index) % 3 == 1
81
82    def __lt__(self, other):
83        if not isinstance(other, Pauli):
84            return NotImplemented
85        return (other._index - self._index) % 3 == 1
86
87    # TODO(#3388) Add documentation for Raises.
88    # pylint: disable=missing-raises-doc
89    def on(self, *qubits: 'cirq.Qid') -> 'SingleQubitPauliStringGateOperation':
90        """Returns an application of this gate to the given qubits.
91
92        Args:
93            *qubits: The collection of qubits to potentially apply the gate to.
94        """
95        if len(qubits) != 1:
96            raise ValueError(f'Expected a single qubit, got <{qubits!r}>.')
97        from cirq.ops.pauli_string import SingleQubitPauliStringGateOperation
98
99        return SingleQubitPauliStringGateOperation(self, qubits[0])
100
101    # pylint: enable=missing-raises-doc
102    @property
103    def _canonical_exponent(self):
104        """Overrides EigenGate._canonical_exponent in subclasses."""
105        return 1
106
107
108class _PauliX(Pauli, common_gates.XPowGate):
109    def __init__(self):
110        Pauli.__init__(self, index=0, name='X')
111        common_gates.XPowGate.__init__(self, exponent=1.0)
112
113    def __pow__(self: '_PauliX', exponent: 'cirq.TParamVal') -> common_gates.XPowGate:
114        return common_gates.XPowGate(exponent=exponent) if exponent != 1 else _PauliX()
115
116    def _with_exponent(self: '_PauliX', exponent: 'cirq.TParamVal') -> common_gates.XPowGate:
117        return self.__pow__(exponent)
118
119    @classmethod
120    def _from_json_dict_(cls, exponent, global_shift, **kwargs):
121        assert global_shift == 0
122        assert exponent == 1
123        return Pauli._XYZ[0]
124
125    @property
126    def basis(self: '_PauliX') -> Dict[int, '_XEigenState']:
127        from cirq.value.product_state import _XEigenState
128
129        return {
130            +1: _XEigenState(+1),
131            -1: _XEigenState(-1),
132        }
133
134
135class _PauliY(Pauli, common_gates.YPowGate):
136    def __init__(self):
137        Pauli.__init__(self, index=1, name='Y')
138        common_gates.YPowGate.__init__(self, exponent=1.0)
139
140    def __pow__(self: '_PauliY', exponent: 'cirq.TParamVal') -> common_gates.YPowGate:
141        return common_gates.YPowGate(exponent=exponent) if exponent != 1 else _PauliY()
142
143    def _with_exponent(self: '_PauliY', exponent: 'cirq.TParamVal') -> common_gates.YPowGate:
144        return self.__pow__(exponent)
145
146    @classmethod
147    def _from_json_dict_(cls, exponent, global_shift, **kwargs):
148        assert global_shift == 0
149        assert exponent == 1
150        return Pauli._XYZ[1]
151
152    @property
153    def basis(self: '_PauliY') -> Dict[int, '_YEigenState']:
154        from cirq.value.product_state import _YEigenState
155
156        return {
157            +1: _YEigenState(+1),
158            -1: _YEigenState(-1),
159        }
160
161
162class _PauliZ(Pauli, common_gates.ZPowGate):
163    def __init__(self):
164        Pauli.__init__(self, index=2, name='Z')
165        common_gates.ZPowGate.__init__(self, exponent=1.0)
166
167    def __pow__(self: '_PauliZ', exponent: 'cirq.TParamVal') -> common_gates.ZPowGate:
168        return common_gates.ZPowGate(exponent=exponent) if exponent != 1 else _PauliZ()
169
170    def _with_exponent(self: '_PauliZ', exponent: 'cirq.TParamVal') -> common_gates.ZPowGate:
171        return self.__pow__(exponent)
172
173    @classmethod
174    def _from_json_dict_(cls, exponent, global_shift, **kwargs):
175        assert global_shift == 0
176        assert exponent == 1
177        return Pauli._XYZ[2]
178
179    @property
180    def basis(self: '_PauliZ') -> Dict[int, '_ZEigenState']:
181        from cirq.value.product_state import _ZEigenState
182
183        return {
184            +1: _ZEigenState(+1),
185            -1: _ZEigenState(-1),
186        }
187
188
189X = _PauliX()
190document(
191    X,
192    """The Pauli X gate.
193
194    Matrix:
195
196        [[0, 1],
197         [1, 0]]
198    """,
199)
200
201Y = _PauliY()
202document(
203    Y,
204    """The Pauli Y gate.
205
206    Matrix:
207
208        [[0, -i],
209         [i, 0]]
210    """,
211)
212
213Z = _PauliZ()
214document(
215    Z,
216    """The Pauli Z gate.
217
218    Matrix:
219
220        [[1, 0],
221         [0, -1]]
222    """,
223)
224
225Pauli._XYZ = (X, Y, Z)
226