Move Rotation#

This notebook demonstrates moving and rotating the spatial boundary of a logical qubit. Rotating the boundary types of a logical qubit is a crucial operation in lattice surgery. For instance, merging two logical qubits requires their boundary types to be aligned. Therefore, performing such boundary-type rotations is often essential to facilitate seamless lattice merging.

Construction#

tqec provides builtin function tqec.gallery.move_rotation to construct it.

[1]:
from tqec import Basis
from tqec.gallery import move_rotation

graph = move_rotation()
graph.view_as_html()
[1]:

This operation rotates the orientation of the logical observable through a spatial L-shape junction. As shown below, the correlation surface initially aligns with the Y-axis and finally aligns with X-axis.

[2]:
correlation_surfaces = graph.find_correlation_surfaces()
graph.view_as_html(
    pop_faces_at_direction="-Y",
    show_correlation_surface=correlation_surfaces[0],
)
[2]:

Example Circuit#

Here we show an example circuit of move rotation with \(d=3\) surface code that is initialized and measured in X basis. You can download the circuit here or view it in Crumble.

[3]:
from tqec import NoiseModel, compile_block_graph

graph = move_rotation(Basis.X)
compiled_graph = compile_block_graph(graph)
circuit = compiled_graph.generate_stim_circuit(
    k=1, noise_model=NoiseModel.uniform_depolarizing(p=0.001)
)

Simulation#

Here we show the simulation results of both X-basis and Z-basis experiments under uniform depolarizing noise model.

Click to show the full code used for simulation

from pathlib import Path

import matplotlib.pyplot as plt
import numpy
import sinter

from tqec.gallery.memory import memory
from tqec.gallery.move_rotation import move_rotation
from tqec import NoiseModel
from tqec.simulation.plotting.inset import plot_observable_as_inset
from tqec.simulation.simulation import start_simulation_using_sinter
from tqec.utils.enums import Basis

SAVE_DIR = Path("results")


def generate_graphs(support_observable_basis: Basis) -> None:
    block_graph = move_rotation(support_observable_basis)
    zx_graph = block_graph.to_zx_graph()

    correlation_surfaces = block_graph.find_correlation_surfaces()

    stats = start_simulation_using_sinter(
        block_graph,
        range(1, 4),
        list(numpy.logspace(-4, -1, 10)),
        NoiseModel.uniform_depolarizing,
        manhattan_radius=2,
        observables=correlation_surfaces,
        num_workers=20,
        max_shots=10_000_000,
        max_errors=5_000,
        decoders=["pymatching"],
        print_progress=True,
    )

    for i, stat in enumerate(stats):
        fig, ax = plt.subplots()
        sinter.plot_error_rate(
            ax=ax,
            stats=stat,
            x_func=lambda stat: stat.json_metadata["p"],
            failure_units_per_shot_func=lambda stat: stat.json_metadata["d"],
            group_func=lambda stat: stat.json_metadata["d"],
        )
        plot_observable_as_inset(ax, zx_graph, correlation_surfaces[i])
        ax.grid(axis="both")
        ax.legend()
        ax.loglog()
        ax.set_title("Move Rotation Error Rate")
        ax.set_xlabel("Physical Error Rate")
        ax.set_ylabel("Logical Error Rate(per round)")
        fig.savefig(
            SAVE_DIR
            / f"move_rotation_result_{support_observable_basis}_observable_{i}.png"
        )


def main():
    SAVE_DIR.mkdir(exist_ok=True)
    generate_graphs(Basis.Z)
    generate_graphs(Basis.X)


if __name__ == "__main__":
    main()

X-basis#

X-basis

Z-basis#

Z-basis