Memory#
This notebook demonstrates the simplest(trivial) form of computation: logical memory. More specifically, a logical qubit with distance \(d\) remains idle for \(d\) error correction cycles.
Construction#
A memory experiment can be represented with a single cube. The color of the cube determines the spatial/temporal boundary types.
tqec
provides builtin functions tqec.gallery.memory
to construct it.
[1]:
from tqec import Basis
from tqec.gallery import memory
graph = memory(Basis.Z)
graph.view_as_html()
[1]:
Memory experiment preserves the logical observable through time:
[2]:
correlation_surfaces = graph.find_correlation_surfaces()
graph.view_as_html(
pop_faces_at_direction="-Y",
show_correlation_surface=correlation_surfaces[0],
)
[2]:
Circuit#
You can download the circuit for a \(d=3\) logical Z memory experiment from here, or generate it with the code below. You can also open the circuit in Crumble.
Note
Note that the syndrome extraction circuits used here are of depth-7 because we use an extra layer of two-qubit gates to align with the potential spatial cubes. The depth can be reduced to 6 with some post-processing on the circuit.
[3]:
from tqec import compile_block_graph, NoiseModel
compiled_graph = compile_block_graph(graph)
circuit = compiled_graph.generate_stim_circuit(
k=1, noise_model=NoiseModel.uniform_depolarizing(p=0.001)
)
Simulation#
Here we show the simulation results of both Z-basis and X-basis memory experiments under uniform depolarizing noise mode.
Click to show the full code used for simulation
from pathlib import Path
import matplotlib.pyplot as plt
import numpy
import sinter
from tqec.gallery.memory import memory
from tqec import NoiseModel
from tqec.simulation.plotting.inset import plot_observable_as_inset
from tqec.simulation.simulation import start_simulation_using_sinter
from tqec.utils.enums import Basis
SAVE_DIR = Path("results")
def generate_graphs(support_observable_basis: Basis) -> None:
block_graph = memory(support_observable_basis)
zx_graph = block_graph.to_zx_graph()
correlation_surfaces = block_graph.find_correlation_surfaces()
stats = start_simulation_using_sinter(
block_graph,
range(1, 4),
list(numpy.logspace(-4, -1, 10)),
NoiseModel.uniform_depolarizing,
manhattan_radius=2,
observables=correlation_surfaces,
num_workers=20,
max_shots=10_000_000,
max_errors=5_000,
decoders=["pymatching"],
print_progress=True,
)
for i, stat in enumerate(stats):
fig, ax = plt.subplots()
sinter.plot_error_rate(
ax=ax,
stats=stat,
x_func=lambda stat: stat.json_metadata["p"],
failure_units_per_shot_func=lambda stat: stat.json_metadata["d"],
group_func=lambda stat: stat.json_metadata["d"],
)
plot_observable_as_inset(ax, zx_graph, correlation_surfaces[i])
ax.grid(axis="both")
ax.legend()
ax.loglog()
ax.set_title("Logical Memory Error Rate")
ax.set_xlabel("Physical Error Rate")
ax.set_ylabel("Logical Error Rate(per round)")
fig.savefig(
SAVE_DIR
/ f"logical_memory_result_{support_observable_basis}_observable_{i}.png"
)
def main():
SAVE_DIR.mkdir(exist_ok=True)
generate_graphs(Basis.Z)
generate_graphs(Basis.X)
if __name__ == "__main__":
main()
X-basis#

Z-basis#

References#
Acharya, Rajeev, et al. “Quantum error correction below the surface code threshold.” arXiv preprint arXiv:2408.13687 (2024).