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