Skip to content

Model

GeminiNoiseModelABC dataclass

GeminiNoiseModelABC(
    check_input_circuit: bool = True,
    scaling_factor: float = 1.0,
    cz_paired_correlated_rates: ndarray | None = None,
    cz_paired_error_probabilities: dict | None = None,
    *,
    local_px: float = 0.0004102,
    local_py: float = 0.0004102,
    local_pz: float = 0.0004112,
    local_loss_prob: float = 0.0,
    local_unaddressed_px: float = 2e-07,
    local_unaddressed_py: float = 2e-07,
    local_unaddressed_pz: float = 1.2e-06,
    local_unaddressed_loss_prob: float = 0.0,
    global_px: float = 6.5e-05,
    global_py: float = 6.5e-05,
    global_pz: float = 6.5e-05,
    global_loss_prob: float = 0.0,
    cz_paired_gate_px: float = 0.0006549,
    cz_paired_gate_py: float = 0.0006549,
    cz_paired_gate_pz: float = 0.003184,
    cz_gate_loss_prob: float = 0.0,
    cz_unpaired_gate_px: float = 0.0005149,
    cz_unpaired_gate_py: float = 0.0005149,
    cz_unpaired_gate_pz: float = 0.002185,
    cz_unpaired_loss_prob: float = 0.0,
    mover_px: float = 0.000806,
    mover_py: float = 0.000806,
    mover_pz: float = 0.002458,
    move_loss_prob: float = 0.0,
    sitter_px: float = 0.0003066,
    sitter_py: float = 0.0003066,
    sitter_pz: float = 0.0004639,
    sit_loss_prob: float = 0.0
)

Bases: NoiseModel, MoveNoiseModelABC

Abstract base class for all Gemini noise models.

check_input_circuit class-attribute instance-attribute

check_input_circuit: bool = True

Determine whether or not to verify that the circuit only contains native gates.

Caution: Disabling this for circuits containing non-native gates may lead to incorrect results!

cz_paired_correlated_rates class-attribute instance-attribute

cz_paired_correlated_rates: ndarray | None = None

The correlated CZ error rates as a 4x4 array.

cz_paired_error_probabilities class-attribute instance-attribute

cz_paired_error_probabilities: dict | None = None

The correlated CZ error rates as a dictionary

scaling_factor class-attribute instance-attribute

scaling_factor: float = 1.0

Factor to multiply all noise probabilities by. Use this to sweep noise levels.

  • 0.0 = no noise (noiseless simulation)
  • 1.0 = default noise levels
  • 2.0 = double the noise probabilities

parallel_cz_errors

parallel_cz_errors(
    ctrls: Sequence[int],
    qargs: Sequence[int],
    rest: Sequence[int],
) -> dict[tuple[float, float, float, float], list[int]]

Takes a set of ctrls and qargs and returns a noise model for all qubits.

Source code in .venv/lib/python3.12/site-packages/bloqade/cirq_utils/noise/model.py
131
132
133
134
135
136
def parallel_cz_errors(
    self, ctrls: Sequence[int], qargs: Sequence[int], rest: Sequence[int]
) -> dict[tuple[float, float, float, float], list[int]]:
    raise NotImplementedError(
        "This noise model doesn't support rewrites on bloqade kernels, but should be used with cirq."
    )

GeminiOneZoneNoiseModel dataclass

GeminiOneZoneNoiseModel(
    check_input_circuit: bool = True,
    scaling_factor: float = 1.0,
    cz_paired_correlated_rates: ndarray | None = None,
    cz_paired_error_probabilities: dict | None = None,
    parallelize_circuit: bool = False,
    *,
    local_px: float = 0.0004102,
    local_py: float = 0.0004102,
    local_pz: float = 0.0004112,
    local_loss_prob: float = 0.0,
    local_unaddressed_px: float = 2e-07,
    local_unaddressed_py: float = 2e-07,
    local_unaddressed_pz: float = 1.2e-06,
    local_unaddressed_loss_prob: float = 0.0,
    global_px: float = 6.5e-05,
    global_py: float = 6.5e-05,
    global_pz: float = 6.5e-05,
    global_loss_prob: float = 0.0,
    cz_paired_gate_px: float = 0.0006549,
    cz_paired_gate_py: float = 0.0006549,
    cz_paired_gate_pz: float = 0.003184,
    cz_gate_loss_prob: float = 0.0,
    cz_unpaired_gate_px: float = 0.0005149,
    cz_unpaired_gate_py: float = 0.0005149,
    cz_unpaired_gate_pz: float = 0.002185,
    cz_unpaired_loss_prob: float = 0.0,
    mover_px: float = 0.000806,
    mover_py: float = 0.000806,
    mover_pz: float = 0.002458,
    move_loss_prob: float = 0.0,
    sitter_px: float = 0.0003066,
    sitter_py: float = 0.0003066,
    sitter_pz: float = 0.0004639,
    sit_loss_prob: float = 0.0
)

Bases: GeminiNoiseModelABC

A Cirq-compatible noise model for a one-zone implementation of the Gemini architecture.

This model introduces custom asymmetric depolarizing noise for both single- and two-qubit gates depending on whether operations are global, local, or part of a CZ interaction. Since the model assumes all atoms are in the entangling zone, errors are applied that stem from application of Rydberg error, even for qubits not actively involved in a gate operation.

Note, that the noise applied to entangling pairs is correlated.

noisy_moments

noisy_moments(
    moments: Iterable[Moment], system_qubits: Sequence[Qid]
) -> Sequence[cirq.OP_TREE]

Adds possibly stateful noise to a series of moments.

Parameters:

Name Type Description Default
moments Iterable[Moment]

The moments to add noise to.

required
system_qubits Sequence[Qid]

A list of all qubits in the system.

required

Returns:

Type Description
Sequence[OP_TREE]

A sequence of OP_TREEEs, with the k'th tree corresponding to the

Sequence[OP_TREE]

noisy operations for the k'th moment.

Source code in .venv/lib/python3.12/site-packages/bloqade/cirq_utils/noise/model.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
def noisy_moments(
    self, moments: Iterable[cirq.Moment], system_qubits: Sequence[cirq.Qid]
) -> Sequence[cirq.OP_TREE]:
    """Adds possibly stateful noise to a series of moments.

    Args:
        moments: The moments to add noise to.
        system_qubits: A list of all qubits in the system.

    Returns:
        A sequence of OP_TREEEs, with the k'th tree corresponding to the
        noisy operations for the k'th moment.
    """

    if self.check_input_circuit:
        self.validate_moments(moments)

    # Split into moments with only 1Q and 2Q gates
    moments_1q = [
        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))
            ]
        )
        for moment in moments
    ]
    moments_2q = [
        cirq.Moment(
            [
                op
                for op in moment.operations
                if (len(op.qubits) == 2) and (not cirq.is_measurement(op))
            ]
        )
        for moment in moments
    ]

    moments_measurement = [
        cirq.Moment(
            [
                op
                for op in moment.operations
                if (cirq.is_measurement(op))
                or (isinstance(op.gate, cirq.ResetChannel))
            ]
        )
        for moment in moments
    ]

    assert len(moments_1q) == len(moments_2q) == len(moments_measurement)

    interleaved_moments = []

    def count_remaining_cz_moments(moments_2q):
        remaining_cz_counts = []
        count = 0
        for m in moments_2q[::-1]:
            if any(isinstance(op.gate, cirq.CZPowGate) for op in m.operations):
                count += 1
            remaining_cz_counts = [count] + remaining_cz_counts
        return remaining_cz_counts

    remaining_cz_moments = count_remaining_cz_moments(moments_2q)

    pm = 2 * self.sitter_pauli_rates[0]
    ps = 2 * self.cz_unpaired_pauli_rates[0]

    # probability of a bitflip error for a sitting, unpaired qubit during a move/cz/move cycle.
    heuristic_1step_bitflip_error: float = (
        2 * pm * (1 - ps) * (1 - pm) + (1 - pm) ** 2 * ps + pm**2 * ps
    )

    for idx, moment in enumerate(moments_1q):
        interleaved_moments.append(moment)
        interleaved_moments.append(moments_2q[idx])
        # Measurements on Gemini will be at the end, so for circuits with mid-circuit measurements we will insert a
        # bitflip error proportional to the number of moments left in the circuit to account for the decoherence
        # that will happen before the final terminal measurement.
        measured_qubits = []
        for op in moments_measurement[idx].operations:
            if cirq.is_measurement(op):
                measured_qubits += list(op.qubits)
        # probability of a bitflip error should be Binomial(moments_left,heuristic_1step_bitflip_error)
        delayed_measurement_error = (
            1
            - (1 - 2 * heuristic_1step_bitflip_error) ** (remaining_cz_moments[idx])
        ) / 2
        interleaved_moments.append(
            cirq.Moment(
                cirq.bit_flip(delayed_measurement_error).on_each(measured_qubits)
            )
        )
        interleaved_moments.append(moments_measurement[idx])

    interleaved_circuit = cirq.Circuit.from_moments(*interleaved_moments)

    # Combine subsequent 1Q gates
    compressed_circuit = cirq.merge_single_qubit_moments_to_phxz(
        interleaved_circuit
    )
    if self.parallelize_circuit:
        compressed_circuit = parallelize(compressed_circuit)

    return self._noisy_moments_impl_moment(
        compressed_circuit.moments, system_qubits
    )

GeminiOneZoneNoiseModelConflictGraphMoves dataclass

GeminiOneZoneNoiseModelConflictGraphMoves(
    check_input_circuit: bool = True,
    scaling_factor: float = 1.0,
    cz_paired_correlated_rates: ndarray | None = None,
    cz_paired_error_probabilities: dict | None = None,
    parallelize_circuit: bool = False,
    max_parallel_movers: int = 10000,
    *,
    local_px: float = 0.0004102,
    local_py: float = 0.0004102,
    local_pz: float = 0.0004112,
    local_loss_prob: float = 0.0,
    local_unaddressed_px: float = 2e-07,
    local_unaddressed_py: float = 2e-07,
    local_unaddressed_pz: float = 1.2e-06,
    local_unaddressed_loss_prob: float = 0.0,
    global_px: float = 6.5e-05,
    global_py: float = 6.5e-05,
    global_pz: float = 6.5e-05,
    global_loss_prob: float = 0.0,
    cz_paired_gate_px: float = 0.0006549,
    cz_paired_gate_py: float = 0.0006549,
    cz_paired_gate_pz: float = 0.003184,
    cz_gate_loss_prob: float = 0.0,
    cz_unpaired_gate_px: float = 0.0005149,
    cz_unpaired_gate_py: float = 0.0005149,
    cz_unpaired_gate_pz: float = 0.002185,
    cz_unpaired_loss_prob: float = 0.0,
    mover_px: float = 0.000806,
    mover_py: float = 0.000806,
    mover_pz: float = 0.002458,
    move_loss_prob: float = 0.0,
    sitter_px: float = 0.0003066,
    sitter_py: float = 0.0003066,
    sitter_pz: float = 0.0004639,
    sit_loss_prob: float = 0.0
)

Bases: GeminiOneZoneNoiseModel

A Cirq noise model that uses a conflict graph to schedule moves in a one-zone Gemini architecture.

Assumes that the qubits are cirq.GridQubits, such that the assignment of row, column coordinates define the initial geometry. An SLM site at the two qubit interaction distance is also assumed next to each cirq.GridQubit to allow for multiple moves before a single Rydberg pulse is applied for a parallel CZ.

GeminiTwoZoneNoiseModel dataclass

GeminiTwoZoneNoiseModel(
    check_input_circuit: bool = True,
    scaling_factor: float = 1.0,
    cz_paired_correlated_rates: ndarray | None = None,
    cz_paired_error_probabilities: dict | None = None,
    *,
    local_px: float = 0.0004102,
    local_py: float = 0.0004102,
    local_pz: float = 0.0004112,
    local_loss_prob: float = 0.0,
    local_unaddressed_px: float = 2e-07,
    local_unaddressed_py: float = 2e-07,
    local_unaddressed_pz: float = 1.2e-06,
    local_unaddressed_loss_prob: float = 0.0,
    global_px: float = 6.5e-05,
    global_py: float = 6.5e-05,
    global_pz: float = 6.5e-05,
    global_loss_prob: float = 0.0,
    cz_paired_gate_px: float = 0.0006549,
    cz_paired_gate_py: float = 0.0006549,
    cz_paired_gate_pz: float = 0.003184,
    cz_gate_loss_prob: float = 0.0,
    cz_unpaired_gate_px: float = 0.0005149,
    cz_unpaired_gate_py: float = 0.0005149,
    cz_unpaired_gate_pz: float = 0.002185,
    cz_unpaired_loss_prob: float = 0.0,
    mover_px: float = 0.000806,
    mover_py: float = 0.000806,
    mover_pz: float = 0.002458,
    move_loss_prob: float = 0.0,
    sitter_px: float = 0.0003066,
    sitter_py: float = 0.0003066,
    sitter_pz: float = 0.0004639,
    sit_loss_prob: float = 0.0
)

Bases: GeminiNoiseModelABC

noisy_moments

noisy_moments(
    moments: Iterable[Moment], system_qubits: Sequence[Qid]
) -> Sequence[cirq.OP_TREE]

Adds possibly stateful noise to a series of moments.

Parameters:

Name Type Description Default
moments Iterable[Moment]

The moments to add noise to.

required
system_qubits Sequence[Qid]

A list of all qubits in the system.

required

Returns:

Type Description
Sequence[OP_TREE]

A sequence of OP_TREEEs, with the k'th tree corresponding to the

Sequence[OP_TREE]

noisy operations for the k'th moment.

Source code in .venv/lib/python3.12/site-packages/bloqade/cirq_utils/noise/model.py
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
def noisy_moments(
    self, moments: Iterable[cirq.Moment], system_qubits: Sequence[cirq.Qid]
) -> Sequence[cirq.OP_TREE]:
    """Adds possibly stateful noise to a series of moments.

    Args:
        moments: The moments to add noise to.
        system_qubits: A list of all qubits in the system.

    Returns:
        A sequence of OP_TREEEs, with the k'th tree corresponding to the
        noisy operations for the k'th moment.
    """

    if self.check_input_circuit:
        self.validate_moments(moments)

    moments = list(moments)

    if len(moments) == 0:
        return []

    nqubs = len(system_qubits)
    noisy_moment_list = []

    prev_moment: cirq.Moment | None = None

    # TODO: clean up error getters so they return a list moments rather than circuits
    for i in range(len(moments)):
        noisy_moment_list.extend(
            [
                moment
                for moment in _two_zone_utils.get_move_error_channel_two_zoned(
                    moments[i],
                    prev_moment,
                    np.array(self.mover_pauli_rates),
                    np.array(self.sitter_pauli_rates),
                    nqubs,
                ).moments
                if len(moment) > 0
            ]
        )

        noisy_moment_list.append(moments[i])

        noisy_moment_list.extend(
            [
                moment
                for moment in _two_zone_utils.get_gate_error_channel(
                    moments[i],
                    np.array(self.local_pauli_rates),
                    np.array(self.global_pauli_rates),
                    self.two_qubit_pauli,
                    np.array(self.cz_unpaired_pauli_rates),
                    nqubs,
                ).moments
                if len(moment) > 0
            ]
        )

        prev_moment = moments[i]

    return noisy_moment_list