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.
14
15import functools
16import itertools
17
18import numpy as np
19import pytest
20
21import cirq
22from cirq.testing import (
23    EqualsTester,
24    assert_allclose_up_to_global_phase,
25)
26
27_bools = (False, True)
28_paulis = (cirq.X, cirq.Y, cirq.Z)
29
30
31def _assert_not_mirror(gate) -> None:
32    trans_x = gate.transform(cirq.X)
33    trans_y = gate.transform(cirq.Y)
34    trans_z = gate.transform(cirq.Z)
35    right_handed = (
36        trans_x.flip ^ trans_y.flip ^ trans_z.flip ^ (trans_x.to.relative_index(trans_y.to) != 1)
37    )
38    assert right_handed, 'Mirrors'
39
40
41def _assert_no_collision(gate) -> None:
42    trans_x = gate.transform(cirq.X)
43    trans_y = gate.transform(cirq.Y)
44    trans_z = gate.transform(cirq.Z)
45    assert trans_x.to != trans_y.to, 'Collision'
46    assert trans_y.to != trans_z.to, 'Collision'
47    assert trans_z.to != trans_x.to, 'Collision'
48
49
50def _all_rotations():
51    for (
52        pauli,
53        flip,
54    ) in itertools.product(_paulis, _bools):
55        yield cirq.PauliTransform(pauli, flip)
56
57
58def _all_rotation_pairs():
59    for px, flip_x, pz, flip_z in itertools.product(_paulis, _bools, _paulis, _bools):
60        if px == pz:
61            continue
62        yield cirq.PauliTransform(px, flip_x), cirq.PauliTransform(pz, flip_z)
63
64
65def _all_clifford_gates():
66    for trans_x, trans_z in _all_rotation_pairs():
67        yield cirq.SingleQubitCliffordGate.from_xz_map(trans_x, trans_z)
68
69
70@pytest.mark.parametrize('pauli,flip_x,flip_z', itertools.product(_paulis, _bools, _bools))
71def test_init_value_error(pauli, flip_x, flip_z):
72    with pytest.raises(ValueError):
73        cirq.SingleQubitCliffordGate.from_xz_map((pauli, flip_x), (pauli, flip_z))
74
75
76@pytest.mark.parametrize('trans_x,trans_z', _all_rotation_pairs())
77def test_init_from_xz(trans_x, trans_z):
78    gate = cirq.SingleQubitCliffordGate.from_xz_map(trans_x, trans_z)
79    assert gate.transform(cirq.X) == trans_x
80    assert gate.transform(cirq.Z) == trans_z
81    _assert_not_mirror(gate)
82    _assert_no_collision(gate)
83
84
85@pytest.mark.parametrize(
86    'trans1,trans2,from1',
87    (
88        (trans1, trans2, from1)
89        for trans1, trans2, from1 in itertools.product(_all_rotations(), _all_rotations(), _paulis)
90        if trans1.to != trans2.to
91    ),
92)
93def test_init_from_double_map_vs_kwargs(trans1, trans2, from1):
94    from2 = cirq.Pauli.by_relative_index(from1, 1)
95    from1_str, from2_str = (str(frm).lower() + '_to' for frm in (from1, from2))
96    gate_kw = cirq.SingleQubitCliffordGate.from_double_map(**{from1_str: trans1, from2_str: trans2})
97    gate_map = cirq.SingleQubitCliffordGate.from_double_map({from1: trans1, from2: trans2})
98    # Test initializes the same gate
99    assert gate_kw == gate_map
100
101    # Test initializes what was expected
102    assert gate_map.transform(from1) == trans1
103    assert gate_map.transform(from2) == trans2
104    _assert_not_mirror(gate_map)
105    _assert_no_collision(gate_map)
106
107
108@pytest.mark.parametrize(
109    'trans1,from1',
110    ((trans1, from1) for trans1, from1 in itertools.product(_all_rotations(), _paulis)),
111)
112def test_init_from_double_invalid(trans1, from1):
113    from2 = cirq.Pauli.by_relative_index(from1, 1)
114    # Test throws on invalid arguments
115    with pytest.raises(ValueError):
116        cirq.SingleQubitCliffordGate.from_double_map({from1: trans1, from2: trans1})
117
118
119@pytest.mark.parametrize('trans,frm', itertools.product(_all_rotations(), _paulis))
120def test_init_from_single_map_vs_kwargs(trans, frm):
121    from_str = str(frm).lower() + '_to'
122    # pylint: disable=unexpected-keyword-arg
123    gate_kw = cirq.SingleQubitCliffordGate.from_single_map(**{from_str: trans})
124    gate_map = cirq.SingleQubitCliffordGate.from_single_map({frm: trans})
125    assert gate_kw == gate_map
126
127
128@pytest.mark.parametrize(
129    'trans,frm',
130    (
131        (trans, frm)
132        for trans, frm in itertools.product(_all_rotations(), _paulis)
133        if trans.to != frm
134    ),
135)
136def test_init_90rot_from_single(trans, frm):
137    gate = cirq.SingleQubitCliffordGate.from_single_map({frm: trans})
138    assert gate.transform(frm) == trans
139    _assert_not_mirror(gate)
140    _assert_no_collision(gate)
141    # Check that it decomposes to one gate
142    assert len(gate.decompose_rotation()) == 1
143    # Check that this is a 90 degree rotation gate
144    assert (
145        gate.merged_with(gate).merged_with(gate).merged_with(gate) == cirq.SingleQubitCliffordGate.I
146    )
147    # Check that flipping the transform produces the inverse rotation
148    trans_rev = cirq.PauliTransform(trans.to, not trans.flip)
149    gate_rev = cirq.SingleQubitCliffordGate.from_single_map({frm: trans_rev})
150    assert gate ** -1 == gate_rev
151
152
153@pytest.mark.parametrize(
154    'trans,frm',
155    (
156        (trans, frm)
157        for trans, frm in itertools.product(_all_rotations(), _paulis)
158        if trans.to == frm and trans.flip
159    ),
160)
161def test_init_180rot_from_single(trans, frm):
162    gate = cirq.SingleQubitCliffordGate.from_single_map({frm: trans})
163    assert gate.transform(frm) == trans
164    _assert_not_mirror(gate)
165    _assert_no_collision(gate)
166    # Check that it decomposes to one gate
167    assert len(gate.decompose_rotation()) == 1
168    # Check that this is a 180 degree rotation gate
169    assert gate.merged_with(gate) == cirq.SingleQubitCliffordGate.I
170
171
172@pytest.mark.parametrize(
173    'trans,frm',
174    (
175        (trans, frm)
176        for trans, frm in itertools.product(_all_rotations(), _paulis)
177        if trans.to == frm and not trans.flip
178    ),
179)
180def test_init_ident_from_single(trans, frm):
181    gate = cirq.SingleQubitCliffordGate.from_single_map({frm: trans})
182    assert gate.transform(frm) == trans
183    _assert_not_mirror(gate)
184    _assert_no_collision(gate)
185    # Check that it decomposes to zero gates
186    assert len(gate.decompose_rotation()) == 0
187    # Check that this is an identity gate
188    assert gate == cirq.SingleQubitCliffordGate.I
189
190
191@pytest.mark.parametrize(
192    'pauli,sqrt,expected',
193    (
194        (cirq.X, False, cirq.SingleQubitCliffordGate.X),
195        (cirq.Y, False, cirq.SingleQubitCliffordGate.Y),
196        (cirq.Z, False, cirq.SingleQubitCliffordGate.Z),
197        (cirq.X, True, cirq.SingleQubitCliffordGate.X_sqrt),
198        (cirq.Y, True, cirq.SingleQubitCliffordGate.Y_sqrt),
199        (cirq.Z, True, cirq.SingleQubitCliffordGate.Z_sqrt),
200    ),
201)
202def test_init_from_pauli(pauli, sqrt, expected):
203    gate = cirq.SingleQubitCliffordGate.from_pauli(pauli, sqrt=sqrt)
204    assert gate == expected
205
206
207def test_pow():
208    assert cirq.SingleQubitCliffordGate.X ** -1 == cirq.SingleQubitCliffordGate.X
209    assert cirq.SingleQubitCliffordGate.H ** -1 == cirq.SingleQubitCliffordGate.H
210    assert cirq.SingleQubitCliffordGate.X_sqrt == cirq.SingleQubitCliffordGate.X ** 0.5
211    assert cirq.SingleQubitCliffordGate.Y_sqrt == cirq.SingleQubitCliffordGate.Y ** 0.5
212    assert cirq.SingleQubitCliffordGate.Z_sqrt == cirq.SingleQubitCliffordGate.Z ** 0.5
213    assert cirq.SingleQubitCliffordGate.X_nsqrt == cirq.SingleQubitCliffordGate.X ** -0.5
214    assert cirq.SingleQubitCliffordGate.Y_nsqrt == cirq.SingleQubitCliffordGate.Y ** -0.5
215    assert cirq.SingleQubitCliffordGate.Z_nsqrt == cirq.SingleQubitCliffordGate.Z ** -0.5
216    assert cirq.SingleQubitCliffordGate.X_sqrt ** -1 == cirq.SingleQubitCliffordGate.X_nsqrt
217    assert cirq.inverse(cirq.SingleQubitCliffordGate.X_nsqrt) == (
218        cirq.SingleQubitCliffordGate.X_sqrt
219    )
220    with pytest.raises(TypeError):
221        _ = cirq.SingleQubitCliffordGate.Z ** 0.25
222
223
224def test_init_from_quarter_turns():
225    eq = cirq.testing.EqualsTester()
226    eq.add_equality_group(
227        cirq.SingleQubitCliffordGate.from_quarter_turns(cirq.X, 0),
228        cirq.SingleQubitCliffordGate.from_quarter_turns(cirq.Y, 0),
229        cirq.SingleQubitCliffordGate.from_quarter_turns(cirq.Z, 0),
230        cirq.SingleQubitCliffordGate.from_quarter_turns(cirq.X, 4),
231        cirq.SingleQubitCliffordGate.from_quarter_turns(cirq.Y, 4),
232        cirq.SingleQubitCliffordGate.from_quarter_turns(cirq.Z, 4),
233        cirq.SingleQubitCliffordGate.from_quarter_turns(cirq.X, 8),
234        cirq.SingleQubitCliffordGate.from_quarter_turns(cirq.Y, 8),
235        cirq.SingleQubitCliffordGate.from_quarter_turns(cirq.Z, 8),
236        cirq.SingleQubitCliffordGate.from_quarter_turns(cirq.X, -4),
237        cirq.SingleQubitCliffordGate.from_quarter_turns(cirq.Y, -4),
238        cirq.SingleQubitCliffordGate.from_quarter_turns(cirq.Z, -4),
239    )
240    eq.add_equality_group(
241        cirq.SingleQubitCliffordGate.from_quarter_turns(cirq.X, 1),
242        cirq.SingleQubitCliffordGate.from_quarter_turns(cirq.X, 5),
243        cirq.SingleQubitCliffordGate.from_quarter_turns(cirq.X, 9),
244        cirq.SingleQubitCliffordGate.from_quarter_turns(cirq.X, -3),
245    )
246    eq.add_equality_group(
247        cirq.SingleQubitCliffordGate.from_quarter_turns(cirq.Y, 1),
248        cirq.SingleQubitCliffordGate.from_quarter_turns(cirq.Y, 5),
249        cirq.SingleQubitCliffordGate.from_quarter_turns(cirq.Y, 9),
250        cirq.SingleQubitCliffordGate.from_quarter_turns(cirq.Y, -3),
251    )
252    eq.add_equality_group(
253        cirq.SingleQubitCliffordGate.from_quarter_turns(cirq.Z, 1),
254        cirq.SingleQubitCliffordGate.from_quarter_turns(cirq.Z, 5),
255        cirq.SingleQubitCliffordGate.from_quarter_turns(cirq.Z, 9),
256        cirq.SingleQubitCliffordGate.from_quarter_turns(cirq.Z, -3),
257    )
258    eq.add_equality_group(
259        cirq.SingleQubitCliffordGate.from_quarter_turns(cirq.X, 2),
260        cirq.SingleQubitCliffordGate.from_quarter_turns(cirq.X, 6),
261    )
262    eq.add_equality_group(
263        cirq.SingleQubitCliffordGate.from_quarter_turns(cirq.X, 3),
264        cirq.SingleQubitCliffordGate.from_quarter_turns(cirq.X, 7),
265    )
266
267
268@pytest.mark.parametrize('gate', _all_clifford_gates())
269def test_init_from_quarter_turns_reconstruct(gate):
270    new_gate = functools.reduce(
271        cirq.SingleQubitCliffordGate.merged_with,
272        (
273            cirq.SingleQubitCliffordGate.from_quarter_turns(pauli, qt)
274            for pauli, qt in gate.decompose_rotation()
275        ),
276        cirq.SingleQubitCliffordGate.I,
277    )
278    assert gate == new_gate
279
280
281def test_init_invalid():
282    with pytest.raises(ValueError):
283        cirq.SingleQubitCliffordGate.from_single_map()
284    with pytest.raises(ValueError):
285        cirq.SingleQubitCliffordGate.from_single_map({})
286    with pytest.raises(ValueError):
287        cirq.SingleQubitCliffordGate.from_single_map(
288            {cirq.X: (cirq.X, False)}, y_to=(cirq.Y, False)
289        )
290    with pytest.raises(ValueError):
291        cirq.SingleQubitCliffordGate.from_single_map(
292            {cirq.X: (cirq.X, False), cirq.Y: (cirq.Y, False)}
293        )
294    with pytest.raises(ValueError):
295        cirq.SingleQubitCliffordGate.from_double_map()
296    with pytest.raises(ValueError):
297        cirq.SingleQubitCliffordGate.from_double_map({})
298    with pytest.raises(ValueError):
299        cirq.SingleQubitCliffordGate.from_double_map({cirq.X: (cirq.X, False)})
300    with pytest.raises(ValueError):
301        cirq.SingleQubitCliffordGate.from_double_map(x_to=(cirq.X, False))
302    with pytest.raises(ValueError):
303        cirq.SingleQubitCliffordGate.from_single_map(
304            {cirq.X: (cirq.Y, False), cirq.Y: (cirq.Z, False), cirq.Z: (cirq.X, False)}
305        )
306    with pytest.raises(ValueError):
307        cirq.SingleQubitCliffordGate.from_single_map(
308            {cirq.X: (cirq.X, False), cirq.Y: (cirq.X, False)}
309        )
310
311
312def test_eq_ne_and_hash():
313    eq = EqualsTester()
314    for trans_x, trans_z in _all_rotation_pairs():
315        gate_gen = lambda: cirq.SingleQubitCliffordGate.from_xz_map(trans_x, trans_z)
316        eq.make_equality_group(gate_gen)
317
318
319@pytest.mark.parametrize(
320    'gate,rep',
321    (
322        (cirq.SingleQubitCliffordGate.I, 'cirq.SingleQubitCliffordGate(X:+X, Y:+Y, Z:+Z)'),
323        (cirq.SingleQubitCliffordGate.H, 'cirq.SingleQubitCliffordGate(X:+Z, Y:-Y, Z:+X)'),
324        (cirq.SingleQubitCliffordGate.X, 'cirq.SingleQubitCliffordGate(X:+X, Y:-Y, Z:-Z)'),
325        (cirq.SingleQubitCliffordGate.X_sqrt, 'cirq.SingleQubitCliffordGate(X:+X, Y:+Z, Z:-Y)'),
326    ),
327)
328def test_repr(gate, rep):
329    assert repr(gate) == rep
330
331
332@pytest.mark.parametrize(
333    'gate,trans_y',
334    (
335        (cirq.SingleQubitCliffordGate.I, (cirq.Y, False)),
336        (cirq.SingleQubitCliffordGate.H, (cirq.Y, True)),
337        (cirq.SingleQubitCliffordGate.X, (cirq.Y, True)),
338        (cirq.SingleQubitCliffordGate.Y, (cirq.Y, False)),
339        (cirq.SingleQubitCliffordGate.Z, (cirq.Y, True)),
340        (cirq.SingleQubitCliffordGate.X_sqrt, (cirq.Z, False)),
341        (cirq.SingleQubitCliffordGate.X_nsqrt, (cirq.Z, True)),
342        (cirq.SingleQubitCliffordGate.Y_sqrt, (cirq.Y, False)),
343        (cirq.SingleQubitCliffordGate.Y_nsqrt, (cirq.Y, False)),
344        (cirq.SingleQubitCliffordGate.Z_sqrt, (cirq.X, True)),
345        (cirq.SingleQubitCliffordGate.Z_nsqrt, (cirq.X, False)),
346    ),
347)
348def test_y_rotation(gate, trans_y):
349    assert gate.transform(cirq.Y) == trans_y
350
351
352@pytest.mark.parametrize(
353    'gate,gate_equiv',
354    (
355        (cirq.SingleQubitCliffordGate.I, cirq.X ** 0),
356        (cirq.SingleQubitCliffordGate.H, cirq.H),
357        (cirq.SingleQubitCliffordGate.X, cirq.X),
358        (cirq.SingleQubitCliffordGate.Y, cirq.Y),
359        (cirq.SingleQubitCliffordGate.Z, cirq.Z),
360        (cirq.SingleQubitCliffordGate.X_sqrt, cirq.X ** 0.5),
361        (cirq.SingleQubitCliffordGate.X_nsqrt, cirq.X ** -0.5),
362        (cirq.SingleQubitCliffordGate.Y_sqrt, cirq.Y ** 0.5),
363        (cirq.SingleQubitCliffordGate.Y_nsqrt, cirq.Y ** -0.5),
364        (cirq.SingleQubitCliffordGate.Z_sqrt, cirq.Z ** 0.5),
365        (cirq.SingleQubitCliffordGate.Z_nsqrt, cirq.Z ** -0.5),
366    ),
367)
368def test_decompose(gate, gate_equiv):
369    q0 = cirq.NamedQubit('q0')
370    mat = cirq.Circuit(gate(q0)).unitary()
371    mat_check = cirq.Circuit(
372        gate_equiv(q0),
373    ).unitary()
374    assert_allclose_up_to_global_phase(mat, mat_check, rtol=1e-7, atol=1e-7)
375
376
377@pytest.mark.parametrize(
378    'gate,gate_equiv',
379    (
380        (cirq.SingleQubitCliffordGate.I, cirq.X ** 0),
381        (cirq.SingleQubitCliffordGate.H, cirq.H),
382        (cirq.SingleQubitCliffordGate.X, cirq.X),
383        (cirq.SingleQubitCliffordGate.Y, cirq.Y),
384        (cirq.SingleQubitCliffordGate.Z, cirq.Z),
385        (cirq.SingleQubitCliffordGate.X_sqrt, cirq.X ** 0.5),
386        (cirq.SingleQubitCliffordGate.X_nsqrt, cirq.X ** -0.5),
387        (cirq.SingleQubitCliffordGate.Y_sqrt, cirq.Y ** 0.5),
388        (cirq.SingleQubitCliffordGate.Y_nsqrt, cirq.Y ** -0.5),
389        (cirq.SingleQubitCliffordGate.Z_sqrt, cirq.Z ** 0.5),
390        (cirq.SingleQubitCliffordGate.Z_nsqrt, cirq.Z ** -0.5),
391    ),
392)
393def test_known_matrix(gate, gate_equiv):
394    assert cirq.has_unitary(gate)
395    mat = cirq.unitary(gate)
396    mat_check = cirq.unitary(gate_equiv)
397    assert_allclose_up_to_global_phase(mat, mat_check, rtol=1e-7, atol=1e-7)
398
399
400@pytest.mark.parametrize('gate', _all_clifford_gates())
401def test_inverse(gate):
402    assert gate == cirq.inverse(cirq.inverse(gate))
403
404
405@pytest.mark.parametrize('gate', _all_clifford_gates())
406def test_inverse_matrix(gate):
407    q0 = cirq.NamedQubit('q0')
408    mat = cirq.Circuit(gate(q0)).unitary()
409    mat_inv = cirq.Circuit(cirq.inverse(gate)(q0)).unitary()
410    assert_allclose_up_to_global_phase(mat, mat_inv.T.conj(), rtol=1e-7, atol=1e-7)
411
412
413def test_commutes_notimplemented_type():
414    with pytest.raises(TypeError):
415        cirq.commutes(cirq.SingleQubitCliffordGate.X, 'X')
416    assert cirq.commutes(cirq.SingleQubitCliffordGate.X, 'X', default='default') == 'default'
417
418
419@pytest.mark.parametrize(
420    'gate,other', itertools.product(_all_clifford_gates(), _all_clifford_gates())
421)
422def test_commutes_single_qubit_gate(gate, other):
423    q0 = cirq.NamedQubit('q0')
424    gate_op = gate(q0)
425    other_op = other(q0)
426    mat = cirq.Circuit(
427        gate_op,
428        other_op,
429    ).unitary()
430    mat_swap = cirq.Circuit(
431        other_op,
432        gate_op,
433    ).unitary()
434    commutes = cirq.commutes(gate, other)
435    commutes_check = cirq.allclose_up_to_global_phase(mat, mat_swap)
436    assert commutes == commutes_check
437
438    # Test after switching order
439    mat_swap = cirq.Circuit(
440        gate.equivalent_gate_before(other)(q0),
441        gate_op,
442    ).unitary()
443    assert_allclose_up_to_global_phase(mat, mat_swap, rtol=1e-7, atol=1e-7)
444
445
446@pytest.mark.parametrize('gate', _all_clifford_gates())
447def test_parses_single_qubit_gate(gate):
448    assert gate == cirq.read_json(json_text=(cirq.to_json(gate)))
449
450
451@pytest.mark.parametrize(
452    'gate,pauli,half_turns',
453    itertools.product(_all_clifford_gates(), _paulis, (1.0, 0.25, 0.5, -0.5)),
454)
455def test_commutes_pauli(gate, pauli, half_turns):
456    # TODO(#4328) cirq.X**1 should be _PauliX instead of XPowGate
457    pauli_gate = pauli if half_turns == 1 else pauli ** half_turns
458    q0 = cirq.NamedQubit('q0')
459    mat = cirq.Circuit(
460        gate(q0),
461        pauli_gate(q0),
462    ).unitary()
463    mat_swap = cirq.Circuit(
464        pauli_gate(q0),
465        gate(q0),
466    ).unitary()
467    commutes = cirq.commutes(gate, pauli_gate)
468    commutes_check = np.allclose(mat, mat_swap)
469    assert commutes == commutes_check, f"gate: {gate}, pauli {pauli}"
470
471
472def test_to_clifford_tableau_util_function():
473
474    tableau = cirq.ops.clifford_gate._to_clifford_tableau(
475        x_to=cirq.PauliTransform(to=cirq.X, flip=False),
476        z_to=cirq.PauliTransform(to=cirq.Z, flip=False),
477    )
478    assert tableau == cirq.CliffordTableau(num_qubits=1, initial_state=0)
479
480    tableau = cirq.ops.clifford_gate._to_clifford_tableau(
481        x_to=cirq.PauliTransform(to=cirq.X, flip=False),
482        z_to=cirq.PauliTransform(to=cirq.Z, flip=True),
483    )
484    assert tableau == cirq.CliffordTableau(num_qubits=1, initial_state=1)
485
486    tableau = cirq.ops.clifford_gate._to_clifford_tableau(
487        rotation_map={
488            cirq.X: cirq.PauliTransform(to=cirq.X, flip=False),
489            cirq.Z: cirq.PauliTransform(to=cirq.Z, flip=False),
490        }
491    )
492    assert tableau == cirq.CliffordTableau(num_qubits=1, initial_state=0)
493
494    tableau = cirq.ops.clifford_gate._to_clifford_tableau(
495        rotation_map={
496            cirq.X: cirq.PauliTransform(to=cirq.X, flip=False),
497            cirq.Z: cirq.PauliTransform(to=cirq.Z, flip=True),
498        }
499    )
500    assert tableau == cirq.CliffordTableau(num_qubits=1, initial_state=1)
501
502    with pytest.raises(ValueError):
503        cirq.ops.clifford_gate._to_clifford_tableau()
504
505
506@pytest.mark.parametrize(
507    'gate,sym,exp',
508    (
509        (cirq.SingleQubitCliffordGate.I, 'I', 1),
510        (cirq.SingleQubitCliffordGate.H, 'H', 1),
511        (cirq.SingleQubitCliffordGate.X, 'X', 1),
512        (cirq.SingleQubitCliffordGate.X_sqrt, 'X', 0.5),
513        (cirq.SingleQubitCliffordGate.X_nsqrt, 'X', -0.5),
514        (
515            cirq.SingleQubitCliffordGate.from_xz_map((cirq.Y, False), (cirq.X, True)),
516            '(X^-0.5-Z^0.5)',
517            1,
518        ),
519    ),
520)
521def test_text_diagram_info(gate, sym, exp):
522    assert cirq.circuit_diagram_info(gate) == cirq.CircuitDiagramInfo(
523        wire_symbols=(sym,), exponent=exp
524    )
525
526
527def test_from_unitary():
528    def _test(clifford_gate):
529        u = cirq.unitary(clifford_gate)
530        result_gate = cirq.SingleQubitCliffordGate.from_unitary(u)
531        assert result_gate == clifford_gate
532
533    _test(cirq.SingleQubitCliffordGate.I)
534    _test(cirq.SingleQubitCliffordGate.H)
535    _test(cirq.SingleQubitCliffordGate.X)
536    _test(cirq.SingleQubitCliffordGate.Y)
537    _test(cirq.SingleQubitCliffordGate.Z)
538    _test(cirq.SingleQubitCliffordGate.X_nsqrt)
539
540
541def test_from_unitary_with_phase_shift():
542    u = np.exp(0.42j) * cirq.unitary(cirq.SingleQubitCliffordGate.Y_sqrt)
543    gate = cirq.SingleQubitCliffordGate.from_unitary(u)
544
545    assert gate == cirq.SingleQubitCliffordGate.Y_sqrt
546
547
548def test_from_unitary_not_clifford():
549    # Not a single-qubit gate.
550    u = cirq.unitary(cirq.CNOT)
551    assert cirq.SingleQubitCliffordGate.from_unitary(u) is None
552
553    # Not an unitary matrix.
554    u = 2 * cirq.unitary(cirq.X)
555    assert cirq.SingleQubitCliffordGate.from_unitary(u) is None
556
557    # Not a Clifford gate.
558    u = cirq.unitary(cirq.T)
559    assert cirq.SingleQubitCliffordGate.from_unitary(u) is None
560
561
562@pytest.mark.parametrize('trans_x,trans_z', _all_rotation_pairs())
563def test_to_phased_xz_gate(trans_x, trans_z):
564    gate = cirq.SingleQubitCliffordGate.from_xz_map(trans_x, trans_z)
565    actual_phased_xz_gate = gate.to_phased_xz_gate()._canonical()
566    expect_phased_xz_gates = cirq.PhasedXZGate.from_matrix(cirq.unitary(gate))
567
568    assert np.isclose(actual_phased_xz_gate.x_exponent, expect_phased_xz_gates.x_exponent)
569    assert np.isclose(actual_phased_xz_gate.z_exponent, expect_phased_xz_gates.z_exponent)
570    assert np.isclose(
571        actual_phased_xz_gate.axis_phase_exponent, expect_phased_xz_gates.axis_phase_exponent
572    )
573
574
575def test_from_xz_to_clifford_tableau():
576    seen_tableau = []
577    for trans_x, trans_z in _all_rotation_pairs():
578        tableau = cirq.SingleQubitCliffordGate.from_xz_map(trans_x, trans_z).clifford_tableau
579        tableau_number = sum(2 ** i * t for i, t in enumerate(tableau.matrix().ravel()))
580        tableau_number = tableau_number * 4 + 2 * tableau.rs[0] + tableau.rs[1]
581        seen_tableau.append(tableau_number)
582        # Satisfy the symplectic property
583        assert sum(tableau.matrix()[0, :2] * tableau.matrix()[1, 1::-1]) % 2 == 1
584
585    # Should not have any duplication.
586    assert len(set(seen_tableau)) == 24
587