15
15
from typing import Any , Optional
16
16
17
17
from cirq .ops .clifford_gate import SingleQubitCliffordGate
18
+ from cirq .ops .dense_pauli_string import DensePauliString
19
+ from cirq ._import import LazyLoader
20
+ import cirq .protocols .unitary_protocol as unitary_protocol
21
+ import cirq .protocols .has_unitary_protocol as has_unitary_protocol
22
+ import cirq .protocols .qid_shape_protocol as qid_shape_protocol
23
+ import cirq .protocols .decompose_protocol as decompose_protocol
18
24
19
- from cirq import protocols
25
+ pauli_string_decomposition = LazyLoader (
26
+ "pauli_string_decomposition" ,
27
+ globals (),
28
+ "cirq.transformers.analytical_decompositions.pauli_string_decomposition" ,
29
+ )
20
30
21
31
22
32
def has_stabilizer_effect (val : Any ) -> bool :
@@ -29,6 +39,7 @@ def has_stabilizer_effect(val: Any) -> bool:
29
39
_strat_has_stabilizer_effect_from_has_stabilizer_effect ,
30
40
_strat_has_stabilizer_effect_from_gate ,
31
41
_strat_has_stabilizer_effect_from_unitary ,
42
+ _strat_has_stabilizer_effect_from_decompose ,
32
43
]
33
44
for strat in strats :
34
45
result = strat (val )
@@ -62,9 +73,44 @@ def _strat_has_stabilizer_effect_from_unitary(val: Any) -> Optional[bool]:
62
73
2x2 unitaries.
63
74
"""
64
75
# Do not try this strategy if there is no unitary or if the number of
65
- # qubits is not 1 since that would be expensive.
66
- qid_shape = protocols .qid_shape (val , default = None )
67
- if qid_shape is None or len (qid_shape ) != 1 or not protocols .has_unitary (val ):
76
+ # qubits is greater than 3 since that would be expensive.
77
+ qid_shape = qid_shape_protocol .qid_shape (val , default = None )
78
+ if (
79
+ qid_shape is None
80
+ or len (qid_shape ) > 3
81
+ or qid_shape != (2 ,) * len (qid_shape )
82
+ or not has_unitary_protocol .has_unitary (val )
83
+ ):
68
84
return None
69
- unitary = protocols .unitary (val )
70
- return SingleQubitCliffordGate .from_unitary (unitary ) is not None
85
+ unitary = unitary_protocol .unitary (val )
86
+ if len (qid_shape ) == 1 :
87
+ return SingleQubitCliffordGate .from_unitary (unitary ) is not None
88
+
89
+ # Check if the action of the unitary on each single qubit pauli string leads to a pauli product.
90
+ # Source: https://quantumcomputing.stackexchange.com/a/13158
91
+ for q_idx in range (len (qid_shape )):
92
+ for g in 'XZ' :
93
+ pauli_string = ['I' ] * len (qid_shape )
94
+ pauli_string [q_idx ] = g
95
+ ps = DensePauliString (pauli_string )
96
+ p = ps ._unitary_ ()
97
+ if not pauli_string_decomposition .unitary_to_pauli_string (
98
+ (unitary @ p @ unitary .T .conj ())
99
+ ):
100
+ return False
101
+ return True
102
+
103
+
104
+ def _strat_has_stabilizer_effect_from_decompose (val : Any ) -> Optional [bool ]:
105
+ qid_shape = qid_shape_protocol .qid_shape (val , default = None )
106
+ if qid_shape is None or len (qid_shape ) <= 3 :
107
+ return None
108
+
109
+ decomposition = decompose_protocol .decompose_once (val , default = None )
110
+ if decomposition is None :
111
+ return None
112
+ for op in decomposition :
113
+ res = has_stabilizer_effect (op )
114
+ if not res :
115
+ return res
116
+ return True
0 commit comments