14
14
15
15
"""An optimization pass that pushes Z gates later and later in the circuit."""
16
16
17
- from typing import cast , Dict , Iterable , List , Optional , Tuple
18
- from collections import defaultdict
19
- import numpy as np
20
- import sympy
17
+ from cirq import circuits , transformers
21
18
22
- from cirq import circuits , ops , protocols
23
- from cirq .transformers .analytical_decompositions import single_qubit_decompositions
24
-
25
-
26
- def _is_integer (n ):
27
- return np .isclose (n , np .round (n ))
28
-
29
-
30
- def _is_swaplike (op : ops .Operation ):
31
- if isinstance (op .gate , ops .SwapPowGate ):
32
- return op .gate .exponent == 1
33
-
34
- if isinstance (op .gate , ops .ISwapPowGate ):
35
- return _is_integer ((op .gate .exponent - 1 ) / 2 )
36
-
37
- if isinstance (op .gate , ops .FSimGate ):
38
- return _is_integer (op .gate .theta / np .pi - 1 / 2 )
39
-
40
- return False
19
+ # from cirq.transformers import eject_z
20
+ from cirq ._compat import deprecated_class
41
21
42
22
23
+ @deprecated_class (deadline = 'v1.0' , fix = 'Use cirq.eject_z instead.' )
43
24
class EjectZ :
44
25
"""Pushes Z gates towards the end of the circuit.
45
26
@@ -62,96 +43,8 @@ def __init__(self, tolerance: float = 0.0, eject_parameterized: bool = False) ->
62
43
self .eject_parameterized = eject_parameterized
63
44
64
45
def optimize_circuit (self , circuit : circuits .Circuit ):
65
- # Tracks qubit phases (in half turns; multiply by pi to get radians).
66
- qubit_phase : Dict [ops .Qid , float ] = defaultdict (lambda : 0 )
67
- deletions : List [Tuple [int , ops .Operation ]] = []
68
- replacements : List [Tuple [int , ops .Operation , ops .Operation ]] = []
69
- insertions : List [Tuple [int , ops .Operation ]] = []
70
- phased_xz_replacements : Dict [Tuple [int , ops .Qid ], int ] = {}
71
-
72
- def dump_tracked_phase (qubits : Iterable [ops .Qid ], index : int ) -> None :
73
- """Zeroes qubit_phase entries by emitting Z gates."""
74
- for q in qubits :
75
- p = qubit_phase [q ]
76
- qubit_phase [q ] = 0
77
- if single_qubit_decompositions .is_negligible_turn (p , self .tolerance ):
78
- continue
79
- dumped = False
80
- moment_index = circuit .prev_moment_operating_on ([q ], index )
81
- if moment_index is not None :
82
- op = circuit .moments [moment_index ][q ]
83
- if op and isinstance (op .gate , ops .PhasedXZGate ):
84
- # Attach z-rotation to replacing PhasedXZ gate.
85
- idx = phased_xz_replacements [moment_index , q ]
86
- _ , _ , repl_op = replacements [idx ]
87
- gate = cast (ops .PhasedXZGate , repl_op .gate )
88
- repl_op = gate .with_z_exponent (p * 2 ).on (q )
89
- replacements [idx ] = (moment_index , op , repl_op )
90
- dumped = True
91
- if not dumped :
92
- # Add a new Z gate
93
- dump_op = ops .Z (q ) ** (p * 2 )
94
- insertions .append ((index , dump_op ))
95
-
96
- for moment_index , moment in enumerate (circuit ):
97
- for op in moment .operations :
98
- # Move Z gates into tracked qubit phases.
99
- h = _try_get_known_z_half_turns (op , self .eject_parameterized )
100
- if h is not None :
101
- q = op .qubits [0 ]
102
- qubit_phase [q ] += h / 2
103
- deletions .append ((moment_index , op ))
104
- continue
105
-
106
- # Z gate before measurement is a no-op. Drop tracked phase.
107
- if isinstance (op .gate , ops .MeasurementGate ):
108
- for q in op .qubits :
109
- qubit_phase [q ] = 0
110
-
111
- # If there's no tracked phase, we can move on.
112
- phases = [qubit_phase [q ] for q in op .qubits ]
113
- if not isinstance (op .gate , ops .PhasedXZGate ) and all (
114
- single_qubit_decompositions .is_negligible_turn (p , self .tolerance )
115
- for p in phases
116
- ):
117
- continue
118
-
119
- if _is_swaplike (op ):
120
- a , b = op .qubits
121
- qubit_phase [a ], qubit_phase [b ] = qubit_phase [b ], qubit_phase [a ]
122
- continue
123
-
124
- # Try to move the tracked phasing over the operation.
125
- phased_op = op
126
- for i , p in enumerate (phases ):
127
- if not single_qubit_decompositions .is_negligible_turn (p , self .tolerance ):
128
- phased_op = protocols .phase_by (phased_op , - p , i , default = None )
129
- if phased_op is not None :
130
- gate = phased_op .gate
131
- if isinstance (gate , ops .PhasedXZGate ) and (
132
- self .eject_parameterized or not protocols .is_parameterized (gate .z_exponent )
133
- ):
134
- qubit = phased_op .qubits [0 ]
135
- qubit_phase [qubit ] += gate .z_exponent / 2
136
- phased_op = gate .with_z_exponent (0 ).on (qubit )
137
- repl_idx = len (replacements )
138
- phased_xz_replacements [moment_index , qubit ] = repl_idx
139
- replacements .append ((moment_index , op , phased_op ))
140
- else :
141
- dump_tracked_phase (op .qubits , moment_index )
142
-
143
- dump_tracked_phase (qubit_phase .keys (), len (circuit ))
144
- circuit .batch_remove (deletions )
145
- circuit .batch_replace (replacements )
146
- circuit .batch_insert (insertions )
147
-
148
-
149
- def _try_get_known_z_half_turns (op : ops .Operation , eject_parameterized : bool ) -> Optional [float ]:
150
- if not isinstance (op , ops .GateOperation ):
151
- return None
152
- if not isinstance (op .gate , ops .ZPowGate ):
153
- return None
154
- h = op .gate .exponent
155
- if not eject_parameterized and isinstance (h , sympy .Basic ):
156
- return None
157
- return h
46
+ circuit ._moments = [
47
+ * transformers .eject_z (
48
+ circuit , atol = self .tolerance , eject_parameterized = self .eject_parameterized
49
+ )
50
+ ]
0 commit comments