Skip to content

Commit df99587

Browse files
authored
Fix up types of common clifford gates (#5585)
Review: @viathor
1 parent e99d9ca commit df99587

File tree

2 files changed

+89
-67
lines changed

2 files changed

+89
-67
lines changed

cirq-core/cirq/ops/clifford_gate.py

Lines changed: 63 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -141,145 +141,141 @@ def _pad_tableau(
141141
return padded_tableau
142142

143143

144+
def _gate_tableau(num_qubits: int, gate: raw_types.Gate) -> 'cirq.CliffordTableau':
145+
qubits = devices.LineQubit.range(num_qubits)
146+
t = qis.CliffordTableau(num_qubits=num_qubits)
147+
args = sim.CliffordTableauSimulationState(
148+
tableau=t, qubits=qubits, prng=np.random.RandomState()
149+
)
150+
protocols.act_on(gate, args, qubits, allow_decompose=False)
151+
return args.tableau
152+
153+
144154
class CommonCliffordGateMetaClass(value.ABCMetaImplementAnyOneOf):
145155
"""A metaclass used to lazy initialize several common Clifford Gate as class attributes."""
146156

157+
# These are class properties so we define them as properties on a metaclass.
158+
# Note that in python 3.9+ @classmethod can be used with @property, so these
159+
# can be moved to CommonCliffordGates.
160+
147161
@property
148-
def I(cls):
149-
if getattr(cls, '_I', None) is None:
150-
cls._I = cls._generate_clifford_from_known_gate(1, identity.I)
162+
def I(cls) -> 'cirq.SingleQubitCliffordGate':
163+
if not hasattr(cls, '_I'):
164+
cls._I = SingleQubitCliffordGate.from_clifford_tableau(_gate_tableau(1, identity.I))
151165
return cls._I
152166

153167
@property
154-
def X(cls):
155-
if getattr(cls, '_X', None) is None:
156-
cls._X = cls._generate_clifford_from_known_gate(1, pauli_gates.X)
168+
def X(cls) -> 'cirq.SingleQubitCliffordGate':
169+
if not hasattr(cls, '_X'):
170+
cls._X = SingleQubitCliffordGate.from_clifford_tableau(_gate_tableau(1, pauli_gates.X))
157171
return cls._X
158172

159173
@property
160-
def Y(cls):
161-
if getattr(cls, '_Y', None) is None:
162-
cls._Y = cls._generate_clifford_from_known_gate(1, pauli_gates.Y)
174+
def Y(cls) -> 'cirq.SingleQubitCliffordGate':
175+
if not hasattr(cls, '_Y'):
176+
cls._Y = SingleQubitCliffordGate.from_clifford_tableau(_gate_tableau(1, pauli_gates.Y))
163177
return cls._Y
164178

165179
@property
166-
def Z(cls):
167-
if getattr(cls, '_Z', None) is None:
168-
cls._Z = cls._generate_clifford_from_known_gate(1, pauli_gates.Z)
180+
def Z(cls) -> 'cirq.SingleQubitCliffordGate':
181+
if not hasattr(cls, '_Z'):
182+
cls._Z = SingleQubitCliffordGate.from_clifford_tableau(_gate_tableau(1, pauli_gates.Z))
169183
return cls._Z
170184

171185
@property
172-
def H(cls):
173-
if getattr(cls, '_H', None) is None:
174-
cls._H = cls._generate_clifford_from_known_gate(1, common_gates.H)
186+
def H(cls) -> 'cirq.SingleQubitCliffordGate':
187+
if not hasattr(cls, '_H'):
188+
cls._H = SingleQubitCliffordGate.from_clifford_tableau(_gate_tableau(1, common_gates.H))
175189
return cls._H
176190

177191
@property
178-
def S(cls):
179-
if getattr(cls, '_S', None) is None:
180-
cls._S = cls._generate_clifford_from_known_gate(1, common_gates.S)
192+
def S(cls) -> 'cirq.SingleQubitCliffordGate':
193+
if not hasattr(cls, '_S'):
194+
cls._S = SingleQubitCliffordGate.from_clifford_tableau(_gate_tableau(1, common_gates.S))
181195
return cls._S
182196

183197
@property
184-
def CNOT(cls):
185-
if getattr(cls, '_CNOT', None) is None:
186-
cls._CNOT = cls._generate_clifford_from_known_gate(2, common_gates.CNOT)
198+
def CNOT(cls) -> 'cirq.CliffordGate':
199+
if not hasattr(cls, '_CNOT'):
200+
cls._CNOT = CliffordGate.from_clifford_tableau(_gate_tableau(2, common_gates.CNOT))
187201
return cls._CNOT
188202

189203
@property
190-
def CZ(cls):
191-
if getattr(cls, '_CZ', None) is None:
192-
cls._CZ = cls._generate_clifford_from_known_gate(2, common_gates.CZ)
204+
def CZ(cls) -> 'cirq.CliffordGate':
205+
if not hasattr(cls, '_CZ'):
206+
cls._CZ = CliffordGate.from_clifford_tableau(_gate_tableau(2, common_gates.CZ))
193207
return cls._CZ
194208

195209
@property
196-
def SWAP(cls):
197-
if getattr(cls, '_SWAP', None) is None:
198-
cls._SWAP = cls._generate_clifford_from_known_gate(2, common_gates.SWAP)
210+
def SWAP(cls) -> 'cirq.CliffordGate':
211+
if not hasattr(cls, '_SWAP'):
212+
cls._SWAP = CliffordGate.from_clifford_tableau(_gate_tableau(2, common_gates.SWAP))
199213
return cls._SWAP
200214

201215
@property
202-
def X_sqrt(cls):
203-
if getattr(cls, '_X_sqrt', None) is None:
216+
def X_sqrt(cls) -> 'cirq.SingleQubitCliffordGate':
217+
if not hasattr(cls, '_X_sqrt'):
204218
# Unfortunately, due the code style, the matrix should be viewed transposed.
205219
# Note xs, zs, and rs are column vector.
206220
# Transformation: X -> X, Z -> -Y
207-
_clifford_tableau = qis.CliffordTableau._from_json_dict_(
221+
clifford_tableau = qis.CliffordTableau._from_json_dict_(
208222
n=1, rs=[0, 1], xs=[[1], [1]], zs=[[0], [1]]
209223
)
210-
cls._X_sqrt = cls.from_clifford_tableau(_clifford_tableau)
224+
cls._X_sqrt = SingleQubitCliffordGate.from_clifford_tableau(clifford_tableau)
211225
return cls._X_sqrt
212226

213227
@property
214-
def X_nsqrt(cls):
215-
if getattr(cls, '_X_nsqrt', None) is None:
228+
def X_nsqrt(cls) -> 'cirq.SingleQubitCliffordGate':
229+
if not hasattr(cls, '_X_nsqrt'):
216230
# Transformation: X->X, Z->Y
217-
_clifford_tableau = qis.CliffordTableau._from_json_dict_(
231+
clifford_tableau = qis.CliffordTableau._from_json_dict_(
218232
n=1, rs=[0, 0], xs=[[1], [1]], zs=[[0], [1]]
219233
)
220-
cls._X_nsqrt = cls.from_clifford_tableau(_clifford_tableau)
234+
cls._X_nsqrt = SingleQubitCliffordGate.from_clifford_tableau(clifford_tableau)
221235
return cls._X_nsqrt
222236

223237
@property
224-
def Y_sqrt(cls):
225-
if getattr(cls, '_Y_sqrt', None) is None:
238+
def Y_sqrt(cls) -> 'cirq.SingleQubitCliffordGate':
239+
if not hasattr(cls, '_Y_sqrt'):
226240
# Transformation: X -> -Z, Z -> X
227-
_clifford_tableau = qis.CliffordTableau._from_json_dict_(
241+
clifford_tableau = qis.CliffordTableau._from_json_dict_(
228242
n=1, rs=[1, 0], xs=[[0], [1]], zs=[[1], [0]]
229243
)
230-
cls._Y_sqrt = cls.from_clifford_tableau(_clifford_tableau)
244+
cls._Y_sqrt = SingleQubitCliffordGate.from_clifford_tableau(clifford_tableau)
231245
return cls._Y_sqrt
232246

233247
@property
234-
def Y_nsqrt(cls):
235-
if getattr(cls, '_Y_nsqrt', None) is None:
248+
def Y_nsqrt(cls) -> 'cirq.SingleQubitCliffordGate':
249+
if not hasattr(cls, '_Y_nsqrt'):
236250
# Transformation: X -> Z, Z -> -X
237-
_clifford_tableau = qis.CliffordTableau._from_json_dict_(
251+
clifford_tableau = qis.CliffordTableau._from_json_dict_(
238252
n=1, rs=[0, 1], xs=[[0], [1]], zs=[[1], [0]]
239253
)
240-
cls._Y_nsqrt = cls.from_clifford_tableau(_clifford_tableau)
254+
cls._Y_nsqrt = SingleQubitCliffordGate.from_clifford_tableau(clifford_tableau)
241255
return cls._Y_nsqrt
242256

243257
@property
244-
def Z_sqrt(cls):
245-
if getattr(cls, '_Z_sqrt', None) is None:
258+
def Z_sqrt(cls) -> 'cirq.SingleQubitCliffordGate':
259+
if not hasattr(cls, '_Z_sqrt'):
246260
# Transformation: X -> Y, Z -> Z
247261
_clifford_tableau = qis.CliffordTableau._from_json_dict_(
248262
n=1, rs=[0, 0], xs=[[1], [0]], zs=[[1], [1]]
249263
)
250-
cls._Z_sqrt = cls.from_clifford_tableau(_clifford_tableau)
264+
cls._Z_sqrt = SingleQubitCliffordGate.from_clifford_tableau(_clifford_tableau)
251265
return cls._Z_sqrt
252266

253267
@property
254-
def Z_nsqrt(cls):
255-
if getattr(cls, '_Z_nsqrt', None) is None:
268+
def Z_nsqrt(cls) -> 'cirq.SingleQubitCliffordGate':
269+
if not hasattr(cls, '_Z_nsqrt'):
256270
# Transformation: X -> -Y, Z -> Z
257271
_clifford_tableau = qis.CliffordTableau._from_json_dict_(
258272
n=1, rs=[1, 0], xs=[[1], [0]], zs=[[1], [1]]
259273
)
260-
cls._Z_nsqrt = cls.from_clifford_tableau(_clifford_tableau)
274+
cls._Z_nsqrt = SingleQubitCliffordGate.from_clifford_tableau(_clifford_tableau)
261275
return cls._Z_nsqrt
262276

263277

264278
class CommonCliffordGates(metaclass=CommonCliffordGateMetaClass):
265-
266-
# We need to use the lazy initialization of these common gates since they need to use
267-
# cirq.sim, which can not be imported when
268-
@classmethod
269-
def _generate_clifford_from_known_gate(
270-
cls, num_qubits: int, gate: raw_types.Gate
271-
) -> Union['SingleQubitCliffordGate', 'CliffordGate']:
272-
qubits = devices.LineQubit.range(num_qubits)
273-
t = qis.CliffordTableau(num_qubits=num_qubits)
274-
args = sim.CliffordTableauSimulationState(
275-
tableau=t, qubits=qubits, prng=np.random.RandomState()
276-
)
277-
278-
protocols.act_on(gate, args, qubits, allow_decompose=False)
279-
if num_qubits == 1:
280-
return SingleQubitCliffordGate.from_clifford_tableau(args.tableau)
281-
return CliffordGate.from_clifford_tableau(args.tableau)
282-
283279
@classmethod
284280
def from_clifford_tableau(cls, tableau: qis.CliffordTableau) -> 'CliffordGate':
285281
"""Create the CliffordGate instance from Clifford Tableau.

cirq-core/cirq/ops/clifford_gate_test.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import functools
1616
import itertools
17+
from typing import Type
1718

1819
import numpy as np
1920
import pytest
@@ -403,6 +404,31 @@ def test_known_matrix(gate, gate_equiv):
403404
assert_allclose_up_to_global_phase(mat, mat_check, rtol=1e-7, atol=1e-7)
404405

405406

407+
@pytest.mark.parametrize(
408+
'name, expected_cls',
409+
[
410+
('I', cirq.SingleQubitCliffordGate),
411+
('H', cirq.SingleQubitCliffordGate),
412+
('X', cirq.SingleQubitCliffordGate),
413+
('Y', cirq.SingleQubitCliffordGate),
414+
('Z', cirq.SingleQubitCliffordGate),
415+
('S', cirq.SingleQubitCliffordGate),
416+
('X_sqrt', cirq.SingleQubitCliffordGate),
417+
('X_nsqrt', cirq.SingleQubitCliffordGate),
418+
('Y_sqrt', cirq.SingleQubitCliffordGate),
419+
('Y_nsqrt', cirq.SingleQubitCliffordGate),
420+
('Z_sqrt', cirq.SingleQubitCliffordGate),
421+
('Z_nsqrt', cirq.SingleQubitCliffordGate),
422+
('CNOT', cirq.CliffordGate),
423+
('CZ', cirq.CliffordGate),
424+
('SWAP', cirq.CliffordGate),
425+
],
426+
)
427+
def test_common_clifford_types(name: str, expected_cls: Type) -> None:
428+
assert isinstance(getattr(cirq.CliffordGate, name), expected_cls)
429+
assert isinstance(getattr(cirq.SingleQubitCliffordGate, name), expected_cls)
430+
431+
406432
@pytest.mark.parametrize('gate', _all_clifford_gates())
407433
def test_inverse(gate):
408434
assert gate == cirq.inverse(cirq.inverse(gate))

0 commit comments

Comments
 (0)