3.1. Conversion example from Eka

Note

The widgets in this notebook are interactive!

We start by defining an Eka object that encodes a simple memory experiment on a distance 3 repetition code, that consists of a reset, a logical X and a measurement, with some syndromes extraction.

from loom.eka.block import Block
from loom.eka.eka import Eka
from loom.eka.lattice import Lattice
from loom.eka.operations.code_operation import (
    LogicalX,
    MeasureBlockSyndromes,
    MeasureLogicalZ,
    ResetAllDataQubits,
)
from loom.eka.pauli_operator import PauliOperator
from loom.eka.stabilizer import Stabilizer
import loom.visualizer as vis
from loom.interpreter.interpreter import interpret_eka
import plotly.io as pio

pio.renderers.default = "notebook"

# Set up a bit-flip repetition code with a distance of 3
distance = 3
lattice = Lattice.linear((distance + 1,))

base_position = (1,)
initial_rep_block = Block(
    unique_label="q1",
    stabilizers=tuple(
        Stabilizer(
            pauli="ZZ",
            data_qubits=(
                (base_position[0] + i, 0),
                (base_position[0] + i + 1, 0),
            ),
            ancilla_qubits=((base_position[0] + i, 1),),
        )
        for i in range(distance - 1)
    ),
    logical_x_operators=(
        PauliOperator("XXX", tuple((base_position[0] + i, 0) for i in range(distance))),
    ),
    logical_z_operators=(PauliOperator("Z", ((base_position[0], 0),)),),
)


meas_block_and_meas_log = [
    ResetAllDataQubits(initial_rep_block.unique_label, state="0"),
    MeasureBlockSyndromes(initial_rep_block.unique_label, n_cycles=1),
    LogicalX(initial_rep_block.unique_label),
    MeasureBlockSyndromes(initial_rep_block.unique_label, n_cycles=1),
    MeasureLogicalZ(initial_rep_block.unique_label),
]

eka = Eka(lattice, blocks=[initial_rep_block], operations=meas_block_and_meas_log)

interpreted_eka = interpret_eka(eka)

stab_plot = vis.StabilizerPlot(
    lattice,
    title=f"Repetition Code",
)
stab_plot.add_dqubit_traces()
stab_plot.plot_blocks(initial_rep_block)
# Sphinx formatting
stab_plot._fig.update_layout(margin=dict(t=60, l=30, b=30), width=680, height=400)
stab_plot.show()

3.1.1. Conversion to QASM

from loom.executor.eka_circuit_to_qasm_converter import convert_circuit_to_qasm
from qiskit import transpile, qasm3
from qiskit_aer import Aer

# Convert the Eka circuit to QASM string representation
QASM_string = convert_circuit_to_qasm(
    interpreted_eka.final_circuit,
    interpreted_eka.syndromes,
    interpreted_eka.detectors,
    interpreted_eka.logical_observables,
)

# Execute the QASM circuit using Qiskit Aer simulator
backend = Aer.get_backend("aer_simulator")
qc = qasm3.loads(QASM_string["qasm_circuit"])  # load QASM‑3
qc = transpile(qc, backend)  # prepare for backend
shots = 10
result = backend.run(qc, shots=shots).result()  # run (no “execute”)

print("Simulation results: ")
print(
    'Each shot gives a bitstring of the form: "ss ss mmm", where each ss is a syndrome '
    "and mmm is the measurement result."
)
print(result.get_counts())
Simulation results: 
Each shot gives a bitstring of the form: "ss ss mmm", where each ss is a syndrome and mmm is the measurement result.
{'00 00 111': 10}

3.1.2. Conversion to Stim

from loom.executor.eka_circuit_to_stim_converter import EkaCircuitToStimConverter


converter = EkaCircuitToStimConverter()

stim_circuit = converter.convert(interpreted_eka)

# Sample results
sampler = stim_circuit.compile_sampler()
num_samples = 3
results = sampler.sample(shots=num_samples)

# Format and print simulation results
print("Results:")
bitstrings = tuple(
    ["".join("1" if b else "0" for b in shot) for shot in results][i]
    for i in range(num_samples)
)

for i in range(num_samples):
    print(f"Shot {i}: {bitstrings[i][:2]} {bitstrings[i][2:4]} {bitstrings[i][4:]}")
Results:
Shot 0: 00 00 111
Shot 1: 00 00 111
Shot 2: 00 00 111

As expected, since there is no noise in the simulations used, the 2 rounds of syndromes extractions yield the same outputs. The measured outcome of the data qubits is always 1, which is what is expected after applying reset and X on the logical qubit.