3.2. Visualizing Circuit objects: QEC experiment with a d=3 repetition code
Note
The widgets in this notebook are interactive!
In this notebook, we construct an experiment for a \(d=3\) repetition code qubit. The circuit consists of a reset, 3 rounds of syndrome extraction and a logical readout operation. For the visualization purpose of this notebook, the syndrome extraction cycles are split into 2 individual sub-circuits — one subcircuit consists of one QEC cycle, and the other consists of two QEC cycles. Before the QEC cycles, there is an initialization circuit where all data qubits are reset to their \(|0\rangle\) state. After the three QEC cycles, the nine data qubits are all read out.
After constructing this Eka object and interpreting it, we then visualize the Circuit and its nested structure.
from loom.eka import Eka, Lattice, Block, Stabilizer, PauliOperator, Circuit
from loom.eka.operations import (
MeasureBlockSyndromes,
MeasureLogicalZ,
ResetAllDataQubits,
)
from loom.interpreter import interpret_eka
import loom.visualizer as vis
from pprint import pprint
We start by defining the logical qubit as a repetition code of distance 3.
# Create a custom block for a repetition code
# Start by defining the geometry of the block
lattice = Lattice.linear((5,))
stabilizers = [
Stabilizer(
pauli="XX",
data_qubits=(
(0, 0),
(1, 0),
),
ancilla_qubits=((3, 1),),
),
Stabilizer(
pauli="XX",
data_qubits=(
(1, 0),
(2, 0),
),
ancilla_qubits=((4, 1),),
),
]
logical_x = PauliOperator(pauli="XXX", data_qubits=((0, 0), (1, 0), (2, 0)))
logical_z = PauliOperator(pauli="ZZZ", data_qubits=((0, 0), (1, 0), (2, 0)))
block = Block(
stabilizers=stabilizers,
logical_x_operators=[logical_x],
logical_z_operators=[logical_z],
unique_label="q1",
)
3.2.1. Stabilizers
Stabilizers are defined by hand using Stabilizer, we can print them for more details on which qubit coordinates are associated with each Stabilizer object.
pprint(block.stabilizers)
(XX ((0, 0), (1, 0)) ((3, 1),), XX ((1, 0), (2, 0)) ((4, 1),))
3.2.2. Quantum circuit for syndrome extraction
The syndrome extraction circuits are defined automatically since we haven’t specified them. We can print them for more details. Note that they are blueprints for the composition that occurs during interpretation. This means that the Channel s associated to these Circuit objects are not valid Channel s in the final circuit but placeholders that can be re-used.
The SyndromeCircuit objects also bear information of which type of stabilizer they are associated to, as well as a name and uuid.
pprint(block.syndrome_circuits)
(SyndromeCircuit(pauli='XX',
name='default_xx',
circuit=stab_xx (6 ticks)
0: reset_0
1: h
2: cx
3: cx
4: h
5: measurement,
uuid='5f3a058a-79b7-4813-9fa2-ef1b9bc127fa'),)
3.2.3. Construct and interpret the Eka object
# We need todefine the operations we want to perform
operations = [
[ResetAllDataQubits("q1", state="0")],
[MeasureBlockSyndromes("q1", n_cycles=1)],
[MeasureBlockSyndromes("q1", n_cycles=2)],
[MeasureLogicalZ("q1")],
]
my_eka = Eka(
lattice=lattice,
blocks=[
block,
],
operations=operations,
)
interpreted = interpret_eka(my_eka)
final_circuit = interpreted.final_circuit
print(final_circuit)
final circuit (20 ticks)
0: reset all data qubits of block q1 to |0>
1: measure q1 syndromes 1 time(s)
7: measure q1 syndromes 2 time(s)
19: measure logical z of q1
3.2.4. Visualization
Now, let’s visualize the Circuit object for the experiment:
fig = vis.plot_circuit_tree(
final_circuit,
max_layer=10, # Deepest layer which should be plotted
layer_labels=[
"Full circuit",
"Operations",
"Gates",
],
num_layers_with_text=2, # Number of layers for which the name of subcircuits is annotated above the marker
)
# Sphinx formatting
fig.update_layout(margin=dict(t=30, l=30, b=30))
fig.show(renderer="notebook")
To highlight why using nesting when constructing Circuits is important, we can also plot the unrolled quantum circuit where only base gates are represented:
unrolled_circuit = Circuit("final_circuit", circuit=Circuit.unroll(final_circuit))
fig = vis.plot_circuit_tree(unrolled_circuit)
# Sphinx formatting
fig.update_layout(margin=dict(t=30, l=30, b=30))
fig.show(renderer="notebook")
There’s no point in plotting a flattened circuit as a tree. If you are interested in the list of operations in the flattened circuit, you can print the circuit directly:
print(unrolled_circuit)
final_circuit (20 ticks)
0: reset_0 reset_0 reset_0
1: reset_0 reset_0
2: h h
3: cx cx
4: cx cx
5: h h
6: measurement measurement
7: reset_0 reset_0
8: h h
9: cx cx
10: cx cx
11: h h
12: measurement measurement
13: reset_0 reset_0
14: h h
15: cx cx
16: cx cx
17: h h
18: measurement measurement
19: measurement measurement measurement