Cat state preparation¶
Cat states are of quantum states of the form \(|0\rangle^{\otimes w}+|1\rangle^{\otimes w}\) which have various application in quantum error correction, most famously in Shor-style syndrome extraction where a weight-\(w\) stabilizer on a code is measured by performing a transversal CNOT from the support of the stabilizer to a cat state. The difficulty of cat states comes from the fact that it is hard to prepare them in a fault-tolerant manner. If we use cat states for syndrome extraction we want them to be as high-quality as possible.
A cat state can be prepared by preparing one (arbitrary) qubit in the \(|+\rangle\) state and the remaining \(w-1\) in the \(|0\rangle\) state and then entangle the \(|+\rangle\) state with the remaining qubits via CNOT gates. The exact pattern is of these CNOTs is not too important. We just have to make sure that the entanglement spreads to every qubit.
One way to do it is by arranging the CNOTs as a perfect balanced binary tree which prepares the state in \(\log_2{w}\) depth. Let’s define a noisy stim circuit that does this.
1import stim
2from qiskit import QuantumCircuit
3
4circ = stim.Circuit()
5p = 0.05 # physical error rate
6
7def noisy_cnot(circ: stim.Circuit, ctrl: int, trgt: int, p: float) -> None:
8 circ.append_operation("CX", [ctrl, trgt])
9 circ.append_operation("DEPOLARIZE2", [ctrl, trgt], p)
10
11circ.append_operation("H", [0])
12circ.append_operation("DEPOLARIZE1", range(8), p)
13
14noisy_cnot(circ, 0, 4, p)
15
16noisy_cnot(circ, 0, 2, p)
17noisy_cnot(circ, 4, 6, p)
18
19noisy_cnot(circ, 0, 1, p)
20noisy_cnot(circ, 2, 3, p)
21noisy_cnot(circ, 4, 5, p)
22noisy_cnot(circ, 6, 7, p)
This circuit is not fault-tolerant. A single \(X\)-error in the circuit might spread to high-weight \(X\)-errors. We can show this by simulating the circuit. The cat state is a particularly easy state to analyse because it is resilient to \(Z\)-errors (every \(Z\)-error is equivalent to a weight-zero or weight-one error) and all \(X\) errors simply flip a bit in the state.
We see that 1,2 and 4 errors occur on the order of the physical error rate, which we set to \(p = 0.05\). In fact, there are about twice as many weight-four errors as there are weight-two errors, since there are four CNOTs that propagate an \(X\) fault to a weight-two error and two CNOTs that propagate an \(X\) fault to a weight-four error. Weight-three errors occur only with a probability of about \(p^2\). This is due to the structure of the circuit. If an \(X\) error occurs, it either propagates to one or two CNOTs, or it doesn’t propagate at all. Three errors are caused by a propagated error and another single-qubit error.
First Attempt at Fault-tolerant Preparation¶
Since the cat-state is CSS, it admits a transversal CNOT. Therefore, we could try to copy the errors of one cat state to another cat state, measure out the qubits of the ancilla state and if we find that an error occurred we restart. QECC provides functionality to set up repeat-until-success cat state preparation experiments.
1from mqt.qecc.circuit_synthesis import CatStatePreparationExperiment, cat_state_balanced_tree
2
3w = 8
4data = cat_state_balanced_tree(w)
5ancilla = cat_state_balanced_tree(w)
6
7experiment = CatStatePreparationExperiment(data, ancilla)
The combined circuit is automatically constructed:
We can now simulate this protocol and look at the error distribution on the data cat state for a specific physical error rate.
1experiment.plot_one_p(p, n_samples=100000)
Compared to the above case, the probability of weight-two and weight-four errors has decreased. However, we see that even though about 60% of states are discarded, the weight-four error on the data still occurs about as often on the data as a weight-two error. The reason for this is that both the data and the ancilla state are prepared using the same circuit structure. Consequently, they will have the same set of faults resulting from errors propagating through the circuit. Identical weight-four errors can then cancel out on the ancilla via the transversal CNOT and are subsequently not detected by the ancilla measurement. The situation is illustrated in this crumble circuit.
Second Attempt at Fault-Tolerant State Preparation¶
The problem in the previous construction is that both circuits propagate errors in the same way. We can try to fix this in one of two ways:
Prepare the ancilla with a different circuit.
Permute the transversal CNOTs.
Permuting how qubits are connected via the transversal CNOT is equivalent to permuting the CNOTs in the ancilla preparation circuit. We want to find a permutation such that no errors cancel each other out anymore.
We have seen that weight-four errors can cancel out in these circuits. There actually only two weight-four errors that can occur as a consequence of a weight-one error in the circuits, namely \(X_0X_1X_2X_3\) and \(X_4X_5X_6X_7\) (these are actually stabilizer equivalent). Therefore, performing the transversal cnot such that it connects qubit \(q_0\) of the data with qubit \(q_7\) of the ancilla and vice versa should avoid that the weight-four errors cancel out.
In QECC we can pass a permutation on integers \(0, \cdot, w-1\) to the CatStatePreparationExperiment object during construction.
1perm = [7,1,2,3,4,5,6,0]
2
3experiment = CatStatePreparationExperiment(data, ancilla, perm)
Again, we can look at the circuit that was actually constructed.
Simulating the circuits shows that now residual weight-four errors on the data are highly unlikely.
1experiment.plot_one_p(p, n_samples=100000)
It worked! And it doesn’t even come at the cost of a lower acceptance rate.
Reducing Qubit Overhead¶
When copying errors from the data to the ancilla cat state, it is not necessary, that the ancilla state has the same size as the data state. In fact, as long as the ancilla state consists of at least two qubits, any transversal CNOT connecting a subset of the data qubits to all ancilla qubits acts trivially on the data state. For the eight qubit case, it turns out that a six qubit ancilla is sufficient. Care still needs to be taken with how the (partial) transversal CNOT is connected.
1from mqt.qecc.circuit_synthesis.cat_states import cat_state_pruned_balanced_circuit
2
3w1 = 8
4w2 = 6
5data = cat_state_pruned_balanced_circuit(w1)
6ancilla = cat_state_pruned_balanced_circuit(w2)
7
8ctrls = [1,2,3,5,6,7]
9perm = [2,5,0,4,3,1]
10experiment = CatStatePreparationExperiment(data, ancilla, perm, ctrls)
11experiment.plot_one_p(p, n_samples=100000)
Preparing larger cat states¶
Constructing fault tolerant partial transversal CNOTs and finding the required ancilla sizes for given cat state preparation circuits becomes more difficult at higher qubit counts and fault distances. QECC has functions for finding such CNOTs automatically.
The most general search approach is search_ft_cnot_cegar which uses counterexample-guided abstraction refinement (CEGAR) to construct both a selection of control qubits and the fault-tolerant permutation at the same time.
1from mqt.qecc.circuit_synthesis.cat_states import search_ft_cnot_cegar
2
3t = 4
4ctrls, perm, _ = search_ft_cnot_cegar(data, ancilla, t)
5
6experiment = CatStatePreparationExperiment(data, ancilla, perm, ctrls)
7experiment.plot_one_p(p, n_samples=100000)
The search_ft_cnot_local_search method uses a heuristic local repair strategy to find fault-tolerant CNOTs. This is faster than the CEGAR approach but is not guaranteed to converge:
1from mqt.qecc.circuit_synthesis.cat_states import search_ft_cnot_local_search
2
3w1 = 16
4w2 = 15
5t= 8
6data = cat_state_pruned_balanced_circuit(w1)
7ancilla = cat_state_pruned_balanced_circuit(w2)
8ctrls = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14]
9ctrls, perm, _ = search_ft_cnot_local_search(data, ancilla, t, ctrls=ctrls)
10
11experiment = CatStatePreparationExperiment(data, ancilla, perm, ctrls)
12experiment.plot_one_p(p, n_samples=100000)
If we already know a good selection of control qubits, performance-wise somewhere in the middle is the search_ft_cnot_smt method, which directly encodes all problematic fault propagations instead of iteratively refining the SMT encoding. Especially for UNSAT instances this usually terminates quickly.
1from mqt.qecc.circuit_synthesis.cat_states import search_ft_cnot_smt
2
3w1 = 12
4w2 = 11
5t= 6
6data = cat_state_pruned_balanced_circuit(w1)
7ancilla = cat_state_pruned_balanced_circuit(w2)
8ctrls, perm, _ = search_ft_cnot_smt(data, ancilla, t)
9
10experiment = CatStatePreparationExperiment(data, ancilla, perm, ctrls)
11experiment.plot_one_p(p, n_samples=100000)
Loading already Constructed FT Cat States¶
To avoid redoing redundant computations, stim circuits for cat states of sizes up \(49\) qubits and fault distances up to \(9\) can be found here. There is also a json file which explicitly lists control qubits and target permutation for a given combination of data cat state size (\(w_1\)), ancilla cat state size (\(w_2\)) and fault distance (\(t\)).