Source code for surface_sim.circuit_blocks.unrot_surface_code_css

from collections.abc import Collection, Generator
from itertools import chain

from stim import Circuit

from ..detectors import Detectors
from ..layouts.layout import Layout
from ..models import Model
from .decorators import qubit_encoding

# methods to have in this script
from .util import (
    general_grow_code_iterator,
    idle_iterator,
    init_qubits,
    init_qubits_iterator,
    init_qubits_x0_iterator,
    init_qubits_x1_iterator,
    init_qubits_z0_iterator,
    init_qubits_z1_iterator,
    log_depolarize1_error_iterator,
    log_fold_trans_h,
    log_fold_trans_h_iterator,
    log_fold_trans_s,
    log_fold_trans_s_iterator,
    log_meas,
    log_meas_iterator,
    log_meas_x_iterator,
    log_meas_z_iterator,
    log_trans_cnot,
    log_trans_cnot_iterator,
    log_x,
    log_x_error_iterator,
    log_x_iterator,
    log_y_error_iterator,
    log_z,
    log_z_error_iterator,
    log_z_iterator,
    pauli_observable_x_iterator,
    pauli_observable_y_iterator,
    pauli_observable_z_iterator,
    qec_round_iterator,
    qec_round_iterator_cnots,
    qubit_coords,
)

__all__ = [
    "qubit_coords",
    "idle_iterator",
    "log_meas",
    "log_meas_iterator",
    "log_meas_z_iterator",
    "log_meas_x_iterator",
    "log_x",
    "log_x_iterator",
    "log_z",
    "log_z_iterator",
    "init_qubits",
    "init_qubits_iterator",
    "init_qubits_z0_iterator",
    "init_qubits_z1_iterator",
    "init_qubits_x0_iterator",
    "init_qubits_x1_iterator",
    "log_fold_trans_s",
    "log_fold_trans_s_iterator",
    "log_fold_trans_h",
    "log_fold_trans_h_iterator",
    "log_trans_cnot",
    "log_trans_cnot_iterator",
    "qec_round",
    "qec_round_iterator",
    "qec_round_cnots",
    "qec_round_iterator_cnots",
    "encoding_qubits_iterator",
    "encoding_qubits_x0_iterator",
    "encoding_qubits_y0_iterator",
    "encoding_qubits_z0_iterator",
    "encoding_qubits_x0_iterator_single_layer_resets",
    "encoding_qubits_y0_iterator_single_layer_resets",
    "encoding_qubits_z0_iterator_single_layer_resets",
    "encoding_qubits_x0_iterator_cnots",
    "encoding_qubits_y0_iterator_cnots",
    "encoding_qubits_z0_iterator_cnots",
    "log_x_error_iterator",
    "log_y_error_iterator",
    "log_z_error_iterator",
    "log_depolarize1_error_iterator",
    "gate_to_iterator",
    "gate_to_iterator_cnots",
    "pauli_observable_x_iterator",
    "pauli_observable_y_iterator",
    "pauli_observable_z_iterator",
]


[docs] def qec_round( model: Model, layout: Layout, detectors: Detectors, anc_reset: bool = True, anc_detectors: Collection[str] | None = None, ) -> Circuit: """ Returns stim circuit corresponding to a QEC round of the given model. Parameters ---------- model Noise model for the gates. layout Code layout. detectors Detector object to use for their definition. anc_reset If ``True``, ancillas are reset at the beginning of the QEC round. By default ``True``. anc_detectors List of ancilla qubits for which to define the detectors. If ``None``, adds all detectors. By default ``None``. Notes ----- This implementation follows: https://doi.org/10.1103/PhysRevApplied.8.034021 It activates all the ancillas in ``detectors`` to always build the detectors. As this function should not be used when building encoded circuits with the iterating functions, it does not matter if the detectors are activated or not. """ circuit = sum( qec_round_iterator(model=model, layout=layout, anc_reset=anc_reset), start=Circuit(), ) # add detectors anc_qubits = layout.anc_qubits if anc_detectors is None: anc_detectors = anc_qubits if set(anc_detectors) > set(anc_qubits): raise ValueError("Elements in 'anc_detectors' are not ancilla qubits.") # activate detectors so that "Detectors.build_from_anc" always populates # the stim detector definitions. inactive_dets = set(anc_detectors).difference(detectors.detectors) detectors.activate_detectors(inactive_dets) circuit += detectors.build_from_anc( model.meas_target, anc_reset, anc_qubits=anc_detectors ) return circuit
[docs] def qec_round_cnots( model: Model, layout: Layout, detectors: Detectors, anc_reset: bool = True, anc_detectors: Collection[str] | None = None, ) -> Circuit: """ Returns stim circuit corresponding to a QEC round of the given model. Parameters ---------- model Noise model for the gates. layout Code layout. detectors Detector object to use for their definition. anc_reset If ``True``, ancillas are reset at the beginning of the QEC round. By default ``True``. anc_detectors List of ancilla qubits for which to define the detectors. If ``None``, adds all detectors. By default ``None``. Notes ----- This implementation uses the following instructions: CNOT, RZ, RX, MZ, MX. Note that if ``anc_reset = False``, then the ancillas are not reset in the first round and stim assumes that, if not specified, they are reset in the Z-basis, which is the incorrect basis for the X-type ancillas. See the initialization iterators from the dodecahedron code. It activates all the ancillas in ``detectors`` to always build the detectors. As this function should not be used when building encoded circuits with the iterating functions, it does not matter if the detectors are activated or not. """ circuit = sum( qec_round_iterator_cnots(model=model, layout=layout, anc_reset=anc_reset), start=Circuit(), ) # add detectors anc_qubits = layout.anc_qubits if anc_detectors is None: anc_detectors = anc_qubits if set(anc_detectors) > set(anc_qubits): raise ValueError("Elements in 'anc_detectors' are not ancilla qubits.") # activate detectors so that "Detectors.build_from_anc" always populates # the stim detector definitions. inactive_dets = set(anc_detectors).difference(detectors.detectors) detectors.activate_detectors(inactive_dets) circuit += detectors.build_from_anc( model.meas_target, anc_reset, anc_qubits=anc_detectors ) return circuit
[docs] def encoding_qubits_iterator( model: Model, layout: Layout, physical_reset_op: str, primitive_gates: str, single_layer_resets: bool = False, ) -> Generator[Circuit]: """ Yields stim circuit blocks which as a whole correspond to an encoding circuit to an unrotated surface code of the given model without the detectors. Note that this encoding circuit is not fault tolerant. Parameters ---------- model Noise model for the gates. layout Code layout. physical_reset_op Reset operation to be applied to the physical qubit that will grow to a unrotated surface code. primitive_gates Set of primitive gates to use. The available options are: (1) ``"cnot"``, which uses RZ, RX, and CNOT gates, and (2) ``"cz"``, which uses RZ, H, and CZ gates. Note that the ``physical_reset_op`` will not be decomposed into primitive gates. single_layer_resets Flag to put all resets in a single operation layer at the beginning of the encoding circuit. By default ``False``. Notes ----- The implementation follows Figures 2 and 9 from: Higgott, Oscar. "Optimal local unitary encoding circuits for the surface code." Quantum 5, 517 (2021). """ if layout.code != "unrotated_surface_code": raise TypeError( f"The given layout is not an unrotated surface code, but a {layout.code}." ) resets_x, resets_z, cnot_layers = [], [], [] if layout.distance % 2 == 1: resets_x.append([(0, -2), (1, 1), (1, -1), (-1, 1), (-1, -1), (0, 2)]) resets_z.append([(2, 2), (2, -2), (-2, 2), (-2, -2), (2, 0), (-2, 0)]) cnot_layers.append([ [(-1, -1), (-2, -2), (1, -1), (2, -2), (0, 0), (-2, 0), (-1, 1), (-2, 2), (1, 1), (2, 2)], [(0, 0), (2, 0), (-1, -1), (-2, 0)], [(0, -2), (0, 0), (-1, 1), (-2, 0), (1, -1), (2, 0)], [(0, 2), (0, 0), (1, 1), (2, 0), (0, -2), (-1, -1)], [(0, -2), (1, -1), (0, 2), (-1, 1)], [(0, 2), (1, 1)], ]) # fmt: skip else: resets_x.append([(1, -1), (-1, 1)]) resets_z.append([(1, 1), (-1, -1)]) cnot_layers.append([ [(0, 0), (-1, -1), (1, -1), (1, 1)], [(-1, 1), (-1, -1), (0, 0), (1, 1)], [(-1, 1), (0, 0)], [(1, -1), (0, 0)], ]) # fmt: skip if layout.distance >= 4: resets_x.append([(-1, -3), (1, -3), (-2, -2), (2, -2), (-2, 0), (2, 0), (-2, 2), (2, 2), (-1, 3), (1, 3)]) # fmt: skip resets_z.append([(3, 3), (-3, 3), (3, -3), (-3, -3), (3, 1), (-3, 1), (3, -1), (-3, -1), (0, 2), (0, -2)]) # fmt: skip cnot_layers.append([ [(-2, -2), (-3, -3), (2, -2), (3, -3), (-1, -1), (-3, -1), (0, -2), (1, -1), (0, 0), (-2, 0), (0, 2), (-1, 1), (1, 1), (3, 1), (-2, 2), (-3, 3), (2, 2), (3, 3)], [(-2, -2), (-3, -1), (-1, -3), (-1, -1), (1, -3), (0, -2), (1, -1), (3, -1), (0, 0), (2, 0), (-1, 1), (-3, 1), (-1, 3), (0, 2), (1, 3), (1, 1), (2, 2), (3, 1)], [(-1, -3), (0, -2), (1, -3), (1, -1), (2, -2), (3, -1), (-2, 0), (-3, -1), (2, 0), (3, 1), (-2, 2), (-3, 1), (-1, 3), (-1, 1), (1, 3), (0, 2)], [(-1, -3), (-2, -2), (1, -3), (2, -2), (-2, 0), (-3, 1), (2, 0), (3, -1), (-1, 3), (-2, 2), (1, 3), (2, 2)], ]) # fmt: skip for d in range(4 - layout.distance % 2, layout.distance, 2): reset_x, reset_z, cnots = _get_grow_code_coordinates(d) resets_x.append(reset_x) resets_z.append(reset_z) cnot_layers.append(cnots) if single_layer_resets: reset_x = list(chain(*resets_x)) reset_z = list(chain(*resets_z)) yield from general_grow_code_iterator( model, layout, reset_x, reset_z, [], physical_reset_op, primitive_gates, block="resets", ) yield from general_grow_code_iterator( model, layout, resets_x[0], resets_z[0], cnot_layers[0], physical_reset_op, primitive_gates, block="unitary" if single_layer_resets else "whole", ) for reset_x, reset_z, cnots in zip(resets_x[1:], resets_z[1:], cnot_layers[1:]): yield from general_grow_code_iterator( model, layout, reset_x, reset_z, cnots, None, primitive_gates, block="unitary" if single_layer_resets else "whole", )
def _get_grow_code_coordinates( d: int, ) -> tuple[list[tuple[int, int]], list[tuple[int, int]], list[list[tuple[int, int]]]]: """ Returns the RX, RZ, and CNOT information to grow a distance ``d > 4`` unrotated surface code to a ``d+2`` one. Note that this circuit is not fault tolerant. Parameters ---------- d Distance of the current unrotated surface code. Returns ------- reset_x_coords Generalized coordinates for the data qubits that need to be reset in the X basis. reset_z_coords Generalized coordinates for the data qubits that need to be reset in the Z basis. cnot_layers_coords List of CNOT layers that need to be applied at each step of the encoding circuit. The data qubits are specified by the generalized coordinates. Notes ----- The implementation follows Figure 2 from: Higgott, Oscar. "Optimal local unitary encoding circuits for the surface code." Quantum 5, 517 (2021). """ reset_x_coords = [] for i in range(-d + 1, d + 1, 2): reset_x_coords += [(i, -d - 1)] # top reset_x_coords += [(i, d + 1)] # bottom for i in range(-d, d + 2, 2): reset_x_coords += [(-d, i)] # left reset_x_coords += [(d, i)] # right reset_z_coords = [] for i in range(-d + 2, d, 2): reset_z_coords += [(i, -d)] # top reset_z_coords += [(i, d)] # bottom for i in range(-d - 1, d + 3, 2): reset_z_coords += [(-d - 1, i)] # left reset_z_coords += [(d + 1, i)] # right cnot_layers_coords = [] # step 1 step1 = [(-d, -d), (-d - 1, -d - 1), (d, -d), (d + 1, -d - 1), (-d, d), (-d - 1, d + 1), (d, d), (d + 1, d + 1)] # fmt: skip for i in range(1 - d, d + 1, 2): step1 += [(1 - d, i), (-d - 1, i)] # left step1 += [(d - 1, i), (d + 1, i)] # right for i in range(2 - d, d, 2): step1 += [(2 - d, i), (-d, i)] # left step1 += [(d - 2, i), (d, i)] # right cnot_layers_coords.append(step1) # step 2 step2 = [(-d, -d), (-d - 1, -d + 1), (d, -d), (d + 1, 1 - d), (-d, d), (-d - 1, d - 1), (d, d), (d + 1, d - 1)] # fmt: skip for i in range(1 - d, d + 1, 2): step2 += [(i, -d - 1), (i, 1 - d)] # top step2 += [(i, d + 1), (i, d - 1)] # bottom for i in range(2 - d, d, 2): step2 += [(i, -d), (i, 2 - d)] # top step2 += [(i, d), (i, d - 2)] # bottom cnot_layers_coords.append(step2) # step 3 step3 = [] for i in range(1 - d, d + 1, 2): step3 += [(i, -d - 1), (i + 1, -d)] # top step3 += [(i, d + 1), (i - 1, d)] # bottom for i in range(2 - d, d, 2): step3 += [(-d, i), (-d - 1, i - 1)] # left step3 += [(d, i), (d + 1, i + 1)] # right cnot_layers_coords.append(step3) # step 4 step4 = [] for i in range(1 - d, d + 1, 2): step4 += [(i, -d - 1), (i - 1, -d)] # top step4 += [(i, d + 1), (i + 1, d)] # bottom for i in range(2 - d, d, 2): step4 += [(-d, i), (-d - 1, i + 1)] # left step4 += [(d, i), (d + 1, i - 1)] # right cnot_layers_coords.append(step4) return reset_x_coords, reset_z_coords, cnot_layers_coords @qubit_encoding def encoding_qubits_x0_iterator( model: Model, layout: Layout, ) -> Generator[Circuit]: """ Yields stim circuit blocks which as a whole correspond to an encoding circuit for the +X eigenstate to an unrotated surface code of the given model without the detectors. Note that this encoding circuit is not fault tolerant. Parameters ---------- model Noise model for the gates. layout Code layout. Notes ----- The implementation follows Figure 9 from: Higgott, Oscar. "Optimal local unitary encoding circuits for the surface code." Quantum 5, 517 (2021). but uses RZ, H, and CZ operations as primitive operations, except for the ``physical_reset_op``. """ yield from encoding_qubits_iterator( model=model, layout=layout, physical_reset_op="reset_x", primitive_gates="cz", ) @qubit_encoding def encoding_qubits_y0_iterator( model: Model, layout: Layout, ) -> Generator[Circuit]: """ Yields stim circuit blocks which as a whole correspond to an encoding circuit for the +Y eigenstate to an unrotated surface code of the given model without the detectors. Note that this encoding circuit is not fault tolerant. Parameters ---------- model Noise model for the gates. layout Code layout. Notes ----- The implementation follows Figures 2 and 9 from: Higgott, Oscar. "Optimal local unitary encoding circuits for the surface code." Quantum 5, 517 (2021). but uses RZ, H, and CZ operations as primitive operations, except for the ``physical_reset_op``. """ yield from encoding_qubits_iterator( model=model, layout=layout, physical_reset_op="reset_y", primitive_gates="cz", ) @qubit_encoding def encoding_qubits_z0_iterator( model: Model, layout: Layout, ) -> Generator[Circuit]: """ Yields stim circuit blocks which as a whole correspond to an encoding circuit for the +Z eigenstate to an unrotated surface code of the given model without the detectors. Note that this encoding circuit is not fault tolerant. Parameters ---------- model Noise model for the gates. layout Code layout. Notes ----- The implementation follows Figures 2 and 9 from: Higgott, Oscar. "Optimal local unitary encoding circuits for the surface code." Quantum 5, 517 (2021). but uses RZ, H, and CZ operations as primitive operations, except for the ``physical_reset_op``. """ yield from encoding_qubits_iterator( model=model, layout=layout, physical_reset_op="reset_z", primitive_gates="cz", ) @qubit_encoding def encoding_qubits_x0_iterator_single_layer_resets( model: Model, layout: Layout, ) -> Generator[Circuit]: """ Yields stim circuit blocks which as a whole correspond to an encoding circuit for the +X eigenstate to an unrotated surface code of the given model without the detectors. Note that this encoding circuit is not fault tolerant. Parameters ---------- model Noise model for the gates. layout Code layout. Notes ----- The implementation follows Figure 9 from: Higgott, Oscar. "Optimal local unitary encoding circuits for the surface code." Quantum 5, 517 (2021). but uses RZ, H, and CZ operations as primitive operations, except for the ``physical_reset_op``. """ yield from encoding_qubits_iterator( model=model, layout=layout, physical_reset_op="reset_x", primitive_gates="cz", single_layer_resets=True, ) @qubit_encoding def encoding_qubits_y0_iterator_single_layer_resets( model: Model, layout: Layout, ) -> Generator[Circuit]: """ Yields stim circuit blocks which as a whole correspond to an encoding circuit for the +Y eigenstate to an unrotated surface code of the given model without the detectors. Note that this encoding circuit is not fault tolerant. Parameters ---------- model Noise model for the gates. layout Code layout. Notes ----- The implementation follows Figures 2 and 9 from: Higgott, Oscar. "Optimal local unitary encoding circuits for the surface code." Quantum 5, 517 (2021). but uses RZ, H, and CZ operations as primitive operations, except for the ``physical_reset_op``. """ yield from encoding_qubits_iterator( model=model, layout=layout, physical_reset_op="reset_y", primitive_gates="cz", single_layer_resets=True, ) @qubit_encoding def encoding_qubits_z0_iterator_single_layer_resets( model: Model, layout: Layout, ) -> Generator[Circuit]: """ Yields stim circuit blocks which as a whole correspond to an encoding circuit for the +Z eigenstate to an unrotated surface code of the given model without the detectors. Note that this encoding circuit is not fault tolerant. Parameters ---------- model Noise model for the gates. layout Code layout. Notes ----- The implementation follows Figures 2 and 9 from: Higgott, Oscar. "Optimal local unitary encoding circuits for the surface code." Quantum 5, 517 (2021). but uses RZ, H, and CZ operations as primitive operations, except for the ``physical_reset_op``. """ yield from encoding_qubits_iterator( model=model, layout=layout, physical_reset_op="reset_z", primitive_gates="cz", single_layer_resets=True, ) @qubit_encoding def encoding_qubits_x0_iterator_cnots( model: Model, layout: Layout, ) -> Generator[Circuit]: """ Yields stim circuit blocks which as a whole correspond to an encoding circuit for the +X eigenstate to an unrotated surface code of the given model without the detectors. Note that this encoding circuit is not fault tolerant. Parameters ---------- model Noise model for the gates. layout Code layout. Notes ----- The implementation follows Figures 2 and 9 from: Higgott, Oscar. "Optimal local unitary encoding circuits for the surface code." Quantum 5, 517 (2021). """ yield from encoding_qubits_iterator( model=model, layout=layout, physical_reset_op="reset_x", primitive_gates="cnot", ) @qubit_encoding def encoding_qubits_y0_iterator_cnots( model: Model, layout: Layout, ) -> Generator[Circuit]: """ Yields stim circuit blocks which as a whole correspond to an encoding circuit for the +Y eigenstate to an unrotated surface code of the given model without the detectors. Note that this encoding circuit is not fault tolerant. Parameters ---------- model Noise model for the gates. layout Code layout. Notes ----- The implementation follows Figures 2 and 9 from: Higgott, Oscar. "Optimal local unitary encoding circuits for the surface code." Quantum 5, 517 (2021). """ yield from encoding_qubits_iterator( model=model, layout=layout, physical_reset_op="reset_y", primitive_gates="cnot", ) @qubit_encoding def encoding_qubits_z0_iterator_cnots( model: Model, layout: Layout, ) -> Generator[Circuit]: """ Yields stim circuit blocks which as a whole correspond to an encoding circuit for the +Z eigenstate to an unrotated surface code of the given model without the detectors. Note that this encoding circuit is not fault tolerant. Parameters ---------- model Noise model for the gates. layout Code layout. Notes ----- The implementation follows Figures 2 and 9 from: Higgott, Oscar. "Optimal local unitary encoding circuits for the surface code." Quantum 5, 517 (2021). """ yield from encoding_qubits_iterator( model=model, layout=layout, physical_reset_op="reset_z", primitive_gates="cnot", ) gate_to_iterator = { "TICK": qec_round_iterator, "I": idle_iterator, "S": log_fold_trans_s_iterator, "H": log_fold_trans_h_iterator, "X": log_x_iterator, "Z": log_z_iterator, "CX": log_trans_cnot_iterator, "CNOT": log_trans_cnot_iterator, "R": init_qubits_z0_iterator, "RZ": init_qubits_z0_iterator, "RX": init_qubits_x0_iterator, "M": log_meas_z_iterator, "MZ": log_meas_z_iterator, "MX": log_meas_x_iterator, "X_ERROR": log_x_error_iterator, "Y_ERROR": log_y_error_iterator, "Z_ERROR": log_z_error_iterator, "DEPOLARIZE1": log_depolarize1_error_iterator, } gate_to_iterator_cnots = { "TICK": qec_round_iterator_cnots, "I": idle_iterator, "S": log_fold_trans_s_iterator, "H": log_fold_trans_h_iterator, "X": log_x_iterator, "Z": log_z_iterator, "CX": log_trans_cnot_iterator, "CNOT": log_trans_cnot_iterator, "R": init_qubits_z0_iterator, "RZ": init_qubits_z0_iterator, "RX": init_qubits_x0_iterator, "M": log_meas_z_iterator, "MZ": log_meas_z_iterator, "MX": log_meas_x_iterator, "X_ERROR": log_x_error_iterator, "Y_ERROR": log_y_error_iterator, "Z_ERROR": log_z_error_iterator, "DEPOLARIZE1": log_depolarize1_error_iterator, }