16
16
17
17
from __future__ import annotations
18
18
19
- from typing import List
19
+ from typing import cast , List
20
20
21
21
import numpy as np
22
22
@@ -156,12 +156,12 @@ class _PhasedXYAndRz:
156
156
The order is --(X|Y|I)--Rz(rad)--phase--.
157
157
"""
158
158
159
- pauli : ops .X | ops .Y | ops . I
159
+ pauli : ops .Pauli | ops .IdentityGate # X|Y| I
160
160
rz_rads : float
161
161
phase_exp : float # phase of the qubit is e^{i*phase_exp*pi}
162
162
163
163
def __init__ (
164
- self , pauli : ops .Pauli | ops .I = ops .I , rz_rads : float = 0 , phase_exp : float = 0
164
+ self , pauli : ops .Pauli | ops .IdentityGate = ops .I , rz_rads : float = 0 , phase_exp : float = 0
165
165
) -> None :
166
166
if pauli == ops .Z : # Merge Z gates to Rz where Z = Rz(π) * e^{iπ/2}
167
167
self .pauli = ops .I
@@ -183,7 +183,7 @@ def _merge_right_rz(self, rads: float):
183
183
"""Merges Rz(rads) from right."""
184
184
self .rz_rads += rads
185
185
186
- def _merge_left_xy (self , other : ops .X | ops . Y ):
186
+ def _merge_left_xy (self , other : ops .Pauli ):
187
187
"""Merges --(X|Y)--self--."""
188
188
if self .pauli == other :
189
189
self .pauli = ops .I
@@ -202,7 +202,7 @@ def _merge_left_xy(self, other: ops.X | ops.Y):
202
202
self .rz_rads -= np .pi
203
203
return
204
204
205
- def _merge_right_xy (self , other : ops .X | ops . Y ):
205
+ def _merge_right_xy (self , other : ops .Pauli ):
206
206
"""Merges --self--(X|Y)--."""
207
207
self .rz_rads *= - 1
208
208
if self .pauli == other :
@@ -227,13 +227,13 @@ def merge_left(self, other: _PhasedXYAndRz) -> None:
227
227
self ._merge_left_rz (other .rz_rads )
228
228
self .phase_exp += other .phase_exp
229
229
if other .pauli != ops .I :
230
- self ._merge_left_xy (other .pauli )
230
+ self ._merge_left_xy (cast ( ops . Pauli , other .pauli ) )
231
231
232
232
def merge_right (self , other : _PhasedXYAndRz ) -> None :
233
233
"""Inplace merge other from right."""
234
234
self .phase_exp += other .phase_exp
235
235
if other .pauli != ops .I :
236
- self ._merge_right_xy (other .pauli )
236
+ self ._merge_right_xy (cast ( ops . Pauli , other .pauli ) )
237
237
self ._merge_right_rz (other .rz_rads )
238
238
239
239
def after_cphase (
@@ -259,41 +259,47 @@ def after_cphase(
259
259
# Similarly for X|Y on qubit 0/1, the result is always flipping cphase and
260
260
# add an extra Rz rotation on the other qubit.
261
261
return (
262
- cphase ** - 1 ,
262
+ cast ( ops . CZPowGate , cphase ** - 1 ) ,
263
263
self ,
264
264
_PhasedXYAndRz (rz_rads = cphase .exponent * np .pi , phase_exp = - cphase .exponent / 2 ),
265
265
)
266
266
267
267
def __str__ (self ) -> str :
268
268
return f"─{ self .pauli } ──Rz({ self .rz_rads } )──phase(e^{{i{ self .phase_exp } π}})─"
269
269
270
- def __eq__ (self , other : _PhasedXYAndRz ) -> bool :
270
+ def __eq__ (self , other : object ) -> bool :
271
+ if not isinstance (other , _PhasedXYAndRz ):
272
+ raise NotImplementedError
271
273
return (
272
274
self .pauli == other .pauli
273
- and np .isclose (self .rz_rads , other .rz_rads , atol = 1e-10 )
274
- and np .isclose (self .phase_exp , other .phase_exp , atol = 1e-10 )
275
+ and bool ( np .isclose (self .rz_rads , other .rz_rads , atol = 1e-10 ) )
276
+ and bool ( np .isclose (self .phase_exp , other .phase_exp , atol = 1e-10 ) )
275
277
)
276
278
277
279
def to_single_gate (self ) -> ops .PhasedXZGate | ops .ZPowGate :
278
- if self .pauli == ops .I :
279
- rz_rads = self .rz_rads
280
- if np .isclose (self .rz_rads , 0 , atol = 1e-2 ):
281
- rz_rads = self .rz_rads + 4 * np .pi
282
- return ops .ZPowGate (
283
- exponent = rz_rads / np .pi , global_shift = np .pi * self .phase_exp / rz_rads - 0.5
284
- )
285
- if self .pauli == ops .X :
286
- return ops .PhasedXZGate (
287
- x_exponent = 1 ,
288
- z_exponent = 2 * self .phase_exp ,
289
- axis_phase_exponent = self .rz_rads / 2 / np .pi - self .phase_exp ,
290
- )
291
- if self .pauli == ops .Y :
292
- return ops .PhasedXZGate (
293
- x_exponent = 1 ,
294
- z_exponent = 2 * self .phase_exp ,
295
- axis_phase_exponent = 1 / 2 - self .phase_exp + self .rz_rads / 2 / np .pi ,
296
- )
280
+ """Converts the _PhasedXYAndRz to a single-qubit gate."""
281
+ match self .pauli :
282
+ case ops .I :
283
+ rz_rads = self .rz_rads
284
+ if np .isclose (self .rz_rads , 0 , atol = 1e-2 ):
285
+ rz_rads = self .rz_rads + 4 * np .pi
286
+ return ops .ZPowGate (
287
+ exponent = rz_rads / np .pi , global_shift = np .pi * self .phase_exp / rz_rads - 0.5
288
+ )
289
+ case ops .X :
290
+ return ops .PhasedXZGate (
291
+ x_exponent = 1 ,
292
+ z_exponent = 2 * self .phase_exp ,
293
+ axis_phase_exponent = self .rz_rads / 2 / np .pi - self .phase_exp ,
294
+ )
295
+ case ops .Y :
296
+ return ops .PhasedXZGate (
297
+ x_exponent = 1 ,
298
+ z_exponent = 2 * self .phase_exp ,
299
+ axis_phase_exponent = 1 / 2 - self .phase_exp + self .rz_rads / 2 / np .pi ,
300
+ )
301
+ case _:
302
+ raise ValueError ("Invalid self.pauli." )
297
303
298
304
299
305
def _pull_through_single_cphase (
@@ -327,26 +333,31 @@ def _pull_through_single_cphase(
327
333
return output_cphase , output0 , output1
328
334
329
335
330
- def _multi_moment_pull_through (
336
+ def _multi_moment_gauge_fn (
331
337
moments : List [circuits .Moment ], rng : np .random .Generator
332
338
) -> List [circuits .Moment ]:
333
- """TO FILL ."""
339
+ """Generates a left layer with random generator, then pulling through all the moments ."""
334
340
all_qubits = [q for q in circuits .Circuit (moments ).all_qubits ()]
335
341
if not all_qubits :
336
342
return moments
337
343
if not any (isinstance (op .gate , ops .CZPowGate ) for moment in moments for op in moment ):
338
344
return moments
339
345
340
- left_moment = circuits .Moment (
341
- [rng .choice ([ops .I , ops .X , ops .Y , ops .Z ]).on (q ) for q in all_qubits ]
342
- )
343
- prev : map [ops .Qid , ops .Gate ] = {
344
- op .qubits [0 ]: _PhasedXYAndRz (pauli = op .gate ) for op in left_moment
346
+ left_moment : List [ops .Operation ] = [
347
+ rng .choice (
348
+ np .array ([ops .I , ops .X , ops .Y , ops .Z ], dtype = ops .Gate ), p = [0.25 , 0.25 , 0.25 , 0.25 ]
349
+ ).on (q )
350
+ for q in all_qubits
351
+ ]
352
+ prev : dict [ops .Qid , _PhasedXYAndRz ] = {
353
+ op .qubits [0 ]: _PhasedXYAndRz (pauli = cast (ops .Pauli | ops .IdentityGate , op .gate ))
354
+ for op in left_moment
355
+ if op .gate
345
356
}
346
357
347
- new_moments : List [circuits .Moment ] = [left_moment ]
358
+ new_moments : List [circuits .Moment ] = [circuits . Moment ( left_moment ) ]
348
359
349
- pulled : map [ops .Qid , ops . Gate ]
360
+ pulled : dict [ops .Qid , _PhasedXYAndRz ]
350
361
for moment in moments :
351
362
pulled = {}
352
363
new_moment : List [ops .Operation ] = []
@@ -368,7 +379,7 @@ def _multi_moment_pull_through(
368
379
if q not in pulled :
369
380
pulled [q ] = prev [q ]
370
381
prev = pulled
371
- new_moments .append (new_moment )
382
+ new_moments .append (circuits . Moment ( new_moment ) )
372
383
373
384
last_moment = circuits .Moment ([pulled [q ].to_single_gate ().on (q ) for q in all_qubits ])
374
385
@@ -377,9 +388,9 @@ def _multi_moment_pull_through(
377
388
return new_moments
378
389
379
390
380
- # Multi-moments pull through version of CZGaugeTransformer
391
+ # Multi-moments pull through version of CPhaseGaugeTransformer
381
392
CPhaseGaugeTransformerMM = GaugeTransformer (
382
393
target = ops .Gateset (ops .CZPowGate , ops .ZPowGate ),
383
394
gauge_selector = CPhaseGaugeSelector ,
384
- multi_moment_pull_thourgh_fn = _multi_moment_pull_through ,
395
+ multi_moment_gauge_fn = _multi_moment_gauge_fn ,
385
396
)
0 commit comments