1212# See the License for the specific language governing permissions and
1313# limitations under the License.
1414
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+
2718
2819import numpy as np
2920
30- from cirq import protocols , value , linalg , qis
31- from cirq ._doc import document
21+ from cirq import _compat , protocols , value , linalg , qis
3222from cirq ._import import LazyLoader
3323from cirq .ops import common_gates , identity , named_qubit , raw_types , pauli_gates , phased_x_z_gate
3424from cirq .ops .pauli_gates import Pauli
4232sim = LazyLoader ("sim" , globals (), "cirq.sim" )
4333transformers = LazyLoader ("transformers" , globals (), "cirq.transformers" )
4434
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
4741
4842
49- def _to_pauli_transform (matrix : np .ndarray ) -> Optional [PauliTransform ]:
43+ def _to_pauli_tuple (matrix : np .ndarray ) -> Optional [Tuple [ Pauli , bool ] ]:
5044 """Converts matrix to PauliTransform.
5145
5246 If matrix is not ±Pauli matrix, returns None.
5347 """
5448 for pauli in Pauli ._XYZ :
5549 p = protocols .unitary (pauli )
5650 if np .allclose (matrix , p ):
57- return PauliTransform (pauli , False )
51+ return (pauli , False )
5852 if np .allclose (matrix , - p ):
59- return PauliTransform (pauli , True )
53+ return (pauli , True )
6054 return None
6155
6256
6357def _to_clifford_tableau (
64- rotation_map : Optional [Dict [Pauli , PauliTransform ]] = None ,
58+ rotation_map : Optional [Dict [Pauli , Tuple [ Pauli , bool ] ]] = None ,
6559 * ,
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 ,
6862) -> qis .CliffordTableau :
6963 """Transfer the rotation map to clifford tableau representation"""
7064 if x_to is None and z_to is None and rotation_map is None :
@@ -79,13 +73,13 @@ def _to_clifford_tableau(
7973 assert x_to is not None and z_to is not None , "Both x_to and z_to have to be provided."
8074
8175 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 )
8478
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 )
8781
88- clifford_tableau .rs = (x_to . flip , z_to . flip )
82+ clifford_tableau .rs = (x_to [ 1 ] , z_to [ 1 ] )
8983 return clifford_tableau
9084
9185
@@ -101,7 +95,7 @@ def _validate_map_input(
10195 x_to : Optional [Tuple [Pauli , bool ]],
10296 y_to : Optional [Tuple [Pauli , bool ]],
10397 z_to : Optional [Tuple [Pauli , bool ]],
104- ) -> Dict [Pauli , PauliTransform ]:
98+ ) -> Dict [Pauli , Tuple [ Pauli , bool ] ]:
10599 if pauli_map_to is None :
106100 xyz_to = {pauli_gates .X : x_to , pauli_gates .Y : y_to , pauli_gates .Z : z_to }
107101 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(
121115 )
122116 if len (set ((to for to , _ in pauli_map_to .values ()))) != len (pauli_map_to ):
123117 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 ()}
125119
126120
127121def _pad_tableau (
@@ -505,7 +499,7 @@ def from_xz_map(
505499 z_to: Which Pauli to transform Z to and if it should negate.
506500 """
507501 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 )
509503 )
510504
511505 @staticmethod
@@ -538,7 +532,7 @@ def from_single_map(
538532 trans_from2 = trans_to
539533 trans_to2 = trans_from
540534 flip2 = not flip
541- rotation_map [trans_from2 ] = PauliTransform (trans_to2 , flip2 )
535+ rotation_map [trans_from2 ] = (trans_to2 , flip2 )
542536 return SingleQubitCliffordGate .from_double_map (
543537 cast (Dict [Pauli , Tuple [Pauli , bool ]], rotation_map )
544538 )
@@ -566,9 +560,9 @@ def from_double_map(
566560 rotation_map = _validate_map_input (2 , pauli_map_to , x_to = x_to , y_to = y_to , z_to = z_to )
567561 (from1 , trans1 ), (from2 , trans2 ) = tuple (rotation_map .items ())
568562 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 )
572566
573567 return SingleQubitCliffordGate .from_clifford_tableau (_to_clifford_tableau (rotation_map ))
574568
@@ -578,15 +572,15 @@ def from_pauli(pauli: Pauli, sqrt: bool = False) -> 'SingleQubitCliffordGate':
578572 next_pauli = Pauli .by_relative_index (pauli , 1 )
579573 if sqrt :
580574 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 ),
584578 }
585579 else :
586580 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 ),
590584 }
591585 return SingleQubitCliffordGate .from_clifford_tableau (_to_clifford_tableau (rotation_map ))
592586
@@ -618,15 +612,22 @@ def from_unitary(u: np.ndarray) -> Optional['SingleQubitCliffordGate']:
618612 return None
619613 x = protocols .unitary (pauli_gates .X )
620614 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 )
623617 if x_to is None or z_to is None :
624618 return None
625619 return SingleQubitCliffordGate .from_clifford_tableau (
626620 _to_clifford_tableau (x_to = x_to , z_to = z_to )
627621 )
628622
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+ """
630631 x_to = self ._clifford_tableau .destabilizers ()[0 ]
631632 z_to = self ._clifford_tableau .stabilizers ()[0 ]
632633 if pauli == pauli_gates .X :
@@ -638,7 +639,19 @@ def transform(self, pauli: Pauli) -> PauliTransform:
638639 to ._coefficient *= 1j
639640 # pauli_mask returns a value between 0 and 4 for [I, X, Y, Z].
640641 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 ])
642655
643656 def to_phased_xz_gate (self ) -> phased_x_z_gate .PhasedXZGate :
644657 """Convert this gate to a PhasedXZGate instance.
@@ -739,7 +752,7 @@ def commutes_with_single_qubit_gate(self, gate: 'SingleQubitCliffordGate') -> bo
739752 return self_then_gate == gate_then_self
740753
741754 def commutes_with_pauli (self , pauli : Pauli ) -> bool :
742- to , flip = self .transform (pauli )
755+ to , flip = self .pauli_tuple (pauli )
743756 return to == pauli and not flip
744757
745758 def merged_with (self , second : 'SingleQubitCliffordGate' ) -> 'SingleQubitCliffordGate' :
@@ -764,16 +777,16 @@ def decompose_rotation(self) -> Sequence[Tuple[Pauli, int]]:
764777 """Returns ((first_rotation_axis, first_rotation_quarter_turns), ...)
765778
766779 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 )
770783 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 ,
774787 )
775788 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 ] )
777790 num_flip = sum (flip_arr )
778791 if num_whole == 3 :
779792 if num_flip == 0 :
@@ -793,7 +806,7 @@ def decompose_rotation(self) -> Sequence[Tuple[Pauli, int]]:
793806 # 180 degree rotation
794807 output .append ((next_pauli , 2 ))
795808 # 90 degree rotation about some axis
796- if self .transform (next_pauli ). flip :
809+ if self .pauli_tuple (next_pauli )[ 1 ] :
797810 # Negative 90 degree rotation
798811 output .append ((pauli , - 1 ))
799812 else :
@@ -802,16 +815,13 @@ def decompose_rotation(self) -> Sequence[Tuple[Pauli, int]]:
802815 return output
803816 elif num_whole == 0 :
804817 # Gate is a 120 degree rotation
805- if x_rot . to == pauli_gates .Y :
818+ if x_rot [ 0 ] == pauli_gates .Y :
806819 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 ),
809822 ]
810823
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 )]
815825 # coverage: ignore
816826 assert (
817827 False
@@ -824,15 +834,15 @@ def equivalent_gate_before(self, after: 'SingleQubitCliffordGate') -> 'SingleQub
824834 return self .merged_with (after ).merged_with (self ** - 1 )
825835
826836 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 '+'
833843 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} )'
836846 )
837847
838848 def _circuit_diagram_info_ (
0 commit comments