ABSTRACT (original article): Many current quantum error-correcting codes that achieve full fault tolerance suffer from having low ratios of logical to physical qubits and significant overhead. This makes them difficult to implement on current noisy intermediate-scale quantum (NISQ) computers and results in the inability to perform quantum algorithms at useful scales with near-term quantum processors. As a result, calculations are generally done without encoding. We propose a middle ground between these two approaches: constructions in the
[[n,n-2,2]]
quantum error-detecting code that can detect any error from a single faulty gate by measuring the stabilizer generators of the code and additional ancillas at the end of the computation. This achieves weak fault tolerance. As we show, this yields a significant improvement over no error correction for small computations with low enough physical error probabilities and requires much less overhead than codes that achieve full fault tolerance. We give constructions for a set of gates that achieve universal quantum computation in this error-detecting code, while satisfying weak fault tolerance up to analog imprecision on the physical rotation gate. CITATION (original article): Christopher Gerhard, Todd A. Brun (2024), Weakly Fault-Tolerant Computation in a Quantum Error-Detecting Code, arXiv:2408.14828. https://doi.org/10.48550/arXiv.2408.14828
The goal of this Mathematica notebook is to search for circuits involving 2 qubits of an [[n,n-2,2]] QEDC (Quantum Error Detecting Code) and a 2-qubit ancilla in the state Phi+ to produce a desired 2-qubit gate on the data qubits that keeps the system within the code space and is fault-tolerant, in the sense that any single faulty gate in the circuit cannot produce an undetectable error (undetectable by measuring both the ancilla and the stabilizers of the QEDC). We will show that we can also search with different ancilla states and demonstrate that a weakly fault-tolerant circuit does exist, which are shown as diagrams in the accompanying paper.
​
The basic gate set are 2-qubit gates of the form (1+ig)/sqrt(2), where g is a Pauli operator. We will search for possible circuits by recursively descending a search tree, and if we find suitable circuits we will then check if they are fault-tolerant in the sense described above.
​
To keep the math tractable, we will use the symplectic representation of the relevant operators. For a 4-qubit system, these are 8-by-8 binary matrices. (This ignores overall phases, but these can always be fixed using transversal gates.) The stabilizer generators of the Phi+ state are XXII and ZZII, and we should be left with equivalent stabilizers at the end of the circuit, and we will at least start by trying to produce the ZZ gate, which takes IIXI --> IIYZ, IIIX --> IIZY, IIZI --> IIZI, IIIZ --> IIIZ. We can also allow the ancilla to end up in a different state at the end of the circuit as long as we keep track of how it changes. For example, our weakly fault-tolerant gates allow the ancilla to switch between the Phi+ state and the |+>|+> state.
​
We start by defining a set of gates that we know is sufficient to solve this problem. Our gates are in the form of symplectic matrices. For those who are unfamiliar with the symplectic formalism, refer to section III B. of the accompanying paper Weakly Fault-Tolerant Computation in a Quantum Error-Detecting Code for a more complete description (https://arxiv.org/pdf/2408.14828). The naming convention for these gates is the type of gate followed by the the qubit indices it operates on. We will define the SWAP, XX, and ZZ gates on all possible pairs of qubits. This is because these gates form the gate set that we chose to form all other gates in our QEDC from. We will leave out the XX34 and ZZ34 gates, since these will immediately form a weight 2 error on our data qubits. This in and of itself is an undetectable error if it is left unchanged. We will allow a two qubit SWAP gate on our data qubits, since we will assume this gate can be implemented by relabeling the qubits and hence is error free. It is important to note that this is not true in architectures where gate operations are limited by the distance between qubits. It is important to note that one can search with many more gate sets to find quantum circuits.
In[]:=
MatrixForm[swap34={{1,0,0,0,0,0,0,0},{0,1,0,0,0,0,0,0},{0,0,0,1,0,0,0,0},{0,0,1,0,0,0,0,0},{0,0,0,0,1,0,0,0},{0,0,0,0,0,1,0,0},{0,0,0,0,0,0,0,1},{0,0,0,0,0,0,1,0}}]
Out[]//MatrixForm=
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
1
0
In[]:=
MatrixForm[zz12={{1,0,0,0,1,1,0,0},{0,1,0,0,1,1,0,0},{0,0,1,0,0,0,0,0},{0,0,0,1,0,0,0,0},{0,0,0,0,1,0,0,0},{0,0,0,0,0,1,0,0},{0,0,0,0,0,0,1,0},{0,0,0,0,0,0,0,1}}]
Out[]//MatrixForm=
1
0
0
0
1
1
0
0
0
1
0
0
1
1
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
In[]:=
MatrixForm[zz13={{1,0,0,0,1,0,1,0},{0,1,0,0,0,0,0,0},{0,0,1,0,1,0,1,0},{0,0,0,1,0,0,0,0},{0,0,0,0,1,0,0,0},{0,0,0,0,0,1,0,0},{0,0,0,0,0,0,1,0},{0,0,0,0,0,0,0,1}}]
Out[]//MatrixForm=
1
0
0
0
1
0
1
0
0
1
0
0
0
0
0
0
0
0
1
0
1
0
1
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
In[]:=
MatrixForm[zz14={{1,0,0,0,1,0,0,1},{0,1,0,0,0,0,0,0},{0,0,1,0,0,0,0,0},{0,0,0,1,1,0,0,1},{0,0,0,0,1,0,0,0},{0,0,0,0,0,1,0,0},{0,0,0,0,0,0,1,0},{0,0,0,0,0,0,0,1}}]
Out[]//MatrixForm=
1
0
0
0
1
0
0
1
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
1
0
0
1
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
In[]:=
MatrixForm[zz23={{1,0,0,0,0,0,0,0},{0,1,0,0,0,1,1,0},{0,0,1,0,0,1,1,0},{0,0,0,1,0,0,0,0},{0,0,0,0,1,0,0,0},{0,0,0,0,0,1,0,0},{0,0,0,0,0,0,1,0},{0,0,0,0,0,0,0,1}}]
Out[]//MatrixForm=
1
0
0
0
0
0
0
0
0
1
0
0
0
1
1
0
0
0
1
0
0
1
1
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
In[]:=
MatrixForm[zz24={{1,0,0,0,0,0,0,0},{0,1,0,0,0,1,0,1},{0,0,1,0,0,0,0,0},{0,0,0,1,0,1,0,1},{0,0,0,0,1,0,0,0},{0,0,0,0,0,1,0,0},{0,0,0,0,0,0,1,0},{0,0,0,0,0,0,0,1}}]
Out[]//MatrixForm=
1
0
0
0
0
0
0
0
0
1
0
0
0
1
0
1
0
0
1
0
0
0
0
0
0
0
0
1
0
1
0
1
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
In[]:=
MatrixForm[xx12={{1,0,0,0,0,0,0,0},{0,1,0,0,0,0,0,0},{0,0,1,0,0,0,0,0},{0,0,0,1,0,0,0,0},{1,1,0,0,1,0,0,0},{1,1,0,0,0,1,0,0},{0,0,0,0,0,0,1,0},{0,0,0,0,0,0,0,1}}]
Out[]//MatrixForm=
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
1
1
0
0
1
0
0
0
1
1
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
In[]:=
MatrixForm[xx13={{1,0,0,0,0,0,0,0},{0,1,0,0,0,0,0,0},{0,0,1,0,0,0,0,0},{0,0,0,1,0,0,0,0},{1,0,1,0,1,0,0,0},{0,0,0,0,0,1,0,0},{1,0,1,0,0,0,1,0},{0,0,0,0,0,0,0,1}}]
Out[]//MatrixForm=
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
1
0
1
0
1
0
0
0
0
0
0
0
0
1
0
0
1
0
1
0
0
0
1
0
0
0
0
0
0
0
0
1
In[]:=
MatrixForm[xx14={{1,0,0,0,0,0,0,0},{0,1,0,0,0,0,0,0},{0,0,1,0,0,0,0,0},{0,0,0,1,0,0,0,0},{1,0,0,1,1,0,0,0},{0,0,0,0,0,1,0,0},{0,0,0,0,0,0,1,0},{1,0,0,1,0,0,0,1}}]
Out[]//MatrixForm=
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
1
0
0
1
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
1
0
0
1
0
0
0
1
In[]:=
MatrixForm[xx23={{1,0,0,0,0,0,0,0},{0,1,0,0,0,0,0,0},{0,0,1,0,0,0,0,0},{0,0,0,1,0,0,0,0},{0,0,0,0,1,0,0,0},{0,1,1,0,0,1,0,0},{0,1,1,0,0,0,1,0},{0,0,0,0,0,0,0,1}}]
Out[]//MatrixForm=
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
1
1
0
0
1
0
0
0
1
1
0
0
0
1
0
0
0
0
0
0
0
0
1
In[]:=
MatrixForm[xx24={{1,0,0,0,0,0,0,0},{0,1,0,0,0,0,0,0},{0,0,1,0,0,0,0,0},{0,0,0,1,0,0,0,0},{0,0,0,0,1,0,0,0},{0,1,0,1,0,1,0,0},{0,0,0,0,0,0,1,0},{0,1,0,1,0,0,0,1}}]
Out[]//MatrixForm=
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
1
0
1
0
1
0
0
0
0
0
0
0
0
1
0
0
1
0
1
0
0
0
1
In[]:=
MatrixForm[yy12={{0,1,0,0,1,1,0,0},{1,0,0,0,1,1,0,0},{0,0,1,0,0,0,0,0},{0,0,0,1,0,0,0,0},{1,1,0,0,0,1,0,0},{1,1,0,0,1,0,0,0},{0,0,0,0,0,0,1,0},{0,0,0,0,0,0,0,1}}]
Out[]//MatrixForm=
0
1
0
0
1
1
0
0
1
0
0
0
1
1
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
1
1
0
0
0
1
0
0
1
1
0
0
1
0
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
In[]:=
MatrixForm[yy13={{0,0,1,0,1,0,1,0},{0,1,0,0,0,0,0,0},{1,0,0,0,1,0,1,0},{0,0,0,1,0,0,0,0},{1,0,1,0,0,0,1,0},{0,0,0,0,0,1,0,0},{1,0,1,0,1,0,0,0},{0,0,0,0,0,0,0,1}}]
Out[]//MatrixForm=
0
0
1
0
1
0
1
0
0
1
0
0
0
0
0
0
1
0
0
0
1
0
1
0
0
0
0
1
0
0
0
0
1
0
1
0
0
0
1
0
0
0
0
0
0
1
0
0
1
0
1
0
1
0
0
0
0
0
0
0
0
0
0
1
In[]:=
MatrixForm[yy14={{0,0,0,1,1,0,0,1},{0,1,0,0,0,0,0,0},{0,0,1,0,0,0,0,0},{1,0,0,0,1,0,0,1},{1,0,0,1,0,0,1,0},{0,0,0,0,0,1,0,0},{0,0,0,0,0,0,1,0},{1,0,0,1,1,0,0,0}}]
Out[]//MatrixForm=
0
0
0
1
1
0
0
1
0
1
0
0
0
0
0
0
0
0
1
0
0
0
0
0
1
0
0
0
1
0
0
1
1
0
0
1
0
0
1
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
1
0
1
0
0
1
1
0
0
0
We will also define a few single qubit gates, since these are used in the weakly fault-tolerant constructions we found for the XX and ZZ gates.
We now define the recursive function that we will use to search for the right circuit. It takes as its input: the starting matrix (which could be the identity if we want to specify the action on a complete set of generators) or a matrix with fewer rows (if we are only interested in certain operators); the target matrix (after the successful circuit); a list to keep track of the sequence of gates, which should start as the empty list {}; a depth, which is the maximum depth of the tree to search; and a gate list, which is the set of possible gates to allow.
From earlier analytical work, we know that the following gate set is sufficient to achieve the following form of the ZZ gate. Note that this form multiplies the qubits by elements of the stabilizer for Phi+, which is OK.
The result above is exactly the circuit that was previously found analytically to solve this problem.
​
In fact, we don’t care about what the gate does to a complete set of generators; we only really care what it does to the six operators XXII, ZZII, IIXI, IIIX, IIZI, and IIIZ.
And it still works for this limited set of operators.
While we found the solution above, it is NOT weakly fault-tolerant, in that an error on the xx12 gate can produce an undetectable error after the circuit. We can try to avoid this by only including gates between ancilla qubits and data qubits, and not between the two ancilla qubits. (Note, the larger the gate set, the longer the search time; similarly with the search depth.)
It’s a bit awkward to need to know exactly what stabilizer elements your operators might be multiplied by. Let’s create a new recursive search function that only looks for solutions modulo the stabilizer elements. We can do this by including a projection matrix that annihilates the components of the two stabilizer generators XXII and ZZII.
This is what the projection matrix looks like. (Note that it isn’t actually a projector, in the sense that P^2=P.)
Let’s test it on the solved version of the problem, only now we won’t assume that our operators are multiplied by particular stabilizer elements.
So the new search function also works. We will now define all possible single and two qubit Pauli X and Z errors that can be generated by a faulty gate in our circuit. The numbers after error refer to the qubits the error affects.
We also need to consider products of each of these individual Pauli errors (i.e., linear combinations of these rows). We can define a function that will output all these linear combinations. Undetectable errors are generated by XXII, ZZII, IIXX and IIZZ. So we can create a matrix to project out these components. Any all-zero rows, then, would be undetectable. Note that we have two different functions for computing the linear combinations of all errors. A fault of length 2 corresponds to an error matrix with two rows (single qubit gate Pauli error), while a fault of length 4 corresponds to an error matrix with 4 rows (2 qubit gate Pauli error).
We can now define a matrix to project out the undetectable components of our binary symplectic matrix.
We can also modify the search algorithm to return ALL working circuits, instead of halting after finding one. This will allow us to consider all possible circuits for a given depth, gate set, and projection matrix.
This is a long list of circuits! It would certainly take too long to go through the process of looking for undetectable errors for each of these circuits by hand. We will need to define some functions to automate the process. This function, called undetectableErrors, will apply our previously defined undetectable matrix and count the number of all zero rows. We will also allow the undetectable error projection matrix to be arbitrary so that one can test multiple ancilla states. Note that the final ancilla state at the end of the target binary symplectic matrix determines the corresponding undetectable error projection matrix.
So every circuit on the list has exactly one undetectable fault. It seems clear that constructions of this form all behave similarly.
We can also search for circuits using a different ancilla state (e.g., a product state instead of a Bell pair) and a different gate set. The Hadamard gate is equivalent to a y gate and the phase gate is equivalent to a z gate.
​

QEDC Analysis of the Four Weakly Fault-Tolerant Gate Sets

After many searches using different sets of gates and different sets of ancillas, it turned out not enforcing that the ancilla remain in the same state allowed us to discover a weakly fault-tolerant set of circuits. Lets state our 4 different sequences of gates that implement the weakly fault-tolerant XX and ZZ gates. Each gate has two versions because in order to make them weakly fault-tolerant we allow the ancilla to alternate between the Phi+ and |+>|+> state. For each we will verify that they work as intended up to a stabilizer generator on the ancilla qubits. Phi to Plus indicates that the ancilla starts in the Phi+ state and ends up in the |+>|+> state at the end of the weakly fault-tolerant circuit. Plus to Phi is the reverse of this.
So all of the above gates work as intended up to a stabilizer generator on the ancilla state we changed to. Now lets show they are weakly fault-tolerant by using the undetectableErrors function defined above.
So all of these gate sequences take a single gate error within the sequence to a detectable error by the end of the circuit. To show these circuits are weakly fault-tolerant in the sense that any single error in a quantum circuit remains detectable, we need to prove they take all possible detectable errors to detectable errors. To do this, one can define a function to output all linear combinations of errors on any of the qubits and simply exhaustively check that they take undetectable errors to undetectable errors. However, this property is immediate from the fact that these circuits by design leave the stabilizer generators of the code and ancillas unchanged, so undetectable errors are mapped to undetectable errors. This proves that the XX and ZZ gates analyzed in this Mathematica notebook are weakly fault-tolerant. Thank you for reading this and we hope that you find it useful in understanding our paper titled “Weakly Fault-Tolerant Computation in a Quantum Error-Detecting Code”.

CITE THIS NOTEBOOK

Weakly fault-tolerant computation in a quantum error-detecting code​
by Christopher Gerhard & Todd A. Brun
Wolfram Community, STAFF PICKS, October 30, 2025
​https://community.wolfram.com/groups/-/m/t/3568033