GHZ Experiment¶
In this tutorial, we’ll build a logical GHZ experiment using Loom’s code-factory and execute it on a simulator.
This will show how to:
- Create logical qubits using a code factory
- Interpret the resulting circuit for execution
- Convert and run the interpreted
Ekaon a simulator or backend
Overview¶
In the previous tutorials, we learned how to define a QEC code directly from its stabilizers and logical operators.
Thanks to Loom’s flexible structure, you can define any code you like as long as you know its stabilizers. However, in most cases, we may want to reuse certain blocks multiple times — for instance, to create several blocks of the same code or logical qubits of the same type but with different distances.
This is where a code-factory becomes useful. Moreover, a factory ensures that our logical circuit satisfies the necessary validation requirements to be converted into a format that can run on a backend or simulator platform.
Step 1: Use a Code Factory¶
We’ll begin by building a \(d=3\) surface code block using the RotatedSurfaceCode factory.
dx and dz define the size of the code in the horizontal and vertical direction, while position refer to the position on the lattice of the top-left qubit in the block.
To change the orientation of the qubit, set the optional x_boundary="horizontal (default vertical)
from loom.eka import Lattice
from loom_rotated_surface_code.code_factory import RotatedSurfaceCode
lattice = Lattice.square_2d((11, 13))
q1 = RotatedSurfaceCode.create(
dx=3,
dz=3,
position=(2, 4),
lattice=lattice,
unique_label="q1",
)
This creates a single surface code block called "q1" positioned with its top-left qubit at (2,4) within the lattice.
Step 2: Create Multiple Logical Qubits¶
To build a GHZ experiment, we’ll need three logical qubits. We can create them by repeating the same process as above, making sure each block occupies a different position on the lattice.
Now we have three independent surface code blocks (q1, q2, and q3) placed in different regions of the lattice. These blocks will act as the logical qubits used to generate a GHZ state.
q2 = RotatedSurfaceCode.create(
dx=3,
dz=3,
position=(6, 0),
lattice=lattice,
unique_label="q2",
)
q3 = RotatedSurfaceCode.create(
dx=3,
dz=3,
position=(6, 8),
lattice=lattice,
unique_label="q3",
)
blocks = [q1, q2, q3]
Step 3: Define the GHZ Experiment Operations¶
We can now generate the three-qubit logical GHZ state of the form:
Where the \(L\) subscript stands for "logical".
We can create this state with the following steps:
- Initialize one logical qubit in the \(|+\rangle\) state and the others in \(|0\rangle\).
- Apply CNOT gates where the \(|+\rangle\) qubit acts as the control, and the \(|0\rangle\) qubits are the targets.
- Measure all logical qubits in the logical-Z basis.
In Loom, these steps are expressed as time-ordered operation frames. Each frame contains all operations that occur simultaneously in time. Notice how each frame is separated into a different entry in the operations tuple.
Some code-specific operations can be found in the relative code-factory, such as AuxCNOT, which performs logical CNOT gates between surface code blocks.
from loom.eka.operations.code_operation import (
ResetAllDataQubits,
MeasureBlockSyndromes,
MeasureLogicalZ,
)
from loom_rotated_surface_code.operations import AuxCNOT
n_cycles = 3
operations = (
(
ResetAllDataQubits("q1", state="+"),
ResetAllDataQubits("q2", state="0"),
ResetAllDataQubits("q3", state="0"),
),
(
MeasureBlockSyndromes("q1", n_cycles=n_cycles),
MeasureBlockSyndromes("q2", n_cycles=n_cycles),
MeasureBlockSyndromes("q3", n_cycles=n_cycles),
),
( # Frame 1
AuxCNOT(("q1", "q2")),
),
(
MeasureBlockSyndromes("q1", n_cycles=n_cycles),
MeasureBlockSyndromes("q2", n_cycles=n_cycles),
MeasureBlockSyndromes("q3", n_cycles=n_cycles),
),
( # Frame 2
AuxCNOT(("q1", "q3")),
),
(
MeasureBlockSyndromes("q1", n_cycles=n_cycles),
MeasureBlockSyndromes("q2", n_cycles=n_cycles),
MeasureBlockSyndromes("q3", n_cycles=n_cycles),
),
( # Frame 3
MeasureLogicalZ("q1"),
MeasureLogicalZ("q2"),
MeasureLogicalZ("q3"),
),
)
Step 4: Interpret the Circuit¶
An Eka object contains all the information about your logical circuit — the lattice, blocks, and operations — in a hardware-agnostic format.
This means it describes the experiment logically, without tying it to any specific hardware or simulator.
We need to interpret it using the interpret_eka() function before can be converted into a backend-specific format.
from loom.eka import Eka
from loom.interpreter import interpret_eka
eka_result = Eka(lattice, blocks=blocks, operations=operations)
ghz_circ = interpret_eka(eka_result)
Step 5: Execute the Circuit¶
Finally, our GHZ circuit is ready to be executed or simulated.
Loom supports conversion to multiple formats, making it easy to compare results across backends or simulators.
These converters are contained in the executor package.
Example: Converting to Stim Format¶
Stim is an open-source, high-performance simulator for stabilizer circuits.
We can convert our GHZ experiment to Stim format using the EkaCircuitToStimConverter class.
from loom.executor import EkaCircuitToStimConverter
converter = EkaCircuitToStimConverter()
stim_circuit = converter.convert(ghz_circ)
Our logical GHZ circuit in Stim can now be efficiently simulated with Stim!
Adding Noise to the Simulation¶
Loom's executor package also provides additional utilities. For instance, you can annotate your circuit with depolarizing and bit-flip noise using the noise_annotated_stim_circuit() function:
from loom.executor import noise_annotated_stim_circuit
noisy_stim_circ = noise_annotated_stim_circuit(
stim_circ=stim_circuit,
before_measure_flip_probability=1e-3,
after_clifford_depolarization=1e-3,
after_reset_flip_probability=1e-3,
)
Summary¶
In this tutorial, we learned how to:
- Use a
code-factoryto build logical surface code blocks - Realize a logical circuit involving multiple logical qubits
- Interpret the resulting
Eka - Convert the logical circuit to an executable format
By leveraging Loom’s modular structure, you can easily scale this workflow to larger experiments, different error correction codes, or other simulation backends.