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.