# How to implement the Circuit of Steane's code for Quantum Error Correction?

I have referred this same question here 'Circuit for implementing Steane's code for Quantum Error Correction' . But the answer discusses the circuit to compute the syndrome and not clearly the encoding part of the stean code. I have searched for other references also but not getting specific anything. I know how to obtain state vector expressions for logical zero and logical one for stean code but I am not getting how to encode it using quantum gates. Please explain. Thank you.

I will do some of the work using a small python library I am writing to play with quantum stabilizer codes. It's called stac.

Let's load up the Steane code.

import stac
cd = stac.CommonCodes.generate_code('[[7,1,3]]')
stac.print_matrix(cd.gens_mat, augmented=True)


$$\displaystyle \left(\begin{array}{ccccccc|ccccccc} 1 & 1 & 1 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 1 & 1 & 0 & 0 & 1 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 1 & 0 & 1 & 0 & 1 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 1 & 1 & 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 1 & 0 & 0 & 1 & 1 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 & 1 & 0 & 1 & 0 & 1 \\ \end{array}\right)$$

Now, we follow the following steps to create an encoding circuit.

1. Bring the generator matrix to standard form $$H = \left(\begin{array}{ccc|ccc} I & A_1 & A_2 & B & C_1 & C_2 \\ 0 & 0 & 0 & D & I & E_2 \end{array}\right).$$ Here, the columns of the blocks are of size $$r$$, $$m-r$$ and $$k$$ respectively. For the Steane code, it is as follows.
cd.standard_form()
stac.print_matrix(cd.standard_gens_mat, augmented=True)


$$\displaystyle \left(\begin{array}{ccccccc|ccccccc} 1 & 0 & 0 & 1 & 0 & 1 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 1 & 0 & 1 & 1 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 1 & 1 & 1 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 1 & 1 & 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 & 1 & 0 & 1 & 0 & 1 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 1 & 0 & 0 & 1 & 1 \\ \end{array}\right)$$

1. Construct logical operators of the code

$$\bar{X}$$s are the rows of the matrix $$(0 E_2^T I | (E_2^TC_1^T + C_2^T) 0 0).$$ Similarly, the logical $$\bar{Z}$$s are $$(0 0 0 | A_2^T 0 I).$$

We can read off these from $$H$$, and for the Steane code, they are as follows.

cd.construct_logical_operators()
print("Logical X =", cd.logical_xs)
print("Logical Z =", cd.logical_zs)

Logical X = [[0 0 0 0 1 1 1 0 0 0 0 0 0 0]]
Logical Z = [[0 0 0 0 0 0 0 1 1 0 0 0 0 1]]

1. Use Gottesman's algorithm  to determine the sequence of gates in the encoding circuit for the zero state.

Let qubits be numbered 0-(n-1), let qubits 0-(n-k-1) be the ancilla qubits in state $$|0\rangle$$ and the remaining $$k$$ qubits be where the unknown state $$|\psi\rangle$$ is fed in. Here, we will let $$|\psi\rangle = |0\rangle$$.

encoding_circuit = []
for i in range(k):
for j in range(r, n-k):
if logical_xs[i, j]:
encoding_circuit.append(["cx", n-k+i, j])

for i in range(r):
encoding_circuit.append(["h", i])
for j in range(n):
if i == j:
continue
if standard_gens_x[i, j] and standard_gens_z[i, j]:
encoding_circuit.append(["cx", i, j])
encoding_circuit.append(["cz", i, j])
elif standard_gens_x[i, j]:
encoding_circuit.append(["cx", i, j])
elif standard_gens_z[i, j]:
encoding_circuit.append(["cz", i, j])


One subtlety here is that instead of using the complex $$Y$$ gate, we instead let $$Y=ZX$$, which is a real matrix. So, the place where we have to apply $$CY$$, we apply $$CX$$ followed by $$CZ$$.

Executing this algorithm, we get the circuit as follows.

enc_circ = cd.construct_encoding_circuit()
stac.draw_circuit(enc_circ, output='latex')
# unfortunately, qiskit has a nasty habit of rearranging the gates. D. Gottesman, Stabilizer Codes and Quantum Error Correction, arXiv:quant-ph/9705052.

• comment on the standard form : it can be simplified more so that $C_1 = 0$. Nov 13, 2022 at 2:16
• You are correct. I did not bother to implement it in my library as of yet. But it will help reduce the number of gates in the encoding circuit. Nov 13, 2022 at 4:10

For a CSS code, initializing all physical qubits into $$|0\rangle$$ and then measuring the stabilizers of the code is an encoding circuit for logical $$|0\rangle$$, and also for logical $$|1\rangle$$, up to the Pauli frame. Correspondingly, to prepare logical $$|+\rangle$$ or $$|-\rangle$$ up to the Pauli frame, start by initializing the physical qubits into $$|+\rangle$$ instead of $$|0\rangle$$.

In stabilizer codes, everything is much easier if you let the Pauli frame do what it wants instead of trying to control it. What this means in practice is that, instead of trying to prepare a complex stabilizer (or observable), you can instead choose to simply measure that stabilizer (or observable). When you later need to check that stabilizer, where you would normally check if it's in its +1 or -1 eigenstate, you instead compare it to the result of the initializing measurement.

When I was first getting into error correction, I tried to make all my stabilizers be +1. As I did more and more complex things, this got more and more onerous and tedious. I was running expensive Dijkstra searches and Gaussian eliminations, to enforce a property that doesn't even achieve anything useful. So... I stopped doing that. And everything got easier. Let the stabilizers fall where they may.

Following this, we perform an algebraic verification of the 7-bit encoding and decoding: Step 0:

$$(a|0\rangle + b|1\rangle)|000000\rangle$$ $$=a|0000000\rangle + b|1000000\rangle$$

Step 1:

$$a|0000\rangle\frac{1}{\sqrt2}(|0\rangle + |1\rangle)\frac{1}{\sqrt2}(|0\rangle + |1\rangle\frac{1}{\sqrt2}(|0\rangle + |1\rangle) + b|1000\rangle\frac{1}{\sqrt2}(|0\rangle + |1\rangle)\frac{1}{\sqrt2}(|0\rangle + |1\rangle)\frac{1}{\sqrt2}(|0\rangle + |1\rangle)$$

$$=(1/ \sqrt8)(a|0000000\rangle + a|0000001\rangle + a|0000010\rangle + a|0000011\rangle + a|0000100\rangle + a|0000101\rangle + a|0000110\rangle + a|0000111\rangle + b|1000000\rangle + b|1000001\rangle + b|1000010\rangle + b|1000011\rangle + b|1000100\rangle + b|1000101\rangle + b|1000110\rangle + b|1000111\rangle)$$

Step 2:

$$(1/ \sqrt8)(a|0000000\rangle + a|0000001\rangle + a|0000010\rangle + a|0000011\rangle + a|0000100\rangle + a|0000101\rangle + a|0000110\rangle + a|0000111\rangle + b|1110000\rangle + b|1110001\rangle + b|1110010\rangle + b|1110011\rangle + b|1110100\rangle + b|1110101\rangle + b|1110110\rangle + b|1110111\rangle)$$

Step 3:

$$(1/ \sqrt8)(a|0000000\rangle + a|1101001\rangle + a|0000010\rangle + a|1101011\rangle + a|0000100\rangle + a|1101101\rangle + a|0000110\rangle + a|1101111\rangle + b|1110000\rangle + b|0011001\rangle + b|1110010\rangle + b|0011011\rangle + b|1110100\rangle + b|0011101\rangle + b|1110110\rangle + b|0011111\rangle)$$

Step 4:

$$(1/ \sqrt8)(a|0000000\rangle + a|1101001\rangle + a|1011010\rangle + a|0110011\rangle + a|0000100\rangle + a|1101101\rangle + a|1011110\rangle + a|0110111\rangle + b|1110000\rangle + b|0011001\rangle + b|0101010\rangle + b|1000011\rangle + b|1110100\rangle + b|0011101\rangle + b|0101110\rangle + b|1000111\rangle)$$

Step 5:

$$(1/ \sqrt8)(a|0000000\rangle + a|1101001\rangle + a|1011010\rangle + a|0110011\rangle + a|0111100\rangle + a|1010101\rangle + a|1100110\rangle + a|0001111\rangle + b|1110000\rangle + b|0011001\rangle + b|0101010\rangle + b|1000011\rangle + |1001100\rangle + b|0100101\rangle + b|0010110\rangle + b|1111111\rangle)$$

$$=\frac{a}{\sqrt8}\begin{bmatrix}|0000000\rangle \\ |1101001\rangle \\ |1011010\rangle \\ |0110011\rangle \\ |0111100\rangle \\ |1010101\rangle \\ |1100110\rangle \\ |0001111\rangle \end{bmatrix} + \frac{b}{\sqrt8}\begin{bmatrix}|1110000\rangle \\ |0011001\rangle \\|0101010\rangle \\|1000011\rangle \\ |1001100\rangle \\ |0100101\rangle \\ |0010110\rangle \\ |1111111\rangle \end{bmatrix}$$

Decoding circuit: Step 0:

$$(1/ \sqrt8)(a|0000000000000\rangle + a|1101001000000\rangle + a|1011010000000\rangle + a|0110011000000\rangle + a|0111100000000\rangle + a|1010101000000\rangle + a|1100110000000\rangle + a|0001111000000\rangle + b|1110000000000\rangle + b|0011001000000\rangle + b|0101010000000\rangle + b|1000011000000\rangle + |1001100000000\rangle + b|0100101000000\rangle + b|0010110000000\rangle + b|1111111000000\rangle)$$

Step 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12:

$$(1/ \sqrt8)(a|0000000000000\rangle + a|1101001000000\rangle + a|1011010000000\rangle + a|0110011000000\rangle + a|0111100000000\rangle + a|1010101000000\rangle + a|1100110000000\rangle + a|0001111000000\rangle + b|1110000000000\rangle + b|0011001000000\rangle + b|0101010000000\rangle + b|1000011000000\rangle + |1001100000000\rangle + b|0100101000000\rangle + b|0010110000000\rangle + b|1111111000000\rangle)$$ Single bit-flip error introduction Single phase-flip error introduction  • Hey James...Thanks a lot for this answer. Jun 5 at 4:33

A concrete circuit allowing to encode the logical $$|\bar{0}\rangle$$ can be as follow (source, page 33 of this paper). Now, in practice, this circuit is not fault-tolerant, which means that a single error introduced by a cNOT gate can propagate toward all the physical qubits (ruining your protection). You could also initialize as Craig and the other post your link refer to proposed (by measuring the stabilizers). However, I also think that for this specific code it will not give you a fault-tolerant initialization. This is because to perform this measurement you would need extra ancilla qubits that would have to interact with your original qubit and that could create more than one error on your logical data qubit during their interactions (making the state un-correctable). ( * )

If you are not worried by having a fault-tolerant initialization, any of the answers is good.

( * ) I am thinking about a brute force measurement of all the stabilizers. What I mean is that some care must be done to do it in a fault-tolerant manner.