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 import EkaToQasmConverter
from qiskit import transpile, qasm3
from qiskit_aer import Aer
converter = EkaToQasmConverter()
# Convert the Eka circuit to QASM string representation
QASM_string, q_register, c_register = converter.convert(interpreted_eka)
# Execute the QASM circuit using Qiskit Aer simulator
backend = Aer.get_backend("aer_simulator")
qc = qasm3.loads(QASM_string) # load QASM‑3
qc = transpile(qc, backend) # prepare for backend
shots = 3
result = backend.run(qc, shots=shots).result().get_counts() # run (no “execute”)
print("Simulation results: ")
converter.parse_target_run_outcome((result, c_register))
Simulation results:
{'c_(1, 0)_0': [1, 1, 1],
'c_(1, 1)_0': [0, 0, 0],
'c_(1, 1)_1': [0, 0, 0],
'c_(2, 0)_0': [1, 1, 1],
'c_(2, 1)_0': [0, 0, 0],
'c_(2, 1)_1': [0, 0, 0],
'c_(3, 0)_0': [1, 1, 1]}
3.1.2. Conversion to Stim
from loom.executor import EkaToStimConverter
converter = EkaToStimConverter()
stim_circuit, q_register, c_register = 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:")
converter.parse_target_run_outcome((results, c_register))
Results:
{'c_(1, 0)_0': [1, 1, 1],
'c_(1, 1)_0': [0, 0, 0],
'c_(1, 1)_1': [0, 0, 0],
'c_(2, 0)_0': [1, 1, 1],
'c_(2, 1)_0': [0, 0, 0],
'c_(2, 1)_1': [0, 0, 0],
'c_(3, 0)_0': [1, 1, 1]}
3.1.3. Other converter available :
Xanadu’s Pennylane:
EkaToPennylaneConverterNvidia’s CudaQ:
EkaToCudaqConverterQPerfect’s MimiQ:
EkaToMimiqConverter
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.