Source code for loom_rotated_surface_code.applicator.y_wall_out.y_wall_out

"""
Copyright 2024 Entropica Labs Pte Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

"""

from loom.eka import PauliOperator
from loom.eka.utilities import Orientation
from loom.interpreter import InterpretationStep

from .y_wall_out_initial_syndrome_measurement import (
    y_wall_out_initial_syndrome_measurement,
)
from .y_wall_measurement_hadamard import (
    y_wall_out_measurement_and_hadamard,
    find_qubit_sets,
)
from .y_wall_out_recombination_swap_then_qec import (
    y_wall_out_recombination_swap_then_qec,
    get_idle_hadamard_info,
)
from .y_wall_out_final_swap_then_qec import y_wall_out_final_swap_then_qec
from .y_wall_out_final_syndrome_measurement import y_wall_out_final_qec_rounds
from ...code_factory import RotatedSurfaceCode


[docs] def y_wall_out( interpretation_step: InterpretationStep, block: RotatedSurfaceCode, wall_position: int, wall_orientation: Orientation, same_timeslice: bool, debug_mode: bool, ) -> InterpretationStep: """ Implement the y_wall_out operation. The operation consists of three steps: - A.) Begin y_wall_out composite operation session - B.) Consistency check - C.) Extract geometric information - D.) Call the sub-operations in sequence: - D.1) One round of syndrome measurement of the initial block, \ with CNOT scheduling specifically designed for fault tolerance - D.2) Y wall measurement and Hadamard - D.3) Recombination of the Block to get rid of the wall via swap-then-QEC - D.4) Swap-then-QEC to move the recombined block back to its original position - D.5) d-2 rounds of syndrome measurement of the final block, \ with CNOT scheduling specifically designed for fault tolerance - E.) End the composite operation session and append the circuit Example: the block on the left is transformed into the block on the right:: X X *(0,0) --- (1,0) --- (2,0)* *(0,0) --- (1,0) --- (2,0)* | | | | | | | Z | X | Z | Z | X | Z | | | | | | (0,1) --- (1,1) --- (2,1) (0,1) --- (1,1) --- (2,1) | | | | | | Z | X | Z | Z | X | Z | | | | | | | (0,2) --- (1,2) --- (2,2) -> (0,2) --- (1,2) --- (2,2)* | | | | | | | Z | X | Z | Z | X | | | | | | | *(0,3) --- (1,3) --- (2,3) (0,3) --- (1,3) --- (2,3) | | | | | | | X | Z | Z | X | Z | X | | | | | | (0,4) --- (1,4) --- (2,4) (0,4) --- (1,4) --- (2,4)* | | | Z X | Z | X | Z | | | (0,5) --- (1,5) --- (2,5)* X Other allowed blocks are reflection along a vertical axis and rotation by 90 degrees. More examples in tests. Parameters ---------- interpretation_step : InterpretationStep The interpretation step containing the blocks involved in the operation. block : RotatedSurfaceCode The block to which the operation will be applied. wall_position : int The position of the wall. wall_orientation : Orientation The orientation of the wall. same_timeslice : bool Flag indicating whether the operation is part of the same timestep as the previous operation. debug_mode : bool Flag indicating whether the interpretation should be done in debug mode. Activating debug mode will enable commutation validation for Block. Returns ------- InterpretationStep The interpretation step after applying the y_wall_out operation. """ # A) Begin y_wall_out composite operation session interpretation_step.begin_composite_operation_session_MUT( same_timeslice=same_timeslice, circuit_name=(f"y_wall_out operation on block {block.unique_label}"), ) # B) Consistency check y_wall_out_consistency_check(block, wall_position, wall_orientation) # C) Extract geometric information is_wall_hor = wall_orientation == Orientation.HORIZONTAL qubits_to_measure, qubits_to_idle, qubits_to_hadamard = find_qubit_sets( block, wall_position, is_wall_hor ) # D) Call the sub-operations in sequence # D.1) One round of syndrome measurement of the initial block interpretation_step = y_wall_out_initial_syndrome_measurement( interpretation_step, block, same_timeslice=False, debug_mode=debug_mode, ) # D.2) Y wall measurement and Hadamard interpretation_step = y_wall_out_measurement_and_hadamard( interpretation_step, block, wall_position, wall_orientation, same_timeslice=False, debug_mode=debug_mode, ) current_block: RotatedSurfaceCode = interpretation_step.get_block( block.unique_label ) # Extract idle side directions for later use _, _, _, idle_side_directions, _ = get_idle_hadamard_info( current_block, qubits_to_hadamard, qubits_to_idle, ) # D.3) Recombination of the Block to get rid of the wall via swap-then-QEC interpretation_step = y_wall_out_recombination_swap_then_qec( interpretation_step, current_block, block, qubits_to_idle, qubits_to_measure, qubits_to_hadamard, same_timeslice=False, debug_mode=debug_mode, ) current_block = interpretation_step.get_block(block.unique_label) # D.4) Swap-then-QEC to move the recombined block back to its original position interpretation_step = y_wall_out_final_swap_then_qec( interpretation_step, current_block, idle_side_directions, same_timeslice=False, debug_mode=debug_mode, ) current_block = interpretation_step.get_block(block.unique_label) # D.5) d-2 rounds of syndrome measurement of the final block interpretation_step = y_wall_out_final_qec_rounds( interpretation_step, current_block, same_timeslice=False, debug_mode=debug_mode, ) # E) End the composite operation session and append the circuit y_wall_out_circuit = interpretation_step.end_composite_operation_session_MUT() interpretation_step.append_circuit_MUT(y_wall_out_circuit, same_timeslice) return interpretation_step
[docs] def y_wall_out_consistency_check( block: RotatedSurfaceCode, wall_pos: int, wall_orientation: Orientation, ) -> None: """ Check if the y_wall_out operation can be applied to a given block. Note that there are 4 different cases defined by: - the orientation of the block - whether the top-left bulk stabilizer is X or Z Parameters ---------- block: RotatedSurfaceCode The block to which the operation will be applied. wall_pos: int The position of the wall. wall_orientation: Orientation The orientation of the wall. Raises ------ ValueError If the block dimensions are not valid. If the wall position is not valid. If the block and the wall do not have perpendicular orientations. If the left boundary of a horizontal block is not a Z-type boundary. If the top boundary of a vertical block is not a Z-type boundary. If the block does not have 3 topological corners located at the geometric corners. If the missing geometric corner is not the expected one. If the non-geometric topological corner is not the expected one. If the Z logical operator is not located at the expected position. If the X logical operator is not located at the expected position. """ # Extract some information about the geometry of the block and the wall is_wall_hor = wall_orientation == Orientation.HORIZONTAL topological_corners = block.topological_corners geometric_corners = block.geometric_corners u_l_qub = block.upper_left_qubit is_u_l_stab_x = block.upper_left_4body_stabilizer.pauli[0] == "X" dim_x, dim_z = block.size larger_dim = max(dim_x, dim_z) smaller_dim = min(dim_x, dim_z) # Check the block dimensions if smaller_dim % 2 == 0 or larger_dim % 2 != 0: raise ValueError( "The smaller dimension of the block must be odd and the larger dimension " "must be even." ) # Check block orientation and wall orientation is_block_hor = dim_x == larger_dim if is_block_hor == is_wall_hor: raise ValueError("The block and the wall must have perpendicular orientations.") if is_block_hor: if block.boundary_type("left") != "Z": raise ValueError( "The left boundary of a horizontal block must be a Z-type boundary." ) else: if block.boundary_type("top") != "Z": raise ValueError( "The top boundary of a vertical block must be a Z-type boundary." ) # Check the wall position if wall_pos != smaller_dim: raise ValueError( "The wall position must be such that the block on the bottom/right side " "of the wall is a square." ) # Check the corners common_corners = set(topological_corners) & set(geometric_corners) # There should be 3 topological corners located at the geometric corners if len(common_corners) != 3: raise ValueError( "The block must have 3 topological corners located at the geometric " "corners." ) # Find which topological corner is not geometric and which geometric corner is not # topological geom_corner_not_topol = next(iter(set(geometric_corners) - common_corners)) topol_corner_not_geom = next(iter(set(topological_corners) - common_corners)) # Find the expected non-geometric topological corner: # s: start of the block (left/top), e: end of the block(right/bot), w: wall_position # If the block is horizontal: # If the block has the top left stabilizer as X: # (e, w) # else: # (s, w) # else: # If the block has the top left stabilizer as X: # (w, e) # else: # (w, s) exp_topol_corner_not_geom = ( u_l_qub[0] + (wall_pos if is_block_hor else is_u_l_stab_x * (smaller_dim - 1)), u_l_qub[1] + (is_u_l_stab_x * (smaller_dim - 1) if is_block_hor else wall_pos), 0, ) if exp_topol_corner_not_geom != topol_corner_not_geom: raise ValueError( f"The non-geometric topological corner should have been " f"{exp_topol_corner_not_geom} but it is {topol_corner_not_geom}." ) # Find the expected non-topological geometric corner: # If the top left stabilizer is X: # it's the botton right corner i.e. maximize(x[0] + x[1]) # else: # If the block is horizontal: # it's the top right corner i.e. maximize(x[0] - x[1]) # else: # it's the bottom left corner i.e. maximize(-x[0] + x[1]) exp_geom_corner_not_topol = max( geometric_corners, key=lambda x: ( x[0] + x[1] if is_u_l_stab_x else (-x[0] + x[1]) * (-1) ** (is_block_hor) ), ) if exp_geom_corner_not_topol != geom_corner_not_topol: raise ValueError( f"The non-topological geometric corner should have been " f"{exp_geom_corner_not_topol} but it is {geom_corner_not_topol}." ) # Check that the logical operators are located in the correct positions expected_z_logical = PauliOperator( "Z" * smaller_dim, block.boundary_qubits("left") if is_block_hor else block.boundary_qubits("top"), ) if block.logical_z_operators[0] != expected_z_logical: raise ValueError( "The Z logical operator is not located at the expected position. It needs " "to be on the left for a horizontal block and on the top for a vertical " "block." ) # The X logical operator suffices to be straight and with the right distance # given the geometry of the block x_log_data_qubits = block.logical_x_operators[0].data_qubits is_x_logical_vert = all(q[0] == x_log_data_qubits[0][0] for q in x_log_data_qubits) is_x_logical_hor = all(q[1] == x_log_data_qubits[0][1] for q in x_log_data_qubits) is_x_straight = is_x_logical_vert or is_x_logical_hor is_x_correct_distance = len(x_log_data_qubits) == smaller_dim + 1 if not (is_x_straight and is_x_correct_distance): raise ValueError( "The X logical operator is not located at the expected position. It needs " "to be straight and with the smallest distance possible given the geometry " "of the block." )