@@ -58,19 +58,53 @@ def _small_sample_qubit_pauli_maps():
5858
5959
6060def assert_conjugation (
61- input_ps : cirq .PauliString , ops : cirq .OP_TREE , expected : cirq .PauliString | None
61+ input_ps : cirq .PauliString ,
62+ op : cirq .Operation ,
63+ expected : cirq .PauliString | None = None ,
64+ force_checking_unitary = True ,
65+ ):
66+ """Verifies that conjugating `input_ps` by `op` results in `expected`.
67+
68+ Also ensures that the unitary representation of the Pauli string is
69+ preserved under the conjugation.
70+ """
71+
72+ def _ps_on_qubits (ps : cirq .PauliString , qubits : tuple [cirq .Qid , ...]):
73+ """Extracts a sub-PauliString from a given PauliString, restricted to
74+ a specified subset of qubits.
75+ """
76+ pauli_map = {}
77+ for q , pauli in ps .items ():
78+ if q in qubits :
79+ pauli_map [q ] = pauli
80+ return cirq .PauliString (qubit_pauli_map = pauli_map , coefficient = ps .coefficient )
81+
82+ conjugation = input_ps .conjugated_by (op )
83+ if expected is None or force_checking_unitary :
84+ # Compares the unitary of the conjugation result and the expected unitary.
85+ clifford = cirq .CliffordGate .from_op_list ([op ], op .qubits )
86+ actual_unitary = cirq .unitary (_ps_on_qubits (conjugation , op .qubits ).dense (op .qubits ))
87+ c = cirq .unitary (clifford )
88+ expected_unitary = (
89+ np .conj (c .T ) @ cirq .unitary (_ps_on_qubits (input_ps , op .qubits ).dense (op .qubits )) @ c
90+ )
91+ assert np .allclose (actual_unitary , expected_unitary , atol = 1e-8 )
92+ if expected is not None :
93+ assert conjugation == expected
94+
95+
96+ def assert_conjugation_multi_ops (
97+ input_ps : cirq .PauliString , ops : list [cirq .Operation ], expected : cirq .PauliString | None = None
6298):
6399 conjugation = input_ps .conjugated_by (ops )
64100 if expected is not None :
65101 assert conjugation == expected
66- else : # Compares the unitary of the conjugation result and the expected unitary.
67- op_list = list (cirq .flatten_to_ops (ops ))
68- qubits_of_clifford = [q for op in op_list for q in op .qubits ]
69- clifford = cirq .CliffordGate .from_op_list (op_list , qubits_of_clifford )
70- actual_unitary = cirq .unitary (conjugation .dense (qubits_of_clifford ))
71- c = cirq .unitary (clifford )
72- expected_unitary = np .conj (c .T ) @ cirq .unitary (input_ps .dense (qubits_of_clifford )) @ c
73- assert np .allclose (actual_unitary , expected_unitary , atol = 1e-8 )
102+ # conj_by(op_{n-1}).conj_by(op_{n-1}).....conj_by(op_0)
103+ conj_in_order = input_ps
104+ for op in ops [::- 1 ]:
105+ assert_conjugation (conj_in_order , op )
106+ conj_in_order = conj_in_order .conjugated_by (op )
107+ assert conjugation == conj_in_order
74108
75109
76110def test_eq_ne_hash ():
@@ -741,26 +775,31 @@ def test_pass_operations_over_double(shift: int, t_or_f1: bool, t_or_f2: bool, n
741775 op0 = cirq .PauliInteractionGate (Z , t_or_f1 , X , t_or_f2 )(q0 , q1 )
742776 ps_before = cirq .PauliString (qubit_pauli_map = {q0 : Z , q2 : Y }, coefficient = sign )
743777 ps_after = cirq .PauliString (qubit_pauli_map = {q0 : Z , q2 : Y }, coefficient = sign )
778+ assert_conjugation (ps_before , op0 , ps_after , True )
744779 _assert_pass_over ([op0 ], ps_before , ps_after )
745780
746781 op0 = cirq .PauliInteractionGate (Y , t_or_f1 , X , t_or_f2 )(q0 , q1 )
747782 ps_before = cirq .PauliString ({q0 : Z , q2 : Y }, sign )
748- ps_after = cirq .PauliString ({q0 : Z , q2 : Y , q1 : X }, sign )
783+ ps_after = cirq .PauliString ({q0 : Z , q2 : Y , q1 : X }, - sign if t_or_f2 else sign )
784+ assert_conjugation (ps_before , op0 , ps_after , True )
749785 _assert_pass_over ([op0 ], ps_before , ps_after )
750786
751787 op0 = cirq .PauliInteractionGate (Z , t_or_f1 , X , t_or_f2 )(q0 , q1 )
752788 ps_before = cirq .PauliString ({q0 : Z , q1 : Y }, sign )
753- ps_after = cirq .PauliString ({q1 : Y }, sign )
789+ ps_after = cirq .PauliString ({q1 : Y }, - sign if t_or_f1 else sign )
790+ assert_conjugation (ps_before , op0 , ps_after , True )
754791 _assert_pass_over ([op0 ], ps_before , ps_after )
755792
756793 op0 = cirq .PauliInteractionGate (Y , t_or_f1 , X , t_or_f2 )(q0 , q1 )
757794 ps_before = cirq .PauliString ({q0 : Z , q1 : Y }, sign )
758795 ps_after = cirq .PauliString ({q0 : X , q1 : Z }, - 1 if neg ^ t_or_f1 ^ t_or_f2 else + 1 )
796+ assert_conjugation (ps_before , op0 , ps_after , True )
759797 _assert_pass_over ([op0 ], ps_before , ps_after )
760798
761799 op0 = cirq .PauliInteractionGate (X , t_or_f1 , X , t_or_f2 )(q0 , q1 )
762800 ps_before = cirq .PauliString ({q0 : Z , q1 : Y }, sign )
763801 ps_after = cirq .PauliString ({q0 : Y , q1 : Z }, + 1 if neg ^ t_or_f1 ^ t_or_f2 else - 1 )
802+ assert_conjugation (ps_before , op0 , ps_after , True )
764803 _assert_pass_over ([op0 ], ps_before , ps_after )
765804
766805
@@ -774,7 +813,9 @@ def test_pass_operations_over_cz():
774813
775814def test_pass_operations_over_no_common_qubits ():
776815 class ExampleGate (cirq .testing .SingleQubitGate ):
777- pass
816+
817+ def _decompose_ (self , qubits ):
818+ return cirq .X (qubits [0 ])
778819
779820 q0 , q1 = _make_qubits (2 )
780821 op0 = ExampleGate ()(q1 )
@@ -786,7 +827,11 @@ class ExampleGate(cirq.testing.SingleQubitGate):
786827def test_pass_unsupported_operations_over ():
787828 (q0 ,) = _make_qubits (1 )
788829 pauli_string = cirq .PauliString ({q0 : cirq .X })
789- with pytest .raises (TypeError , match = 'not a known Clifford' ):
830+ with pytest .raises (
831+ ValueError ,
832+ match = 'Clifford Gate can only be constructed from the operations'
833+ ' that has stabilizer effect.' ,
834+ ):
790835 pauli_string .pass_operations_over ([cirq .T (q0 )])
791836
792837
@@ -1523,8 +1568,8 @@ def _decompose_(self, qubits):
15231568def test_conjugated_by_move_into_uninvolved ():
15241569 a , b , c , d = cirq .LineQubit .range (4 )
15251570 ps = cirq .X (a ) * cirq .Z (b )
1526- assert_conjugation (ps , [cirq .SWAP (c , d ), cirq .SWAP (b , c )], cirq .X (a ) * cirq .Z (d ))
1527- assert_conjugation (ps , [cirq .SWAP (b , c ), cirq .SWAP (c , d )], cirq .X (a ) * cirq .Z (c ))
1571+ assert_conjugation_multi_ops (ps , [cirq .SWAP (c , d ), cirq .SWAP (b , c )], cirq .X (a ) * cirq .Z (d ))
1572+ assert_conjugation_multi_ops (ps , [cirq .SWAP (b , c ), cirq .SWAP (c , d )], cirq .X (a ) * cirq .Z (c ))
15281573
15291574
15301575def test_conjugated_by_common_single_qubit_gates ():
@@ -1549,7 +1594,7 @@ def test_conjugated_by_common_single_qubit_gates():
15491594 # pauli gate on a, clifford on b: pauli gate preserves.
15501595 assert_conjugation (p (a ), g (b ), p (a ))
15511596 # pauli gate on a, clifford on a: check conjugation in matrices.
1552- assert_conjugation (p (a ), g (a ), None )
1597+ assert_conjugation (p (a ), g (a ))
15531598
15541599
15551600def test_conjugated_by_common_two_qubit_gates ():
@@ -1580,7 +1625,7 @@ def test_conjugated_by_common_two_qubit_gates():
15801625 assert_conjugation (p , g (c , d ), p )
15811626 # pauli_string on (a,b), clifford on (a,b): compare unitaries of
15821627 # the conjugated_by and actual matrix conjugation.
1583- assert_conjugation (p , g .on (a , b ), None )
1628+ assert_conjugation (p , g .on (a , b ))
15841629
15851630
15861631def test_conjugated_by_ordering ():
@@ -1602,7 +1647,7 @@ def _decompose_(self, qubits):
16021647
16031648 a , b = cirq .LineQubit .range (2 )
16041649 inp = cirq .Z (b )
1605- out1 = inp .pass_operations_over ([ OrderSensitiveGate ().on (a , b )] )
1650+ out1 = inp .pass_operations_over (OrderSensitiveGate ().on (a , b ))
16061651 out2 = inp .pass_operations_over ([cirq .CNOT (a , b ), cirq .Y (a ) ** - 0.5 ])
16071652 out3 = inp .pass_operations_over ([cirq .CNOT (a , b )]).pass_operations_over ([cirq .Y (a ) ** - 0.5 ])
16081653 assert out1 == out2 == out3 == cirq .X (a ) * cirq .Z (b )
@@ -1618,7 +1663,7 @@ def _decompose_(self, qubits):
16181663
16191664 a , b = cirq .LineQubit .range (2 )
16201665 inp = cirq .X (a ) * cirq .Z (b )
1621- out1 = inp .pass_operations_over ([ OrderSensitiveGate ().on (a , b )] , after_to_before = True )
1666+ out1 = inp .pass_operations_over (OrderSensitiveGate ().on (a , b ), after_to_before = True )
16221667 out2 = inp .pass_operations_over ([cirq .Y (a ) ** - 0.5 , cirq .CNOT (a , b )], after_to_before = True )
16231668 out3 = inp .pass_operations_over ([cirq .Y (a ) ** - 0.5 ], after_to_before = True ).pass_operations_over (
16241669 [cirq .CNOT (a , b )], after_to_before = True
0 commit comments