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)

Hide code cell source

1QuantumCircuit.from_qasm_str(circ.without_noise().to_qasm(open_qasm_version=2)).draw('mpl')
_images/1fa536b8a5d31608c00a4ad34fbe97b8466613520d2aed8f2492f8fb7f23a215.png

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. The distribution of bit flips can be obtained via simulations.

Hide code cell source

 1import matplotlib.pyplot as plt
 2import numpy as np
 3
 4cat_state = circ.copy()
 5circ.append_operation("DEPOLARIZE1", range(8), p)
 6cat_state.append_operation("MR", range(8))
 7
 8w=8
 9n_samples = 1000000
10sampler = cat_state.compile_sampler()
11samples = sampler.sample(n_samples).astype(int)
12
13error_weights = np.min(np.vstack((samples.sum(axis=1), 8 - samples.sum(axis=1))), axis=0)  # at most 4 bits can flip
14hist = np.histogram(error_weights, bins=range(4 + 2))[0]/n_samples
15
16x = np.arange(w // 2 + 1)
17_fig, ax = plt.subplots()
18
19cmap = plt.cm.plasma
20colors = cmap(np.linspace(0, 1, len(x)))
21
22bar_width = 0.8
23for xi, yi, color in zip(x, hist, colors):
24    ax.bar(
25        xi,
26        yi,
27        width=bar_width,
28        color=color,
29        alpha=0.8,
30        edgecolor="black",
31        hatch="//",
32        label=f"Error count {xi}" if xi == 0 else "",
33    )
34    ax.errorbar(xi, yi, fmt="none", capsize=5, color="black", linewidth=1.5)
35
36ax.set_xlabel("Number of errors")
37ax.set_ylabel("Probability")
38ax.set_xticks(x)
39ax.set_yscale("log")
40ax.margins(0.2, 0.2)
41plt.title(f"Error distribution for w = {w}, p = {p:.2f}")
42plt.show()
_images/3320c925cacf768eba93bfacc53264a2a4de286f8a4c3460ab4930c2b1d830d2.png

We see that 1,2 and 4 errors occur on the order of the physical error rate, which we set to \(p = 0.05\). Interestingly, 3 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:

Hide code cell source

1from qiskit.transpiler.passes import RemoveFinalReset
2pass_ = RemoveFinalReset()
3
4pass_(pass_(QuantumCircuit.from_qasm_str(experiment.circ.to_qasm(open_qasm_version=2)))).draw('mpl')
_images/dfff33e3546ba2cbb737fd26394073fabce964e2e20d8b025b9c55eb247de8e0.png

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)
_images/fc0814f826ba9fbe59afb3e531298adcd39cf89c1fff473f07a932d77173f53b.png

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.

The second case is actually a special case of the first one. 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 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.

Hide code cell source

1pass_(pass_(QuantumCircuit.from_qasm_str(experiment.circ.to_qasm(open_qasm_version=2)))).draw('mpl')
_images/c5fde7ccdcaf95a6230f7058f94f6cd21afbf8ebc76f9228a93d1c198e551fae.png

Simulating the circuits shows that now residual weight-four errors on the data are highly unlikely.

1experiment.plot_one_p(p, n_samples=100000)
_images/5b27c57c03d25f3d2e5d39ba702fa174a48f4308b2c4c2511bdf14180c688a55.png

It worked! And it doesn’t even come at the cost of a lower acceptance rate.

Preparing larger cat states

The question now is whether we can make this work for higher-weight cat states. With the framework in place, we can just plug in higher-weight cat states and try different permutations. Let’s consider the case of \(w=16\) and try the following:

  • \(\pi_1 = \mathrm{id}\)

  • \(\pi_2 = (0 \quad 15)\)

  • \(\pi_2 = \begin{pmatrix} 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 & 11 & 12 & 13 & 14 & 15\\ 0 & 1 & 6 & 10 & 12 & 3 & 5 & 15 & 2 & 8 & 11 & 14 & 4 & 9 & 12 & 7 \end{pmatrix}\)

 1from mqt.qecc.circuit_synthesis import CatStatePreparationExperiment, cat_state_balanced_tree
 2
 3w = 16
 4data = cat_state_balanced_tree(w)
 5ancilla = cat_state_balanced_tree(w)
 6
 7pi_1 = list(range(w))
 8
 9pi_2 = list(range(w))
10pi_2[0] = 15
11pi_2[15] = 0
12
13pi_3 = [0, 1, 6, 10, 13, 3, 5, 15, 2, 8, 11, 14, 4, 9, 12, 7]
14
15e_1 = CatStatePreparationExperiment(data, ancilla, pi_1)
16e_2 = CatStatePreparationExperiment(data, ancilla, pi_2)
17e_3 = CatStatePreparationExperiment(data, ancilla, pi_3)

Let’s see the distribution for the identity permutation \(\pi_1\) first.

Hide code cell source

1e_1.plot_one_p(p, n_samples=10000000)
_images/92d9da9d9fd3c2fdf15f636845a256c1752ff9625379135c1a9fa7b9becad1f4.png

At \(p=0.05\) we only accept about \(12\%\) of all states. We also see that while errors of weight three or higher are less likely as lower-weight errors, the distribution shows that higher-weight errors all occur more or less similarly often.

When we permute the transversal CNOT slightly according to \(\pi_2\), we also suppress the errors to some extent.

Hide code cell source

1e_2.plot_one_p(p, n_samples=10000000)
2print(e_2.circ.to_crumble_url())
_images/cbdf783aafb23b3f4cec6608e5cfec58cf2ee512b0ea60aba43d76fa8d65e94c.png
https://algassert.com/crumble#circuit=H_0;CX_0_8_0_4_0_2_0_1_2_3_4_6_4_5_6_7_8_12_8_10_8_9_10_11_12_14_12_13_14_15;H_16;CX_16_24_16_20_16_18_16_17_18_19_20_22_20_21_22_23_24_28_24_26_24_25_26_27_28_30_28_29_30_31_0_31_1_17_2_18_3_19_4_20_5_21_6_22_7_23_8_24_9_25_10_26_11_27_12_28_13_29_14_30_15_16;MR_16_17_18_19_20_21_22_23_24_25_26_27_28_29_30_31_

We see that simply exchanging two qubits is not sufficient to protect the \(w=16\) cat state against higher-weight errors cancelling out. There are, in fact many undetected weight-four errors that lead to a residual error of higher weight on the data qubits. One example is shown in this crumble circuit.

Since there are fewer combinations of errors that cancel out in such a fashion, the error rate still declines, but for a fault-tolerant preparation we would wish for an error of weight \(t\) on the data to occur with probability \(O(p^t)\)

Applying \(\pi_3\) actually yields the desired result:

Hide code cell source

1e_3.plot_one_p(p, n_samples=10000000)
_images/1e9059446eb74377687debb9724e1ecaa442f379a60753d28d1f7b36b3a6d0e5.png

At some point, high-weight errors are so unlikely that they do not occur during the simulation. Getting a better error estimate therefore requires a larger sample-size. Furthermore, to get an estimate of the scaling of the probability of a residual error of a certain size on the data requires sampling at different physical error rates. The cat_prep_experiment method of the CatStatePreparationExperiment class returns the histogram over multiple physical error rates.

Permuting the connectivity of the transversal CNOT is not the only way to improve the robustness of non-deterministic cat state preparation. Another way would be to use different circuits or combine the two methods. The CatStatePreparationExperiment class is intended for evaluating such different preparation schemes.