Checkpoint-3

System #0

You are a physics research assistant specializing in solving complex, research-level problems using precise, step-by-step reasoning.

Input Problems will be provided in Markdown format.

Output (Markdown format)

  1. Step-by-Step Derivation - Show every non-trivial step in the solution. Justify steps using relevant physical laws, theorems, mathematical identities or numerical codes.
  2. Mathematical Typesetting - Use LaTeX for all mathematics: $...$ for inline expressions, $$...$$ for display equations.
  3. Conventions and Units - Follow the unit system and conventions specified in the problem.
  4. Final Answer - At the end of the solution, start a new line with “Final Answer:”, and present the final result.

    For final answers involving values, follow the precision requirements specified in the problem. If no precision is specified: - If an exact value is possible, provide it (e.g., \$\sqrt(2)\$, \$\pi/4\$). - If exact form is not feasible, retain at least 12 significant digits in the result.

  5. Formatting Compliance - If the user requests a specific output format (e.g., code, table), provide the final answer accordingly.

User #1

Problem setup:

In quantum error correction, you encode quantum states into logical states made of many qubits in order to improve their resilience to errors. In quantum error detection, you do the same but can only detect the presence of errors and not correct them. In this problem, we will consider a single [[4,2,2]] quantum error detection code, which encodes two logical qubits into four physical qubits, and investigate how robust logical quantum operations in this code are to quantum errors.

Our convention is that the four physical qubits in the [[4,2,2]] code are labelled 0,1,2,3. The two logical qubits are labelled A and B. The stabilizers are \(XXXX\) and \(ZZZZ\), where \(X\) and \(Z\) are Pauli matrices. The logical \(X\) and \(Z\) operators on the two qubits are \(X_A = XIXI\), \(X_B=XXII\), \(Z_A = ZZII\), \(Z_B = ZIZI\), up to multiplication by stabilizers.

We will consider different state preparation circuits consisting of controlled not \(CNOT_{ij}\) gates, where \(CNOT_{ij}\) has control qubit \(i\) and target qubit \(j\). As a simple model of quantum errors in hardware, we will suppose that each \(CNOT_{ij}\) gate in the circuit has a two qubit depolarizing error channel following it that produces one of the 15 non-identity two-qubit Paulis with equal probability \(p/15\). The probability \(p\) indicates the probability of an error in a single two-qubit gate. We will assess the logical infidelity of certain state preparation protocols as a function of the physical infidelity \(p\).

Checkpoints

Checkpoint 1

Suppose that we wish to prepare a logical two-qubit GHZ state \((|00\rangle_{AB}+|11\rangle_{AB})/\sqrt{2}\) in the [[4,2,2]] code. To do so, we use the following state preparation circuit:

\[ (CNOT_{03}) (H_0) (CNOT_{21}) (H_2). \]

Note that this equation is written in matrix multiplication order, while the quantum operations in the circuit occur in the reverse order (from right-to-left in the above equation). \(H\) is a single-qubit Hadamard gate.

What is the physical state fidelity of the final physical 4-qubit state at the end of the circuit as a function of the two-qubit gate error rate \(p\)?

Assistant #2
Tool Call (code to execute)
import numpy as np

# Pauli matrices
I = np.eye(2)
X = np.array([[0,1],[1,0]], dtype=complex)
Y = np.array([[0,-1j],[1j,0]], dtype=complex)
Z = np.array([[1,0],[0,-1]], dtype=complex)
H = (1/np.sqrt(2))*np.array([[1,1],[1,-1]], dtype=complex)

# Tensor product utility
from functools import reduce

def kron(*ops):
    return reduce(np.kron, ops)

# 4-qubit basis: qubits ordered (0,1,2,3)

# Build CNOT_ij unitary on 4 qubits

def CNOT_ij(i,j):
    # Build projector form: |0><0|_i ⊗ I + |1><1|_i ⊗ X_j
    P0 = np.array([[1,0],[0,0]], dtype=complex)
    P1 = np.array([[0,0],[0,1]], dtype=complex)
    ops0 = [I,I,I,I]
    ops1 = [I,I,I,I]
    ops0[i] = P0
    ops1[i] = P1
    # X on j for second term
    ops1[j] = X
    return kron(*ops0) + kron(*ops1)

# Hadamard on qubit q (others identity)

def H_q(q):
    ops = [I,I,I,I]
    ops[q] = H
    return kron(*ops)

# Two-qubit Pauli labels and matrices
Psingle = [I, X, Y, Z]
Pnames = ['I','X','Y','Z']

# Build two-qubit Pauli on a specified pair (a,b) with order qubit index 0..3

def two_qubit_pauli_on_pair(a,b, pa, pb):
    ops = [I,I,I,I]
    ops[a] = Psingle[pa]
    ops[b] = Psingle[pb]
    return kron(*ops)

# Circuit unitaries (ideal): U = CNOT_03 * H0 * CNOT_21 * H2
U = CNOT_ij(0,3) @ H_q(0) @ CNOT_ij(2,1) @ H_q(2)

# Initial |0000>
zero = np.array([[1],[0]], dtype=complex)
psi0 = kron(zero, zero, zero, zero)
psi_ideal = U @ psi0
rho_ideal = psi_ideal @ psi_ideal.conj().T

# Build the noisy rho(p) symbolically as sum over errors with weights
# But we'll just compute the coefficient of fidelity as a function of p by summing contributions

# Enumerate two-qubit paulis on pair (2,1) and (0,3)
non_id_pairs = []
for a in range(4):
    for b in range(4):
        name = Pnames[a]+Pnames[b]
        non_id_pairs.append((a,b,name))

# Build the list of 16 two-qubit paulies and classify which are identity
pairs = non_id_pairs

# Let's compute the probability weight that maps to fidelity 1: we check for each pair of errors whether
# E03 * U * E21 * ... acting on |0000> equals psi_ideal up to global phase. We can test overlap squared ~1.

# Build E for pair (2,1) and (0,3)

import math

def fidelity_weight():
    # We'll compute count of (pauli on 03, on 21) that leave the state invariant up to phase.
    survivors = []
    for a03 in range(4):
        for b03 in range(4):
            E03 = two_qubit_pauli_on_pair(0,3, a03, b03)
            for a21 in range(4):
                for b21 in range(4):
                    E21 = two_qubit_pauli_on_pair(2,1, a21, b21)
                    # Build final state with this error model: errors applied after CNOTs
                    # That is: first H2, then CNOT21, then error E21, then H0, then CNOT03, then error E03
                    psi = E03 @ CNOT_ij(0,3) @ H_q(0) @ E21 @ CNOT_ij(2,1) @ H_q(2) @ psi0
                    # Overlap magnitude
                    overlap = (psi_ideal.conj().T @ psi)[0,0]
                    if np.allclose(abs(overlap), 1.0, atol=1e-12):
                        survivors.append(((a03,b03),(a21,b21)))
    return survivors

survivors = fidelity_weight()
len_survivors, survivors[:10]
Tool #3
Traceback (most recent call last):
  File "<stdin>", line 99, in <module>
NameError: name 'len_survivors' is not defined. Did you mean: 'survivors'?
Assistant #4
len(survivors), survivors[:20]
Tool #5
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'survivors' is not defined
Assistant #6
# Re-run the computation quickly
import numpy as np
I = np.eye(2); X = np.array([[0,1],[1,0]], complex)
Y = np.array([[0,-1j],[1j,0]], complex)
Z = np.array([[1,0],[0,-1]], complex)
H = (1/np.sqrt(2))*np.array([[1,1],[1,-1]], complex)
from functools import reduce

def kron(*ops):
    return reduce(np.kron, ops)

def CNOT_ij(i,j):
    P0 = np.array([[1,0],[0,0]], complex)
    P1 = np.array([[0,0],[0,1]], complex)
    ops0 = [I,I,I,I]
    ops1 = [I,I,I,I]
    ops0[i] = P0
    ops1[i] = P1
    ops1[j] = X
    return kron(*ops0) + kron(*ops1)

def H_q(q):
    ops = [I,I,I,I]
    ops[q] = H
    return kron(*ops)

Psingle = [I,X,Y,Z]

U = CNOT_ij(0,3) @ H_q(0) @ CNOT_ij(2,1) @ H_q(2)
zero = np.array([[1],[0]], complex)
psi0 = kron(zero,zero,zero,zero)
psi_ideal = U @ psi0

survivors = []
for a03 in range(4):
    for b03 in range(4):
        E03 = kron(Psingle[a03], I, I, Psingle[b03])
        for a21 in range(4):
            for b21 in range(4):
                E21 = kron(I, Psingle[b21], Psingle[a21], I)  # careful: pair (2,1)
                psi = E03 @ CNOT_ij(0,3) @ H_q(0) @ E21 @ CNOT_ij(2,1) @ H_q(2) @ psi0
                overlap = (psi_ideal.conj().T @ psi)[0,0]
                if np.allclose(abs(overlap), 1.0, atol=1e-10):
                    survivors.append(((a03,b03),(a21,b21)))

len(survivors), survivors[:12]
Tool #7
''
Assistant #8
Tool Call (code to execute)
print(len(survivors))
print(survivors[:16])
Tool #9
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'survivors' is not defined
Assistant #10
# Recompute succinctly and print
import numpy as np
I = np.eye(2); X = np.array([[0,1],[1,0]], complex); Y = np.array([[0,-1j],[1j,0]], complex); Z = np.array([[1,0],[0,-1]], complex)
H = (1/np.sqrt(2))*np.array([[1,1],[1,-1]], complex)
from functools import reduce

def kron(*ops):
    return reduce(np.kron, ops)

def CNOT_ij(i,j):
    P0 = np.array([[1,0],[0,0]], complex)
    P1 = np.array([[0,0],[0,1]], complex)
    ops0 = [I,I,I,I]
    ops1 = [I,I,I,I]
    ops0[i] = P0
    ops1[i] = P1
    ops1[j] = X
    return kron(*ops0) + kron(*ops1)

def H_q(q):
    ops = [I,I,I,I]
    ops[q] = H
    return kron(*ops)

Psingle = [I,X,Y,Z]
U = CNOT_ij(0,3) @ H_q(0) @ CNOT_ij(2,1) @ H_q(2)
zero = np.array([[1],[0]], complex)
psi0 = kron(zero,zero,zero,zero)
psi_ideal = U @ psi0

survivors = []
for a03 in range(4):
    for b03 in range(4):
        E03 = kron(Psingle[a03], np.eye(2), np.eye(2), Psingle[b03])
        for a21 in range(4):
            for b21 in range(4):
                E21 = kron(np.eye(2), Psingle[b21], Psingle[a21], np.eye(2))
                psi = E03 @ CNOT_ij(0,3) @ H_q(0) @ E21 @ CNOT_ij(2,1) @ H_q(2) @ psi0
                ov = (psi_ideal.conj().T @ psi)[0,0]
                if np.allclose(abs(ov), 1.0, atol=1e-10):
                    survivors.append(((a03,b03),(a21,b21)))

print('number survivors =', len(survivors))
# Show which two-qubit paulis on each pair survive (indexes 0:I,1:X,2:Y,3:Z)
print(survivors[:20])
Tool #11
number survivors = 16
[((0, 0), (0, 0)), ((0, 0), (1, 1)), ((0, 0), (2, 2)), ((0, 0), (3, 3)), ((1, 1), (0, 0)), ((1, 1), (1, 1)), ((1, 1), (2, 2)), ((1, 1), (3, 3)), ((2, 2), (0, 0)), ((2, 2), (1, 1)), ((2, 2), (2, 2)), ((2, 2), (3, 3)), ((3, 3), (0, 0)), ((3, 3), (1, 1)), ((3, 3), (2, 2)), ((3, 3), (3, 3))]
Assistant #12
Tool Call (code to execute)
import numpy as np
I = np.eye(2); X = np.array([[0,1],[1,0]], complex); Y = np.array([[0,-1j],[1j,0]], complex); Z = np.array([[1,0],[0,-1]], complex)
H = (1/np.sqrt(2))*np.array([[1,1],[1,-1]], complex)
from functools import reduce

def kron(*ops):
    return reduce(np.kron, ops)

def CNOT_ij(i,j):
    P0 = np.array([[1,0],[0,0]], complex)
    P1 = np.array([[0,0],[0,1]], complex)
    ops0 = [I,I,I,I]
    ops1 = [I,I,I,I]
    ops0[i] = P0
    ops1[i] = P1
    ops1[j] = X
    return kron(*ops0) + kron(*ops1)

def H_q(q):
    ops = [I,I,I,I]
    ops[q] = H
    return kron(*ops)

Psingle = [I,X,Y,Z]
U = CNOT_ij(0,3) @ H_q(0) @ CNOT_ij(2,1) @ H_q(2)
zero = np.array([[1],[0]], complex)
psi0 = kron(zero,zero,zero,zero)
psi_ideal = U @ psi0
rho_ideal = psi_ideal @ psi_ideal.conj().T

# Build final density matrix under the channel parameter p

def rho_of_p(p):
    rho = np.zeros((16,16), complex)
    # enumerate E21 and E03 errors and accumulate weights
    for a03 in range(4):
        for b03 in range(4):
            weight03 = (1-p) if (a03==0 and b03==0) else (p/15)
            E03 = kron(Psingle[a03], I, I, Psingle[b03])
            for a21 in range(4):
                for b21 in range(4):
                    weight21 = (1-p) if (a21==0 and b21==0) else (p/15)
                    E21 = kron(I, Psingle[b21], Psingle[a21], I)
                    psi = E03 @ CNOT_ij(0,3) @ H_q(0) @ E21 @ CNOT_ij(2,1) @ H_q(2) @ psi0
                    rho += weight03 * weight21 * (psi @ psi.conj().T)
    return rho

for p in [0.0, 0.01, 0.1, 0.2, 0.5, 1.0]:
    rho = rho_of_p(p)
    F = (psi_ideal.conj().T @ rho @ psi_ideal)[0,0].real
    print(p, F, 'vs formula', (1 - 4*p/5)**2)
Tool #13
0.0 0.9999999999999992 vs formula 1.0
0.01 0.9840639999999993 vs formula 0.9840639999999999
0.1 0.8463999999999989 vs formula 0.8464
0.2 0.7056000000000002 vs formula 0.7055999999999999
0.5 0.3599999999999996 vs formula 0.36

1.0 0.039999999999999994 vs formula 0.03999999999999998
Assistant #14
  1. Step-by-Step Derivation
  • Ideal state prepared by the circuit
  • Start from |0000⟩.
  • Apply H2: puts qubit 2 in (|0⟩ + |1⟩)/√2.
  • Apply CNOT21 (control 2 → target 1): prepares a Bell pair on qubits (2,1), |Φ+⟩21 = (|00⟩ + |11⟩)/√2.
  • Apply H0 and then CNOT03 (control 0 → target 3): prepares a Bell pair on qubits (0,3), |Φ+⟩03 = (|00⟩ + |11⟩)/√2.
  • Hence the ideal final state is the product of two Bell pairs
\[ |\psi_{\text{ideal}}\rangle = |\Phi^+\rangle_{03}\otimes|\Phi^+\rangle_{21}. \]
In the computational basis (qubits ordered 0,1,2,3):
\[ |\psi_{\text{ideal}}\rangle = \tfrac{1}{2}\big(|0000\rangle + |0110\rangle + |1001\rangle + |1111\rangle\big). \]
  • This state is a code state stabilized by the given code stabilizers \(XXXX\), \(ZZZZ\) and by the logical Bell stabilizers \(Z_AZ_B=IZZI\) and \(X_AX_B=IXXI\).

  • Noise model and where it acts

  • A two-qubit depolarizing channel acts after each CNOT:
    • After CNOT21, a channel on qubits (2,1).
    • After CNOT03, a channel on qubits (0,3).
  • Each channel applies \(II\) with probability \(1-p\), and one of the 15 non-identity two-qubit Paulis with probability \(p/15\) each.
  • The two channels are independent and act on disjoint pairs, so the total error is \(E_{03}\otimes E_{21}\).

  • Fidelity for a Bell pair under two-qubit depolarizing noise

  • For a Bell state \(|\Phi^+\rangle\), the Pauli operators that leave the state invariant (up to an irrelevant global phase) are precisely
\[ \{II,\; XX,\; YY,\; ZZ\}. \]
  • Therefore, for a single two-qubit depolarizing channel with parameter \(p\),
\[ F_{\text{pair}}(p) = \Pr(II) + \Pr(XX) + \Pr(YY) + \Pr(ZZ) = (1-p) + 3\cdot \frac{p}{15} = 1 - \frac{4}{5}p. \]
  • Total four-qubit fidelity
  • Since the ideal state is a product \(|\Phi^+\rangle_{03}\otimes|\Phi^+\rangle_{21}\) and the two channels are independent on the two disjoint pairs, the total fidelity factorizes:
\[ F_{\text{phys}}(p) = \big(F_{\text{pair}}(p)\big)^2 = \left(1 - \frac{4}{5}p\right)^2. \]
  1. Mathematical Typesetting
  • Final expression:
\[ F_{\text{phys}}(p) = \left(1 - \frac{4}{5}p\right)^2 = 1 - \frac{8}{5}p + \frac{16}{25}p^2. \]
  1. Conventions and Units
  • Probabilities are dimensionless. The parameter \(p\in[0,1]\) is the error probability per two-qubit gate.
  1. Final Answer: \(F_{\text{phys}}(p) = \left(1 - \dfrac{4}{5}p\right)^2 = 1 - \dfrac{8}{5}p + \dfrac{16}{25}p^2.\)
User #15

The correct answer is: $ F_{\rm{physical}}=(1-\frac{12}{15}p)^2 $

Now let’s solve the next Checkpoint.

Checkpoint 2

Suppose that we wish to prepare a logical two-qubit GHZ state \((|00\rangle_{AB}+|11\rangle_{AB})/\sqrt{2}\) in the [[4,2,2]] code. To do so, we use the following state preparation circuit:

\[ (CNOT_{03}) (H_0) (CNOT_{21}) (H_2). \]

Note that this equation is written in matrix multiplication order, while the quantum operations in the circuit occur in the reverse order (from right-to-left in the above equation). \(H\) is a single-qubit Hadamard gate.

What is the logical state fidelity of the final 2-qubit logical state at the end of the circuit as a function of the two-qubit gate error rate \(p\), assuming the state is post-selected on all detectable errors in the code?

Assistant #16
Tool Call (code to execute)
import numpy as np
from functools import reduce

# Single-qubit Paulis
I = np.eye(2, dtype=complex)
X = np.array([[0,1],[1,0]], dtype=complex)
Y = np.array([[0,-1j],[1j,0]], dtype=complex)
Z = np.array([[1,0],[0,-1]], dtype=complex)
H = (1/np.sqrt(2))*np.array([[1,1],[1,-1]], dtype=complex)

Psingle = [I,X,Y,Z]

# Kronecker helper
kron = lambda *ops: reduce(np.kron, ops)

# Build circuit

def CNOT_ij(i,j):
    P0 = np.array([[1,0],[0,0]], complex)
    P1 = np.array([[0,0],[0,1]], complex)
    ops0 = [I,I,I,I]
    ops1 = [I,I,I,I]
    ops0[i] = P0
    ops1[i] = P1
    ops1[j] = X
    return kron(*ops0) + kron(*ops1)

def H_q(q):
    ops = [I,I,I,I]
    ops[q] = H
    return kron(*ops)

# Circuit: U = CNOT_03 * H0 * CNOT_21 * H2
U = CNOT_ij(0,3) @ H_q(0) @ CNOT_ij(2,1) @ H_q(2)
zero = np.array([[1],[0]], complex)
psi0 = kron(zero,zero,zero,zero)
psi_ideal = U @ psi0

# Stabilizers S1=XXXX, S2=ZZZZ
S1 = kron(X,X,X,X)
S2 = kron(Z,Z,Z,Z)

# Logical operators (one representative choice matching problem statement)
XA = kron(X, I, X, I)
XB = kron(X, X, I, I)
ZA = kron(Z, Z, I, I)
ZB = kron(Z, I, Z, I)

# Logical stabilizers of |Phi+>_AB in code space
Lstab = [np.eye(16), XA@XB, ZA@ZB, (XA@XB)@(ZA@ZB)]

# Enumerate all two-qubit Pauli on pairs (0,3) and (2,1)
# Build E_total(P03, P21)

Ops = [I,X,Y,Z]

# Function to classify a 4-qubit Pauli E as accepted and as good (leaves psi_ideal up to phase)

def is_in_normalizer(E):
    c1 = np.allclose(E@S1 - S1@E, 0)
    c2 = np.allclose(E@S2 - S2@E, 0)
    return c1 and c2

def leaves_state(E):
    psi = E @ psi_ideal
    ov = (psi_ideal.conj().T @ psi)[0,0]
    return np.allclose(abs(ov), 1.0, atol=1e-10)

# Precompute acceptance and good masks for each pair of local paulis
accepted = {}
leaves = {}
for a03 in range(4):
    for b03 in range(4):
        E03 = kron(Ops[a03], I, I, Ops[b03])
        for a21 in range(4):
            for b21 in range(4):
                E21 = kron(I, Ops[b21], Ops[a21], I)
                E = E03 @ E21
                key = (a03,b03,a21,b21)
                accepted[key] = is_in_normalizer(E)
                leaves[key] = leaves_state(E)

# Count categories for sanity
n_acc = sum(accepted.values())
n_leave = sum(v and accepted[k] for k,v in leaves.items())
print('Total combos:', 16*16, 'Accepted (normalizer):', n_acc, 'Good within accepted:', n_leave)

# As expected: normalizer has size 64; E_total restricted to product of two local 2-qubit pauli sets gives 256 combos; acceptance 64

# Build weights for each key given depolarizing probability p

def weights_of_p(p):
    # weights for each local 2-qubit pauli on a pair
    w = np.zeros((4,4))
    for a in range(4):
        for b in range(4):
            if a==0 and b==0:
                w[a,b] = 1-p
            else:
                w[a,b] = p/15
    return w

# Compute acceptance probability and conditional fidelity

def acceptance_and_fidelity(p):
    w03 = weights_of_p(p)
    w21 = weights_of_p(p)
    Pacc = 0.0
    Pgood = 0.0
    for a03 in range(4):
        for b03 in range(4):
            for a21 in range(4):
                for b21 in range(4):
                    key = (a03,b03,a21,b21)
                    w = w03[a03,b03]*w21[a21,b21]
                    if accepted[key]:
                        Pacc += w
                        if leaves[key]:
                            Pgood += w
    Fcond = Pgood/Pacc if Pacc>0 else 1.0
    return Pacc, Fcond

for p in [0.0, 0.01, 0.1, 0.2, 0.5, 1.0]:
    Pacc, Fcond = acceptance_and_fidelity(p)
    print(p, 'Pacc=', Pacc, 'F_logical=', Fcond)
Tool #17
Total combos: 256 Accepted (normalizer): 64 Good within accepted: 16
0.0 Pacc= 1.0 F_logical= 1.0
0.01 Pacc= 0.9840853333333347 F_logical= 0.9999783216631608
0.1 Pacc= 0.848533333333331 F_logical= 0.9974858579509766
0.2 Pacc= 0.7141333333333368 F_logical= 0.9880507841672854
0.5 Pacc= 0.4133333333333337 F_logical= 0.8709677419354832

1.0 Pacc= 0.25333333333333335 F_logical= 0.15789473684210525
Assistant #18
Tool Call (code to execute)
import itertools
import numpy as np

# Build classify functions as before
I = np.eye(2, dtype=complex)
X = np.array([[0,1],[1,0]], dtype=complex)
Y = np.array([[0,-1j],[1j,0]], dtype=complex)
Z = np.array([[1,0],[0,-1]], dtype=complex)
Ops = [I,X,Y,Z]

# Parity functions for a 2-qubit operator P⊗Q

def parity_counts_2q(Pa, Pb):
    # returns e_z = (#Z or Y) mod 2, e_x = (#X or Y) mod 2 on the two-qubit pair
    def single_labels(M):
        if np.allclose(M, I): return 'I'
        if np.allclose(M, X): return 'X'
        if np.allclose(M, Y): return 'Y'
        if np.allclose(M, Z): return 'Z'
        raise ValueError
    la = single_labels(Pa)
    lb = single_labels(Pb)
    e_z = (la in ['Z','Y']) + (lb in ['Z','Y'])
    e_x = (la in ['X','Y']) + (lb in ['X','Y'])
    return (e_z % 2, e_x % 2)

# Count distributions across 2-qubit paulis
classes = {(ez,ex):0 for ez in [0,1] for ex in [0,1]}
classes_nonid = {(ez,ex):0 for ez in [0,1] for ex in [0,1]}

for a in range(4):
    for b in range(4):
        ez, ex = parity_counts_2q(Ops[a], Ops[b])
        classes[(ez,ex)] += 1
        if not (a==0 and b==0):
            classes_nonid[(ez,ex)] += 1

classes, classes_nonid
Tool #19
''
Assistant #20
print('All 2-qubit paulis counts by parity (ez,ex):', classes)
print('Non-identity counts by parity (ez,ex):', classes_nonid)
Tool #21
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'classes' is not defined
Assistant #22
# Re-run quickly
import numpy as np
I = np.eye(2, dtype=complex)
X = np.array([[0,1],[1,0]], dtype=complex)
Y = np.array([[0,-1j],[1j,0]], dtype=complex)
Z = np.array([[1,0],[0,-1]], dtype=complex)
Ops = [I,X,Y,Z]

def parity_counts_2q(a,b):
    labels = [I,X,Y,Z]
    def lab(M):
        if np.allclose(M, I): return 'I'
        if np.allclose(M, X): return 'X'
        if np.allclose(M, Y): return 'Y'
        if np.allclose(M, Z): return 'Z'
        raise ValueError
    la = lab(a); lb = lab(b)
    ez = (la in 'ZY') + (lb in 'ZY')
    ex = (la in 'XY') + (lb in 'XY')
    return (ez%2, ex%2)

classes = {(ez,ex):0 for ez in [0,1] for ex in [0,1]}
classes_nonid = {(ez,ex):0 for ez in [0,1] for ex in [0,1]}
for a in Ops:
    for b in Ops:
        ez,ex = parity_counts_2q(a,b)
        classes[(ez,ex)] += 1
        if not (a is I and b is I):
            classes_nonid[(ez,ex)] += 1

print(classes)
print(classes_nonid)
Tool #23
{(0, 0): 4, (0, 1): 4, (1, 0): 4, (1, 1): 4}
{(0, 0): 3, (0, 1): 4, (1, 0): 4, (1, 1): 4}
Assistant #24
  1. Step-by-Step Derivation
  • Ideal state prepared by the circuit
  • Start from |0000⟩.
  • Apply H2, then CNOT21: prepares a Bell pair on (2,1): |Φ+⟩21 = (|00⟩ + |11⟩)/√2.
  • Apply H0, then CNOT03: prepares a Bell pair on (0,3): |Φ+⟩03 = (|00⟩ + |11⟩)/√2.
  • Hence the ideal final 4-qubit state is the product
\[ |\psi_{\text{ideal}}\rangle = |\Phi^+\rangle_{03}\otimes|\Phi^+\rangle_{21}. \]
This is a valid code state stabilized by S = {XXXX, ZZZZ}, and for the logical Bell state on A,B it is also stabilized by the logical operators X_AX_B = IXXI and Z_AZ_B = IZZI.
  • Noise model and placement
  • A two-qubit depolarizing channel acts after each CNOT:
    • After CNOT21, a channel on qubits (2,1).
    • After CNOT03, a channel on qubits (0,3).
  • Each channel applies II with probability 1 − p, and one of the 15 non-identity two-qubit Paulis with probability p/15 each.
  • The two channels are independent and on disjoint pairs; thus the net error is E = E03 ⊗ E21, and because they occur after the CNOTs and on disjoint qubits, they commute with the subsequent gates and are effectively applied at the end to |ψideal⟩.

  • Post-selection on detectable errors

  • We post-select on measuring +1 for both stabilizers XXXX and ZZZZ. For a code state, an error E is accepted iff E commutes with both stabilizers, i.e., E lies in the normalizer N(S).
  • Characterize commutation by parities. For a 4-qubit Pauli P0⊗P1⊗P2⊗P3:
    • Commutation with XXXX requires an even number of factors in {Z,Y}.
    • Commutation with ZZZZ requires an even number of factors in {X,Y}.
  • Since E = E03 ⊗ E21 with E03 on (0,3) and E21 on (2,1), define for each pair (e_z, e_x) parities:
    • e_z = parity of the count of {Z,Y} on the two qubits of the pair,
    • e_x = parity of the count of {X,Y} on the two qubits of the pair.
  • Acceptance requires the parity vectors to match across the two pairs:
\[ (e_z^{03}, e_x^{03}) = (e_z^{21}, e_x^{21}). \]
  • Counting on a single two-qubit pair
  • Among the 16 two-qubit Paulis, the parity classes (e_z,e_x) are equally populated: 4 elements each.
  • Among the 15 non-identity operators, the counts by parity are:

    • (0,0): 3 elements (XX, YY, ZZ),
    • (0,1): 4 elements,
    • (1,0): 4 elements,
    • (1,1): 4 elements.
  • Acceptance probability Pacc(p)

  • Sum weights over all accepted combinations (E03, E21). There are three cases: 1) (II, II): 1 combination, weight (1 − p)^2. 2) Exactly one non-identity (say on one pair): accepted only if it is in class (0,0), i.e., 3 choices on that pair; total 2×3 combinations, each with weight (1 − p)·(p/15). 3) Both non-identity: accepted if parity classes match across pairs; number of such combinations is 3^2 + 4^2 + 4^2 + 4^2 = 57, each with weight (p/15)^2.
  • Therefore,
\[ P_{\text{acc}}(p) &= (1-p)^2 + 2\cdot 3\,(1-p)\frac{p}{15} + 57\left(\frac{p}{15}\right)^2 \\ &= 1 - \frac{24}{15}p + \frac{64}{75}p^2. \]
  • “Good” (logically correct) accepted events
  • The target logical Bell state |Φ+⟩AB is stabilized (up to global phase) by the logical operators {I, X_AX_B, Z_AZ_B, Y_AY_B}. At the physical level, this corresponds to applying on each Bell pair one of {II, XX, YY, ZZ}.
  • Thus “good” events are exactly those for which each pair’s two-qubit Pauli is in the (0,0) class. Counting and weighting: 1) (II, II): 1 combination, weight (1 − p)^2. 2) Exactly one non-identity, in (0,0): 2×3 combinations, each of weight (1 − p)(p/15). 3) Both non-identity, both in (0,0): 3×3 = 9 combinations, each of weight (p/15)^2.
  • Hence
\[ P_{\text{good}}(p) &= (1-p)^2 + 2\cdot 3\,(1-p)\frac{p}{15} + 9\left(\frac{p}{15}\right)^2 \\ &= \Big((1-p) + 3\frac{p}{15}\Big)^2 \\ &= \left(1 - \frac{12}{15}p\right)^2. \]
  • Logical state fidelity after post-selection
  • The conditional logical fidelity is the fraction of accepted runs that are “good”:
\[ F_{\text{logical}}(p) = \frac{P_{\text{good}}(p)}{P_{\text{acc}}(p)} = \frac{\left(1 - \frac{12}{15}p\right)^2}{1 - \frac{24}{15}p + \frac{64}{75}p^2}. \]
  • Small-p expansion shows first-order cancellation due to post-selection:
\[ F_{\text{logical}}(p) = 1 - \frac{16}{75}p^2 + O(p^3). \]
  1. Mathematical Typesetting
  • Key expressions are displayed above.
  1. Conventions and Units
  • Probability p ∈ [0,1] is dimensionless; fidelity is dimensionless.
  1. Final Answer: \(F_{\text{logical}}(p) = \dfrac{\left(1 - \dfrac{12}{15}p\right)^2}{1 - \dfrac{24}{15}p + \dfrac{64}{75}p^2} = \dfrac{\left(1 - \dfrac{4}{5}p\right)^2}{1 - \dfrac{8}{5}p + \dfrac{64}{75}p^2}.\)
User #25

The correct answer is: $ F_{\rm{logical}}=1 - \frac{\frac{16}{75}p^2}{1-\frac{8}{5}p + \frac{64}{75}p^2} $

Now let’s solve the next Checkpoint.

Checkpoint 3

Suppose that we prepare a logical two-qubit \(|00\rangle_{AB}\) state in the [[4,2,2]] code. To do so, we introduce an ancilla qubit, qubit 4, and use the following state preparation circuit:

\[ M_4 (CNOT_{04}) (CNOT_{34}) (CNOT_{23}) (CNOT_{10}) (CNOT_{12}) (H_1) \]

Note that this equation is written in matrix multiplication order, while the quantum operations in the circuit occur in the reverse order (from right-to-left in the above equation). \(H\) is a single-qubit Hadamard gate and \(M\) is a single-qubit measurement. The ancilla is used to detect errors in the state preparation circuit and makes the circuit fault-tolerant. If the ancilla measurement is \(|0\rangle\) (\(|1\rangle\)), the state preparation succeeds (fails).

What is the logical state fidelity of the final 2-qubit logical state at the end of the circuit as a function of two-qubit gate error rate \(p\), assuming the state is post-selected on all detectable errors in the code and on the ancilla qubit measuring \(|0\rangle\)?

Assistant #26
Tool Call (code to execute)
import numpy as np
from itertools import product

# Symplectic representation for 5-qubit Paulis: bitstrings z[5], x[5]
# Conjugation by CNOT(c,t):
#   x_t ^= x_c
#   z_c ^= z_t

def conj_cnot(z, x, c, t):
    x[t] ^= x[c]
    z[c] ^= z[t]

# Build local 2-qubit pauli on pair (i,j)
# map single label to (z,x) bits: I:(0,0), X:(0,1), Z:(1,0), Y:(1,1)
label_to_bits = {0:(0,0), 1:(0,1), 2:(1,1), 3:(1,0)}  # order: I,X,Y,Z

pairs = [(1,2), (1,0), (2,3), (3,4), (0,4)]  # after each CNOT in time order H1, CNOT12,...,
# So subsequent gates for index k are CNOTs in pairs[k+1:]

# For each insertion site k, produce list of 16 resulting 10-bit bitstrings after propagating to end (before measurement)
S = []
for k, (a,b) in enumerate(pairs):
    arr = []
    for pa in range(4):
        for pb in range(4):
            z = [0]*5
            x = [0]*5
            za, xa = label_to_bits[pa]
            zb, xb = label_to_bits[pb]
            z[a] = za; x[a] = xa
            z[b] = zb; x[b] = xb
            # propagate through subsequent CNOTs from k+1 to end
            for c2, t2 in pairs[k+1:]:
                conj_cnot(z, x, c2, t2)
            # pack to 10-bit integer [z0..z4, x0..x4]
            bint = 0
            for i in range(5):
                bint |= (z[i] & 1) << i
            for i in range(5):
                bint |= (x[i] & 1) << (5+i)
            arr.append(bint)
    S.append(arr)

# Sanity: identity at each site should map to 0 bitstring
for k in range(5):
    assert S[k][0] == 0

# Build distribution vector for each site with parameter p: d_k (length 1024)
N = 1<<10

# We'll compute P_acc(p) and P_good(p) using XOR convolution via FWT

def fwt_xor(a, inv=False):
    n = len(a); h = 1
    a = a.copy()
    while h < n:
        for i in range(0, n, h<<1):
            for j in range(i, i+h):
                u = a[j]; v = a[j+h]
                a[j] = u+v
                a[j+h] = u-v
        h <<= 1
    if inv:
        a /= n
    return a

# classification functions

def accept_mask_bint(b):
    # ancilla index 4: x4 must be 0
    x4 = (b >> (5+4)) & 1
    if x4:
        return False
    # data parities
    x_parity = 0
    z_parity = 0
    for i in range(4):
        x_parity ^= (b >> (5+i)) & 1
        z_parity ^= (b >> i) & 1
    return (x_parity == 0) and (z_parity == 0)

# logical operator anticommute bits for data part
# Compute x_A=(x0+x1) mod 2, x_B=(x0+x2) mod 2 as anticommute with Z_A, Z_B

def xA_xB_from_bint(b):
    # Data x bits: positions 5..8
    x0 = (b >> (5+0)) & 1
    x1 = (b >> (5+1)) & 1
    x2 = (b >> (5+2)) & 1
    x3 = (b >> (5+3)) & 1
    xA = (x0 ^ x1) & 1
    xB = (x0 ^ x2) & 1
    return xA, xB

# Build accept mask and good mask arrays of length 1024
acc_mask = np.zeros(N, dtype=bool)
good_mask = np.zeros(N, dtype=bool)
for b in range(N):
    if accept_mask_bint(b):
        acc_mask[b] = True
        xA, xB = xA_xB_from_bint(b)
        if (xA == 0) and (xB == 0):
            good_mask[b] = True

# Build site distributions in transform domain once for numeric p values

def site_distribution_transforms(p):
    dTs = []
    for k in range(5):
        d = np.zeros(N)
        arr = S[k]
        # identity index 0 weight 1-p, others p/15 uniformly but there can be duplicates mapping to same bint
        # accumulate weights for non-identity entries
        # arr has length 16 with arr[0]==0
        d[0] += 1-p
        # accumulate p/15 to each arr[j] for j>0
        for j in range(1,16):
            d[arr[j]] += p/15
        dT = fwt_xor(d)
        dTs.append(dT)
    return dTs

# Compute distribution of E_final and Pacc, Pgood

def acceptance_and_fidelity(p):
    dTs = site_distribution_transforms(p)
    # multiply pointwise transforms
    prodT = dTs[0]
    for k in range(1,5):
        prodT = prodT * dTs[k]
    dist = fwt_xor(prodT, inv=True)  # length 1024
    # ensure non-negative typical rounding
    dist = np.maximum(dist, 0)
    Pacc = float(dist[acc_mask].sum())
    Pgood = float(dist[good_mask].sum())
    F = Pgood / Pacc if Pacc > 0 else 1.0
    return Pacc, Pgood, F, dist

for p in [0.0, 0.001, 0.01, 0.05, 0.1, 0.2, 0.5, 1.0]:
    Pacc, Pgood, F, dist = acceptance_and_fidelity(p)
    print(p, 'Pacc=', Pacc, 'Pgood=', Pgood, 'F=', F)
Tool #27
0.0 Pacc= 1.0 Pgood= 1.0 F= 1.0
0.001 Pacc= 0.9954760436293117 Pgood= 0.9954754046527048 F= 0.9999993581195539
0.01 Pacc= 0.9555956743560909 Pgood= 0.9555326923008884 F= 0.9999340913140435
0.05 Pacc= 0.7956173988345682 Pgood= 0.7941416466962963 F= 0.9981451484841413
0.1 Pacc= 0.6313155735703704 Pgood= 0.6258801866271605 F= 0.9913903803885744
0.2 Pacc= 0.39882243919012356 Pgood= 0.38048496071111115 F= 0.9540209459722232
0.5 Pacc= 0.14752790123456788 Pgood= 0.0816474074074074 F= 0.5534370564764481

1.0 Pacc= 0.1250014814814815 Pgood= 0.03164049382716049 F= 0.253120950665276
Assistant #28
Tool Call (code to execute)
import numpy as np

# Use the previous functions acceptance_and_fidelity

# Fit polynomials Pacc(p) and Pgood(p) of degree <=5 using values at 6 points
ps = np.array([0.0, 0.2, 0.4, 0.6, 0.8, 1.0])
vals_acc = []
vals_good = []
for p in ps:
    Pacc, Pgood, F, _ = acceptance_and_fidelity(p)
    vals_acc.append(Pacc)
    vals_good.append(Pgood)
vals_acc = np.array(vals_acc)
vals_good = np.array(vals_good)

# Vandermonde matrix
V = np.vstack([ps**k for k in range(6)]).T
coeff_acc = np.linalg.solve(V, vals_acc)
coeff_good = np.linalg.solve(V, vals_good)
print('coeff_acc:', coeff_acc)
print('coeff_good:', coeff_good)

# Check fit
for p in [0.1,0.3,0.5,0.7,0.9]:
    Pacc_est = sum(coeff_acc[k]*p**k for k in range(6))
    Pgood_est = sum(coeff_good[k]*p**k for k in range(6))
    Pacc, Pgood, F,_ = acceptance_and_fidelity(p)
    print(p, 'acc diff', Pacc-Pacc_est, 'good diff', Pgood-Pgood_est)
Tool #29
Traceback (most recent call last):
  File "<stdin>", line 10, in <module>
NameError: name 'acceptance_and_fidelity' is not defined
Assistant #30
import numpy as np
from itertools import product

# Reconstruct needed functions quickly from previous cell by reusing definitions
# We'll regenerate everything necessary

def conj_cnot(z, x, c, t):
    x[t] ^= x[c]
    z[c] ^= z[t]

label_to_bits = {0:(0,0), 1:(0,1), 2:(1,1), 3:(1,0)}
pairs = [(1,2), (1,0), (2,3), (3,4), (0,4)]
S = []
for k, (a,b) in enumerate(pairs):
    arr = []
    for pa in range(4):
        for pb in range(4):
            z = [0]*5
            x = [0]*5
            za, xa = label_to_bits[pa]
            zb, xb = label_to_bits[pb]
            z[a] = za; x[a] = xa
            z[b] = zb; x[b] = xb
            for c2, t2 in pairs[k+1:]:
                conj_cnot(z, x, c2, t2)
            bint = 0
            for i in range(5):
                bint |= (z[i] & 1) << i
            for i in range(5):
                bint |= (x[i] & 1) << (5+i)
            arr.append(bint)
    S.append(arr)

N = 1<<10

def fwt_xor(a, inv=False):
    n = len(a); h = 1
    a = a.copy()
    while h < n:
        for i in range(0, n, h<<1):
            for j in range(i, i+h):
                u = a[j]; v = a[j+h]
                a[j] = u+v
                a[j+h] = u-v
        h <<= 1
    if inv:
        a /= n
    return a

# acceptance masks
acc_mask = np.zeros(N, dtype=bool)
good_mask = np.zeros(N, dtype=bool)
for b in range(N):
    x4 = (b >> (5+4)) & 1
    if x4: continue
    z_par = 0; x_par = 0
    for i in range(4):
        z_par ^= (b >> i) & 1
        x_par ^= (b >> (5+i)) & 1
    if (z_par == 0) and (x_par == 0):
        acc_mask[b] = True
        x0 = (b >> (5+0)) & 1
        x1 = (b >> (5+1)) & 1
        x2 = (b >> (5+2)) & 1
        xA = x0 ^ x1
        xB = x0 ^ x2
        if xA==0 and xB==0:
            good_mask[b] = True


def site_distribution_transforms(p):
    dTs = []
    for k in range(5):
        d = np.zeros(N)
        arr = S[k]
        d[0] += 1-p
        for j in range(1,16):
            d[arr[j]] += p/15
        dTs.append(fwt_xor(d))
    return dTs


def acceptance_and_fidelity(p):
    dTs = site_distribution_transforms(p)
    prodT = dTs[0]
    for k in range(1,5):
        prodT *= dTs[k]
    dist = fwt_xor(prodT, inv=True)
    dist = np.maximum(dist, 0)
    Pacc = float(dist[acc_mask].sum())
    Pgood = float(dist[good_mask].sum())
    F = Pgood/Pacc if Pacc>0 else 1.0
    return Pacc, Pgood, F

ps = np.array([0.0, 0.2, 0.4, 0.6, 0.8, 1.0])
vals_acc = np.array([acceptance_and_fidelity(float(p))[0] for p in ps])
vals_good = np.array([acceptance_and_fidelity(float(p))[1] for p in ps])
V = np.vstack([ps**k for k in range(6)]).T
coeff_acc = np.linalg.solve(V, vals_acc)
coeff_good = np.linalg.solve(V, vals_good)
print('coeff_acc:', coeff_acc)
print('coeff_good:', coeff_good)

for p in [0.1,0.3,0.5,0.7,0.9]:
    Pacc_est = sum(coeff_acc[k]*p**k for k in range(6))
    Pgood_est = sum(coeff_good[k]*p**k for k in range(6))
    Pacc, Pgood, F = acceptance_and_fidelity(p)
    print(p, 'acc diff', Pacc-Pacc_est, 'good diff', Pgood-Pgood_est)
Tool #31
coeff_acc: [ 1.         -4.53333333  9.38666667 -9.70903704  5.0163358  -1.03563062]
coeff_good: [ 1.         -4.53333333  8.74666667 -8.68503704  4.40952099 -0.90617679]
0.1 acc diff -6.661338147750939e-16 good diff -2.220446049250313e-16
0.3 acc diff -4.440892098500626e-16 good diff -2.7755575615628914e-17
0.5 acc diff -2.498001805406602e-16 good diff 1.8041124150158794e-16
0.7 acc diff -9.43689570931383e-16 good diff -1.5265566588595902e-16
0.9 acc diff 1.0269562977782698e-15 good diff 6.661338147750939e-16
Assistant #32
Tool Call (code to execute)
from fractions import Fraction

coeff_acc_float = [1.0, -4.533333333333333, 9.386666666666667, -9.709037037037037, 5.016335800865801, -1.0356306172839506]
coeff_good_float = [1.0, -4.533333333333333, 8.746666666666666, -8.685037037037037, 4.409520987654321, -0.9061767901234566]

# approximate with denominator bounded by e.g., 15**5
Dmax = 15**6

coeff_acc_rat = [Fraction(x).limit_denominator(Dmax) for x in coeff_acc_float]
coeff_good_rat = [Fraction(x).limit_denominator(Dmax) for x in coeff_good_float]
coeff_acc_rat, coeff_good_rat
Tool #33
''
Assistant #34
# display as fractions
for name, coeff_rat in [('acc', coeff_acc_rat), ('good', coeff_good_rat)]:
    print(name)
    for f in coeff_rat:
        print(f, float(f))
Tool #35
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
NameError: name 'coeff_acc_rat' is not defined
Assistant #36
from fractions import Fraction
coeff_acc_float = [1.0, -4.533333333333333, 9.386666666666667, -9.709037037037037, 5.016335800865801, -1.0356306172839506]
coeff_good_float = [1.0, -4.533333333333333, 8.746666666666666, -8.685037037037037, 4.409520987654321, -0.9061767901234566]
Dmax = 10**9
coeff_acc_rat = [Fraction(x).limit_denominator(Dmax) for x in coeff_acc_float]
coeff_good_rat = [Fraction(x).limit_denominator(Dmax) for x in coeff_good_float]
print('acc:')
for f in coeff_acc_rat:
    print(f)
print('good:')
for f in coeff_good_rat:
    print(f)
Tool #37
acc:
1
-68/15
704/75
-32768/3375
1655594011/330040507
-262144/253125
good:
1
-68/15
656/75
-29312/3375
223232/50625
-229376/253125
Assistant #38
Tool Call (code to execute)
from fractions import Fraction
from itertools import product

# Setup mapping as earlier for bitstrings S[k] etc.

def conj_cnot_bits(z, x, c, t):
    x[t] ^= x[c]
    z[c] ^= z[t]

label_to_bits = {0:(0,0), 1:(0,1), 2:(1,1), 3:(1,0)}
pairs = [(1,2), (1,0), (2,3), (3,4), (0,4)]
S = []
for k, (a,b) in enumerate(pairs):
    arr = []
    for pa in range(4):
        for pb in range(4):
            z = [0]*5
            x = [0]*5
            za, xa = label_to_bits[pa]
            zb, xb = label_to_bits[pb]
            z[a] = za; x[a] = xa
            z[b] = zb; x[b] = xb
            for c2, t2 in pairs[k+1:]:
                conj_cnot_bits(z, x, c2, t2)
            bint = 0
            for i in range(5):
                bint |= (z[i] & 1) << i
            for i in range(5):
                bint |= (x[i] & 1) << (5+i)
            arr.append(bint)
    S.append(arr)

N = 1<<10

# Build per-site arrays A_k (constant term) and B_k (coefficient of p) in transform domain
# For site k: d_k(b;p) = A_k(b) + p * B_k(b), with A_k(b) = delta_{b,0}, and B_k(0)=-1, B_k(b!=0)=n_k(b)/15

A_list = []
B_list = []
for k in range(5):
    A = [Fraction(0,1)]*N
    B = [Fraction(0,1)]*N
    arr = S[k]
    # identity mapping index 0 occurs once
    A[0] = Fraction(1,1)
    B[0] = Fraction(-1,1)
    # count multiplicities for b!=0 among the 15 non-identity entries
    counts = {}
    for idx in range(1,16):
        b = arr[idx]
        counts[b] = counts.get(b, 0) + 1
    for b, cnt in counts.items():
        B[b] = Fraction(cnt, 15)
    A_list.append(A)
    B_list.append(B)

# Walsh-Hadamard Transform for Fractions

def fwt_xor_frac(a):
    n = len(a); h = 1
    a = a[:]  # copy
    while h < n:
        for i in range(0, n, h<<1):
            for j in range(i, i+h):
                u = a[j]; v = a[j+h]
                a[j] = u + v
                a[j+h] = u - v
        h <<= 1
    return a

# forward transforms for A_k and B_k
AT_list = [fwt_xor_frac(A) for A in A_list]
BT_list = [fwt_xor_frac(B) for B in B_list]

# Multiply across sites: For each frequency index f, compute poly P_f(p) = prod_k (AT_k[f] + p * BT_k[f])
# Return coefficients C[d][f] for d=0..5 as Fractions
DcoeffT = [[Fraction(0,1)]*N for _ in range(6)]  # init zeros
for f in range(N):
    # start with degree-0 polynomial '1'
    coeffs = [Fraction(1,1)] + [Fraction(0,1)]*5
    for k in range(5):
        a = AT_list[k][f]
        b = BT_list[k][f]
        # multiply coeffs by (a + p b)
        new = [Fraction(0,1)]*6
        for d in range(6):
            if coeffs[d] != 0:
                new[d] += coeffs[d] * a
                if d+1 <= 5:
                    new[d+1] += coeffs[d] * b
        coeffs = new
    for d in range(6):
        DcoeffT[d][f] = coeffs[d]

# Inverse transform: divide by N=1024
invN = Fraction(1, N)
Dcoeff = []
for d in range(6):
    inv = fwt_xor_frac(DcoeffT[d])
    inv = [x * invN for x in inv]
    Dcoeff.append(inv)

# Define masks
acc_mask = [False]*N
good_mask = [False]*N
for b in range(N):
    x4 = (b >> (5+4)) & 1
    if x4:
        continue
    z_par = 0; x_par = 0
    for i in range(4):
        z_par ^= (b >> i) & 1
        x_par ^= (b >> (5+i)) & 1
    if z_par==0 and x_par==0:
        acc_mask[b] = True
        x0 = (b >> (5+0)) & 1
        x1 = (b >> (5+1)) & 1
        x2 = (b >> (5+2)) & 1
        xA = x0 ^ x1
        xB = x0 ^ x2
        if xA==0 and xB==0:
            good_mask[b] = True

# Sum coefficients over masks to get Pacc(p) and Pgood(p)
Pacc_coeffs = [Fraction(0,1)]*6
Pgood_coeffs = [Fraction(0,1)]*6
for d in range(6):
    Pacc_coeffs[d] = sum(Dcoeff[d][b] for b in range(N) if acc_mask[b])
    Pgood_coeffs[d] = sum(Dcoeff[d][b] for b in range(N) if good_mask[b])

print('Pacc coefficients by degree 0..5:')
print(Pacc_coeffs)
print('Pgood coefficients by degree 0..5:')
print(Pgood_coeffs)

# Check by evaluating polynomials at a few p values and comparing numeric earlier acceptance_and_fidelity results
import math
for p in [0.0,0.1,0.2,0.5,1.0]:
    Pacc = sum(float(Pacc_coeffs[d])*(p**d) for d in range(6))
    Pgood = sum(float(Pgood_coeffs[d])*(p**d) for d in range(6))
    Pacc_num, Pgood_num, F_num = (None, None, None)
    # We'll recompute using earlier float method quickly
    # Rebuild site distributions
    # But we skip due to interplay; Instead just print values
    print('p=', p, 'Pacc poly', Pacc, 'Pgood poly', Pgood)
Tool #39
Pacc coefficients by degree 0..5:
[Fraction(1, 1), Fraction(-68, 15), Fraction(704, 75), Fraction(-32768, 3375), Fraction(253952, 50625), Fraction(-262144, 253125)]
Pgood coefficients by degree 0..5:
[Fraction(1, 1), Fraction(-68, 15), Fraction(656, 75), Fraction(-29312, 3375), Fraction(223232, 50625), Fraction(-229376, 253125)]
p= 0.0 Pacc poly 1.0 Pgood poly 1.0
p= 0.1 Pacc poly 0.6313155735703703 Pgood poly 0.6258801866271604
p= 0.2 Pacc poly 0.39882243919012345 Pgood poly 0.38048496071111104
p= 0.5 Pacc poly 0.14752790123456802 Pgood poly 0.08164740740740722
p= 1.0 Pacc poly 0.12500148148148216 Pgood poly 0.031640493827159055
Assistant #40
Tool Call (code to execute)
from fractions import Fraction

Pacc = [Fraction(1,1), Fraction(-68,15), Fraction(704,75), Fraction(-32768,3375), Fraction(253952,50625), Fraction(-262144,253125)]
Pgood = [Fraction(1,1), Fraction(-68,15), Fraction(656,75), Fraction(-29312,3375), Fraction(223232,50625), Fraction(-229376,253125)]

# series for ratio up to p^5
def ratio_series(num, den, order=5):
    # compute coefficients a_k for (sum n_k p^k)/(sum d_k p^k)
    a = [Fraction(0,1)]*(order+1)
    a[0] = num[0]/den[0]
    for k in range(1, order+1):
        # sum_{j=0}^k d_j a_{k-j} = n_k
        s = num[k]
        for j in range(1, k+1):
            s -= den[j]*a[k-j]
        a[k] = s/den[0]
    return a

series = ratio_series(Pgood, Pacc, 5)
series
Tool #41
''
Assistant #42
from fractions import Fraction
Pacc = [Fraction(1,1), Fraction(-68,15), Fraction(704,75), Fraction(-32768,3375), Fraction(253952,50625), Fraction(-262144,253125)]
Pgood = [Fraction(1,1), Fraction(-68,15), Fraction(656,75), Fraction(-29312,3375), Fraction(223232,50625), Fraction(-229376,253125)]

def ratio_series(num, den, order):
    a = [Fraction(0,1)]*(order+1)
    a[0] = num[0]/den[0]
    for k in range(1,order+1):
        s = num[k]
        for j in range(1,k+1):
            s -= den[j]*a[k-j]
        a[k] = s/den[0]
    return a

series = ratio_series(Pgood, Pacc, 5)
print(series)
print([float(s) for s in series])
Tool #43
[Fraction(1, 1), Fraction(0, 1), Fraction(-16, 25), Fraction(-704, 375), Fraction(-10496, 3375), Fraction(-216064, 84375)]
[1.0, 0.0, -0.64, -1.8773333333333333, -3.1099259259259258, -2.5607585185185187]
Assistant #44
Tool Call (code to execute)
from fractions import Fraction
Pacc = [Fraction(1,1), Fraction(-68,15), Fraction(704,75), Fraction(-32768,3375), Fraction(253952,50625), Fraction(-262144,253125)]
Pgood = [Fraction(1,1), Fraction(-68,15), Fraction(656,75), Fraction(-29312,3375), Fraction(223232,50625), Fraction(-229376,253125)]

def poly_val(coeffs,p):
    s=0.0
    for k,c in enumerate(coeffs):
        s+= float(c)*(p**k)
    return s

for p in [0.01, 0.05, 0.1, 0.2]:
    Paccv=poly_val(Pacc,p)
    Pgoodv=poly_val(Pgood,p)
    print(p,Pgoodv/Paccv)
Tool #45
0.01 0.9999340913140434
0.05 0.9981451484841417
0.1 0.9913903803885744
0.2 0.9540209459722232
Assistant #46
  1. Step-by-Step Derivation
  • Ideal target and circuit
  • Data qubits (0,1,2,3) start in |0000⟩ and ancilla (4) in |0⟩.
  • Apply, right-to-left: H1, CNOT12, CNOT10, CNOT23, CNOT34, CNOT04, then measure M4.
  • With no errors, the data end in
\[ |\psi_{\text{ideal}}\rangle_{0123}=\frac{|0000\rangle+|1111\rangle}{\sqrt{2}}, \]
while the ancilla deterministically measures |0⟩. This code state is the logical |00⟩AB of the [[4,2,2]] code (it is +1 eigenstate of Z_A=ZZII and Z_B=ZIZI, and of the stabilizers XXXX, ZZZZ).
  • Noise model
  • After each of the five CNOTs, a two-qubit depolarizing channel acts on the gate’s two qubits: II with probability 1−p and any of the 15 non-identity two-qubit Paulis with probability p/15 each.
  • The five CNOTs (and their error locations, from earliest to latest) are on pairs: (1,2), (1,0), (2,3), (3,4), (0,4).

  • Pauli-frame propagation (Clifford-symplectic)

  • The circuit is Clifford, so any sequence of Pauli errors propagates to a single effective Pauli at the end of the circuit. Represent Paulis by their binary symplectic vector (z|x) over 5 qubits.
  • Conjugation by CNOT(c→t) acts on bits as:
    • x_t ← x_t ⊕ x_c,
    • z_c ← z_c ⊕ z_t, and leaves the other bits unchanged.
  • For each insertion point k and each of the 16 local two-qubit Paulis on its pair, propagate (z|x) forward through the remaining CNOTs to obtain a final 10-bit string b_k ∈ {0,1}^10. The overall effective Pauli is the XOR sum b = b_1 ⊕ b_2 ⊕ b_3 ⊕ b_4 ⊕ b_5.

  • Post-selection criteria

  • Ancilla post-selection: The ancilla is measured in the Z basis and ideally is |0⟩. A final ancilla Pauli of type X or Y flips/rejects; acceptance requires no X on ancilla: x4=0 (z4 arbitrary).
  • Code error-detection post-selection: The final data Pauli must commute with both stabilizers XXXX and ZZZZ. In symplectic bits this is exactly:

    • even parity of data z bits: z0⊕z1⊕z2⊕z3 = 0,
    • even parity of data x bits: x0⊕x1⊕x2⊕x3 = 0.
  • Logical success condition (fidelity with |00⟩AB)

  • Within the normalizer, the induced logical Pauli on A,B is determined by its commutation with ZA, XA, ZB, XB. For our chosen logicals,
    • x_A = (x0 ⊕ x1) = anticomm(E_data, Z_A),
    • z_A = (z0 ⊕ z2) = anticomm(E_data, X_A),
    • x_B = (x0 ⊕ x2) = anticomm(E_data, Z_B),
    • z_B = (z0 ⊕ z1) = anticomm(E_data, X_B).
  • The logical |00⟩AB is +1 eigenstate of Z_A and Z_B. Therefore any accepted event whose induced logical Pauli contains an X on A or on B (i.e., x_A=1 or x_B=1) flips |00⟩. Logical Z-type byproducts (z_A, z_B) leave |00⟩ unchanged.
  • Hence, among accepted events, “good” = those with x_A=0 and x_B=0.

  • Exact evaluation via XOR convolution

  • At each of the five CNOT locations, the local error variable takes value 0 (identity) with weight 1−p and one of 15 propagated bitstrings with weight p/15 each (possibly with multiplicities). The five independent contributions add by XOR to yield the final bitstring b. The corresponding distribution is an XOR convolution of five mixtures.
  • Using the fast Walsh–Hadamard transform for XOR-convolutions (and exact rational arithmetic), one obtains:
    • Acceptance probability:
\[ P_{\text{acc}}(p) &= 1 - \frac{68}{15}p + \frac{704}{75}p^2 - \frac{32768}{3375}p^3 \\ &\quad + \frac{253952}{50625}p^4 - \frac{262144}{253125}p^5. \]
- “Good” (logically correct) probability:
\[ P_{\text{good}}(p) &= 1 - \frac{68}{15}p + \frac{656}{75}p^2 - \frac{29312}{3375}p^3 \\ &\quad + \frac{223232}{50625}p^4 - \frac{229376}{253125}p^5. \]
  • The logical state fidelity conditioned on post-selection is
\[ F_{\text{logical}}(p)=\frac{P_{\text{good}}(p)}{P_{\text{acc}}(p)}. \]
  • Small-p expansion
  • Expanding the ratio gives
\[ F_{\text{logical}}(p)=1 - \frac{16}{25}p^2 - \frac{704}{375}p^3 - \frac{10496}{3375}p^4 - \frac{216064}{84375}p^5 + O(p^6), \]
showing first-order cancellation (no p term) from the fault-tolerant detection and that the leading degradation is quadratic.
  1. Mathematical Typesetting
  • Final closed-form expression:
\[ F_{\text{logical}}(p)=\frac{1 - \frac{68}{15}p + \frac{656}{75}p^2 - \frac{29312}{3375}p^3 + \frac{223232}{50625}p^4 - \frac{229376}{253125}p^5}{1 - \frac{68}{15}p + \frac{704}{75}p^2 - \frac{32768}{3375}p^3 + \frac{253952}{50625}p^4 - \frac{262144}{253125}p^5}. \]
  1. Conventions and Units
  • p ∈ [0,1] is the two-qubit gate error probability per CNOT.
  • Fidelity is dimensionless.
  1. Final Answer: \(F_{\text{logical}}(p) = \dfrac{1 - \dfrac{68}{15}p + \dfrac{656}{75}p^2 - \dfrac{29312}{3375}p^3 + \dfrac{223232}{50625}p^4 - \dfrac{229376}{253125}p^5}{1 - \dfrac{68}{15}p + \dfrac{704}{75}p^2 - \dfrac{32768}{3375}p^3 + \dfrac{253952}{50625}p^4 - \dfrac{262144}{253125}p^5}\,.\)
User #47

Populate your final answer into the code template provided below. This step is purely for formatting/display purposes. No additional reasoning or derivation should be performed. Do not import any modules or packages beyond what is provided in the template.

import sympy as sp

p = sp.symbols('p')

def answer(p):
    r"""
    Return the expression of the logical state fidelity of the final 2-qubit logical state
    at the end of the circuit as a function of two-qubit gate error rate $p$.

    Inputs
    ----------
    p: sympy.Symbol, two-qubit gate error rate, $p$

    Outputs
    ----------
    F_logical: sympy.Expr, logical state fidelity of the final 2-qubit logical state
    """

    # ------------------ FILL IN YOUR RESULTS BELOW ------------------
    F_logical = ...  # a SymPy expression of inputs
    # ---------------------------------------------------------------

    return F_logical
Assistant #48
import sympy as sp

p = sp.symbols('p')

def answer(p):
    r"""
    Return the expression of the logical state fidelity of the final 2-qubit logical state
    at the end of the circuit as a function of two-qubit gate error rate $p$.

    Inputs
    ----------
    p: sympy.Symbol, two-qubit gate error rate, $p$

    Outputs
    ----------
    F_logical: sympy.Expr, logical state fidelity of the final 2-qubit logical state
    """

    # ------------------ FILL IN YOUR RESULTS BELOW ------------------
    numerator = (
        1
        - sp.Rational(68, 15)*p
        + sp.Rational(656, 75)*p**2
        - sp.Rational(29312, 3375)*p**3
        + sp.Rational(223232, 50625)*p**4
        - sp.Rational(229376, 253125)*p**5
    )
    denominator = (
        1
        - sp.Rational(68, 15)*p
        + sp.Rational(704, 75)*p**2
        - sp.Rational(32768, 3375)*p**3
        + sp.Rational(253952, 50625)*p**4
        - sp.Rational(262144, 253125)*p**5
    )
    F_logical = numerator / denominator  # a SymPy expression of inputs
    # ---------------------------------------------------------------

    return F_logical