Toric CodeΒΆ

This example is a larger Guppy program that implements a toric-code style workflow with syndrome extraction, decoding, and conditional correction.

Source file: guppy_examples/general/toric_code.py

r"""Even-distanced [[16, 2, 4]] code using fixed-size runtime arrays."""

import sys
from typing import no_type_check

from guppylang import guppy
from guppylang.std.builtins import array, output, qubit
from guppylang.std.quantum import cx, h, measure, z

NDQ = 16


@guppy
def syndrome_extraction(q: array[qubit, NDQ], idxs: array[int, 4]) -> bool:
    ancilla = qubit()
    h(q[idxs[0]])
    cx(q[idxs[0]], ancilla)
    h(q[idxs[0]])
    h(q[idxs[1]])
    cx(q[idxs[1]], ancilla)
    h(q[idxs[1]])
    h(q[idxs[2]])
    cx(q[idxs[2]], ancilla)
    h(q[idxs[2]])
    h(q[idxs[3]])
    cx(q[idxs[3]], ancilla)
    h(q[idxs[3]])
    return measure(ancilla).read()


@guppy
def correct_if(needed: bool, data_qubit: qubit) -> None:
    if needed:
        z(data_qubit)


@guppy
@no_type_check
def apply_first_round_correction(
    syndromes: array[bool, 4],
    q: array[qubit, NDQ],
    idxs: array[int, 4],
) -> None:
    correct_if(syndromes[0], q[idxs[0]])
    correct_if(syndromes[1], q[idxs[1]])
    correct_if(syndromes[2], q[idxs[2]])
    correct_if(syndromes[3], q[idxs[3]])


@guppy
@no_type_check
def apply_second_round_correction(
    syndrome: int,
    q: array[qubit, NDQ],
) -> None:
    if syndrome == 3:
        z(q[10])
        z(q[11])
    elif syndrome == 5:
        z(q[4])
        z(q[8])
    elif syndrome == 6:
        z(q[0])
        z(q[13])
    elif syndrome == 9:
        z(q[2])
        z(q[15])
    elif syndrome == 10:
        z(q[5])
        z(q[9])
    elif syndrome == 12:
        z(q[6])
        z(q[7])
    elif syndrome == 15:
        z(q[2])
        z(q[3])
        z(q[14])
        z(q[15])


@guppy
@no_type_check
def bool_array_as_int(values: array[bool, 4]) -> int:
    return (
        (int(values[0]) << 3)
        | (int(values[1]) << 2)
        | (int(values[2]) << 1)
        | int(values[3])
    )


stabilizer_indices_x = {
    "round_1": [
        [4, 5, 8, 9],
        [6, 7, 10, 11],
        [12, 13, 0, 1],
        [14, 15, 2, 3],
    ],
    "round_2": [
        [1, 2, 5, 6],
        [3, 0, 7, 4],
        [9, 10, 13, 14],
        [11, 8, 15, 12],
    ],
}

first_round_correction_indices = [8, 10, 0, 2]


@guppy.comptime
@no_type_check
def main() -> None:
    data_qubits = array(qubit() for _ in range(NDQ))
    first_syndromes = array(
        syndrome_extraction(data_qubits, st_idxs)
        for st_idxs in stabilizer_indices_x["round_1"]
    )
    output("s0", bool_array_as_int(first_syndromes))
    apply_first_round_correction(
        first_syndromes,
        data_qubits,
        first_round_correction_indices,
    )
    second_syndromes = array(
        syndrome_extraction(data_qubits, st_idxs)
        for st_idxs in stabilizer_indices_x["round_2"]
    )
    second_syndrome = bool_array_as_int(second_syndromes)
    output("s1", second_syndrome)

    apply_second_round_correction(
        second_syndrome,
        data_qubits,
    )
    codeword = 0
    for data_qubit in data_qubits:
        codeword = (codeword << 1) | int(measure(data_qubit).read())
    output("c", codeword)


if __name__ == "__main__":
    sys.stdout.buffer.write(main.compile().to_bytes())