12
12
# See the License for the specific language governing permissions and
13
13
# limitations under the License.
14
14
15
- from typing import (
16
- Any ,
17
- cast ,
18
- Dict ,
19
- List ,
20
- NamedTuple ,
21
- Optional ,
22
- Sequence ,
23
- Tuple ,
24
- TYPE_CHECKING ,
25
- Union ,
26
- )
15
+ import dataclasses
16
+ from typing import Any , cast , Dict , List , Optional , Sequence , Tuple , TYPE_CHECKING , Union
17
+
27
18
28
19
import numpy as np
29
20
30
- from cirq import protocols , value , linalg , qis
31
- from cirq ._doc import document
21
+ from cirq import _compat , protocols , value , linalg , qis
32
22
from cirq ._import import LazyLoader
33
23
from cirq .ops import common_gates , identity , named_qubit , raw_types , pauli_gates , phased_x_z_gate
34
24
from cirq .ops .pauli_gates import Pauli
42
32
sim = LazyLoader ("sim" , globals (), "cirq.sim" )
43
33
transformers = LazyLoader ("transformers" , globals (), "cirq.transformers" )
44
34
45
- PauliTransform = NamedTuple ('PauliTransform' , [('to' , Pauli ), ('flip' , bool )])
46
- document (PauliTransform , """+X, -X, +Y, -Y, +Z, or -Z.""" )
35
+
36
+ @_compat .deprecated_class (deadline = 'v0.16' , fix = 'Use DensePauliString instead.' )
37
+ @dataclasses .dataclass
38
+ class PauliTransform :
39
+ to : Pauli
40
+ flip : bool
47
41
48
42
49
- def _to_pauli_transform (matrix : np .ndarray ) -> Optional [PauliTransform ]:
43
+ def _to_pauli_tuple (matrix : np .ndarray ) -> Optional [Tuple [ Pauli , bool ] ]:
50
44
"""Converts matrix to PauliTransform.
51
45
52
46
If matrix is not ±Pauli matrix, returns None.
53
47
"""
54
48
for pauli in Pauli ._XYZ :
55
49
p = protocols .unitary (pauli )
56
50
if np .allclose (matrix , p ):
57
- return PauliTransform (pauli , False )
51
+ return (pauli , False )
58
52
if np .allclose (matrix , - p ):
59
- return PauliTransform (pauli , True )
53
+ return (pauli , True )
60
54
return None
61
55
62
56
63
57
def _to_clifford_tableau (
64
- rotation_map : Optional [Dict [Pauli , PauliTransform ]] = None ,
58
+ rotation_map : Optional [Dict [Pauli , Tuple [ Pauli , bool ] ]] = None ,
65
59
* ,
66
- x_to : Optional [PauliTransform ] = None ,
67
- z_to : Optional [PauliTransform ] = None ,
60
+ x_to : Optional [Tuple [ Pauli , bool ] ] = None ,
61
+ z_to : Optional [Tuple [ Pauli , bool ] ] = None ,
68
62
) -> qis .CliffordTableau :
69
63
"""Transfer the rotation map to clifford tableau representation"""
70
64
if x_to is None and z_to is None and rotation_map is None :
@@ -79,13 +73,13 @@ def _to_clifford_tableau(
79
73
assert x_to is not None and z_to is not None , "Both x_to and z_to have to be provided."
80
74
81
75
clifford_tableau = qis .CliffordTableau (num_qubits = 1 )
82
- clifford_tableau .xs [0 , 0 ] = x_to . to in (pauli_gates .X , pauli_gates .Y )
83
- clifford_tableau .zs [0 , 0 ] = x_to . to in (pauli_gates .Y , pauli_gates .Z )
76
+ clifford_tableau .xs [0 , 0 ] = x_to [ 0 ] in (pauli_gates .X , pauli_gates .Y )
77
+ clifford_tableau .zs [0 , 0 ] = x_to [ 0 ] in (pauli_gates .Y , pauli_gates .Z )
84
78
85
- clifford_tableau .xs [1 , 0 ] = z_to . to in (pauli_gates .X , pauli_gates .Y )
86
- clifford_tableau .zs [1 , 0 ] = z_to . to in (pauli_gates .Y , pauli_gates .Z )
79
+ clifford_tableau .xs [1 , 0 ] = z_to [ 0 ] in (pauli_gates .X , pauli_gates .Y )
80
+ clifford_tableau .zs [1 , 0 ] = z_to [ 0 ] in (pauli_gates .Y , pauli_gates .Z )
87
81
88
- clifford_tableau .rs = (x_to . flip , z_to . flip )
82
+ clifford_tableau .rs = (x_to [ 1 ] , z_to [ 1 ] )
89
83
return clifford_tableau
90
84
91
85
@@ -101,7 +95,7 @@ def _validate_map_input(
101
95
x_to : Optional [Tuple [Pauli , bool ]],
102
96
y_to : Optional [Tuple [Pauli , bool ]],
103
97
z_to : Optional [Tuple [Pauli , bool ]],
104
- ) -> Dict [Pauli , PauliTransform ]:
98
+ ) -> Dict [Pauli , Tuple [ Pauli , bool ] ]:
105
99
if pauli_map_to is None :
106
100
xyz_to = {pauli_gates .X : x_to , pauli_gates .Y : y_to , pauli_gates .Z : z_to }
107
101
pauli_map_to = {cast (Pauli , p ): trans for p , trans in xyz_to .items () if trans is not None }
@@ -121,7 +115,7 @@ def _validate_map_input(
121
115
)
122
116
if len (set ((to for to , _ in pauli_map_to .values ()))) != len (pauli_map_to ):
123
117
raise ValueError ('A rotation cannot map two Paulis to the same' )
124
- return {frm : PauliTransform (to , flip ) for frm , (to , flip ) in pauli_map_to .items ()}
118
+ return {frm : (to , flip ) for frm , (to , flip ) in pauli_map_to .items ()}
125
119
126
120
127
121
def _pad_tableau (
@@ -505,7 +499,7 @@ def from_xz_map(
505
499
z_to: Which Pauli to transform Z to and if it should negate.
506
500
"""
507
501
return SingleQubitCliffordGate .from_clifford_tableau (
508
- _to_clifford_tableau (x_to = PauliTransform ( * x_to ) , z_to = PauliTransform ( * z_to ) )
502
+ _to_clifford_tableau (x_to = x_to , z_to = z_to )
509
503
)
510
504
511
505
@staticmethod
@@ -538,7 +532,7 @@ def from_single_map(
538
532
trans_from2 = trans_to
539
533
trans_to2 = trans_from
540
534
flip2 = not flip
541
- rotation_map [trans_from2 ] = PauliTransform (trans_to2 , flip2 )
535
+ rotation_map [trans_from2 ] = (trans_to2 , flip2 )
542
536
return SingleQubitCliffordGate .from_double_map (
543
537
cast (Dict [Pauli , Tuple [Pauli , bool ]], rotation_map )
544
538
)
@@ -566,9 +560,9 @@ def from_double_map(
566
560
rotation_map = _validate_map_input (2 , pauli_map_to , x_to = x_to , y_to = y_to , z_to = z_to )
567
561
(from1 , trans1 ), (from2 , trans2 ) = tuple (rotation_map .items ())
568
562
from3 = from1 .third (from2 )
569
- to3 = trans1 . to . third (trans2 . to )
570
- flip3 = trans1 . flip ^ trans2 . flip ^ ((from1 < from2 ) != (trans1 . to < trans2 . to ))
571
- rotation_map [from3 ] = PauliTransform (to3 , flip3 )
563
+ to3 = trans1 [ 0 ]. third (trans2 [ 0 ] )
564
+ flip3 = trans1 [ 1 ] ^ trans2 [ 1 ] ^ ((from1 < from2 ) != (trans1 [ 0 ] < trans2 [ 0 ] ))
565
+ rotation_map [from3 ] = (to3 , flip3 )
572
566
573
567
return SingleQubitCliffordGate .from_clifford_tableau (_to_clifford_tableau (rotation_map ))
574
568
@@ -578,15 +572,15 @@ def from_pauli(pauli: Pauli, sqrt: bool = False) -> 'SingleQubitCliffordGate':
578
572
next_pauli = Pauli .by_relative_index (pauli , 1 )
579
573
if sqrt :
580
574
rotation_map = {
581
- prev_pauli : PauliTransform (next_pauli , True ),
582
- pauli : PauliTransform (pauli , False ),
583
- next_pauli : PauliTransform (prev_pauli , False ),
575
+ prev_pauli : (next_pauli , True ),
576
+ pauli : (pauli , False ),
577
+ next_pauli : (prev_pauli , False ),
584
578
}
585
579
else :
586
580
rotation_map = {
587
- prev_pauli : PauliTransform (prev_pauli , True ),
588
- pauli : PauliTransform (pauli , False ),
589
- next_pauli : PauliTransform (next_pauli , True ),
581
+ prev_pauli : (prev_pauli , True ),
582
+ pauli : (pauli , False ),
583
+ next_pauli : (next_pauli , True ),
590
584
}
591
585
return SingleQubitCliffordGate .from_clifford_tableau (_to_clifford_tableau (rotation_map ))
592
586
@@ -618,15 +612,22 @@ def from_unitary(u: np.ndarray) -> Optional['SingleQubitCliffordGate']:
618
612
return None
619
613
x = protocols .unitary (pauli_gates .X )
620
614
z = protocols .unitary (pauli_gates .Z )
621
- x_to = _to_pauli_transform (u @ x @ u .conj ().T )
622
- z_to = _to_pauli_transform (u @ z @ u .conj ().T )
615
+ x_to = _to_pauli_tuple (u @ x @ u .conj ().T )
616
+ z_to = _to_pauli_tuple (u @ z @ u .conj ().T )
623
617
if x_to is None or z_to is None :
624
618
return None
625
619
return SingleQubitCliffordGate .from_clifford_tableau (
626
620
_to_clifford_tableau (x_to = x_to , z_to = z_to )
627
621
)
628
622
629
- def transform (self , pauli : Pauli ) -> PauliTransform :
623
+ def pauli_tuple (self , pauli : Pauli ) -> Tuple [Pauli , bool ]:
624
+ """Returns a tuple of a Pauli operator and a boolean.
625
+
626
+ The pauli is the operator of the transform and the boolean
627
+ determines whether the operator should be flipped. For instance,
628
+ it is True if the coefficient is -1, and False if the coefficient
629
+ is 1.
630
+ """
630
631
x_to = self ._clifford_tableau .destabilizers ()[0 ]
631
632
z_to = self ._clifford_tableau .stabilizers ()[0 ]
632
633
if pauli == pauli_gates .X :
@@ -638,7 +639,19 @@ def transform(self, pauli: Pauli) -> PauliTransform:
638
639
to ._coefficient *= 1j
639
640
# pauli_mask returns a value between 0 and 4 for [I, X, Y, Z].
640
641
to_gate = Pauli ._XYZ [to .pauli_mask [0 ] - 1 ]
641
- return PauliTransform (to = to_gate , flip = bool (to .coefficient != 1.0 ))
642
+ return (to_gate , bool (to .coefficient != 1.0 ))
643
+
644
+ def dense_pauli_string (self , pauli : Pauli ) -> 'cirq.DensePauliString' :
645
+ from cirq .ops import dense_pauli_string
646
+
647
+ pauli_tuple = self .pauli_tuple (pauli )
648
+ coefficient = - 1 if pauli_tuple [1 ] else 1
649
+ return dense_pauli_string .DensePauliString (str (pauli_tuple [0 ]), coefficient = coefficient )
650
+
651
+ @_compat .deprecated (deadline = 'v0.16' , fix = 'Use pauli_tuple() or dense_pauli_string() instead' )
652
+ def transform (self , pauli : Pauli ) -> PauliTransform :
653
+ pauli_tuple = self .pauli_tuple (pauli )
654
+ return PauliTransform (to = pauli_tuple [0 ], flip = pauli_tuple [1 ])
642
655
643
656
def to_phased_xz_gate (self ) -> phased_x_z_gate .PhasedXZGate :
644
657
"""Convert this gate to a PhasedXZGate instance.
@@ -739,7 +752,7 @@ def commutes_with_single_qubit_gate(self, gate: 'SingleQubitCliffordGate') -> bo
739
752
return self_then_gate == gate_then_self
740
753
741
754
def commutes_with_pauli (self , pauli : Pauli ) -> bool :
742
- to , flip = self .transform (pauli )
755
+ to , flip = self .pauli_tuple (pauli )
743
756
return to == pauli and not flip
744
757
745
758
def merged_with (self , second : 'SingleQubitCliffordGate' ) -> 'SingleQubitCliffordGate' :
@@ -764,16 +777,16 @@ def decompose_rotation(self) -> Sequence[Tuple[Pauli, int]]:
764
777
"""Returns ((first_rotation_axis, first_rotation_quarter_turns), ...)
765
778
766
779
This is a sequence of zero, one, or two rotations."""
767
- x_rot = self .transform (pauli_gates .X )
768
- y_rot = self .transform (pauli_gates .Y )
769
- z_rot = self .transform (pauli_gates .Z )
780
+ x_rot = self .pauli_tuple (pauli_gates .X )
781
+ y_rot = self .pauli_tuple (pauli_gates .Y )
782
+ z_rot = self .pauli_tuple (pauli_gates .Z )
770
783
whole_arr = (
771
- x_rot . to == pauli_gates .X ,
772
- y_rot . to == pauli_gates .Y ,
773
- z_rot . to == pauli_gates .Z ,
784
+ x_rot [ 0 ] == pauli_gates .X ,
785
+ y_rot [ 0 ] == pauli_gates .Y ,
786
+ z_rot [ 0 ] == pauli_gates .Z ,
774
787
)
775
788
num_whole = sum (whole_arr )
776
- flip_arr = (x_rot . flip , y_rot . flip , z_rot . flip )
789
+ flip_arr = (x_rot [ 1 ] , y_rot [ 1 ] , z_rot [ 1 ] )
777
790
num_flip = sum (flip_arr )
778
791
if num_whole == 3 :
779
792
if num_flip == 0 :
@@ -793,7 +806,7 @@ def decompose_rotation(self) -> Sequence[Tuple[Pauli, int]]:
793
806
# 180 degree rotation
794
807
output .append ((next_pauli , 2 ))
795
808
# 90 degree rotation about some axis
796
- if self .transform (next_pauli ). flip :
809
+ if self .pauli_tuple (next_pauli )[ 1 ] :
797
810
# Negative 90 degree rotation
798
811
output .append ((pauli , - 1 ))
799
812
else :
@@ -802,16 +815,13 @@ def decompose_rotation(self) -> Sequence[Tuple[Pauli, int]]:
802
815
return output
803
816
elif num_whole == 0 :
804
817
# Gate is a 120 degree rotation
805
- if x_rot . to == pauli_gates .Y :
818
+ if x_rot [ 0 ] == pauli_gates .Y :
806
819
return [
807
- (pauli_gates .X , - 1 if y_rot . flip else 1 ),
808
- (pauli_gates .Z , - 1 if x_rot . flip else 1 ),
820
+ (pauli_gates .X , - 1 if y_rot [ 1 ] else 1 ),
821
+ (pauli_gates .Z , - 1 if x_rot [ 1 ] else 1 ),
809
822
]
810
823
811
- return [
812
- (pauli_gates .Z , 1 if y_rot .flip else - 1 ),
813
- (pauli_gates .X , 1 if z_rot .flip else - 1 ),
814
- ]
824
+ return [(pauli_gates .Z , 1 if y_rot [1 ] else - 1 ), (pauli_gates .X , 1 if z_rot [1 ] else - 1 )]
815
825
# coverage: ignore
816
826
assert (
817
827
False
@@ -824,15 +834,15 @@ def equivalent_gate_before(self, after: 'SingleQubitCliffordGate') -> 'SingleQub
824
834
return self .merged_with (after ).merged_with (self ** - 1 )
825
835
826
836
def __repr__ (self ) -> str :
827
- x = self .transform (pauli_gates .X )
828
- y = self .transform (pauli_gates .Y )
829
- z = self .transform (pauli_gates .Z )
830
- x_sign = '-' if x . flip else '+'
831
- y_sign = '-' if y . flip else '+'
832
- z_sign = '-' if z . flip else '+'
837
+ x = self .pauli_tuple (pauli_gates .X )
838
+ y = self .pauli_tuple (pauli_gates .Y )
839
+ z = self .pauli_tuple (pauli_gates .Z )
840
+ x_sign = '-' if x [ 1 ] else '+'
841
+ y_sign = '-' if y [ 1 ] else '+'
842
+ z_sign = '-' if z [ 1 ] else '+'
833
843
return (
834
- f'cirq.SingleQubitCliffordGate(X:{ x_sign } { x . to !s} , '
835
- f'Y:{ y_sign } { y . to !s} , Z:{ z_sign } { z . to !s} )'
844
+ f'cirq.SingleQubitCliffordGate(X:{ x_sign } { x [ 0 ] !s} , '
845
+ f'Y:{ y_sign } { y [ 0 ] !s} , Z:{ z_sign } { z [ 0 ] !s} )'
836
846
)
837
847
838
848
def _circuit_diagram_info_ (
0 commit comments