diff --git a/README.md b/README.md index 529c5e2..c3034f9 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,91 @@ -# Tesseract Code +# Stim implementation of the [[16,4,4]] Tesseract Code -This repository contains implementations and simulations of the Tesseract quantum error correction code using Stim and PyMatching. +This repository contains implementations and simulations of the Tesseract quantum error correction code [[1]](#references) using Stim [[3]](#references). -## Features +## Overview +### Motivation +The 16-qubit tesseract subsystem color code offers a useful comprosmise between protection against errors and overhead. It encodes 4 logical qubits with a code rate of 1/4. By reducing 2 logical qubit from the original [[16,6,4]] code, this code achieves single-shot error correction, with only 2 ancilla qubits. This makes this code practical for current trapped-ion platforms. Recent experiments on Quantinuum hardware[[1]](#references) demonstrated preparing high-fidelity encoded graph states of up to 12 logical qubits, as well as protecting encoded states through 5 rounds of error correction. This repository reproduces these results in simulation, providing modular Stim circuits, noise modelling, and verification tests to support further research on low-overhead fault tolerance. -- Circuit implementation of the Tesseract code -- Error simulation and correction -- Jupyter notebooks with various experiments and visualizations -- Utility functions for circuit manipulation and analysis +![Figure 1: Tesseract code structure (from [1])](docs/images/fig1_tesseract_code.png) -## Installation +### Project status +This codebase is an active work-in-progress. +• All building blocks (encoding, noise, measurement, decoder) are implemented. +• The end-to-end error-correction success rate is **not yet at the target level**. +Community testing, bug-fixes, and feature PRs are highly appreciated! + +**Disclaimer:** This repository is an independent, community-driven implementation. +It is **not** affiliated with Microsoft, Quantinuum, nor the authors of the original tesseract-code paper. + +### Features + +- Circuit implementation of the [[16,4,4]] Tesseract subsystem color code [[2]](#references) in Stim, including encoding, error correction rounds and final measurements. +- Simulation of an error correction experiment with configurable noise setting, rounds, shot and more. +- Plotting: sweeping of different parameters and obtaining acceptance rate and logical success rate. + +### Implementation details + +In order to mimic the original paper's error correction, the different parts of the experiment are implemented in the gate level, and not using Stim's `MPP` stabilizer measurements, or detectors, for example. +The experiment implemented here is an error correction experiment based on Microsoft's paper[[1]](#references), and resembles the experimental setup shown in Figure 8 below. + +![Figure 8: Error correction experiment (from [1])](docs/images/fig8_error_correction_experiment.png) + +The experiment goes as follows: + +1. **Encoding** - The initial state is encoded using the circuits in Fig. 9a or 9b. This part is noiseless for simplicity. +2. **Channel Noise** - Optional noise is applied on all qubits. +3. **Error correction rounds** - Each round is composed of measureing rows/columns and X/Z stabilizers. Measurements results are saved. +4. **Logical measurements** - Qubits are measured by breaking apart the code into two smaller codes. Each code is the [[8,3,2]] color code [[4]](#references). See [measure_logical_operators_tesseract](tesseract_sim/error_correction/measurement_rounds.py) and [verify_final_state](tesseract_sim/error_correction/decoder_manual.py) for more details. +5. **Post processing** - Each shot is accepted or not (based on the error correction rounds); For accepted rounds, the qubits are corrected based on Pauli frame (if enabled in simulation). Next, logical qubits are measured and validated to determine the logical error rate. + +### Code +#### Structure + +``` +tesseract-code-stim/ +├── tesseract_sim/ # Main simulation package +│ ├── common/ # Shared utilities and base components +│ │ ├── circuit_base.py # Basic circuit operations and initialization +│ │ └── code_commons.py # Tesseract code definitions (stabilizers, operators) +│ ├── encoding/ # State encoding implementations +│ │ ├── encoding_manual_9a.py # |++0000⟩ encoding (Fig 9a) +│ │ └── encoding_manual_9b.py # |+0+0+0⟩ encoding (Fig 9b) +│ ├── error_correction/ # Error correction and measurement +│ │ ├── correction_rules.py # Correction logic for different error types +│ │ ├── decoder_manual.py # Manual decoder implementation +│ │ └── measurement_rounds.py # Stabilizer measurements and rounds +│ ├── noise/ # Noise modeling and injection +│ │ ├── noise_cfg.py # Noise configuration dataclass +│ │ └── noise_utils.py # Noise injection utilities +│ ├── plotting/ # Visualization and analysis +│ │ └── plot_acceptance_rates.py # Generate acceptance/success rate plots +│ └── run.py # Main simulation entry point +├── notebooks/ # Jupyter notebooks for experiments +│ ├── encoding_circuits_visualization.ipynb # Circuit visualization +│ ├── entire_experiment_circuit.ipynb # Complete experiment demo +│ └── tesseract_stim_simulation_real_decoder.ipynb # Full simulation +├── tests/ # Test suite +│ ├── encoding/ # Encoding tests +│ ├── noise/ # Noise injection tests +│ └── test_*.py # Various experiment and functionality tests +├── plots/ # Generated plot outputs +├── requirements.txt # Python dependencies +└── setup.py # Package configuration +``` + +#### Key Components + +- **`tesseract_sim/run.py`**: Main entry point for running simulations with configurable noise parameters +- **`tesseract_sim/encoding/`**: Two encoding modes based on paper figures 9a and 9b +- **`tesseract_sim/error_correction/`**: Manual decoder with correction rules and measurement rounds +- **`tesseract_sim/noise/`**: Configurable noise injection for encoding and error correction phases +- **`tesseract_sim/plotting/`**: Analysis and visualization tools for acceptance rates and logical success rates +- **`notebooks/`**: Interactive Jupyter notebooks for experiments and visualization +- **`tests/`**: Comprehensive test suite covering all major functionality + +## Quick Start + +### Installation ```bash # Clone the repository @@ -24,19 +100,6 @@ source venv/bin/activate # On Windows: venv\Scripts\activate pip install -r requirements.txt ``` -## Structure - -- `circuit_commons.py`: Core circuit components and helper functions -- `stim_simulation_utils.py`: Simulation and plotting utilities -- `stim_utils.py`: Stim-specific helper functions -- Notebooks: - - `01_stim_playground.ipynb`: Initial Stim experiments - - `03_tesseract_stim_simulation_pymatching.ipynb`: Main simulation with PyMatching - - `04_stim_example_5_qubit_code.ipynb`: Five-qubit code reference - - And more... - -## Usage - The main workflow is through Jupyter notebooks. After installation: ```bash @@ -45,9 +108,9 @@ jupyter notebook Then navigate to one of the notebooks to run experiments and simulations. -### Running Simulations with Noise +### Running Simulations -The `tesseract_sim/run.py` script now supports configurable noise injection during encoding and error correction phases. You can control whether noise is active and set independent 1-qubit and 2-qubit error rates for each phase. +The `tesseract_sim/run.py` script supports configurable noise injection during encoding and error correction phases. You can control whether noise is active and set independent 1-qubit and 2-qubit error rates for each phase. ```bash python -m tesseract_sim.run --help @@ -88,33 +151,47 @@ The `plotting/plot_acceptance_rates.py` script generates acceptance and logical * **Generate plots with Pauli frame correction enabled and 9a encoding:** ```bash - python plotting/plot_acceptance_rates.py --apply_pauli_frame true --encoding-mode 9a + python tesseract_sim/plotting/plot_acceptance_rates.py --apply_pauli_frame true --encoding-mode 9a ``` * **Generate plots with Pauli frame correction disabled and 9a encoding:** ```bash - python plotting/plot_acceptance_rates.py --apply_pauli_frame false --encoding-mode 9a + python tesseract_sim/plotting/plot_acceptance_rates.py --apply_pauli_frame false --encoding-mode 9a ``` * **Generate plots with 9b encoding and custom shot count:** ```bash - python plotting/plot_acceptance_rates.py --apply_pauli_frame true --encoding-mode 9b --shots 5000 + python tesseract_sim/plotting/plot_acceptance_rates.py --apply_pauli_frame true --encoding-mode 9b --shots 5000 ``` * **Generate plots with channel noise sweep instead of EC noise:** ```bash - python plotting/plot_acceptance_rates.py --apply_pauli_frame true --encoding-mode 9a --sweep-channel-noise + python tesseract_sim/plotting/plot_acceptance_rates.py --apply_pauli_frame true --encoding-mode 9a --sweep-channel-noise ``` * **Specify custom output directory:** ```bash - python plotting/plot_acceptance_rates.py --apply_pauli_frame true --encoding-mode 9a --out-dir ./custom_plots + python tesseract_sim/plotting/plot_acceptance_rates.py --apply_pauli_frame true --encoding-mode 9a --out-dir ./custom_plots ``` The script generates two types of plots: - **Acceptance Rate Plots**: Show how well the error correction accepts states across different noise levels and rounds - **Logical Success Rate Plots**: Show the conditional probability of logical success given acceptance +**Example Results:** + +![Acceptance Rates vs Error Correction Noise](plots/acceptance_rates_ec_noise_ec_experiment.png) + +## References + +[1] B. W. Reichardt et al., "Demonstration of quantum computation and error correction with a tesseract code", (2024) [arXiv:2409.04628](https://arxiv.org/abs/2409.04628) + +[2] "\([[16,6,4]]\) Tesseract color code", The Error Correction Zoo (V. V. Albert & P. Faist, eds.), 2024. https://errorcorrectionzoo.org/c/stab_16_6_4 + +[3] C. Gidney, "Stim: a fast stabilizer circuit simulator", Quantum 5, 497 (2021). https://doi.org/10.22331/q-2021-07-06-497 + +[4] E. Campbell, "The smallest interesting colour code", (2016). https://earltcampbell.com/2016/09/26/the-smallest-interesting-colour-code/ + ## License MIT License \ No newline at end of file diff --git a/docs/images/fig1_tesseract_code.png b/docs/images/fig1_tesseract_code.png new file mode 100644 index 0000000..fe9b875 Binary files /dev/null and b/docs/images/fig1_tesseract_code.png differ diff --git a/docs/images/fig8_error_correction_experiment.png b/docs/images/fig8_error_correction_experiment.png new file mode 100644 index 0000000..ccb6e8e Binary files /dev/null and b/docs/images/fig8_error_correction_experiment.png differ diff --git a/docs/images/fig9_encoding.png b/docs/images/fig9_encoding.png new file mode 100644 index 0000000..f97f6a7 Binary files /dev/null and b/docs/images/fig9_encoding.png differ diff --git a/notebooks/encoding_circuits_visualization.ipynb b/notebooks/encoding_circuits_visualization.ipynb index ca42552..47dcee7 100644 --- a/notebooks/encoding_circuits_visualization.ipynb +++ b/notebooks/encoding_circuits_visualization.ipynb @@ -18,12 +18,12 @@ } }, "source": [ - "import stim\n", "\n", - "from tesseract_sim.circuit_base import init_circuit\n", - "from tesseract_sim.encoding_manual_9a import encode_manual_fig9a\n", - "from tesseract_sim.encoding_manual_9b import encode_manual_fig9b\n", - "from tesseract_sim.noise_cfg import NO_NOISE\n" + "\n", + "from tesseract_sim.common.circuit_base import init_circuit\n", + "from tesseract_sim.encoding.encoding_manual_9a import encode_manual_fig9a\n", + "from tesseract_sim.encoding.encoding_manual_9b import encode_manual_fig9b\n", + "from tesseract_sim.noise.noise_cfg import NO_NOISE\n" ], "outputs": [], "execution_count": 2 diff --git a/notebooks/entire_experiment_circuit.ipynb b/notebooks/entire_experiment_circuit.ipynb index 224f405..dcc8f20 100644 --- a/notebooks/entire_experiment_circuit.ipynb +++ b/notebooks/entire_experiment_circuit.ipynb @@ -99,7 +99,7 @@ ], "source": [ "from tesseract_sim.run import build_circuit_ec_experiment\n", - "from tesseract_sim.noise_cfg import NO_NOISE\n", + "from tesseract_sim.noise.noise_cfg import NO_NOISE\n", "\n", "# Initialize circuit with experiment 2 parameters\n", "circuit = build_circuit_ec_experiment(rounds=1, cfg=NO_NOISE, encoding_mode='9a')\n", diff --git a/notebooks/tesseract_stim_simulation_real_decoder.ipynb b/notebooks/tesseract_stim_simulation_real_decoder.ipynb index 29b2905..16081e1 100644 --- a/notebooks/tesseract_stim_simulation_real_decoder.ipynb +++ b/notebooks/tesseract_stim_simulation_real_decoder.ipynb @@ -12,7 +12,7 @@ "%autoreload 2\n", "\n", "import stim\n", - "import numpy as np\n", + "\n", "print(stim.__version__)" ] }, @@ -74,7 +74,7 @@ "SHOTS = 100000\n", "\n", "# Create noise configuration\n", - "from tesseract_sim.noise_cfg import NoiseCfg\n", + "from tesseract_sim.noise.noise_cfg import NoiseCfg\n", "noise_cfg = NoiseCfg(\n", " ec_active=True, # Enable noise during error correction\n", " ec_rate_1q=0.001, # 0.1% error rate for 1-qubit gates\n", @@ -123,7 +123,7 @@ "SHOTS = 100000\n", "\n", "# Create noise configuration\n", - "from tesseract_sim.noise_cfg import NoiseCfg\n", + "from tesseract_sim.noise.noise_cfg import NoiseCfg\n", "noise_cfg = NoiseCfg(\n", " ec_active=True, # Enable noise during error correction\n", " ec_rate_1q=0.001, # 0.1% error rate for 1-qubit gates\n", diff --git a/original_paper/arXiv-2409.04628v2/tesseract.tex b/original_paper/arXiv-2409.04628v2/tesseract.tex deleted file mode 100644 index 3decbbe..0000000 --- a/original_paper/arXiv-2409.04628v2/tesseract.tex +++ /dev/null @@ -1,836 +0,0 @@ -% !TEX TS-program = pdflatex -\ifx\compilefullpaper\undefined -\documentclass[10pt, twocolumn, aps, nofootinbib, longbibliography, nobibnotes, superscriptaddress]{revtex4-1} %dvips, superscriptaddress, showpacs, aps, prl -%\documentclass[preprintnumbers,11pt,twocolumn]{article} -%\pdfoutput=1 % needed for the arXiv to properly compile with pdflatex -\input{header} -\usepackage[utf8]{inputenc} -\usepackage[table]{xcolor} - -\usepackage[frozencache,cachedir=.]{minted} % python syntax highlighting -% for an arXiv submission, use finalizecache option to freeze the cache, then switch to frozencache before uploading -\usepackage{makecell} % thicker hlines: \Xhline{2\arrayrulewidth} - -% watermarks (turn off xcolor package above) -%\usepackage[printwatermark]{xwatermark} -%\usepackage{tikz} -%\newsavebox\mybox -%\savebox\mybox{\tikz[color=red,opacity=0.3]\node{DRAFT, do not distribute};} -%\newwatermark*[ -% allpages, -% angle=45, -% scale=6, -% xpos=-20, -% ypos=15 -%]{\usebox\mybox} -\usepackage[only,llbracket,rrbracket]{stmaryrd} - -%\usepackage[anythingbreaks]{breakurl} % to fix long urls in bibliography - -\usepackage{enumitem} % added to remove itemize environment indentations, with \begin{itemize}[leftmargin=*] -\usepackage{color} -\setlength{\arrayrulewidth}{0.2mm} -%\setlength{\tabcolsep}{10pt} -\renewcommand{\arraystretch}{1.2} -%\newcolumntype{s}{>{} p{3cm}} -%\arrayrulecolor[HTML]{DB5800} -%\usepackage[table]{xcolor} -%\usepackage{float} - -\definecolor{cardinal}{rgb}{0.827, 0, 0} - -\begin{document} -%\tableofcontents - -\fi - -%\def\compilefullpaper{} -\vfuzz2pt % Don't report over-full v-boxes if over-edge is small - -\title{ -%Demonstrating fault-tolerant quantum computation with a tesseract subsystem color code -Demonstration of quantum computation and error correction %\\ -with a tesseract %color -code %, \\ -%demonstrated on trapped-ion quantum computers -%Fault-tolerant quantum computation with a tesseract subsystem color code -} -%\author{\color{white}Ben W. Reichardt} - -\affiliation{Microsoft Azure Quantum} -\affiliation{Quantinuum} - -\author{Ben W. Reichardt} -\affiliation{Microsoft Azure Quantum} -\affiliation{University of Southern California} - -\author{David Aasen} -\author{Rui Chao} -\affiliation{Microsoft Azure Quantum} - -\author{Alex Chernoguzov} -\affiliation{Quantinuum} - -\author{Wim van Dam} -\affiliation{Microsoft Azure Quantum} - -\author{John P. Gaebler} -\author{Dan Gresh} -\author{Dominic Lucchetti} -\author{Michael Mills} -\author{Steven A. Moses} -\author{Brian Neyenhuis} -\affiliation{Quantinuum} - -\author{Adam Paetznick} -\author{Andres Paz} -\affiliation{Microsoft Azure Quantum} - -\author{Peter E. Siegfried} -\affiliation{Quantinuum} - -\author{Marcus P. da Silva} -\author{Krysta M. Svore} -\author{Zhenghan Wang} -\author{Matt Zanner} -\affiliation{Microsoft Azure Quantum} - -%\renewcommand{\comment}[1]{} -%\renewcommand{\comments}[1]{} - -\begin{abstract} -A critical milestone for quantum computers is to demonstrate fault-tolerant computation that outperforms computation on physical qubits. -The tesseract subsystem color code protects four logical qubits in $16$ physical qubits, to distance four. -%It allows efficient single-shot error correction with two ancilla qubits. -Using the tesseract code on Quantinuum's trapped-ion quantum computers, we prepare high-fidelity encoded graph states on up to $12$ logical qubits, beneficially combining for the first time fault-tolerant error correction and computation. -We also protect encoded states through up to five rounds of error correction. -Using performant quantum software and hardware together allows moderate-depth logical quantum circuits to have an order of magnitude less error than the equivalent unencoded circuits. -\end{abstract} - -\maketitle - -\section{Introduction} - -Even the best qubits are insufficiently reliable to run large quantum algorithms. By encoding qubits into error-correcting codes, dramatic reductions in effective error rates should be possible. -%KS: However, error-correcting codes require additional operations and qubits, and must be specially designed to tolerate faults in their implementations. -Fault tolerance allows software to handle hardware flaws---with a cost in overhead. -%Fault-tolerance theory says that carefully designed error correction and encoded circuits can still function even with faulty qubits; software can overcome hardware flaws. This comes at a cost in overhead, of course. - -We introduce a new fault-tolerance scheme that is well-suited to state-of-the-art quantum computers. -The scheme comprises a $16$-qubit ``tesseract" code, sketched in \figref{f:tesseract}, and methods for error correction and encoded computation. It is qubit efficient, with just a four-to-one overhead in qubit count, and also offers expeditious error correction (single shot, with only two extra qubits) and enough protection to reduce error rates substantially. The code is distance four, %meaning that -so three physical faults have to occur to cause a logical error. % on one or more logical qubits - -\begin{figure}[b] -%\subfigure[\label{f:tesseract}] -{\raisebox{0cm}{\includegraphics[width=.3\textwidth]{images/tesseract}}} -\caption{ -%We consider -The $[[16,6,4]]$ color code on the 4D hypercube, or tesseract. %(a). -%There is a qubit at each of the $16$ vertices. -Each of the $16$ vertices is a qubit. Cubes are $X$ and $Z$ stabilizers, and squares are logical operators, e.g., 0145. -%For example, logical $X_3$ is $X_0 X_1 X_4 X_5$, the square highlighted in red. Logical $X_4$ is highlighted in blue, logical $X_5$ in green, and logical $X_6$ in yellow. -} -\label{f:tesseract} -\end{figure} - -The %$16$-qubit -tesseract code has been studied before~\cite{delfosse2020short, PrabhuReichardt24code16}. Our main innovation to the code is to deliberately sacrifice two of the original six encoded qubits. Only having to protect four of the encoded qubits dramatically simplifies fault-tolerant error correction and computation. -%For technical reasons, this allows much more efficient error correction. -In particular, it lets error correction work by measuring operators supported on only four qubits, instead of eight, which improves efficiency and gives the single-shot property. -%which in turn allows very efficient single-shot error correction. -%with only weight-four measurements, instead of more difficult weight-eight measurements. There are other important advantages. - -We evaluate and demonstrate our fault-tolerance scheme on Quantinuum's H1 (20 qubit) and H2 (56 qubit) trapped-ion quantum computers~\cite{Moses23quantinuum}, obtaining and computing with up to $12$ logical qubits -in three code blocks. -\tabref{f:experiments_summary} summarizes the experiments. -The Path-$4$ experiment demonstrates an encoded, targeted CNOT gate within a code block; operations targeted to specific qubits allow more flexible computation than if we were limited to transversal gates in which every encoded qubit gets the same operation. -Cube-$8$ uses three rounds of transversal CNOT gates between blocks, with two rounds of error correction; this demonstrates a deeper circuit, on more encoded qubits, for which good error correction -during the computation -is critical. -Cat-$12$ prepares a high fidelity cat state on $12$ logical qubits. -%We prepare an encoded $12$-qubit cat state. -Finally, we show five rounds of error correction on four and eight encoded qubits, paving the way for deeper logical circuits on more encoded qubits. All of these encoded circuits work with significantly lower error rates than the unencoded baseline versions. - -\begin{table} -\caption{\label{f:experiments_summary} -%Experiments summary. -Computing fault tolerantly on encoded data gives dramatic error rate improvements over baseline, unencoded circuits. Path-$4$ is run on H1, the others on H2. -The first three experiments prepare graph states, on four, eight, or 12 qubits. %For these experiments, -Their reported error rates are averaged over $X$ and $Z$ measurement settings. %the average of the error rates for $X$ and $Z$ measurements. -%The physical baseline comparison for five rounds of error correction is perhaps arguable, %almost of necessity, %though we make a case for it below, -%and in any case more data is needed to get a reliable estimate for the gain. -Full data is given in \tabref{f:fulldata}. -%infidelity is the sum of error rates for two experiments, one measuring $X$ stabilizers and another $Z$ stabilizers. Since $Z$ and $X$ errors are correlated, these are upper bounds on the actual infidelities. -%The error bars indicate 95\% confidence intervals. %and full data is given in \tabref{f:fulldata}. -%For encoded trials, the acceptance rate is the fraction of trials that pass fault-tolerance tests, e.g., two detected faults may cause a run to be rejected. -} -\setlength{\tabcolsep}{1.9pt} -\begin{tabular}{c@{$\!\!\!\!\!\!\!\!\!\!$}cc@{$\,\,\,\,$}c@{$\,\,\,\,$}c@{$\,\,\,\,$}c} -%\hline \hline -\Xhline{2\arrayrulewidth} -& && Baseline & Encoded & %Encoded - \\[-.1cm] -\multicolumn{2}{c}{Experiment} & $\!\!\!$Qubits$\!$ -& error rate & error rate & Gain \\ -\hline -{$\;\;$\includegraphics[scale=.1]{images/path4}} & Path-$4$ & 4 & -1.5(2)\% %$1.5\%^{+0.3\%}_{-0.3\%}$ -& $0.10^{+0.11}_{-0.06}\%$ & $15 \times$ \\ -{$\;\;$\raisebox{-.1cm}{\includegraphics[scale=.1]{images/cube8}}} & Cube-$8$ & 8 & %{\raisebox{-.22cm}{\includegraphics[scale=.2]{images/cube8}}} & -2.3(3)\% %$2.3\%^{+0.3\%}_{-0.3\%}$ -& $0.2^{+0.2}_{-0.1}\%$ & $11 \times$ \\ -$\;\;$\raisebox{.05cm}{\scalebox{.5}{$\ket{0^{12}} + \ket{1^{12}}$}} & Cat-$12$ & 12 & -2.4(3)\% %$2.4\%^{+0.3\%}_{-0.3\%}$ -& $0.11^{+0.16}_{-0.08}\%$ & $22 \times$ \\[.1 cm] -\hline \\[-.35cm] -\multicolumn{2}{c}{Error correction $5 \times$} -%\hspace{-.15cm}\begin{tabular}{r}Error \\[-.1cm] correction \\[-.1cm] (5 rounds)\end{tabular} -& \begin{tabular}{c}4 \\ 8\end{tabular} & \begin{tabular}{c}$2.7(4)\%$ \\ $5.6(6)\%$ \end{tabular} & \begin{tabular}{c} $0.11^{+0.21}_{-0.09}\%$ \\ $0.7^{+0.7}_{-0.4}\%$ \end{tabular} &\begin{tabular}{c}$24 \times$ \\ $8 \times$ %90x* \\ 11.2x* -\end{tabular} \\ -%\hline \hline -\Xhline{2\arrayrulewidth} -\end{tabular} -\end{table} - -Note that with a distance-four code, two faults may bring the system to an uncorrectable state. When an uncorrectable state is detected, our fault-tolerance procedure rejects the trial. This ``postselection" %increases the time overhead -means that more trials are needed to collect a given amount of encoded data. The acceptance rates in our experiments are at least $50\%$, so this extra time overhead is not critical. It is far better than with a distance-two code for which a single fault can cause rejection. Still, the overhead will get worse for longer experiments on more code blocks. %Using simulations we study this exponential dependence below. - -Compared to codes that have been implemented previously, the tesseract code has a higher rate (ratio of encoded to physical qubits) and higher distance. %In general, -These parameters come with tradeoffs. A higher rate usually means a code requiring more physical qubits, better connectivity between those qubits, and more complex logical computation. A higher distance often forces a lower rate, and sometimes more complicated fault-tolerance schemes, meaning physical error rates have to be lower before the code is useful. For current ion trap devices, with 20+ well-connected and high-fidelity qubits, the tesseract code is in a sweet spot for achieving fault-tolerant computation. %As the hardware develops, so surely will fault-tolerance schemes. - -%Noisy Intermediate-Scale Quantum (NISQ) algorithms for eigensolvers~\cite{Peruzzo2014, Wecker15eigen} and machine learning~\cite{Biamonte2017} are growing as popular applications for state-of-the-art few-qubit quantum systems. Unfortunately, these devices are still prone to large amounts of noise~\cite{mooney2021wholedevice, Google2019, Pogorelov21ionexp}. Although error correction can decrease error rates~\cite{Shor95decoherence, Gottesman97thesis, terhal15review}, current experiments encode only one logical qubit that is still fairly noisy~\cite{honeywell21steane, Google2021, duke21BScode, Wootton20ignis}. In this paper we simulate storing multiple logical qubits in a lattice, as a first step toward modeling few-qubit computations. We repeatedly correct and remove single-qubit errors. On detecting a more dangerous---and less common---two-qubit error, we reject and restart. This ``postselection" technique allows distance-four codes to achieve similar logical error rates to distance-five codes. - -%Postselection is a versatile tool in the quantum toolkit. In experiments, it has been used to decode the $\llbracket 4,2,2 \rrbracket$ error-detecting code~\cite{Linke17edexp, Takita17edexp} and the $\llbracket 4,1,2 \rrbracket$ surface code~\cite{Andersen2020, Google2021}. In theoretical research, it has been used to reduce the logical error probability of state preparation~\cite{Paetznickgolay2013, ZhengBrunFTancprep18} and magic state distillation~\cite{ BravyiKitaev04magic, Meier13MSD}. - -%\begin{figure}[b] -%{\includegraphics[width=.38\textwidth]{images/disterrtable.pdf}} -%\caption{Distance-four codes with postselection lead to $O(p^3)$ logical error rates, much like distance-five codes. -%Even-distance codes require restarts, however, unlike odd-distance codes.} -%\label{f:disterrtab} -%\end{figure} - -% As~\figref{f:disterrtab} indicates, distance-two codes can detect single errors and distance-three codes can correct them, meaning logical errors are due to second-order faults. Distance-three codes may alternatively be used to detect one or two errors, but then they lose the ability to correct and computations are very short-lived. We choose to use distance-four codes since they can simultaneously correct an error and detect two errors. Correcting some errors ensures restarts are less frequent, so longer computations can be run. Since logical errors are caused only by third-order faults, logical error rates are very low. - -%\medskip -%\noindent -%\textbf{Results.} - -\medskip -\noindent -\textbf{Related work.} -The latest generations of quantum hardware are enabling impressive demonstrations of fault-tolerant quantum error correction and computation, validating fault tolerance theory's applicability to real devices. -%Most of these experiments have been run with fewer logical qubits and significantly higher logical noise rates. - -Distance-two codes have been implemented with two~\cite{GuptaIBM24magiccz}, three~\cite{MenendezRayVasmer23colorcode832, Wang23color832addition}, four~\cite{Yamamoto23phaseestimattionerrordetection}, eight~\cite{self22icebergcode}, and $48$ logical qubits, divided across $16$ $[[8,3,2]]$ code blocks~\cite{BluvsteinHarvard23neutralatoms}. Distance-two codes are challenging to scale because they cannot correct errors, only detect them, and this leads to high rejection rates. - -For repeated error correction, Ryan-Anderson et al.~\cite{honeywell21steane} have experimented with up to six rounds of error correction, but on only one encoded qubit, in the $[[7,1,3]]$ color code, and after five rounds the logical error rate was over $10\%$. Postler et al.~\cite{Postler23steaneec} implemented repeated error correction on the same code, and after one round the error rate was already about $35\%$. %\cite{Postler21universal} -%have shown up to three rounds of error correction on one encoded qubit, but after one round the error rate was already about $35\%$. -Krinner et al.~\cite{Krinner21repeatedsurfaceec} have implemented up to $16$ cycles of syndrome extraction on the $[[9,1,3]]$ surface code, equivalent to about $16/3 \approx 5.3$ rounds of error correction, with over $3\%$ logical error rate per cycle. -Google Quantum AI and collaborators have implemented up to $250$ cycles of syndrome extraction on the $[[49,1,7]]$ surface code, equivalent to about $250 / 7 \approx 35.7$ rounds of error correction, with a $0.143(3)\%$ logical error rate per cycle~\cite{google23surface, google24surfacecode}. Importantly, this makes the one logical qubit have a longer lifetime than a physical qubit in their system. -Repeated error correction on multiple qubits has been implemented by Silva et al.~\cite{Silva24microsoft12qubitcode}. They run three rounds of error correction on two qubits encoded in a $[[12,2,4]]$ code, with a logical error rate of only $0.8(3)\%$, also beating a physical baseline. %---beating a physical baseline---but with much higher postselection; only $36\%$ of trials passed. -Here we achieve five rounds of error correction, on up to eight encoded qubits, with lower logical error rates---up to $24 \times$ lower error per logical qubit per error correction round---and higher acceptance rates (\tabref{f:experiments_summary}). - -Yamamoto et al.~\cite{Yamamoto23phaseestimattionerrordetection} have combined computation with error detection. -As for computation with error-correcting codes, Ryan-Anderson et al.~\cite{quantinuum22h1} study logical CNOT gates between two code blocks, for the $[[5,1,3]]$ and $[[7,1,3]]$ codes. For their $[[5,1,3]]$ code experiment, some error correction is necessary for fault tolerance, and yet they find that it increases logical error rates. For their $[[7,1,3]]$ code experiment, error correction is unnecessary, and so again they find that it increases logical error rates. -Postler et al.~\cite{Postler21universal} and Mayer et al.~\cite{Mayer24steaneftexperiment} do a computation on two and three encoded qubits, respectively, using the $[[7,1,3]]$ code, notably including non-Clifford gates, %(though not implemented fault tolerantly), -but not using rounds of quantum error correction. -%\cite{google23surface} -%First time demonstrating multiple rounds of error correction as part of a computation? -% -We extend these capabilities by demonstrating a beneficial {combination} of fault-tolerant error correction with computation, perhaps for the first time. - -Creating large cat states, also known as GHZ states, is a common metric to advertise hardware progress. For example, Quantinuum researchers have prepared on H2 the $20$-qubit cat state with an $86\%$ fidelity, and the $32$-qubit cat state with an $82\%$ fidelity~\cite{Moses23quantinuum}. Using superconducting qubits, Bao et al.~\cite{Bao24catstate60} have prepared a $60$-qubit cat state with a $59\%$ fidelity. (For a list of cat state experiments, see~\cite{Krenn24catstates}.) -Encoded cat states can have much higher fidelities. -Hong et al.~\cite{Hong24cat4onh2} prepare a four-qubit cat state encoded in a $[[25,4,3]]$ code on H2, with fidelity at least $99.5^{+0.2}_{-0.4}\%$. %a $0.3^{+0.2}_{-0.1}\%$ error rate. %(Up to qubit permutations, the encoded cat state is equivalent to encoded $\ket{{+}000}$.) -Bluvstein et al.~\cite{BluvsteinHarvard23neutralatoms} prepare a four-qubit cat state encoded in the $[[7,1,3]]$ code, with a fidelity of $72(2)\%$ using error correction, ranging to $99.85^{+0.1}_{-1.0}\%$ using full error detection. We prepare an encoded cat state on $12$ logical qubits, with comparably high fidelity and a higher acceptance rate. See \tabref{f:catstateexperiments}. %(We discuss the relation of error rate and fidelity in \secref{s:experiments}.) -(Note that our experiments, and \cite{Hong24cat4onh2}, measure $X$ and $Z$ error rates, $p_X$ and $p_Z$, with \tabref{f:experiments_summary} reporting $\tfrac12(p_X + p_Z)$. The fidelity to the ideal cat state is between $1 - p_X - p_Z$ and $1 - \max (p_X, p_Z)$.) - -\begin{table} -\caption{\label{f:catstateexperiments} -%Experiments summary. -Experiments preparing encoded cat states. -} -\setlength{\tabcolsep}{1.9pt} -\begin{tabular}{cccc} -%\hline \hline -\Xhline{2\arrayrulewidth} -Reference & Logical qubits & Fidelity \\ -\hline -%\cite{Moses23quantinuum} & \begin{tabular}{c}20\\32\end{tabular} & \begin{tabular}{c}86\%\\82\%\end{tabular} \\ -%\cite{Bao24catstate60} & 60 & 59\% \\ -\cite{Hong24cat4onh2} & 4 in $[[25,4,3]]$ code & $99.5^{+0.2}_{-0.4}\%$ to $99.7^{+0.2}_{-0.3}\%$ \\ -\cite{BluvsteinHarvard23neutralatoms} & 4 in $[[7,1,3]]$ code & \scalebox{.9}{$\!\!\!\left\{\begin{array}{c} \\[.20cm] \end{array}\right.\!\!\!\!\!$}\begin{tabular}{c}72(2)\% error correction\\ $99.85^{+0.1}_{-1.0}\%$ error detection\end{tabular}$\!\!\!$ \\ -This work & 12 in $[[16,4,4]]$ code & $99.82^{+0.12}_{-0.4}\%$ to $99.90^{+0.1}_{-0.3}\%$ \\ -%\hline \hline -\Xhline{2\arrayrulewidth} -\end{tabular} -\end{table} - -\medskip -\noindent -\textbf{Our experiments.} -In general, our work is notable in showing multiple rounds of error correction on multiple code blocks, with more encoded qubits and a variety of logical operations---all at error rates an order of magnitude lower than the unencoded versions. - -%We proceed to set up our experiments, and explain the results. -The experiments demonstrate different capabilities of the tesseract code fault-tolerance scheme. -The first three experiments are to prepare and verify encoded graph states~\cite{Hein06graphstates}. %Graph states~\cite{Hein06graphstates} are a rich family of stabilizer states. %Each vertex corresponds to a qubit and a stabilizer that is $X$ on that qubit and $Z$ on its neighbors; for bipartite graphs like ours, a Hadamard basis change allows the stabilizers for one side to be written using only $X$ operators and those for the other side using only $Z$s. -%(See~\cite{CabelloDanielsenLopezTarridaPortillo10graphstatedatabase} for a study and database of graph states on up to $12$ qubits.) -Preparing graph states reliably is a good test of entangling Clifford gates. %on a quantum computer. - -%In addition, to reliably preparing interesting entangled states, our experiments are chosen to demonstrate different capabilities of the tesseract code fault-tolerance scheme. %, run on Quantinuum's H1 and H2 quantum computers. - -\smallskip -%\begin{itemize}[leftmargin=*] -%\item {\bf Path-$4$:} -The Path-$4$ experiment demonstrates an encoded CNOT gate between two logical qubits in the same code block. For codes encoding more than one qubit per block, targeted operations within the block are usually much more difficult than applying the same operation to every encoded qubit. A common technique, from~\cite{Gottesman97}, is to teleport the encoded qubits of interest into their own code blocks, that are otherwise empty, so that they can be addressed separately---but this wastes the high rate capability of the code. Targeted internal operations without overhead is an important advantage of the tesseract scheme. - -%Note that -Here and in the other experiments, we use the basic tesseract code fault-tolerance ingredients from \secref{s:tesseractcode} as separate modules, plugging them together to get the desired encoded circuit. Had we simply wanted to prepare the encoded graph state with as high fidelity as possible, it is likely that a specially tailored encoding circuit could perform better. We felt it was more important to demonstrate that the fault-tolerance modules worked well, since they can be used for a broad variety of experiments and circuit constructions. - -The unencoded Path-$4$ experiment uses three CNOT gates, while the encoded experiment gets away with one. %The reason is that -Two of the encoded CNOT gates come basically for free, by permuting the physical qubits. % (\figref{f:permutation_automorphisms}). -Permuting qubits is an easy operation for the ion trap hardware. - -\smallskip -%\item {\bf Cube-$8$:} -The Cube-$8$ experiment shows a deeper logical circuit, on more encoded qubits, with essential error correction. -Preparing the cube graph state is a challenge because it requires more CNOT gates ($12$) than any other bipartite eight-qubit graph state~\cite{CabelloDanielsenLopezTarridaPortillo10graphstatedatabase}. -The encoded circuit involves three rounds of transversal CNOT gates, between two code blocks, with two rounds of error correction on each code block (\figref{f:encodedcube}). The error correction is key. Without it, single faults can cause logical errors---e.g., an $X$ error on the control code block could spread to a weight-three $X$ error on the target block---and in simulations the circuit is immediately overwhelmed by noise. -%The Cube-$8$ experiment also again shows the tesseract code's flexible permutation automorphisms. -% -We do not know of prior work %prior to our Cube-8 experiment -that combines fault-tolerant error correction and computation to reduce logical error rates. -%used multiple rounds of error correction as part of a state-preparation procedure. - -\begin{figure} -$\!\!\!\!\!$ \includegraphics[scale=1]{images/encodedcube} $\;\;$ \raisebox{1cm}{\includegraphics[scale=.55]{images/encodedcubevisualcube}} -\caption{Encoded circuit to prepare the cube graph state, in two code blocks.} \label{f:encodedcube} -\end{figure} - -\smallskip -%\item {\bf Cat-$12$:} -The Cat-$12$ experiment is not as deep, but shows even more entangled logical qubits, with a high fidelity. -%Our cat state certainly has many fewer logical qubits, but its fidelity is much higher. -%Comparing to the physical baseline, the fidelity of the $12$-qubit cat state is at most $1 - \max(p_X, p_Z) = 97.3(4)\%$, while the fidelity of the encoded $12$-qubit cat state is at least $1 - p_X - p_Z = 99.82^{+0.12}_{-0.4}\%$. -%From \tabref{f:fulldata}, the {infidelity} ($1 - \text{fidelity}$) of the physical baseline $12$-qubit cat state is at least $\max(p_X, p_Z) = 2.7(4)\%$, while the infidelity of the encoded $12$-qubit cat state is at most $p_X + p_Z = 0.22\%^{+0.5\%}_{-0.2\%}$. -%Moreover, we only use two measurement choices, $X$ and $Z$, to judge our state, and our fidelity estimate is inclusive of the measurement error. This hardly harms us because transversal measurements are extremely reliable. It does harm our physical baseline, though. Presumably, the unencoded cat state actually had a higher fidelity than {\color{red}$96-98\%$}$\ldots$ -%As another comparison, Hong et al.~\cite{Hong24cat4onh2} have prepared, also on the H2 device, a four-qubit cat state encoded in a $[[25,4,3]]$ code with error rates $p_X = 0.3(1)\%$ and $p_Z = 0.2(1)\%$. Bluvstein et al.~\cite{BluvsteinHarvard23neutralatoms} have prepared in a neutral atom system an encoded four-qubit cat state with infidelity $28(2)\%$, or as low as $0.15^{+1.0}_{-0.1}\%$ postselecting on no detected errors, i.e., with no error correction. - -%Yifan Hong replied: -%Total X measurement shots: 2500 -%Postselected X measurement shots: 2461 -%# shots with logical Z error: 5 -% -%Total Z measurement shots: 2500 -%Postselected Z measurement shots: 2456 -%# shots with logical X error: 8 -% -%Note that 5/2461 ~ 2E-3 and 8/2456 ~ 3E-3, which is the displayed logical failure rates "with QEC" in Table 1. "z mismatch" refers to Z measurement basis trials; in particular it corresponds to logical X errors since it counts whenever the logical Z outcomes of the 4 logical qubits disagree. - -\smallskip -%\item {\bf Repeated error correction on four and eight qubits:} -The goal of our repeated error correction experiments is to better protect more encoded qubits, through more error correction rounds. This is a challenge because in the future, as the field moves toward large-scale fault-tolerant computations, experiments will deploy many more logical qubits through much deeper logical circuits. As periodic error correction is needed to maintain fault tolerance~\cite{AharonovBenOr99, KnillLaflammeZurekScience98, AliferisGottesmanPreskill05}, highly reliable repeated error correction is a prerequisite. %Many rounds of error correction will be required. -%reliable repeated error correction should allow deeper logical circuits, since fault tolerance requires periodic error correction. -Although \cite{Silva24microsoft12qubitcode} and this work both show that repeated error correction gives an improvement compared to physical baselines, more important is the low absolute error rate. -%\end{itemize} - - -\section{Tesseract subsystem color code} \label{s:tesseractcode} - -The $[[16,6,4]]$ tesseract code is a self-dual color code on the 4D hypercube, encoding into $16$ physical qubits six logical qubits protected to distance four -(\figref{f:tesseract}). This code was introduced in~\cite{delfosse2020short} and simulated extensively, with a fault-tolerance scheme requiring weight-eight measurements, in~\cite{PrabhuReichardt24code16}. -The main idea behind our scheme is to sacrifice two encoded qubits, not using them to store data (except sometimes temporarily). -The sacrificed qubits can be thought of as ``gauge qubits"~\cite{Bacon05operator}, even though they have the same distance as the data qubits. -The resulting $[[16,4,4]]$ code we call the tesseract subsystem code. - -%To facilitate fault-tolerant operations, especially error correction, we will generally store information in the last four logical qubits. The first two logical qubits will be gauge qubits~\cite{Bacon05operator}, used only for temporary information storage. The resulting code we refer to as the $[[16,4,4]]$ tesseract subsystem code. - -The advantage of sacrificing two encoded qubits is not hard to see. Consider the code presentation in \figref{f:stabilizers}. Notice that $X^{\otimes 4}$ along any row of the grid is a representative of encoded $X_1$. The product of any two $X^{\otimes 4}$ rows is a stabilizer. Similarly, $X^{\otimes 4}$ along any column is a representative of encoded $X_2$. The product of any two columns gives a stabilizer. Thus measuring the first two logical qubits, using weight-four measurements, is enough to determine the syndromes for all $X$ stabilizers, enabling $Z$ error correction. Weight-four measurements are much easier to make fault tolerantly to distance four than weight-eight measurements~\cite{prabhu21, PrabhuReichardt24code16}. -%Ref.~\cite{PrabhuReichardt24code16} develops a weight-eight stabilizer measurement circuit with six ancillas that is fault-tolerant to distance four. -Moreover, it is fault tolerant to measure the four rows in parallel followed by the columns in parallel; error correction is ``single shot"~\cite{delfosse2020short}. - -\begin{figure} -%\subfigure[\label{f:tesseract}]{\raisebox{0cm}{\includegraphics[width=.3\textwidth]{images/tesseract}}} -%$\qquad\qquad$ -%\subfigure[\label{}] -{\includegraphics[width=.3\textwidth]{images/stabilizers}} -\caption{ -%(b) -It is convenient to arrange the tesseract code's $16$ qubits in a $4 \times 4$ grid. The $X$ and $Z$ stabilizers are supported on pairs of rows and pairs of columns; a set of generators is shown in black. Last, we highlight the supports of the logical operators in the basis we use. For example, logical $Z_2$ is $Z_0 Z_1 Z_2 Z_3$. Observe that the weight-four logical operators are not self dual, but come in three pairs of two. -} -\label{f:stabilizers} -\end{figure} - -Even better, when measuring $X^{\otimes 4}$ across a row (logical $X_1$), we can simultaneously measure $Z^{\otimes 4}$ across the same row (logical $Z_2$). As shown in \figref{f:measurement_xxxxandzzzz}, $X^{\otimes 4}$ and $Z^{\otimes 4}$ can be efficiently and fault-tolerantly measured in parallel, with one measurement outcome flagging the other for possible correlated errors~\cite{Reichardt18steane}. Each pair of weight-four measurements takes only eight CNOT gates, with two ancilla qubits. We will reuse the same two ancilla qubits for all measurements on a code block, so one code block uses $18$ physical qubits in hardware. Interestingly, this method allows for simultaneously correcting $X$ and $Z$ errors, a major efficiency advantage over having sequential procedures as is common. - -\begin{figure} -\subfigure[\label{}]{\includegraphics[scale=.8]{images/measurement_nonft}}$\qquad$ -\subfigure[\label{f:measurement_fullyft}]{\includegraphics[scale=.8]{images/measurement_fullyft}} -\subfigure[\label{f:measurement_oneflag}]{\includegraphics[scale=.8]{images/measurement_oneflag}}$\qquad$ -\subfigure[\label{f:measurement_xxxxandzzzz}]{\includegraphics[scale=.8]{images/measurement_xxxxandzzzz}} -\caption{Circuits to measure weight-four operators. -The circuit in~(a) measures $X^{\otimes 4}$, but it is not fault tolerant, as a single $X$ fault (red) on the ancilla qubit can propagate to a weight-two error on the data. -The circuit in~(b), using one syndrome and two flag qubits, is fully fault tolerant. The corrections can be tracked classically, as part of the ``Pauli frame." -(c)~This circuit's single flag qubit is enough to detect a possible correlated error, but not correct it. A subsequent measurement will have to take the flag into account to correct the answer. -(d) We can also measure $X^{\otimes 4}$ and $Z^{\otimes 4}$ simultaneously. In our applications, the results should be uniformly random. But repeating the $X^{\otimes 4}$ and $Z^{\otimes 4}$ four times, on disjoint qubit sets (\figref{f:ftmeasurements}), if, e.g., one of the $Z^{\otimes 4}$ measurements disagrees with the other three, that can mean that either a weight-one $X$ error has been detected, or a weight-two correlated $X$ error has spread to the data.} -\label{f:weight4measurements} -%\vspace{-0.3cm} -\end{figure} - -Finally, %we will explain below that -the two gauge qubits give workspace for implementing targeted operations, such as H, CNOT or CZ, on any one or two encoded qubits within the same or different code blocks. Essentially, these operations can be implemented by teleporting through the gauge qubits. -%targeted operations within the code block by teleporting through the sacrificed ``gauge" qubits - -\smallskip - -%Figure~\ref{f:stabilizers} presents the code's stabilizers and our logical basis. -The tesseract code is closely connected to other codes (\figref{f:code_relationships}). Notably, it can be obtained by applying transversal CNOTs between the $[[8,3,2]]$ color code on the 3D cube~\cite{Campbell16eight32colorcode}, and its dual; or from four copies of the $[[4,2,2]]$ color code on the square. - -\begin{figure} -\subfigure[\label{}]{\includegraphics[scale=.5]{images/punctured1573}} -\subfigure[\label{f:832colorcode}]{\includegraphics[scale=.6]{images/832colorcode}} -\subfigure[\label{}]{\includegraphics[scale=.6]{images/422encoder}} -\caption{Relationships to other codes. (a) Removing a qubit from the $[[16,6,4]]$ code leaves the well-known $[[15,7,3]]$ Hamming code. (b) Applying CNOT gates between two halves of the 16-qubit code disentangles it into two $[[8,3,2]]$ color codes on the cube; the left color code has $X$ distance $2$ and $Z$ distance $4$, and the right color code vice versa. (c) Left: an encoding circuit for the $[[4,2,2]]$ color code. Stacking four copies of this color code, with the first in encoded $\ket{00}$ (a cat state) and the last in encoded $\ket{{+}{+}}$, and applying the same encoding circuit transversally yields our $[[16,4,4]]$ code with fixed gauge qubits~\cite{BreuckmannBurton22foldtransversalclifford}.} -\label{f:code_relationships} -%\vspace{-0.3cm} -\end{figure} - -\medskip -\noindent -\textbf{Fault-tolerant error correction.} -An efficient fault-tolerant error correction procedure is the foundation for any fault-tolerant computation scheme. One round of error correction consists of measuring $X^{\otimes 4}$ and $Z^{\otimes 4}$ across four rows, then down four columns. The $X^{\otimes 4}$ measurement outcomes across the rows may be random, but should be correlated. In the absence of noise, one should obtain either $(0,0,0,0)$ or $(1,1,1,1)$. If one outcome disagrees with the other three, it indicates a $Z$ error in that row, e.g., $(0,1,0,0)$ and $(1,0,1,1)$ both indicate a $Z$ error in the second row. The column $X^{\otimes 4}$ measurements should similarly identify the column of the $Z$ error, so it can be corrected. If two rows disagree with the others, e.g., $(0,0,1,1)$ or $(0,1,0,1)$, this indicates an uncorrectable error and we reject the trial. - -Actually, it is not so simple. In the circuit of \figref{f:measurement_xxxxandzzzz}, a single $Z$ fault when measuring a column can cause a weight-two $Z$ error in that column of qubits. We cannot reject these first-order events or the rejection rate would be too high; worse, if a single fault can cause weight-two errors, the logical error rate will only be second order. For a distance-four code, the logical error rate should be third order. - -Fortunately, when a single $Z$ fault causes a weight-two $Z$ error in a column, it will be flagged by the $X^{\otimes 4}$ column measurement. Thus when measuring $X^{\otimes 4}$ along the rows, we proceed differently when a column has been flagged for $Z$ errors. If a flag has been raised, then we accept $(0,0,1,1)$ or $(1,1,0,0)$ row measurement outcomes, and correct them by applying $ZZII$ (or equivalently $IIZZ$) down the column. - -Figure~\ref{f:errorcorrectionexamples} illustrates a variety of cases, and \figref{f:errorcorrectionrules} lays out the error correction rules in detail. -%Figure~\ref{f:errorcorrectionrules} lays out the error correction rules in detail, with a variety of cases illustrated in \figref{f:errorcorrectionexamples}. -Figure~\ref{f:repeatederrorcorrection} shows a complete example of three rounds of repeated error correction, with a flagged $Z^{\otimes 4}$ measurement leading to an $XXII$ correction applied to that column after the next row measurements. %The error correction rules are laid out in detail in \figref{f:errorcorrectionrules}, and a larger variety of examples shown in \figref{f:errorcorrectionexamples}. -%Figure~\ref{f:errorcorrectionrules} lays out the error correction rules in detail. - -\begin{figure} -{\includegraphics[scale=.4]{images/error_correction_rules}} -\caption{Error correction examples. -$X^{\otimes 4}$ outcomes are red, $Z^{\otimes 4}$ outcomes are blue, and corrections are indicated in green. -If one row $X$ measurement disagrees with the others, and one column $X$ measurement disagrees with the others, then we apply a $Z$ correction to the row, column intersection. A disagreeing row $X$ measurement also flags that row for a possible correlated $ZZII$ or $IIZZ$ error. However, if there are two $0$ and two $1$ row measurements (with no column flagged in the previous column measurements), then we have to reject.} -\label{f:errorcorrectionexamples} -%\vspace{-0.3cm} -\end{figure} - -\begin{figure} -% to compile minted code, delete the _minted-gummy directory, run pdflatex -shell-escape gummy.tex in the terminal, and then recompile in TexShop -\begin{minted}{python} -if flagX == -1: # no row flagged already - if sum(measX) == 2: - return "reject" - if sum(measX) in (1,3): - if sum(measX) == 1: flagX = measX.index(1) - else: flagX = measX.index(0) -else: # row flagX in (0,1,2,3) flagged - if sum(measX) in (1,3): - if sum(measX) == 1: col = measX.index(1) - else: col = measX.index(0) - frameZ[4*flagX + col] += 1 # Z correction - if sum(measX) == 2: - if measX in ([0,0,1,1], [1,1,0,0]): - frameZ[[4*flagX, 4*flagX+1]] += 1 # ZZII - else: - return "reject" - flagX = -1 -\end{minted} -\caption{Error correction rules. -This code processes $Z$ error correction for column measurements. -Let {\tt measX} store the results of $X^{\otimes 4}$ measurements on four columns. Let $\text{\tt{flagX}} = -1$ if no preceding row $X^{\otimes 4}$ measurement was flagged, or $\text{\tt flagX} \in \{0,1,2,3\}$ the flagged row with a possible weight-one $Z$ error or correlated $ZZII$ or $IIZZ$ error. -A $Z$ correction is stored in the Pauli frame. -Similar code works for $X$ error correction, and for row $X$ and $Z$ measurements. -} -\label{f:errorcorrectionrules} -%\vspace{-0.3cm} -\end{figure} - -\begin{figure*} -{\includegraphics[scale=.4]{images/ec_3x}} -\caption{Repeated error correction experiment with three rounds of $X$ and $Z$ error correction. Starting from the left, each round of error correction consists of measuring $X^{\otimes 4}$ and $Z^{\otimes 4}$ across four rows, then down four columns, using the circuit in \figref{f:measurement_xxxxandzzzz}. -The error correction rules are ``rolling," meaning that if an error is first detected in the column measurements, then the subsequent row measurements are used to correct it.} -\label{f:repeatederrorcorrection} -%\vspace{-0.3cm} -\end{figure*} - -It is not obvious that the resulting scheme is fault tolerant. A full argument requires case checking, the tricky case being when there are two faults, e.g., a flagged correlated error and one additional fault. This should not cause a logical error. - -%We follow the extended rectangle formalism of Ref.~\cite{AliferisGottesmanPreskill05} to determine rules for fault-tolerant error correction. - -\medskip -\noindent -\textbf{Fault-tolerant operations.} -%The tesseract code has elegant properties that facilitate fault-tolerant computation and error correction. -There is a large variety of primitives enabling fault-tolerant computation with the tesseract subsystem code. -We will mention here only those essential for our experiments. -%We will mention a few of the essential primitives here, and will highlight others as they arise in the experimental setups below. -%Rather than presenting a long list here, we will introduce these primitives as they are needed in the descriptions of our experiments below. - -The experiments all begin by initializing basic encoded states $\ket{{+}{+}0000}$ or $\ket{{+}0{+}0{+}0}$, with the circuits in \figref{f:initialization}. Here the flag and syndrome measurements are postselected, meaning that if any measurement is nontrivial we reject the state and start again. Rejections during state preparation are in theory less of a concern than rejections deeper into a computation, when starting over is costly. In the full experimental data below, we break out as ``prerejection rate" the fraction of trials that were rejected during state preparation. - -\begin{figure} -\subfigure[\label{f:preparation_xxzzzz}]{\includegraphics[scale=.8]{images/preparation_xxzzzz}} -\subfigure[\label{f:preparation_xzxzxz}]{\includegraphics[scale=.8]{images/preparation_xzxzxz}} -\caption{Initialization circuits. (a) Fault-tolerant postselected circuit to prepare encoded $\ket{{+}{+}0000}$. The red gates are for flags, and the blue gates for measuring a stabilizer. -(b) Taking two copies of this postselected circuit fault tolerantly prepares encoded $\ket{{+}0{+}0{+}0}$. Each copy is $\ket{000}$ encoded in the $[[8,3,2]]$ color code. -} -\label{f:initialization} -%\vspace{-0.3cm} -\end{figure} - -Of course, transversal $X$ and $Z$ measurements, followed by classical decoding, can be used to destructively measure all of the logical $X$ or $Z$ operators fault tolerantly. Interestingly, there is also a simple procedure for measuring half the logical qubits in the $X$ basis and half in the $Z$ basis. -%For example, to measure $X, Z, X, Z, X, Z$, apply row-transversal CNOT gates from row 1 to row 2, and row 4 to row 3. -For example, to measure $Z, X, Z, X, Z, X$, apply row-transversal CNOT gates from row 1 to row 4, and row 2 to row 3; then measure each qubit in the top half in the $X$ basis, and each in the bottom in the $Z$ basis. As shown in \figref{f:832colorcode}, the CNOT gates divide the logical qubits between two $[[8,3,2]]$ color codes. Although these codes only have distance two, they have distance four in the direction that matters ($Z$ distance four for the top half, $X$ distance four for the bottom), so one can reliably decode the measurement results. -We will use this measurement procedure in the repeated error correction experiments below. - -Importantly, we can also projectively measure single logical qubits, and some operators across multiple logical qubits, with single-shot weight-four measurements. (For most codes, this would require multiple repeated rounds of measurements~\cite{delfosse2020short}.) The error-correction procedure above uses this property to measure logical qubits 1 and~2. Figure~\ref{f:ftmeasurements} shows more examples. - -\begin{figure} -{\includegraphics[scale=.3]{images/ftmeasurements}} -\caption{Logical measurements. Each logical operator $X_1$, $Z_1, \ldots, Z_6$ has four qubit-disjoint weight-four representatives. Measuring the representatives fault tolerantly and taking the majority of the results, postselecting on no tie, fault tolerantly measures that logical operator. Many weight-two logical operators can also be measured this way, as well as higher-weight logical operators. Importantly, $X$ or $Z$ logical measurements supported on both qubits of a pair (1,2), (3,4) or (5,6) can \emph{not} generally be measured this way, as their minimum-weight representatives can have weight six. But, e.g., $X_1 Z_2$ has four $Y^{\otimes 4}$ representatives. Shown is a selection of logical operators and their minimum-weight representatives' supports.} -\label{f:ftmeasurements} -%\vspace{-0.3cm} -\end{figure} - -In particular, these projective logical measurements allow for measurement-based computation~\cite{RaussendorfBriegel01cluster}, using the gauge qubits $1$ and~$2$ as workspace. For example, to get an encoded CNOT gate from logical qubit~$i$ to~$j$, start with, say, logical qubit~$2$ in $\ket 0$. Measure $X_2 X_i$ (correcting with $Z_i$ if the result is $1$), then measure $Z_2 Z_j$ (correcting with $X_2 X_i$ if the result is $1$), and finally measure $X_2$ (correcting with $Z_2 Z_j$ if the result is~$1$). -The weight-four measurements can be made with either of the circuits in Figs.~\ref{f:measurement_fullyft} or~\ref{f:measurement_oneflag}; we use the latter, more efficient circuit, and if a flag is raised the next measurement (in the dual basis) can correct the possible correlated error. - -\smallskip - -Many qubit permutations preserve the tesseract code space, and they can have a nontrivial logical effect. Figure~\ref{f:permutation_automorphisms} gives the full group of permutation automorphisms. - -\begin{figure} -\setlength{\tabcolsep}{1.9pt} -\begin{tabular}{c@{$\qquad$}c} -%\hline \hline -Permutation & Logical effect \\ -\hline \\[-.1in] -\raisebox{-.5cm}{\includegraphics[scale=.5]{images/permutation_swapcolumns_trivial}} & $e$ \\[.2in] -\raisebox{-.5cm}{\hspace{-.08cm}\includegraphics[scale=.5]{images/permutation_swapcolumns_nontrivial}} & (35)(46) \\[.2in] -\raisebox{-.5cm}{\hspace{0cm}\includegraphics[scale=.5]{images/permutation_shiftrightdown}} & (34)(56) \\[.2in] -\raisebox{-.5cm}{\hspace{-.06cm}\includegraphics[scale=.5]{images/permutation_funny}} & (15)(26) \\[.2in] -\raisebox{-.5cm}{\hspace{-.04cm}\includegraphics[scale=.5]{images/permutation_cnot}} & %$\text{CNOT}_{3,6} \, \text{CNOT}_{5,4}$ -\raisebox{-.75cm}{\includegraphics[scale=.5]{images/cnot36cnot54}} \\[.2in] -%\hline \hline \\ -\end{tabular} -\caption{Permutation automorphisms. There are $16 \cdot 20160$ qubit permutations that preserve the code. For example, applying any of the four permutations $e,(12)(34),(13)(24),(14)(23)$ to the columns and another to the rows (16 possibilities) %in all) -has trivial logical effect. Shown is a set of group generators, and their logical effects. Note that certain combinations of logical CNOT gates can be implemented with permutations. -%Then for example a right or left cyclic shift of the qubits implements the logical qubit permutation $(35)(46)$. A diagonal shift implements logical permutation $(34)(56)$. Overall, there are $24$ possible logical qubit permutations, each achievable in $16$ ways. -} -\label{f:permutation_automorphisms} -%figure{-0.3cm} -\end{figure} - - -\section{Experiments} \label{s:experiments} - -The subsections below give the details for the encoded and unencoded experiments. -Experimental data %, from running the \mbox{Path-$4$} experiments on H1 and the others on H2, -is collected in \tabref{f:fulldata}. Certainly %more machine time would be useful to run more trials and -more trials would be useful to -narrow the confidence intervals. However, it is already clear that the encoded circuits have significantly lower error rates than the unencoded baselines. %, sometimes dramatically. -%\tabref{f:fulldata} collects the results of the experiments. - - -\subsection{Quantum computers H1 and H2} - -The experiments described in this section were performed on the Quantinuum H1 and H2 trapped-ion quantum computers, which have 20 and 56 physical qubits, respectively, with all-to-all connectivity through ion-transport operations~\cite{quantinuum22h1, Moses23quantinuum, DeCross24randomcircuitsh2}. Since the time of the referenced publications improvements were made to the H2 two-qubit gate laser, and the transport and cooling times were further optimized resulting in the following error parameters: two-qubit gate error $1.15(5) \times 10^{-3}$, single-qubit gate error $2.9(4) \times 10^{-5}$, SPAM error $1.47(9) \times 10^{-3}$, and memory error per qubit per depth-one circuit time $2.5(5) \times 10^{-4}$, where the depth-one circuit time is $\sim\!68$ ms. -Current specifications for the H1 device can be found at~\cite{quantinuum24h1}. - -Note that using a distance-four code makes the logical error rate particularly sensitive to physical error parameters, with a cubic dependence. - -The specific structure of the quantum error correction circuit described in this work can leave some qubits idling for times much longer than the depth-one circuit time, which most accurately describes timing for circuits with densely packed two-qubit gates and arbitrary connectivity. The naive approach originally taken by the compiler's qubit routing algorithm focused on maximizing the number of gates that can be done in parallel and was found to be suboptimal for these circuits. Trading some parallelism for faster rearrangement resulted in reduction of overall circuit execution duration and consequently smaller overall memory error. Although the improvements from these compiler optimizations are extremely circuit dependent we find for these circuits a 10--15\% reduction in total execution time. - -On H2, to further reduce memory error due to slow drifts in the qubit frequency for qubits with long idle times we insert dynamical decoupling pulses. After the quantum circuit has been compiled to a set of transport instructions and gate operations, single-qubit gates are added to perform dynamical decoupling on qubits with more than 300ms between gates. These dynamical decoupling pulses are inserted opportunistically as the qubits move through the gating zones during the transport operations used to reconfigure the qubits between rounds of two-qubit gates. - -\begin{table*} -\caption{\label{f:fulldata} -Experimental data. -A trial is ``prerejected" if it is rejected during the initial state preparation, of encoded $\ket{00{+}{+}{+}{+}}$, $\ket{{+}{+}0000}$ or $\ket{{+}0{+}0{+}0}$. Our preparation circuits allow prerejection to occur with first-order probability in the error rate; this is acceptable because only that block's initialization must be restarted and not the whole computation. A trial is ``postrejected" if it is rejected any time after the initial state preparation, due to two faults being detected in close proximity. With distance-four fault tolerance, postrejection should occur with a second-order probability. -} -\setlength{\tabcolsep}{2pt} -\begin{tabular}{r@{$\quad$}ccccccc} -%\hline \hline -\Xhline{1.8\arrayrulewidth} - & Meas. & & & & Acceptance & & \\[-.1cm] -Experiment & basis & Trials & Prerejected & Postrejected & rate & Errors & Error rate \\ -\hline -Path-$4$ encoded - & $X$ & 3000 & 220 & 89 & 90(1)\% & 3 & $0.12^{+0.18}_{-0.09}\%$ \\ - & $Z$ & 3000 & 192 & 96 & 90(1)\% & 2 & $0.08^{+0.16}_{-0.06}\%$ \\ - unencoded - & $X$ & 6000 & --- & --- & --- & 88 & $1.5(3)\%$ \\ - & $Z$ & 6000 & --- & --- & --- & 88 & $1.5(3)\%$\\ -\hline -Cube-$8$ encoded - & $X$ & 2000 & 298 & 272 & 71(2)\% & 2 & $0.2^{+0.3}_{-0.1}\%$ \\ - & $Z$ & 2000 & 256 & 238 & 75(2)\% & 3 & $0.2^{+0.3}_{-0.2}\%$ \\ -unencoded - & $X$ & 6000 & --- & --- & --- & 151 & $2.5(4)\%$ \\ - & $Z$ & 6000 & --- & --- & --- & 119 & $2.0^{+0.4}_{-0.3}\%$ \\ -\hline -Cat-$12$ encoded - & $X$ & 2000 & 482 & 49 & $73(2)\%$ & 1 & $0.08^{+0.24}_{-0.07}\%$ \\ - & $Z$ & 2000 & 478 & 30 & 75(2)\% & 2 & $0.1^{+0.3}_{-0.1}\%$ \\ -unencoded - & $X$ & 6000 & --- & --- & --- & 130 & $2.2^{+0.4}_{-0.3}\%$ \\ - & $Z$ & 6000 & --- & --- & --- & 163 & $2.7(4)\%$ \\%[.25 cm] -\hline -\hspace{-.15cm}\begin{tabular}{r}5 rounds of 4 qubit \\[-.1cm] error correction\end{tabular} - & --- & 2500 & 297 & 148 & $82^{+1}_{-2}\%$ & 2 & $0.11^{+0.21}_{-0.09}\%$ \\ -\begin{tabular}{r}teleportation \\[-.1cm] baseline%* -\end{tabular} - & --- & 6000 & --- & --- & --- & 163 & $2.7(4)\%$ \\ -\hline -\hspace{-.15cm}\begin{tabular}{r}5 rounds of 8 qubit \\[-.1cm] error correction\end{tabular} - & --- & 2000 & 579 & 418 & 50(2)\% & 7 & $0.7^{+0.7}_{-0.4}\%$ \\ -\begin{tabular}{r}teleportation \\[-.1cm] baseline%* -\end{tabular} - & --- & 6000 & --- & --- & --- & 338 & $5.6(6)\%$ \\ -%\hline \hline -\Xhline{1.8\arrayrulewidth} -\end{tabular} -\end{table*} - - -\subsection{Path-$4$ state preparation experiment} - -The stabilizers for the $4$-qubit path graph state are $XXII, IXXX, ZZZI, IIZZ$. The unencoded state preparation circuit is simply -$$ -\includegraphics[scale=1.2]{images/path4encoder} -$$ -We follow this preparation with transversal $X$ or $Z$ measurements ($6000$ trials for each), and count the fraction of trials in which a stabilizer violation is observed. - -\medskip - -The encoded state preparation procedure has four steps: -\begin{enumerate}[leftmargin=*] -\item -Prepare encoded $\ket{{+}0{+}0{+}0}$, as in \figref{f:preparation_xzxzxz}. -\item -Permute the qubits, as in \figref{f:permutation_automorphisms}, to implement two encoded CNOT gates, and get two encoded Bell pairs, on qubits $3,6$ and $4,5$. -\item -Implement an encoded CNOT gate, from qubit~$6$ to~$5$. This is done with three fault-tolerant measurements, using qubit~$2$ as workspace: -\begin{enumerate}[leftmargin=*] -\item Measure $X_2 X_6$ (correcting $Z_2$ if the result is~$1$). This is done by using the circuit in \figref{f:measurement_oneflag} to measure each of the $X^{\otimes 4}$ operators with supports -$$ -\includegraphics[scale=.3]{images/measurex26} -$$ -\item Measure $Z_2 Z_5$ (correcting $X_2 X_6$ if the result is~$1$). This time measure the $Z^{\otimes 4}$ operators -$$ -\includegraphics[scale=.3]{images/measurez25} -$$ -Note that each of these $Z$ operators overlaps each of the previous $X$ operators on exactly one qubit. If one of the $X$ measurements was flagged, then the $Z$ measurements can correct the possible $X$ or $XX$ error. A trial with two flags is rejected. -\item Measure $X_2$ (correcting $Z_2 Z_5$ if the result is~$1$). The $X^{\otimes 4}$ operators have support -$$ -\includegraphics[scale=.3]{images/measurex2} -$$ -Once again, an $X$ or $XX$ error from a flagged $Z$ measurement can be corrected. -\end{enumerate} -\item -Transversal $X$ or $Z$ measurement. The measurement results are updated with the stored Pauli frame, then decoded classically, taking into account a possible $X$ measurement flag. -\end{enumerate} - -We ran this experiment on H1, with a total of 6000 trials divided between the $X$ and $Z$ measurement settings. 5403 trials were accepted, and, among those, five logical errors were found. See \tabref{f:fulldata}. - -%We used H1, instead of H2, because simulations suggested that it should be able to soundly beat the physical baseline. -For H2, simulations suggest that the physical baseline should be about the same, but the encoded circuit's error rate perhaps about $10 \times$ lower. Since it would take many trials to distinguish H2 from H1, we did not run the experiment on H2. - -In an earlier version of the experiment, we used the two-flag circuit of \figref{f:measurement_fullyft} to make the measurements. That is slightly simpler, because flags don't have to be passed to the next step. However, in H1 and H2 simulations the one-flag version has about a 5x lower logical error rate. - - -\subsection{Cube-$8$ state preparation experiment} - -A circuit to prepare the %unencoded -$8$-qubit cube graph state is: -$$ -\includegraphics[scale=1.0]{images/cube8encoder} -\qquad -\raisebox{.4cm}{\includegraphics[scale=.5]{images/cube8cube}} -$$ -Here, each qubit is labeled by its coordinates in a unit cube. The first round of CNOT gates connects qubits in the $z$ direction, and the second and third rounds connect in the $y$ and $x$ directions. -%the second round of CNOT gates connects qubits in the $y$ direction, and the third round connects in the $x$ direction. -As the cube has $12$ edges, there are $12$ CNOT gates. - -The encoded procedure starts with encoded $\ket{00{+}{+}{+}{+}}$ and $\ket{{+}{+}0000}$ in two code blocks, prepared as in \figref{f:preparation_xxzzzz}. As shown in \figref{f:encodedcube}, three rounds of transversal CNOT gates between the blocks generate the encoded cube state. -%The logical qubits 3,4,5,6 in each block correspond to the cube's vertices as follows: -In the second and third CNOT gate rounds, the target block's qubits are permuted so as to permute the encoded qubits. (We swap two rows in the second round, and swap two columns in the third round.) -The state is then measured transversally, and decoded. - -The four error correction steps are essential here, because CNOT gates copy errors between blocks. Without error correction, for example, a single $X$ error on the first block could spread to a weight-three $X$ error on the target block, which would decode to a logical error. (In fact, fault tolerance requires only $X$ error correction on the control block, and $Z$ error correction on the target block. We choose to correct $X$ and $Z$ errors on both blocks.) - -Note that when interpreting the results, measurement flags also need to be passed between the blocks. For example, if a column in the first block is flagged for a possible $XX$ error, since that error would be copied to the second block the flag also needs to be copied. If the second block was already flagged for an $X$ or $XX$ error, then we reject. Aside from passing flags like this, we do not use correlated error decoding between the blocks. - - -\subsection{Cat-$12$ state preparation experiment} - -The $12$-qubit cat state $\tfrac{1}{\sqrt 2}\big(\ket{0^{12}} + \ket{1^{12}}\big)$ can be prepared with four rounds of CNOT gates, $11$ CNOTs total: -$$ -\includegraphics[scale=.8]{images/cat12encoder} -$$ -It is a graph state for the star graph. In fact, in this case the physical baseline we compare to is the dual cat state $\tfrac{1}{\sqrt 2}\big(\ket{+^{12}} + \ket{-^{12}}\big)$, because it has slightly lower error rates in simulation. - -To prepare the encoded cat state, on three code blocks, we start by preparing an encoded cat state $\ket{0^4} + \ket{1^4}$ in qubits $3,4,5,6$ of one code block. This is similar to the Path-$4$ experiment, but simpler: -\begin{enumerate}[leftmargin=*] -\item -Prepare encoded $\ket{{+}0{+}0{+}0}$, as in \figref{f:preparation_xzxzxz}. -\item -Permute the qubits, as in \figref{f:permutation_automorphisms}, to implement two encoded CNOT gates, and get two encoded Bell pairs, on qubits $3,6$ and $4,5$. -\item -Finally, merge the two Bell pairs by measuring $Z_4 Z_6$ (correcting $X_3 X_6$ if the result is~$1$). This is done by using the one-flag circuit in \figref{f:measurement_oneflag} to measure each of the $Z^{\otimes 4}$ operators with supports -$$ -\includegraphics[scale=.3]{images/measurez46} -$$ -\end{enumerate} -Then apply transversal CNOT gates into two copies of $\ket{{+}{+}0000}$, and measure all $48$ physical qubits in the $X$ or $Z$ basis. With transversal $X$ measurements, the classical decoder must take into account that one of the $Z$ measurements may have raised a flag, indicating a possible $ZZ$ error in the first code block. - -Although there are $12$ encoded qubits, using all $56$ physical qubits in H2, the encoded circuit is shallower and simpler than the Cube-$8$ experiment. %In particular, it does not use fault-tolerant error correction. - - -\subsection{Five rounds of repeated error correction, with $10$ one-qubit teleportations} - -Ryan-Anderson et al.~\cite{honeywell21steane} found that each round of error correction introduced about $2.7\%$ logical error to their one encoded qubit, biased toward logical $Z$ errors. Silva et al.~\cite{Silva24microsoft12qubitcode} prepared $\ket{{+}{+}}$, a state sensitive to $Z$ errors, encoded in the $[[12,2,4]]$ code, and found that three rounds of error correction introduced $0.8(3)\%$ logical error. Since $\ket{{+}{+}}$ is invariant under $X$ errors, the total error rate is slightly higher. We prepare and protect encoded $\ket{{+}0{+}0}$. This state is sensitive to both $X$ and $Z$ errors. In fact, though, simulations starting with encoded $\ket{0000}$ and $\ket{{+}{+}{+}{+}}$ did not show significant differences in the logical error rate, suggesting that the logical $Z$ bias may have been largely ameliorated by the memory optimizations and dynamical decoupling introduced in~H2. - -\smallskip - -The unencoded physical baseline for our repeated error correction experiment is the following circuit consisting of five rounds, having in each round two one-qubit teleportation steps: -\begin{equation*} %\label{e:repeatederrorcorrectionbaseline} -\includegraphics[scale=.8]{images/repeatederrorcorrectionbaseline} -\end{equation*} -The Pauli corrections for the one-qubit teleportations are not shown, but are $Z$ for $X$ measurements, and $X$ for $Z$ measurements. Our interpretation for this circuit is that data qubits $3,4,5,6$ are cyclically rotating through the six qubit positions. We call a trial a success if in the end, after Pauli corrections, the final four measurement outcomes are ${+}, 0, {+}, 0$, and otherwise declare an error. - -In the eight-qubit version of this experiment, we repeat the above circuit %of Eq.~\eqnref{e:repeatederrorcorrectionbaseline} -twice in parallel, as shown in \figref{f:repeatederrorcorrectionbaseline_withbarrier}. We enforce that the circuits are run in parallel, and not scheduled sequentially, by inserting a compiler barrier on all eight data qubits before the final measurements. This forces all operations before the barrier to finish before any operations after it begin~\cite{Quantinuum20userguide}. -Sequential execution would presumably be easier since it lets the device devote its limited parallelism to one block at a time, and needs fewer qubits in memory. - -\begin{figure} -\subfigure[]{ -\includegraphics[scale=.38]{images/repeatederrorcorrectionbaseline_withoutbarrier} -} -\subfigure[\label{f:repeatederrorcorrectionbaseline_withbarrier}]{ -\includegraphics[scale=.7]{images/repeatederrorcorrectionbaseline_withbarrier} -} -\caption{ -(a) Two independent circuits intended to run in parallel could instead be scheduled sequentially. The compiler barrier in (b) disallows fully sequential compilation. -} -\end{figure} - -\smallskip - -The encoded version %of this circuit -is \emph{not} just repeated error correction with alternating row and column measurements, as in \figref{f:repeatederrorcorrection}. The baseline comparison for that experiment would be four idling physical qubits, and %at least with dynamical decoupling~\cite{ViolaKnillLloyd99DD}, -idle ion trap qubits are a very good memory. - -\begin{figure*} -\subfigure[]{ -\includegraphics[scale=.35]{images/1blocksimulatedrotatedecfailures} -$\qquad$ -\includegraphics[scale=.35]{images/1blocksimulatedrotatedecacceptance} -} -\subfigure[]{ -\includegraphics[scale=.35]{images/1blocksimulatedrotatedecfailures50} -$\qquad$ -\includegraphics[scale=.35]{images/1blocksimulatedrotatedecacceptance50} -} -\caption{(a) Simulated data showing the logical error probability and acceptance rate plotted versus rounds of error correction, on one code block. The red points are real data from our five-round experiment. (b) Simulations extended to 50 rounds of error correction. The fit line and exponential are still based on the first 10 rounds.} \label{f:1blocksimulatedrotatedec} -\end{figure*} - -Instead, we implement a more complicated version of repeated error correction that simultaneously makes the above cyclic one-qubit teleportations %of Eq.~\eqnref{e:repeatederrorcorrectionbaseline} -among the six encoded qubits. This alternative error correction experiment proceeds as follows: -\begin{enumerate}[leftmargin=*] -\item Prepare encoded $\ket{{+}0{+}0{+}0}$, as in \figref{f:preparation_xzxzxz}. -\item Measure together encoded $Z_1$ and $X_2$ to initialize encoded $\ket{0{+}{+}0{+}0}$. (If the $Z_1$ outcome is~$1$, correct with $X_1$. If the $X_2$ outcome is~$1$, correct with $Z_2$.) -\item Repeat five times: -\begin{enumerate}[leftmargin=*] -\item Measure together $X_1 X_3$ (correction $Z_1$) and $Z_2 Z_4$ (correction $X_2$), using the circuit in \figref{f:measurement_xxxxandzzzz}. -\item Rotate the encoded qubits cyclically forward two steps, i.e., with the permutation $(3,1,5)(4,2,6)$. This logical permutation is implemented with the physical qubit permutation $(0,2,5)(3,6,4)(8,15,10)(9,12,14)$. -\item Measure $Z_1$ (correction $X_1 X_5$) and $X_2$ (correction $Z_2 Z_6$) by measuring $X^{\otimes 4}$ and $Z^{\otimes 4}$ down each column. This restores the gauge qubits to $\ket{0{+}}$. -\end{enumerate} -In each step, the four $X^{\otimes 4}$ and $Z^{\otimes 4}$ measurement outcomes are used to correct errors, including possible flagged correlated errors, just as in \figref{f:repeatederrorcorrection}. -\item Destructively measure encoded $X_3$, $Z_4$, $X_5$, $Z_6$. As explained in \secref{s:tesseractcode}, and similar to \figref{f:832colorcode}, apply row-transversal CNOT gates from row 1 to row 2, and row 4 to row 3; then measure each control qubit in the $X$ basis, and each target qubit in the $Z$ basis. Decoding the $[[8,3,2]]$ code for the control half gives $X_3$ and $X_5$, while decoding the target half gives $Z_4$ and $Z_6$. -\end{enumerate} -In the repeated error correction experiment of \figref{f:repeatederrorcorrection}, the logical measurement outcomes do not matter, i.e., measurements $0000$ and $1111$ are treated the same. The gauge qubits' states are irrelevant. However, in this experiment the logical measurement outcomes do matter, since they determine the one-qubit teleportation Pauli corrections. We expect this experiment to be slightly more challenging. - -\smallskip - -A state-vector emulator for the H2 system is available from Quantinuum~\cite{Quantinuum24emulator}. -%that in our limited experience is very accurate for small experiments -In our experiments, the emulator gives very accurate results for small circuits such as our physical baseline comparisons on up to $12$ qubits. It is less accurate for our experiments on one code block, tending to underestimate acceptance probabilities. Nonetheless, we have simulated up to $50$ rounds of the one-qubit teleportation version of error correction, on one code block. Figure~\ref{f:1blocksimulatedrotatedec} plots the simulated logical error and acceptance probabilities. The logical error probability's growth is at least consistent with a straight line, as one would hope, while the acceptance probability has a slow exponential decline. (The acceptance rate starts below~$1$ because about $13.6\%$ of trials are discarded due to a detected state preparation error.) In the first $10$ error correction rounds, the logical error rate increases by $2.1(1) \times 10^{-4}$ per round, i.e., this is the slope of the fit line, and $2.02(2)\%$ of the surviving trials are discarded. %If we accept this fiction as meaningful, then even after $50$ rounds of error correction, the logical error rate could still be only just over $1.0\%$ and the acceptance rate about $28\%$. In fact, in a simulation of 100,000 trials of $50$ rounds of error correction, the acceptance rate was $23\%$ and the error rate $0.8(1)\%$, so perhaps the extrapolation is slightly off. -Certainly we would have liked to validate these simulations with more experiments, but our time on H2 was limited. - -%Interestingly, this version of repeated error correction performs better on the H2 hardware than the simple repeated error correction experiment. It is unclear why, but perhaps this version allows better scheduling by the compiler. - -We have not run similar simulations for two code blocks, because the state-vector emulator cannot simulate the $36$ qubits this would require. The stabilizer emulator can simulate all $56$ qubits in the H2 device, but while it can be useful for guidance it is not accurate enough in $18+$ qubit simulations to draw conclusions. - - -%\section{Fault-tolerant error correction} \label{s:ftec} -% -%A circuit is fault-tolerant to distance $d$ if $j \leq t= \lfloor \frac{d-1}{2} \rfloor $ mid-circuit faults cause an output error of weight at most~$j$. -%Additionally for even distance fault tolerance, sets of $d/2$ faults spreading to weight $>d/2$ errors should be detected so the computation can be restarted. -%When these faults yield an error of weight $d/2$, the computation is restarted if the faults can be detected, else it is rejected in the next round of error correction. -% -% -%\medskip -%\noindent -%\textbf{Fault tolerance rules.} -%For distance-four fault tolerance, we must consider the effect of up to two input errors or internal faults. If the input error has weight two and there are no internal faults, then the stabilizer measurement sequence must detect the error and restart the computation. -%If there is a weight-one input error and an internal fault, either the computation is restarted, or the output of the error correction block must have error of $X$ and $Z$ weight at most one. -%Finally, if two internal faults occur with no input error, either the computation is restarted, or the output error must have weight at most two. -% -%\begin{figure}[b] -%{\includegraphics[width=.315\textwidth]{images/smcrules.pdf}} -%\caption{For distance-four fault tolerance, the key property is that it should take at least three faults to cause a logical error, and at least two faults to cause a rejection. Furthermore, the faults must be close enough together---within an ``extended rectangle"---to cause a logical error or rejection. In CSS fault tolerance, $X$ and $Z$ errors may be considered separately. -%(b)~Rules for fault tolerance. One fault should be corrected to an error of $X/Z$ weight at most one---this is sufficient for distance $3$. Two faults should either be rejected ({\color{cardinal} R}) or result in an error of weight~two. -%} -%\label{f:smcrules} -%\end{figure} - - -%If these operations introduce a low amount of error, it may be possible to execute relatively high-depth circuits. These tools can then be used to execute short NISQ and magic state distillation algorithms. As an example, our results show that just $50$ physical qubits may be sufficient to demonstrate $10$-to-$2$ MEK distillation experimentally with $O(p^3)$ logical errors~\cite{Meier13MSD}. - - -\section{Outlook} - -We have presented a collection of fault-tolerance gadgets for the tesseract subsystem code, and have used them together to demonstrate a variety of fault-tolerant, error-corrected computations. These computations beneficially combine fault-tolerant computation and error correction, for the first time, while outperforming the physical baselines in fidelity. -%The tesseract subsystem code certainly enables a variety of high-fidelity, fault-tolerant logical state preparation experiments. - -It would be interesting to study %more deeply -the composability of the different gadgets, measuring the error rate through each extended rectangle~\cite{AliferisGottesmanPreskill05}, and verifying that logical error rates compose sub-additively. -There are also more fault-tolerance gadgets to develop. %and test. -We have not %yet -demonstrated a full set of Clifford operations. In particular, we need targeted CNOT gates between blocks, e.g., from logical qubit~$1$ of block~$1$ to logical qubit~$2$ of block~$2$, and logical $S$ gates. -Due to identities like -$$ -\includegraphics[scale=.9]{images/targetedcnotbetweenblocks} -$$ -targeted CNOTs within a block, together with transversal CNOTs between them, imply targeted CNOTs between blocks---but that is an inefficient and inelegant construction. -In fact, a targeted CNOT between blocks can be done using measurements, as in the Path-$4$ experiment, and a logical $S$ gate can be implemented %by teleportation -with a carefully prepared ancilla code block. We have not yet tested these gadgets experimentally. - -Beyond that, it is important to achieve full universality, potentially with multiple encoded non-Clifford gates. There are several options for achieving logical universality to different degrees of fault tolerance: non-fault tolerant (distance one), distance two, or distance four. We aim to develop some of these techniques and deploy them in a computation including both Clifford and non-Clifford operations. -%For instance, on the $[[8,3,2]]$ color code, a pattern of $T$ and $T^\adjoint$ operators applied transversally implements a logical CC$Z$ gate. - -Finally, there are good prospects for scaling up the tesseract subsystem code fault-tolerance scheme to larger experiments. The H2 device has limited parallelism, but, with dynamical decoupling, it exhibits much better memory than~H1. This is why we ran the four-qubit %repeated -error correction experiment on H2. %rather than H1. -Early experiments with five rounds of error correction on $12$ logical qubits---an admittedly difficult test---have shown a potentially problematically high error rate, $2^{+3}_{-1}\%$. There is room for improvement, to increase both the number of logical qubits and the depth of logical computation, and to further reduce the logical error rate. Improvements to the hardware, the compiler, and the fault-tolerance scheme all need to come together to address this challenge. - -%There are many choices of fault-tolerance schemes, and which scheme is best depends on the hardware; particularly the number of qubits, error rates, and qubit connectivity. Early quantum computing experiments had fewer, less reliable qubits, so error-correction experiments had to use small codes, with distance two or three. These small codes usually protect one or two logical qubits per code block, and so have a high qubit overhead (or low rate: ratio of logical qubits to physical qubits). They can also incur a high time overhead. Distance two codes cannot correct errors, only detect them, and trials with detected errors are discarded. The acceptance rate drops exponentially with the number of rounds of error detection, so the number of trials required grows exponentially. For a distance-two or -three code, the logical error rate drops roughly as $c \, p^2$, where $p$ parameterizes the physical error rate and $c$ is a combinatorial factor. Even for~$p$ below the break-even point, or ``pseudo-threshold," the logical error rate drops relatively slowly. Larger quantum computers allow larger codes, which offer higher rates and higher distances, but they can need many more physical qubits to get started. The higher-rate codes usually require qubit connectivity beyond what some platforms, and in particular superconducting qubits, offer. Furthermore, it can be complex and inefficient to fault-tolerantly compute on individual logical qubits within a code with multiple logical qubits. This is all only a quick sketch of a rich field of study. We hope to get across some of the tradeoffs involved in designing or choosing a fault-tolerance scheme. For now, the tesseract subsystem code is in a sweet spot for ion trap hardware with good connectivity among many qubits and low error rates. As the hardware evolves, surely so will our fault-tolerance schemes. - - -\section*{Acknowledgments} - -%This work required the help of a whole team at Microsoft Azure Quantum. B.R.\ would particularly like to thank Andres Paz, Adam Paetznick, Rui Chao, Wim van Dam, David Aasen, Zhenghan Wang and Krysta Svore for assistance and feedback. -We would like to thank Dennis Tom and Jenni Strabley for general feedback and support of the collaborative work. -We also thank the Quantinuum hardware team for enabling these experiments. - - -\ifx\compilefullpaper\undefined -\bibliographystyle{halpha-abbrv} -\bibliography{q} - -\end{document} -\fi \ No newline at end of file diff --git a/plots/acceptance_rates_ec_noise.png b/plots/acceptance_rates_ec_noise.png deleted file mode 100644 index 13ddee7..0000000 Binary files a/plots/acceptance_rates_ec_noise.png and /dev/null differ diff --git a/plots/acceptance_rates_ec_noise_ec_experiment.png b/plots/acceptance_rates_ec_noise_ec_experiment.png new file mode 100644 index 0000000..1867533 Binary files /dev/null and b/plots/acceptance_rates_ec_noise_ec_experiment.png differ diff --git a/plots/acceptance_rates_ec_noise_exp2.png b/plots/acceptance_rates_ec_noise_exp2.png deleted file mode 100644 index 3223910..0000000 Binary files a/plots/acceptance_rates_ec_noise_exp2.png and /dev/null differ diff --git a/plots/acceptance_rates_ec_noise_experiment1.png b/plots/acceptance_rates_ec_noise_experiment1.png deleted file mode 100644 index 13ddee7..0000000 Binary files a/plots/acceptance_rates_ec_noise_experiment1.png and /dev/null differ diff --git a/plots/logical_rates_ec_noise_exp2_no_pauli_correction.png b/plots/logical_rates_ec_noise_exp2_no_pauli_correction.png deleted file mode 100644 index a57ed3d..0000000 Binary files a/plots/logical_rates_ec_noise_exp2_no_pauli_correction.png and /dev/null differ diff --git a/plots/logical_rates_ec_noise_exp2_with_correction.png b/plots/logical_rates_ec_noise_exp2_with_correction.png deleted file mode 100644 index 500b853..0000000 Binary files a/plots/logical_rates_ec_noise_exp2_with_correction.png and /dev/null differ diff --git a/tesseract_sim/circuit_base.py b/tesseract_sim/circuit_base.py deleted file mode 100644 index 6bab800..0000000 --- a/tesseract_sim/circuit_base.py +++ /dev/null @@ -1,139 +0,0 @@ -import stim - -# general circuit helper functions: -def append_stabilizer(circuit, pauli_type, targets): - """Append a multi-qubit Pauli product measurement to the circuit.""" - pauli_targets = [] - for t in targets: - if pauli_type == 'X': - pauli_targets.append(stim.target_x(t)) - elif pauli_type == 'Z': - pauli_targets.append(stim.target_z(t)) - # Add a combiner for each qubit except the last one - pauli_targets.append(stim.target_combiner()) - - # Remove the last combiner (no combiner after the final target) - pauli_targets.pop() - - # Append the MPP operation with the constructed targets - circuit.append("MPP", pauli_targets) - circuit.append("TICK") - - -def append_detector(circuit, index1, index2): - """Append a DETECTOR instruction with two recorded targets.""" - circuit.append("DETECTOR", [ - stim.target_rec(index1), - stim.target_rec(index2) - ]) - - -# Tesseract code circuit parts: - -#Code stabilizer definitions: -# X-type stabilizers -x_stabilizers = [ - [0, 1, 2, 3, 4, 5, 6, 7], - [4, 5, 6, 7, 8, 9, 10, 11], - [8, 9, 10, 11, 12, 13, 14, 15], - [0, 1, 4, 5, 8, 9, 12, 13], - [1, 2, 5, 6, 9, 10, 13, 14], -] - -# Z-type stabilizers -z_stabilizers = [ - [0, 1, 2, 3, 4, 5, 6, 7], - [4, 5, 6, 7, 8, 9, 10, 11], - [8, 9, 10, 11, 12, 13, 14, 15], - [0, 1, 4, 5, 8, 9, 12, 13], - [1, 2, 5, 6, 9, 10, 13, 14], -] - -#Tesseract code circuit creation functions: - -## Trying to create circuit from tableau based on stabilizer generators -# In order to encode some codeword in the codespace, I've used this reference: -# https://quantumcomputing.stackexchange.com/questions/32437/a-simple-way-of-encoding-qubit-in-stabilizer-codes-with-stim -def init_circuit(qubits=16, ancillas=0): - circuit = stim.Circuit() - # Adding qubit coordinates metadata for qubits - for qubit in range(qubits): - x = qubit % 4 - y = qubit // 4 - circuit.append_operation("QUBIT_COORDS", [qubit], [x, y]) - - # Adding qubit coordinates metadata for ancillas - for ancilla in range(ancillas): - circuit.append_operation("QUBIT_COORDS", [qubits + ancilla], [5, ancilla]) - - circuit.append_operation("TICK") - return circuit - - -def encode(circuit): - # Encoding step: Measuring X and Z stabilizers: - - # Append X-type stabilizers - for targets in x_stabilizers: - append_stabilizer(circuit, 'X', targets) - - # Append Z-type stabilizers - for targets in z_stabilizers: - append_stabilizer(circuit, 'Z', targets) - - # print("Encoding complete.") - - -def channel(circuit, error_rate, noisy_qubits=list(range(16)), noise_type="X_ERROR"): - # Noise channel: Adding errors with some probability - circuit.append("TICK") - circuit.append(noise_type, noisy_qubits, error_rate) - circuit.append("TICK") - # print("Noise channel applied.") - -# def channel_erasure_errors(circuit, error_rate, noisy_qubits=list(range(16))): -# # Noise channel: Adding X errors with probability 0 (for testing, can be adjusted) -# circuit.append("TICK") -# # TODO how? -# # Reference for erasure errors in Stim: https://quantumcomputing.stackexchange.com/questions/26582/how-do-i-perform-an-erasure-error-in-stim/33887#33887 -# circuit.append("DEPOLARIZE1", noisy_qubits, 0.75) # 10% error probability for demonstration -# circuit.append("HERALDED_ERASE", noisy_qubits, 0.75) # 10% error probability for demonstration -# circuit.append("TICK") -# # print("Noise channel applied.") - - -def error_correction(circuit): - # Error correction: Measuring the stabilizers again - - # Append X-type stabilizers - for targets in x_stabilizers: - append_stabilizer(circuit, 'X', targets) - - # Append Z-type stabilizers - for targets in z_stabilizers: - append_stabilizer(circuit, 'Z', targets) - - # Adding detectors for error syndrome extraction - # Append DETECTOR instructions to matching measurements - for i in range(10): - append_detector(circuit, index1=-(10 - i), index2=-(20 - i)) - - # Logical observables and corresponding observable indices - logical_observables = [ - (['Z', [0, 4, 8, 12]], 0), - (['Z', [0, 1, 2, 3]], 1), - (['Z', [0, 1, 4, 5]], 2), - (['Z', [5, 6, 9, 10]], 3), - (['Z', [4, 5, 8, 9]], 4), - (['Z', [1, 2, 5, 6]], 5), - ] - - # Append logical observables - for (pauli_type, targets), observable_index in logical_observables: - # Append the stabilizer measurement - append_stabilizer(circuit, pauli_type, targets) - - # Inline the OBSERVABLE_INCLUDE operation - circuit.append("OBSERVABLE_INCLUDE", [stim.target_rec(-1)], observable_index) - - # print("Error correction complete.") diff --git a/tesseract_sim/common/__init__.py b/tesseract_sim/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tesseract_sim/common/circuit_base.py b/tesseract_sim/common/circuit_base.py new file mode 100644 index 0000000..8d74958 --- /dev/null +++ b/tesseract_sim/common/circuit_base.py @@ -0,0 +1,56 @@ +import stim + +# general circuit helper functions: +def append_stabilizer(circuit, pauli_type, targets): + """Append a multi-qubit Pauli product measurement to the circuit.""" + pauli_targets = [] + for t in targets: + if pauli_type == 'X': + pauli_targets.append(stim.target_x(t)) + elif pauli_type == 'Z': + pauli_targets.append(stim.target_z(t)) + # Add a combiner for each qubit except the last one + pauli_targets.append(stim.target_combiner()) + + # Remove the last combiner (no combiner after the final target) + pauli_targets.pop() + + # Append the MPP operation with the constructed targets + circuit.append("MPP", pauli_targets) + circuit.append("TICK") + + +def append_detector(circuit, index1, index2): + """Append a DETECTOR instruction with two recorded targets.""" + circuit.append("DETECTOR", [ + stim.target_rec(index1), + stim.target_rec(index2) + ]) + +#Tesseract code circuit creation functions: + +## Trying to create circuit from tableau based on stabilizer generators +# In order to encode some codeword in the codespace, I've used this reference: +# https://quantumcomputing.stackexchange.com/questions/32437/a-simple-way-of-encoding-qubit-in-stabilizer-codes-with-stim +def init_circuit(qubits=16, ancillas=0): + circuit = stim.Circuit() + # Adding qubit coordinates metadata for qubits + for qubit in range(qubits): + x = qubit % 4 + y = qubit // 4 + circuit.append_operation("QUBIT_COORDS", [qubit], [x, y]) + + # Adding qubit coordinates metadata for ancillas + for ancilla in range(ancillas): + circuit.append_operation("QUBIT_COORDS", [qubits + ancilla], [5, ancilla]) + + circuit.append_operation("TICK") + return circuit + + +def channel(circuit, error_rate, noisy_qubits=list(range(16)), noise_type="X_ERROR"): + # Noise channel: Adding errors with some probability + circuit.append("TICK") + circuit.append(noise_type, noisy_qubits, error_rate) + circuit.append("TICK") + diff --git a/tesseract_sim/common/code_commons.py b/tesseract_sim/common/code_commons.py new file mode 100644 index 0000000..d82126c --- /dev/null +++ b/tesseract_sim/common/code_commons.py @@ -0,0 +1,90 @@ +import stim +from typing_extensions import deprecated + +from tesseract_sim.common.circuit_base import append_stabilizer, append_detector + +measurement_operators_rows = [ + [0,1,2,3], + [4,5,6,7], + [8,9,10,11], + [12,13,14,15] +] +measurement_operators_columns = [ + [0,4,8,12], + [1,5,9,13], + [2,6,10,14], + [3,7,11,15] +] + +# Tesseract code circuit parts: + +#Code stabilizer definitions: +# X-type stabilizers +x_stabilizers = [ + [0, 1, 2, 3, 4, 5, 6, 7], + [4, 5, 6, 7, 8, 9, 10, 11], + [8, 9, 10, 11, 12, 13, 14, 15], + [0, 1, 4, 5, 8, 9, 12, 13], + [1, 2, 5, 6, 9, 10, 13, 14], +] + +# Z-type stabilizers +z_stabilizers = [ + [0, 1, 2, 3, 4, 5, 6, 7], + [4, 5, 6, 7, 8, 9, 10, 11], + [8, 9, 10, 11, 12, 13, 14, 15], + [0, 1, 4, 5, 8, 9, 12, 13], + [1, 2, 5, 6, 9, 10, 13, 14], +] + +@deprecated("Use error_correct_manual instead") +def error_correction(circuit): + # Error correction: Measuring the stabilizers again + + # Append X-type stabilizers + for targets in x_stabilizers: + append_stabilizer(circuit, 'X', targets) + + # Append Z-type stabilizers + for targets in z_stabilizers: + append_stabilizer(circuit, 'Z', targets) + + # Adding detectors for error syndrome extraction + # Append DETECTOR instructions to matching measurements + for i in range(10): + append_detector(circuit, index1=-(10 - i), index2=-(20 - i)) + + # Logical observables and corresponding observable indices + logical_observables = [ + (['Z', [0, 4, 8, 12]], 0), + (['Z', [0, 1, 2, 3]], 1), + (['Z', [0, 1, 4, 5]], 2), + (['Z', [5, 6, 9, 10]], 3), + (['Z', [4, 5, 8, 9]], 4), + (['Z', [1, 2, 5, 6]], 5), + ] + + # Append logical observables + for (pauli_type, targets), observable_index in logical_observables: + # Append the stabilizer measurement + append_stabilizer(circuit, pauli_type, targets) + + # Inline the OBSERVABLE_INCLUDE operation + circuit.append("OBSERVABLE_INCLUDE", [stim.target_rec(-1)], observable_index) + + # print("Error correction complete.") + + +@deprecated("Use encode_manual_fig9a or encode_manual_fig9b instead") +def encode_deprecated(circuit): + # Encoding step: Measuring X and Z stabilizers: + + # Append X-type stabilizers + for targets in x_stabilizers: + append_stabilizer(circuit, 'X', targets) + + # Append Z-type stabilizers + for targets in z_stabilizers: + append_stabilizer(circuit, 'Z', targets) + + # print("Encoding complete.") diff --git a/tesseract_sim/commons.py b/tesseract_sim/commons.py deleted file mode 100644 index 8e06f08..0000000 --- a/tesseract_sim/commons.py +++ /dev/null @@ -1,12 +0,0 @@ -measurement_operators_rows = [ - [0,1,2,3], - [4,5,6,7], - [8,9,10,11], - [12,13,14,15] -] -measurement_operators_columns = [ - [0,4,8,12], - [1,5,9,13], - [2,6,10,14], - [3,7,11,15] -] diff --git a/tesseract_sim/encoding/__init__.py b/tesseract_sim/encoding/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tesseract_sim/encoding_manual_9a.py b/tesseract_sim/encoding/encoding_manual_9a.py similarity index 95% rename from tesseract_sim/encoding_manual_9a.py rename to tesseract_sim/encoding/encoding_manual_9a.py index d527f2c..fcab8a0 100644 --- a/tesseract_sim/encoding_manual_9a.py +++ b/tesseract_sim/encoding/encoding_manual_9a.py @@ -1,5 +1,5 @@ -from .noise_utils import append_1q, append_2q -from .noise_cfg import NoiseCfg, NO_NOISE +from tesseract_sim.noise.noise_utils import append_1q, append_2q +from tesseract_sim.noise.noise_cfg import NoiseCfg, NO_NOISE # This file contains the functions for encoding the state |++0000> as described in Fig. 9a of that paper diff --git a/tesseract_sim/encoding_manual_9b.py b/tesseract_sim/encoding/encoding_manual_9b.py similarity index 97% rename from tesseract_sim/encoding_manual_9b.py rename to tesseract_sim/encoding/encoding_manual_9b.py index 547fe3f..1d8e172 100644 --- a/tesseract_sim/encoding_manual_9b.py +++ b/tesseract_sim/encoding/encoding_manual_9b.py @@ -1,5 +1,5 @@ -from tesseract_sim.noise_cfg import NoiseCfg, NO_NOISE -from tesseract_sim.noise_utils import append_1q, append_2q +from tesseract_sim.noise.noise_cfg import NoiseCfg, NO_NOISE +from tesseract_sim.noise.noise_utils import append_1q, append_2q def encode_000_in_8_3_2_color_code(circuit, participating_qubits: list[int], ancillas: list[int], cfg: NoiseCfg = NO_NOISE): diff --git a/tesseract_sim/error_correction/__init__.py b/tesseract_sim/error_correction/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tesseract_sim/correction_rules.py b/tesseract_sim/error_correction/correction_rules.py similarity index 100% rename from tesseract_sim/correction_rules.py rename to tesseract_sim/error_correction/correction_rules.py diff --git a/tesseract_sim/decoder_manual.py b/tesseract_sim/error_correction/decoder_manual.py similarity index 98% rename from tesseract_sim/decoder_manual.py rename to tesseract_sim/error_correction/decoder_manual.py index a79dbfc..f35fe07 100644 --- a/tesseract_sim/decoder_manual.py +++ b/tesseract_sim/error_correction/decoder_manual.py @@ -1,6 +1,6 @@ import stim import numpy as np -from .correction_rules import correct_row_Z, correct_row_X, correct_column_Z, correct_column_X +from tesseract_sim.error_correction.correction_rules import correct_row_Z, correct_row_X, correct_column_Z, correct_column_X def append_detector_on_last_n_measurements(circuit, num_measurements=4): circuit.append("DETECTOR", [ diff --git a/tesseract_sim/measurement_rounds.py b/tesseract_sim/error_correction/measurement_rounds.py similarity index 95% rename from tesseract_sim/measurement_rounds.py rename to tesseract_sim/error_correction/measurement_rounds.py index fd42c66..28b6e0d 100644 --- a/tesseract_sim/measurement_rounds.py +++ b/tesseract_sim/error_correction/measurement_rounds.py @@ -1,7 +1,6 @@ -import stim -from .commons import measurement_operators_rows, measurement_operators_columns -from .noise_utils import append_1q, append_2q -from .noise_cfg import NoiseCfg, NO_NOISE +from tesseract_sim.common.code_commons import measurement_operators_rows, measurement_operators_columns +from tesseract_sim.noise.noise_utils import append_1q, append_2q +from tesseract_sim.noise.noise_cfg import NoiseCfg, NO_NOISE def get_qubits_and_ancillas(): diff --git a/tesseract_sim/noise/__init__.py b/tesseract_sim/noise/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tesseract_sim/noise_cfg.py b/tesseract_sim/noise/noise_cfg.py similarity index 100% rename from tesseract_sim/noise_cfg.py rename to tesseract_sim/noise/noise_cfg.py diff --git a/tesseract_sim/noise_utils.py b/tesseract_sim/noise/noise_utils.py similarity index 100% rename from tesseract_sim/noise_utils.py rename to tesseract_sim/noise/noise_utils.py diff --git a/tesseract_sim/plotting/__init__.py b/tesseract_sim/plotting/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/plotting/plot_acceptance_rates.py b/tesseract_sim/plotting/plot_acceptance_rates.py similarity index 95% rename from plotting/plot_acceptance_rates.py rename to tesseract_sim/plotting/plot_acceptance_rates.py index 224e019..79292b3 100644 --- a/plotting/plot_acceptance_rates.py +++ b/tesseract_sim/plotting/plot_acceptance_rates.py @@ -1,9 +1,9 @@ import numpy as np import matplotlib.pyplot as plt from tesseract_sim.run import run_simulation_ec_experiment -from tesseract_sim.noise_cfg import NoiseCfg +from tesseract_sim.noise.noise_cfg import NoiseCfg import os -from typing import Callable, Dict, List, Any, TypeVar, Tuple, Literal +from typing import Callable, Dict, List, TypeVar, Tuple, Literal import argparse T = TypeVar('T') # Type of experiment result @@ -133,7 +133,7 @@ def main(): help='Output directory for plots') parser.add_argument('--apply_pauli_frame', type=bool, default=False, help='Perform final correction - apply the measured Pauli frame. The error correction rounds and measurements (besides the actual correction at the end) happen regardless, based on the number of rounds.') parser.add_argument('--encoding-mode', type=str, choices=['9a', '9b'], default='9a', help='Encoding mode') - parser.add_argument('--sweep-channel-noise', action='store_true', help='Sweep channel noise instead of EC noise') + parser.add_argument('--sweep-channel-noise', action='store_true', help='Sweep channel noise instead of EC noise. Channel noise acts once after encoding and before the error correction rounds.') args = parser.parse_args() # Combine detailed lower rounds with higher rounds diff --git a/tesseract_sim/run.py b/tesseract_sim/run.py index 74a38ed..fef6841 100644 --- a/tesseract_sim/run.py +++ b/tesseract_sim/run.py @@ -1,12 +1,12 @@ import argparse -from tesseract_sim.encoding_manual_9b import encode_manual_fig9b -from .circuit_base import init_circuit, channel -from .encoding_manual_9a import encode_manual_fig9a +from tesseract_sim.encoding.encoding_manual_9b import encode_manual_fig9b +from tesseract_sim.common.circuit_base import init_circuit, channel +from tesseract_sim.encoding.encoding_manual_9a import encode_manual_fig9a from typing import Literal -from .measurement_rounds import error_correct_manual, measure_logical_operators_tesseract -from .decoder_manual import run_manual_error_correction -from .noise_cfg import NoiseCfg, NO_NOISE +from tesseract_sim.error_correction.measurement_rounds import error_correct_manual, measure_logical_operators_tesseract +from tesseract_sim.error_correction.decoder_manual import run_manual_error_correction +from tesseract_sim.noise.noise_cfg import NoiseCfg, NO_NOISE def build_circuit_ec_experiment(rounds: int, cfg: NoiseCfg = NO_NOISE, encoding_mode: Literal['9a', '9b'] = '9b'): @@ -60,7 +60,7 @@ def run_simulation_ec_experiment(rounds: int, shots: int, cfg: NoiseCfg = NO_NOI parser.add_argument("--channel-noise-type", type=str, default="DEPOLARIZE1", help="Channel noise type (e.g., DEPOLARIZE1, X_ERROR, Z_ERROR).") parser.add_argument("--experiment", type=int, choices=[1], default=1, help="Which experiment to run (only 1 available)") parser.add_argument("--no-apply-pauli-frame", action="store_false", dest="apply_pauli_frame", help="Disable Pauli frame corrections during logical verification") - parser.add_argument("--encoding-mode", type=str, choices=['9a', '9b'], default='9b', help="Encoding mode") + parser.add_argument("--encoding-mode", type=str, choices=['9a', '9b'], default='9b', help="Encoding mode, based on Fig 9a or 9b in the paper") args = parser.parse_args() diff --git a/tests/test_9a_encoding.py b/tests/encoding/test_9a_encoding.py similarity index 95% rename from tests/test_9a_encoding.py rename to tests/encoding/test_9a_encoding.py index e40f2bc..9ef4d81 100644 --- a/tests/test_9a_encoding.py +++ b/tests/encoding/test_9a_encoding.py @@ -1,8 +1,6 @@ -import stim -import numpy as np from tesseract_sim.run import build_circuit_ec_experiment -from tesseract_sim.decoder_manual import run_manual_error_correction -from tesseract_sim.noise_cfg import NO_NOISE +from tesseract_sim.error_correction.decoder_manual import run_manual_error_correction +from tesseract_sim.noise.noise_cfg import NO_NOISE def test_9a_encoding_no_noise_perfect_state(): """Test that 9a encoding (|++0000>) with only Z checks gives perfect results with no noise.""" diff --git a/tests/test_encoding_manual.py b/tests/encoding/test_encoding_manual.py similarity index 88% rename from tests/test_encoding_manual.py rename to tests/encoding/test_encoding_manual.py index 3f666ca..0ae5ffe 100644 --- a/tests/test_encoding_manual.py +++ b/tests/encoding/test_encoding_manual.py @@ -1,7 +1,7 @@ import stim -from tesseract_sim.encoding_manual_9a import encode_manual_fig9a -from tesseract_sim.commons import measurement_operators_rows, measurement_operators_columns -from tesseract_sim.measurement_rounds import measure_x_z_stabilizer +from tesseract_sim.encoding.encoding_manual_9a import encode_manual_fig9a +from tesseract_sim.common.code_commons import measurement_operators_rows, measurement_operators_columns +from tesseract_sim.error_correction.measurement_rounds import measure_x_z_stabilizer import pytest # This tests is not working currently. we are going to try and implement the |+0+0+0> encoder from fig 9b anyway. @@ -40,7 +40,7 @@ def test_encoded_state_is_stabilizer_eigenstate_9a(): def test_encode_manual_fig9b(): """Test that encode_manual_fig9b correctly encodes the |+0+0+0> state in both blocks.""" import stim - from tesseract_sim.encoding_manual_9b import encode_manual_fig9b + from tesseract_sim.encoding.encoding_manual_9b import encode_manual_fig9b # Create circuit circuit = stim.Circuit() diff --git a/tests/test_noise_injection.py b/tests/noise/test_noise_injection.py similarity index 93% rename from tests/test_noise_injection.py rename to tests/noise/test_noise_injection.py index 9957677..611bd03 100644 --- a/tests/test_noise_injection.py +++ b/tests/noise/test_noise_injection.py @@ -1,9 +1,6 @@ -import pytest import stim -from tesseract_sim.noise_cfg import NoiseCfg, NO_NOISE -from tesseract_sim.noise_utils import append_op, append_1q, append_2q -from tesseract_sim.encoding_manual_9a import encode_manual_fig9a -from tesseract_sim.measurement_rounds import error_correct_manual +from tesseract_sim.noise.noise_cfg import NoiseCfg, NO_NOISE +from tesseract_sim.noise.noise_utils import append_1q, append_2q from tesseract_sim.run import build_circuit_ec_experiment def count_noise_ops(circuit: stim.Circuit, op_type: str) -> int: diff --git a/tests/test_rejects_some_noise.py b/tests/noise/test_rejects_some_noise.py similarity index 93% rename from tests/test_rejects_some_noise.py rename to tests/noise/test_rejects_some_noise.py index fc13e3f..7eae491 100644 --- a/tests/test_rejects_some_noise.py +++ b/tests/noise/test_rejects_some_noise.py @@ -1,5 +1,5 @@ from tesseract_sim import run_simulation_ec_experiment -from tesseract_sim.noise_cfg import NoiseCfg +from tesseract_sim.noise.noise_cfg import NoiseCfg def test_rejects_some_noise(): """ diff --git a/tests/test_ec_experiment_no_noise.py b/tests/test_ec_experiment_no_noise.py index 3d16852..af058c5 100644 --- a/tests/test_ec_experiment_no_noise.py +++ b/tests/test_ec_experiment_no_noise.py @@ -1,5 +1,5 @@ from tesseract_sim.run import run_simulation_ec_experiment -from tesseract_sim.noise_cfg import NO_NOISE +from tesseract_sim.noise.noise_cfg import NO_NOISE def test_ec_experiment_no_noise_accepts_all(): """ diff --git a/tests/test_ec_experiment_random_noise.py b/tests/test_ec_experiment_random_noise.py index 3649f3b..4f6d44f 100644 --- a/tests/test_ec_experiment_random_noise.py +++ b/tests/test_ec_experiment_random_noise.py @@ -1,5 +1,5 @@ from tesseract_sim.run import run_simulation_ec_experiment -from tesseract_sim.noise_cfg import NoiseCfg +from tesseract_sim.noise.noise_cfg import NoiseCfg def test_ec_experiment_noise_rejects_some(): """ diff --git a/tests/test_no_noise_accepts_all.py b/tests/test_no_noise_accepts_all.py index 355f850..a879f74 100644 --- a/tests/test_no_noise_accepts_all.py +++ b/tests/test_no_noise_accepts_all.py @@ -1,5 +1,5 @@ from tesseract_sim import run_simulation_ec_experiment -from tesseract_sim.noise_cfg import NO_NOISE +from tesseract_sim.noise.noise_cfg import NO_NOISE def test_no_noise_accepts_all(): """ diff --git a/tests/test_pauli_frame_correction.py b/tests/test_pauli_frame_correction.py index a358790..26b5cd2 100644 --- a/tests/test_pauli_frame_correction.py +++ b/tests/test_pauli_frame_correction.py @@ -1,9 +1,7 @@ -import stim -import numpy as np import pytest from tesseract_sim.run import build_circuit_ec_experiment -from tesseract_sim.decoder_manual import run_manual_error_correction -from tesseract_sim.noise_cfg import NO_NOISE +from tesseract_sim.error_correction.decoder_manual import run_manual_error_correction +from tesseract_sim.noise.noise_cfg import NO_NOISE # TODO these tests are not working correctly for 9b encoding/measurement. need to fix that with 9b and without the only_z_checks flag. diff --git a/tests/test_plot_logical_normalization.py b/tests/test_plot_logical_normalization.py deleted file mode 100644 index c25857f..0000000 --- a/tests/test_plot_logical_normalization.py +++ /dev/null @@ -1,138 +0,0 @@ -""" -Test that logical success rates are properly normalized by acceptance rates. - -This test ensures that the plotting code correctly computes conditional probabilities -P(logical success | accepted) rather than raw P(logical success). -""" - -import pytest -import numpy as np -from typing import Dict, List, Tuple - - -def compute_logical_data_from_raw_results(raw_results: Dict[float, List[Tuple[int, int, int]]]) -> Dict[float, List[float]]: - """ - Extract the logical data computation logic from plot_acceptance_rates.py - for testing purposes. - - Args: - raw_results: Dict mapping noise levels to lists of (accepted, logical_pass, total_shots) tuples - - Returns: - Dict mapping noise levels to lists of conditional logical success rates - """ - logical_data = { - noise: [ - t[1]/t[0] if t[0] > 0 else 0.0 # logical_pass/accepted (conditional probability) - for t in tuples - ] - for noise, tuples in raw_results.items() - } - return logical_data - - -def test_logical_normalization_basic(): - """ - Test basic logical normalization with simple numbers. - - Story: Given 10 shots with 4 accepted and 3 logical successes, - the conditional logical success rate should be 3/4 = 0.75 - """ - # Arrange: (accepted, logical_pass, total_shots) - raw_results = { - 0.01: [(4, 3, 10)] # 4 accepted, 3 logical passes out of 10 total shots - } - - # Act - logical_data = compute_logical_data_from_raw_results(raw_results) - - # Assert - expected_rate = 3.0 / 4.0 # 3 logical successes out of 4 accepted - assert logical_data[0.01][0] == pytest.approx(expected_rate, rel=1e-6) - - -def test_logical_normalization_zero_acceptance(): - """ - Test that zero acceptance is handled gracefully. - - Story: When no shots are accepted, the conditional logical success rate - should be 0.0 to avoid division by zero. - """ - # Arrange: (accepted, logical_pass, total_shots) - raw_results = { - 0.01: [(0, 0, 10)] # 0 accepted, 0 logical passes out of 10 total shots - } - - # Act - logical_data = compute_logical_data_from_raw_results(raw_results) - - # Assert - assert logical_data[0.01][0] == 0.0 - - -def test_logical_normalization_perfect_acceptance(): - """ - Test normalization when all shots are accepted. - - Story: When all 10 shots are accepted and 8 pass logical checks, - the conditional rate should be 8/10 = 0.8 - """ - # Arrange: (accepted, logical_pass, total_shots) - raw_results = { - 0.001: [(10, 8, 10)] # 10 accepted, 8 logical passes out of 10 total shots - } - - # Act - logical_data = compute_logical_data_from_raw_results(raw_results) - - # Assert - expected_rate = 8.0 / 10.0 - assert logical_data[0.001][0] == pytest.approx(expected_rate, rel=1e-6) - - -def test_logical_normalization_multiple_rounds(): - """ - Test normalization across multiple rounds with different acceptance rates. - - Story: Across different rounds, each should have its own conditional - logical success rate computed independently. - """ - # Arrange: Multiple rounds with different acceptance/logical patterns - raw_results = { - 0.005: [ - (5, 4, 10), # Round 1: 4/5 = 0.8 conditional rate - (8, 6, 10), # Round 2: 6/8 = 0.75 conditional rate - (2, 1, 10) # Round 3: 1/2 = 0.5 conditional rate - ] - } - - # Act - logical_data = compute_logical_data_from_raw_results(raw_results) - - # Assert - expected_rates = [4.0/5.0, 6.0/8.0, 1.0/2.0] - for i, expected in enumerate(expected_rates): - assert logical_data[0.005][i] == pytest.approx(expected, rel=1e-6) - - -def test_logical_normalization_vs_raw_normalization(): - """ - Test that conditional normalization gives different results than raw normalization. - - Story: To demonstrate the fix, show that P(logical | accepted) != P(logical) - when acceptance rate < 1.0 - """ - # Arrange: (accepted, logical_pass, total_shots) - raw_results = { - 0.01: [(6, 3, 10)] # 6 accepted, 3 logical passes out of 10 total shots - } - - # Act - logical_data_conditional = compute_logical_data_from_raw_results(raw_results) - logical_data_raw = 3.0 / 10.0 # Old way: logical_pass / total_shots - - # Assert - conditional_rate = logical_data_conditional[0.01][0] - assert conditional_rate == pytest.approx(3.0/6.0, rel=1e-6) # 0.5 - assert conditional_rate != logical_data_raw # 0.5 != 0.3 - assert conditional_rate > logical_data_raw # Conditional rate should be higher