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.solo_node_block_graph
to construct it.
[6]:
from tqec.gallery import solo_node_block_graph
graph = solo_node_block_graph(support_observable_basis="Z")
graph.view_as_html()
[6]:
Memory experiment preserves the logical observable through time:
[7]:
from tqec import SignedDirection3D, Direction3D
correlation_surfaces = graph.to_zx_graph().find_correlation_surfaces()
graph.view_as_html(
pop_faces_at_direction=SignedDirection3D(Direction3D.Y, False),
show_correlation_surface=correlation_surfaces[0],
)
[7]:
Circuit#
You can download the circuit for a \(d=3\) CSS-type logical Z memory experiment from here, or generate it with the code below. You can also open the circuit in Crumble.
[8]:
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. The simulated surface code is CSS type and the noise model is uniform depolarizing.
Click to show the full code used for simulation
from pathlib import Path
from typing import Literal
import matplotlib.pyplot as plt
import numpy
import sinter
from tqec.gallery.solo_node import solo_node_block_graph
from tqec.noise_model import NoiseModel
from tqec.simulation.plotting.inset import plot_observable_as_inset
from tqec.simulation.simulation import start_simulation_using_sinter
SAVE_DIR = Path("results")
def generate_graphs(support_observable_basis: Literal["Z", "X"]) -> None:
block_graph = solo_node_block_graph(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("Z")
generate_graphs("X")
if __name__ == "__main__":
main()
X-basis#
![X-basis](../_images/x_memory.png)
Z-basis#
![Z-basis](../_images/z_memory.png)
References#
Acharya, Rajeev, et al. “Quantum error correction below the surface code threshold.” arXiv preprint arXiv:2408.13687 (2024).