{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Detailed plots with `tqec`\n",
"\n",
"The `tqec` library provides tools to create detailed plots of the results obtained with `sinter`. Below is an example of the type of plots you can generate with `tqec`. \n",
"\n",
"\n",
"\n",
"This notebook will guide you through the process of creating such a plot for a basic memory experiment."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 1. Create the computation\n",
"\n",
"The first step of any plot is to create the quantum computation that will be used to generate the plots. In our case, this is a memory experiment."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"from tqec.gallery.memory import memory\n",
"from tqec.utils.enums import Basis\n",
"\n",
"block_graph = memory(Basis.Z)\n",
"observables = block_graph.find_correlation_surfaces()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 2. Perform the simulations \n",
"\n",
"Then, we need to perform multiple simulation to gather statistics to plot.\n",
"\n",
"This gathering stage is split in 3 parts:\n",
"\n",
"1. Computing general statistics over a large range of physical error-rate (e.g., $p \\in [10^{-4}, 10^{-1}]$),\n",
"2. Computing an estimate of the threshold $p_\\text{thres}$ under which increasing the code distance corrects more errors,\n",
"3. Computing fine statistics around the computed threshold.\n",
"\n",
"First, define the parameters of our simulation."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import numpy\n",
"import sinter\n",
"from tqec.compile.specs.library.css import CSS_BLOCK_BUILDER, CSS_SUBSTITUTION_BUILDER\n",
"\n",
"# Define the values of k (scaling factor) and p (physical error-rate) for which\n",
"# we want data points.\n",
"ks = list(range(1, 4))\n",
"ps = list(numpy.logspace(-4, -1, 10))\n",
"\n",
"# For the moment, use CSS-style code.\n",
"block_builder = CSS_BLOCK_BUILDER\n",
"substitution_builder = CSS_SUBSTITUTION_BUILDER\n",
"\n",
"# Only use a low number of shots for demonstration purposes.\n",
"max_shots = 1_000_000\n",
"max_errors = 500\n",
"\n",
"# All the data will be collected observable per observable, let's have\n",
"# data-structures to store the results\n",
"main_statistics: list[list[sinter.TaskStats]] = []\n",
"thresholds: list[float] = []\n",
"threshold_statistics: list[list[sinter.TaskStats]] = []"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 2.1. Gathering general statistics\n",
"\n",
"This part should be quite familiar if you already used the `tqec.simulation` module."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"from tqec.simulation.simulation import start_simulation_using_sinter\n",
"from tqec.utils.noise_model import NoiseModel\n",
"\n",
"for i, obs in enumerate(observables):\n",
" stats = start_simulation_using_sinter(\n",
" block_graph,\n",
" range(1, 4),\n",
" list(numpy.logspace(-4, -1, 10)),\n",
" NoiseModel.uniform_depolarizing,\n",
" manhattan_radius=2,\n",
" block_builder=block_builder,\n",
" substitution_builder=substitution_builder,\n",
" observables=[obs],\n",
" max_shots=max_shots,\n",
" max_errors=max_errors,\n",
" decoders=[\"pymatching\"],\n",
" split_observable_stats=False,\n",
" )\n",
" main_statistics.append(stats[0])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 2.2. Estimating the threshold\n",
"\n",
"The next step will be to have a good-enough estimation of the provided computation threshold. This threshold will help us calibrating the next step where we perform more sampling around the estimated threshold value to have a detailed view of the code behaviour near its threshold."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"from math import log10\n",
"from tqec.simulation.threshold import binary_search_threshold\n",
"\n",
"for obs in observables:\n",
" threshold, _ = binary_search_threshold(\n",
" block_graph,\n",
" obs,\n",
" NoiseModel.uniform_depolarizing,\n",
" manhattan_radius=2,\n",
" minp=10**-5,\n",
" maxp=0.1,\n",
" block_builder=block_builder,\n",
" substitution_builder=substitution_builder,\n",
" max_shots=max_shots,\n",
" max_errors=max_errors,\n",
" decoders=[\"pymatching\"],\n",
" )\n",
" thresholds.append(threshold)\n",
"\n",
"log10_thresholds = [log10(t) for t in thresholds]\n",
"mint, maxt = min(log10_thresholds), max(log10_thresholds)\n",
"log10_threshold_bounds = (mint - 0.2, maxt + 0.2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 2.3. Gathering statistics around the threshold\n",
"\n",
"Now that we have a good estimation of the computation threshold, we can gather statistics around it."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"for obs in observables:\n",
" threshold_stats = start_simulation_using_sinter(\n",
" block_graph,\n",
" ks,\n",
" list(numpy.logspace(*log10_threshold_bounds, 20)),\n",
" NoiseModel.uniform_depolarizing,\n",
" manhattan_radius=2,\n",
" block_builder=block_builder,\n",
" substitution_builder=substitution_builder,\n",
" observables=[obs],\n",
" num_workers=30,\n",
" max_shots=10_000_000,\n",
" max_errors=5_000,\n",
" decoders=[\"pymatching\"],\n",
" split_observable_stats=False,\n",
" )\n",
" threshold_statistics.append(threshold_stats[0])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 3. Plot\n",
"\n",
"All the statistics we need should now be computed. Let's plot!"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
"\n",
"\n",
"\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%matplotlib inline\n",
"import matplotlib.pyplot as plt\n",
"\n",
"from tqec.simulation.plotting.inset import plot_threshold_as_inset\n",
"\n",
"zx_graph = block_graph.to_zx_graph()\n",
"\n",
"for i, obs in enumerate(observables):\n",
" main_stats = main_statistics[i]\n",
" threshold = thresholds[i]\n",
" thres_stats = threshold_statistics[i]\n",
"\n",
" fig, ax = plt.subplots()\n",
" sinter.plot_error_rate(\n",
" ax=ax,\n",
" stats=main_stats,\n",
" x_func=lambda stat: stat.json_metadata[\"p\"],\n",
" group_func=lambda stat: stat.json_metadata[\"d\"],\n",
" )\n",
" xmin = 10 ** log10_threshold_bounds[0]\n",
" xmax = 10 ** log10_threshold_bounds[1]\n",
" # Note: the below values require prior knowledge about the values to look\n",
" # for on the Y-axis.\n",
" ymin, ymax = 1e-2, 3e-1\n",
" plot_threshold_as_inset(\n",
" ax,\n",
" thres_stats,\n",
" # Note: ymax is **before** ymin because the y axis is inversed.\n",
" zoom_bounds=(xmin, ymax, xmax, ymin),\n",
" threshold=threshold,\n",
" inset_bounds=(0.5, 0.25, 0.4, 0.4),\n",
" )\n",
" ax.grid(which=\"both\", axis=\"both\")\n",
" ax.legend()\n",
" ax.loglog()\n",
" ax.set_title(\"Z-basis memory error rate\")\n",
" ax.set_xlabel(\"Physical error rate (uniform depolarizing noise)\")\n",
" ax.set_ylabel(\"Logical error rate per shot\")\n",
" ax.set_ylim(10**-7.5, 10**0)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.6"
}
},
"nbformat": 4,
"nbformat_minor": 2
}