Logical CNOT#

This notebook shows the construction and simulation results of the logical CNOT gate between two logical qubits with lattice surgery.

Construction#

A logical CNOT between two logical qubits can be implemented with the help of an ancilla qubit. It can be accomplished by the following steps:

  1. \(M_{ZZ}\) parity measurement between \(Q_{control}\) and \(Q_{ancilla}\).

  2. \(M_{XX}\) parity measurement between \(Q_{target}\) and \(Q_{ancilla}\).

  3. \(M_{Z}\) measurement of \(Q_{ancilla}\).

tqec provides builtin functions tqec.gallery.logical_cnot_block_graph to construct the logical CNOT gate.

[7]:
from tqec.gallery import logical_cnot_block_graph

graph = logical_cnot_block_graph(support_observable_basis="BOTH")
graph.view_as_html()
[7]:

The logical CNOT has four independent stabilizer flow generators: XI -> XX, IX -> IX, ZI -> ZI, IZ -> ZZ. Here we show the correlation surfaces for the generators.

[8]:
from tqec import SignedDirection3D, Direction3D

correlation_surfaces = graph.to_zx_graph().find_correlation_surfaces()

XI -> XX#

[9]:
graph.view_as_html(
    pop_faces_at_direction=SignedDirection3D(Direction3D.Y, False),
    show_correlation_surface=correlation_surfaces[1],
)
[9]:

IX -> IX#

[10]:
graph.view_as_html(
    pop_faces_at_direction=SignedDirection3D(Direction3D.Y, False),
    show_correlation_surface=correlation_surfaces[5],
)
[10]:

ZI -> ZI#

[11]:
graph.view_as_html(
    pop_faces_at_direction=SignedDirection3D(Direction3D.Y, False),
    show_correlation_surface=correlation_surfaces[2],
)
[11]:

IZ -> ZZ#

[12]:
graph.view_as_html(
    pop_faces_at_direction=SignedDirection3D(Direction3D.Y, False),
    show_correlation_surface=correlation_surfaces[4],
)
[12]:

Example Circuit#

Here we show an example circuit of logical CNOT with \(d=3\) CSS-type surface code. The annotated logical observable corresponds to XI -> XX stabilizer flow. You can download the circuit here or view it in Crumble.

[17]:
from tqec import compile_block_graph, NoiseModel

graph = logical_cnot_block_graph(support_observable_basis="X")
correlation_surfaces = graph.find_correlation_surfaces()
compiled_graph = compile_block_graph(graph, observables=[correlation_surfaces[1]])
circuit = compiled_graph.generate_stim_circuit(
    k=1, noise_model=NoiseModel.uniform_depolarizing(p=0.001)
)

Simulation#

Here we show the simulation results for all the stabilizer flow generators as well as XX -> XI and ZZ -> IZ. The simulated surface code is CSS type and the noise model is uniform depolarizing.

Click to show the full code used for simulation

from pathlib import Path
from typing import Literal

import matplotlib.pyplot as plt
import numpy
import sinter

from tqec.gallery.logical_cnot import logical_cnot_block_graph
from tqec.noise_model import NoiseModel
from tqec.simulation.plotting.inset import plot_observable_as_inset
from tqec.simulation.simulation import start_simulation_using_sinter

SAVE_DIR = Path("results")


def generate_graphs(support_observable_basis: Literal["Z", "X"]) -> None:
    block_graph = logical_cnot_block_graph(support_observable_basis)
    zx_graph = block_graph.to_zx_graph()

    correlation_surfaces = 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"],
            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("Logical CNOT Error Rate")
        ax.set_xlabel("Physical Error Rate")
        ax.set_ylabel("Logical Error Rate")
        fig.savefig(
            SAVE_DIR
            / f"logical_cnot_result_{support_observable_basis}_observable_{i}.png"
        )


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


if __name__ == "__main__":
    main()

XI -> XX#

XIXX

IX -> IX#

IXIX

XX -> XI#

XXXI

ZI -> ZI#

ZIZI

IZ -> ZZ#

IZZZ

ZZ -> IZ#

ZZIZ

References#

  • Horsman, D., Fowler, A. G., Devitt, S., & Van Meter, R. (2012). Surface code quantum computing by lattice surgery. New Journal of Physics, 14(12), 123011.