@@ -67,8 +67,8 @@ def __post_init__(self):
6767 _default_cz_paired_correlated_rates (),
6868 )
6969 elif (
70- self .cz_paired_correlated_rates is not None
71- and self .cz_paired_error_probabilities is None
70+ self .cz_paired_correlated_rates is not None
71+ and self .cz_paired_error_probabilities is None
7272 ):
7373
7474 if self .cz_paired_correlated_rates .shape != (4 , 4 ):
@@ -83,8 +83,8 @@ def __post_init__(self):
8383 correlated_noise_array_to_dict (self .cz_paired_correlated_rates ),
8484 )
8585 elif (
86- self .cz_paired_correlated_rates is not None
87- and self .cz_paired_error_probabilities is not None
86+ self .cz_paired_correlated_rates is not None
87+ and self .cz_paired_error_probabilities is not None
8888 ):
8989 raise ValueError (
9090 "Received both `cz_paired_correlated_rates` and `cz_paired_correlated_rates` as input. This is ambiguous, please only set one."
@@ -113,7 +113,7 @@ def validate_moments(moments: Iterable[cirq.Moment]):
113113 )
114114
115115 def parallel_cz_errors (
116- self , ctrls : list [int ], qargs : list [int ], rest : list [int ]
116+ self , ctrls : list [int ], qargs : list [int ], rest : list [int ]
117117 ) -> dict [tuple [float , float , float , float ], list [int ]]:
118118 raise NotImplementedError (
119119 "This noise model doesn't support rewrites on bloqade kernels, but should be used with cirq."
@@ -179,7 +179,7 @@ class GeminiOneZoneNoiseModel(GeminiNoiseModelABC):
179179 parallelize_circuit : bool = False
180180
181181 def _single_qubit_moment_noise_ops (
182- self , moment : cirq .Moment , system_qubits : Sequence [cirq .Qid ]
182+ self , moment : cirq .Moment , system_qubits : Sequence [cirq .Qid ]
183183 ) -> tuple [list , list ]:
184184 """
185185 Helper function to determine the noise operations for a single qubit moment.
@@ -211,7 +211,7 @@ def _single_qubit_moment_noise_ops(
211211 op .qubits [0 ]
212212 for op in moment .operations
213213 if not (
214- np .isclose (op .gate .x_exponent , 0 ) and np .isclose (op .gate .z_exponent , 0 )
214+ np .isclose (op .gate .x_exponent , 0 ) and np .isclose (op .gate .z_exponent , 0 )
215215 )
216216 ]
217217
@@ -247,7 +247,8 @@ def noisy_moment(self, moment, system_qubits):
247247 gate_noise_ops = []
248248 # Check if the moment contains 1-qubit gates or 2-qubit gates
249249 elif len (moment .operations [0 ].qubits ) == 1 :
250- if (isinstance (moment .operations [0 ].gate , cirq .ResetChannel )) or (cirq .is_measurement (moment .operations [0 ])):
250+ if (isinstance (moment .operations [0 ].gate , cirq .ResetChannel )) or (
251+ cirq .is_measurement (moment .operations [0 ])) or (isinstance (moment .operations [0 ].gate , cirq .BitFlipChannel )):
251252 move_noise_ops = []
252253 gate_noise_ops = []
253254 else :
@@ -301,7 +302,7 @@ def noisy_moment(self, moment, system_qubits):
301302 ]
302303
303304 def noisy_moments (
304- self , moments : Iterable [cirq .Moment ], system_qubits : Sequence [cirq .Qid ]
305+ self , moments : Iterable [cirq .Moment ], system_qubits : Sequence [cirq .Qid ]
305306 ) -> Sequence [cirq .OP_TREE ]:
306307 """Adds possibly stateful noise to a series of moments.
307308
@@ -319,7 +320,8 @@ def noisy_moments(
319320
320321 # Split into moments with only 1Q and 2Q gates
321322 moments_1q = [
322- cirq .Moment ([op for op in moment .operations if (len (op .qubits ) == 1 ) and (not cirq .is_measurement (op )) and (not isinstance (op .gate , cirq .ResetChannel ))])
323+ cirq .Moment ([op for op in moment .operations if (len (op .qubits ) == 1 ) and (not cirq .is_measurement (op )) and (
324+ not isinstance (op .gate , cirq .ResetChannel ))])
323325 for moment in moments
324326 ]
325327 moments_2q = [
@@ -328,16 +330,46 @@ def noisy_moments(
328330 ]
329331
330332 moments_measurement = [
331- cirq .Moment ([op for op in moment .operations if (cirq .is_measurement (op )) or (isinstance (op .gate , cirq .ResetChannel ))])
333+ cirq .Moment ([op for op in moment .operations if
334+ (cirq .is_measurement (op )) or (isinstance (op .gate , cirq .ResetChannel ))])
332335 for moment in moments
333336 ]
334337
335338 assert len (moments_1q ) == len (moments_2q ) == len (moments_measurement )
336339
337340 interleaved_moments = []
341+
342+ def count_remaining_cz_moments (moments_2q ):
343+ cz = cirq .CZ
344+ remaining_cz_counts = []
345+ count = 0
346+ for m in moments_2q [::- 1 ]:
347+ if any (isinstance (op .gate , type (cz )) for op in m .operations ):
348+ count += 1
349+ remaining_cz_counts = [count ] + remaining_cz_counts
350+ return remaining_cz_counts
351+
352+ remaining_cz_moments = count_remaining_cz_moments (moments_2q )
353+
354+ pm = 2 * self .sitter_pauli_rates [0 ]
355+ ps = 2 * self .cz_unpaired_pauli_rates [0 ]
356+
357+ #probability of a bitflip error for a sitting, unpaired qubit during a move/cz/move cycle.
358+ heuristic_1step_bitflip_error : float = 2 * pm * (1 - ps ) * (1 - pm ) + (1 - pm )** 2 * ps + pm ** 2 * ps
359+
338360 for idx , moment in enumerate (moments_1q ):
339361 interleaved_moments .append (moment )
340362 interleaved_moments .append (moments_2q [idx ])
363+ # Measurements on Gemini will be at the end, so for circuits with mid-circuit measurements we will insert a
364+ # bitflip error proportional to the number of moments left in the circuit to account for the decoherence
365+ # that will happen before the final terminal measurement.
366+ measured_qubits = []
367+ for op in moments_measurement [idx ].operations :
368+ if cirq .is_measurement (op ):
369+ measured_qubits += list (op .qubits )
370+ # probability of a bitflip error should be Binomial(moments_left,heuristic_1step_bitflip_error)
371+ delayed_measurement_error = (1 - (1 - 2 * heuristic_1step_bitflip_error ) ** (remaining_cz_moments [idx ])) / 2
372+ interleaved_moments .append (cirq .Moment (cirq .bit_flip (delayed_measurement_error ).on_each (measured_qubits )))
341373 interleaved_moments .append (moments_measurement [idx ])
342374
343375 interleaved_circuit = cirq .Circuit .from_moments (* interleaved_moments )
@@ -379,7 +411,8 @@ def noisy_moment(self, moment, system_qubits):
379411 gate_noise_ops = []
380412 # Check if the moment contains 1-qubit gates or 2-qubit gates
381413 elif len (moment .operations [0 ].qubits ) == 1 :
382- if (isinstance (moment .operations [0 ].gate , cirq .ResetChannel )) or (cirq .is_measurement (moment .operations [0 ])):
414+ if (isinstance (moment .operations [0 ].gate , cirq .ResetChannel )) or (
415+ cirq .is_measurement (moment .operations [0 ])) or (isinstance (moment .operations [0 ].gate , cirq .BitFlipChannel )):
383416 gate_noise_ops = []
384417 else :
385418 gate_noise_ops , _ = self ._single_qubit_moment_noise_ops (
@@ -449,7 +482,7 @@ def noisy_moment(self, moment, system_qubits):
449482@dataclass (frozen = True )
450483class GeminiTwoZoneNoiseModel (GeminiNoiseModelABC ):
451484 def noisy_moments (
452- self , moments : Iterable [cirq .Moment ], system_qubits : Sequence [cirq .Qid ]
485+ self , moments : Iterable [cirq .Moment ], system_qubits : Sequence [cirq .Qid ]
453486 ) -> Sequence [cirq .OP_TREE ]:
454487 """Adds possibly stateful noise to a series of moments.
455488
@@ -481,12 +514,12 @@ def noisy_moments(
481514 [
482515 moment
483516 for moment in _two_zone_utils .get_move_error_channel_two_zoned (
484- moments [i ],
485- prev_moment ,
486- np .array (self .mover_pauli_rates ),
487- np .array (self .sitter_pauli_rates ),
488- nqubs ,
489- ).moments
517+ moments [i ],
518+ prev_moment ,
519+ np .array (self .mover_pauli_rates ),
520+ np .array (self .sitter_pauli_rates ),
521+ nqubs ,
522+ ).moments
490523 if len (moment ) > 0
491524 ]
492525 )
@@ -497,13 +530,13 @@ def noisy_moments(
497530 [
498531 moment
499532 for moment in _two_zone_utils .get_gate_error_channel (
500- moments [i ],
501- np .array (self .local_pauli_rates ),
502- np .array (self .global_pauli_rates ),
503- self .two_qubit_pauli ,
504- np .array (self .cz_unpaired_pauli_rates ),
505- nqubs ,
506- ).moments
533+ moments [i ],
534+ np .array (self .local_pauli_rates ),
535+ np .array (self .global_pauli_rates ),
536+ self .two_qubit_pauli ,
537+ np .array (self .cz_unpaired_pauli_rates ),
538+ nqubs ,
539+ ).moments
507540 if len (moment ) > 0
508541 ]
509542 )
0 commit comments