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
  1. Step-by-Step Derivation
  • Ideal output state of the circuit
  • Start from |0000⟩.
  • Apply H on qubit 2, then CNOT21: this prepares a Bell pair on qubits (2,1),
\[ |\Phi^+\rangle_{21}=\frac{|00\rangle_{21}+|11\rangle_{21}}{\sqrt{2}}, \]
while qubits (0,3) remain in |00⟩.
  • Apply H on qubit 0, then CNOT03: this prepares a Bell pair on qubits (0,3),
\[ |\Phi^+\rangle_{03}=\frac{|00\rangle_{03}+|11\rangle_{03}}{\sqrt{2}}. \]
  • Therefore, the ideal final 4-qubit state is
\[ |\psi_{\mathrm{id}}\rangle = |\Phi^+\rangle_{21}\otimes |\Phi^+\rangle_{03}. \]
  • Stabilizers of the ideal state
  • The Bell state |Φ+⟩ on a pair (i,j) is stabilized by XiXj and ZiZj, and hence by the 4-element subgroup {I⊗I, XiXj, YiYj, ZiZj} (up to phase).
  • Hence
\[ \mathcal{S} = \langle X_2X_1,\,Z_2Z_1,\,X_0X_3,\,Z_0Z_3\rangle, \]
and factorizes as
\[ \mathcal{S}_{21}=\{II,\,X_2X_1,\,Y_2Y_1,\,Z_2Z_1\},\quad \]
  • Noise model after the CNOTs
  • After each CNOT, a two-qubit depolarizing channel acts on that pair:
\[ \mathcal{E}(\rho)=(1-p)\rho+\frac{p}{15}\sum_{P\in\mathcal{P}_2\setminus\{II\}}P\rho P, \]
where the sum runs over the 15 non-identity two-qubit Paulis on that pair.
  • In our circuit there are two such channels: one on qubits (2,1) after CNOT21 and one on (0,3) after CNOT03. These errors are independent and act on disjoint qubit sets. The single-qubit H gates are assumed perfect.

  • Fidelity with a pure stabilizer state

  • For a pure stabilizer state |ψid⟩, a Pauli error P maps |ψid⟩ to either the same state (up to a global phase) if P∈𝒮, or to a state orthogonal to |ψid⟩ if P∉𝒮. Therefore, the state fidelity is exactly the probability that the net Pauli error lies in the stabilizer group 𝒮.

  • Probability that a single two-qubit depolarizing error is “benign”

  • On a given pair (say 2,1), the “benign” Paulis (those in the pair’s stabilizer) are {II, XX, YY, ZZ}. Under the depolarizing channel:
    • Pr[II] = 1 − p,
    • Pr[XX] = Pr[YY] = Pr[ZZ] = p/15.
  • Hence, the probability that the error on that pair preserves the Bell state is
\[ P_{\mathrm{benign}} = (1-p)+3\frac{p}{15}=1-\frac{4p}{5}. \]
  • Total fidelity
  • Because the two error channels act on disjoint pairs and are independent, the final 4-qubit state fidelity is the product of the two benign probabilities:
\[ F(p)=\left(1-\frac{4p}{5}\right)^2. \]
  1. Mathematical Typesetting

All equations are given above in LaTeX.

  1. Conventions and Units
  • Qubits labeled 0,1,2,3; CNOTij has control i, target j.
  • Two-qubit depolarizing noise of strength p acts after each CNOT.
  • Fidelity is F = ⟨ψid|ρ|ψid⟩.
  1. Final Answer: F(p) = (1 - 4p/5)^2.
User #3

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 #4
Tool Call (code to execute)
import itertools

# Define Pauli multiplication ignoring phase; represent as strings 'I','X','Y','Z'
# Provide function to compute commutation: returns True if commute, False if anticommute.

paulis = ['I','X','Y','Z']

# Single-qubit commutation table: anticommute pairs are X-Z, X-Y, Y-Z
anticommute_pairs = {('X','Z'),('Z','X'),('X','Y'),('Y','X'),('Y','Z'),('Z','Y')}

def single_anticommute(a,b):
    if a=='I' or b=='I' or a==b: return False
    return (a,b) in anticommute_pairs

# For tensor products represented as strings over length 4

def anticommutes(P,Q):
    # Count number of positions where local Paulis anticommute
    count = 0
    for a,b in zip(P,Q):
        if single_anticommute(a,b):
            count += 1
    # anticommute iff odd number of local anticommutes
    return (count % 2)==1

def commutes(P,Q):
    return not anticommutes(P,Q)

# Code stabilizers
S1 = 'XXXX'
S2 = 'ZZZZ'

# Logical operators
X_A = 'XIXI'
Z_A = 'ZZII'
X_B = 'XXII'
Z_B = 'ZIZI'

# Allowed pair Paulis on (2,1) and (0,3)
# Note our order is (0,1,2,3), so to place a 2-qubit Pauli on pair (2,1)
# we fill positions: 0:'I',1:P1,2:P2,3:'I' with P2 on qubit2, P1 on qubit1.

def embed_pair_21(p1,p2):
    # p1 acts on qubit1, p2 acts on qubit2
    return 'I' + p1 + p2 + 'I'

def embed_pair_03(p0,p3):
    # p0 acts on qubit0, p3 acts on qubit3
    return p0 + 'I' + 'I' + p3

# Function to multiply Pauli strings ignoring phase
# We just need for combining P21 and P03, which act on disjoint supports, so multiplication is just overlay

def combine(P,Q):
    # they act on disjoint supports in our construction; assert no conflicts
    out = []
    for a,b in zip(P,Q):
        out.append(a if b=='I' else (b if a=='I' else None))
    # None should not appear
    return ''.join(out)

# Construct all allowed two-qubit Paulis on a pair, with probabilities depending on p
p = 0.123 # placeholder, probabilities symbolic later

pair_nonidentity = [x for x in itertools.product(paulis, paulis) if not (x[0]=='I' and x[1]=='I')]

# We'll classify types per pair: parity (x,z)
XZ = {'I':(0,0),'X':(1,0),'Y':(1,1),'Z':(0,1)}

def pair_type(p,q):
    x = (XZ[p][0] + XZ[q][0]) % 2
    z = (XZ[p][1] + XZ[q][1]) % 2
    return (x,z)

# Verify classes
from collections import defaultdict
classes = defaultdict(list)
for a,b in itertools.product(paulis,paulis):
    classes[pair_type(a,b)].append((a,b))

classes_count = {k:len(v) for k,v in classes.items()}
print('Class sizes per pair type (x,z):', classes_count)

# Now enumerate all P_total = P21 * P03 with types equal for acceptance and compute logical effect

all_pairs = list(itertools.product(paulis, paulis))

# define commutation with logicals -> determine logical effect Ebar as a pair of single-qubit Paulis on (A,B)

def logical_action(P):
    # Determine x,z bits for A and B via commutation
    # x_bit = anticommutes with Z, z_bit = anticommutes with X
    xA = 1 if anticommutes(P, Z_A) else 0
    zA = 1 if anticommutes(P, X_A) else 0
    xB = 1 if anticommutes(P, Z_B) else 0
    zB = 1 if anticommutes(P, X_B) else 0
    # map bits to char
    inv = {(0,0):'I',(1,0):'X',(0,1):'Z',(1,1):'Y'}
    return inv[(xA,zA)] + inv[(xB,zB)]

# Now compute probabilities symbolically in terms of p: we'll tally weights as linear/quadratic polynomials in p and 1-p
from fractions import Fraction

# Probabilities per 2-qubit Pauli on a pair:
# II: 1-p; each other: p/15

def pair_prob(a,b):
    if a=='I' and b=='I':
        return ('lin', Fraction(1,1), Fraction(-1,1))  # Represent as (type, A, B) meaning A + B p, but we want (1-p)
    else:
        return ('constp', Fraction(1,15)) # means (p/15)

# We will accumulate probabilities for acceptance and for GHZ-stabilizing logical actions

from collections import Counter

# We'll symbolically compute: total weights as functions: W = c0 + c1 p + c2 p^2
# Because two independent errors -> polynomial up to p^2.

class Poly:
    def __init__(self,c0=Fraction(0),c1=Fraction(0),c2=Fraction(0)):
        self.c0=Fraction(c0); self.c1=Fraction(c1); self.c2=Fraction(c2)
    def __iadd__(self, other):
        self.c0 += other.c0; self.c1 += other.c1; self.c2 += other.c2
        return self
    def __add__(self, other):
        out=Poly(self.c0,self.c1,self.c2); out+=other; return out
    def __mul__(self, other):
        # multiply polynomials up to degree 2 only; but we will only multiply up to deg1 x deg1 -> deg2 (fine)
        out=Poly()
        out.c0 = self.c0*other.c0
        out.c1 = self.c0*other.c1 + self.c1*other.c0
        out.c2 = self.c0*other.c2 + self.c1*other.c1 + self.c2*other.c0
        return out
    def __repr__(self):
        return f"{self.c0} + {self.c1} p + {self.c2} p^2"

# Build polynomial probability for each (a,b) pair on each pair (21 or 03)

def prob_poly_pair(a,b):
    if a=='I' and b=='I':
        # 1-p = 1 + (-1) p
        return Poly(1, -1, 0)
    else:
        # p/15 = 0 + (1/15) p
        return Poly(0, Fraction(1,15), 0)

# Now enumerate and sum accepted cases.

# For acceptance, type equality across pairs:

def pair_type_key(pair):
    return pair_type(pair[0], pair[1])

# GHZ logical stabilizer set on (A,B): {II, XX, ZZ, YY}
GHZ_stab = {'II', 'XX', 'ZZ', 'YY'}

P_accept = Poly()
P_good = Poly()
# Also tally distribution over logical actions for accepted events
logdist = {}
for a in all_pairs:
    P21 = embed_pair_21(a[1], a[0])  # careful: a is (p0, p1) ??? For pair21 we used p1 on qubit1, p2 on qubit2: here let a=(p1,p2)
    # Actually our 'a' will be (p1,p2) for pair (1,2); we set P21 accordingly
    P21 = embed_pair_21(a[0], a[1])
    prob21 = prob_poly_pair(a[0], a[1])
    type21 = pair_type(a[0], a[1])
    for b in all_pairs:
        P03 = embed_pair_03(b[0], b[1])
        prob03 = prob_poly_pair(b[0], b[1])
        type03 = pair_type(b[0], b[1])
        if type03 != type21:
            continue
        Ptot = combine(P21, P03)
        # Confirm commute with stabilizers
        if not (commutes(Ptot,S1) and commutes(Ptot,S2)):
            # Shouldn't happen: type equality implies commute with both
            pass
        act = logical_action(Ptot)
        logdist.setdefault(act, Poly())
        weight = prob21 * prob03
        P_accept += weight
        logdist[act] += weight
        if act in GHZ_stab:
            P_good += weight

print('P_accept polynomial:', P_accept)
print('P_good polynomial:', P_good)
print('Distribution over logical actions (polynomials):')
for k,v in sorted(logdist.items()):
    print(k, v)

# Simplify to rational coefficients
acc = P_accept
good = P_good

# Compute logical fidelity F_log(p) = good / acc (as rational functions)
# For general p, F(p) = (c0g + c1g p + c2g p^2)/(c0a + c1a p + c2a p^2)

num = (good.c0, good.c1, good.c2)
den = (acc.c0, acc.c1, acc.c2)
print('num:', num)
print('den:', den)
Tool #5
Class sizes per pair type (x,z): {(0, 0): 4, (1, 0): 4, (1, 1): 4, (0, 1): 4}
P_accept polynomial: 1 + -8/5 p + 64/75 p^2
P_good polynomial: 1 + -8/5 p + 16/25 p^2
Distribution over logical actions (polynomials):
II 1 + -2 p + 76/75 p^2
IX 0 + 0 p + 4/225 p^2
IY 0 + 0 p + 4/225 p^2
IZ 0 + 0 p + 4/225 p^2
XI 0 + 0 p + 4/225 p^2
XX 0 + 2/15 p + -28/225 p^2
XY 0 + 0 p + 4/225 p^2
XZ 0 + 0 p + 4/225 p^2
YI 0 + 0 p + 4/225 p^2
YX 0 + 0 p + 4/225 p^2
YY 0 + 2/15 p + -28/225 p^2
YZ 0 + 0 p + 4/225 p^2
ZI 0 + 0 p + 4/225 p^2
ZX 0 + 0 p + 4/225 p^2
ZY 0 + 0 p + 4/225 p^2
ZZ 0 + 2/15 p + -28/225 p^2
num: (Fraction(1, 1), Fraction(-8, 5), Fraction(16, 25))
den: (Fraction(1, 1), Fraction(-8, 5), Fraction(64, 75))
Assistant #6
  1. Step-by-Step Derivation
  • Ideal encoded target and the prepared state
  • The circuit (H2 → CNOT21) prepares a Bell pair on qubits (2,1), and (H0 → CNOT03) prepares a Bell pair on (0,3):
\[ |\psi_{\text{id}}\rangle = |\Phi^+\rangle_{21}\otimes|\Phi^+\rangle_{03},\quad |\Phi^+\rangle=\frac{|00\rangle+|11\rangle}{\sqrt{2}}. \]
  • This state is stabilized by
\[ X_2X_1,\ Z_2Z_1,\ X_0X_3,\ Z_0Z_3. \]
The [[4,2,2]] code stabilizers are $XXXX$ and $ZZZZ$, and the logical Bell (GHZ) stabilizers are $X_A X_B=X_2X_1$ and $Z_A Z_B=Z_2Z_1$.
  • Noise model and independence
  • After CNOT21, a two-qubit depolarizing channel acts on qubits (2,1); after CNOT03, the same on (0,3). These two channels are independent and act on disjoint qubits.
  • For a two-qubit depolarizing channel with error rate \(p\):

    • \(II\) occurs with probability \(1-p\),
    • each of the 15 non-identity two-qubit Paulis occurs with probability \(p/15\).
  • Parity classification on a pair

  • For a two-qubit Pauli \(P\) on a fixed pair, define the parity type
\[ t(P)=(x,z)\in\{0,1\}^2, \]
where $x$ is the parity of the number of $X$ or $Y$ factors, and $z$ is the parity of the number of $Z$ or $Y$ factors on the two qubits.
  • The 16 pair Paulis split into four equally sized classes:
    • Type I: \((0,0)\) = {II, XX, YY, ZZ} (4 elements)
    • Type X: \((1,0)\) (4 elements)
    • Type Z: \((0,1)\) (4 elements)
    • Type Y: \((1,1)\) (4 elements)
  • The probabilities for a single pair are:
\[ \]
  • Post-selection (detectable-error rejection)
  • A 4-qubit Pauli \(P\) on the two pairs is accepted (undetected) iff it commutes with both code stabilizers \(XXXX\) and \(ZZZZ\).
  • Using the parity picture: \(P\) commutes with \(ZZZZ\) iff the total \(x\)-parity across all four qubits is even, and commutes with \(XXXX\) iff the total \(z\)-parity is even. Since the error is a product of a Pauli on (2,1) and one on (0,3), this is equivalent to requiring the two pair-types match:
\[ t(P_{21})=t(P_{03}). \]
  • Therefore, the acceptance probability is
\[ =w_I^2+3\,w_X^2 \]
  • Which accepted errors preserve the logical Bell state?
  • The logical GHZ (Bell) state on \((A,B)\) is stabilized by \(X_A X_B\) and \(Z_A Z_B\); physically these are \(X_2X_1\) and \(Z_2Z_1\).
  • Any accepted error from Type I on both pairs (i.e., \(t=I\) on (2,1) and \(t=I\) on (0,3)) is a product of the four pair stabilizers \(X_2X_1,\ Z_2Z_1,\ X_0X_3,\ Z_0Z_3\), hence lies in the full stabilizer of \(|\psi_{\text{id}}\rangle\) and preserves the logical Bell state.
  • If both pairs are Type X, then the total error anticommutes with \(Z_AZ_B\) and changes the Bell eigenvalue (similarly, Type Z anticommutes with \(X_AX_B\), and Type Y anticommutes with both). Thus types X×X, Z×Z, Y×Y never preserve the logical Bell state.
  • Therefore, the probability of an accepted, logically correct outcome is exactly
\[ P_{\text{good}}=w_I^2=\left(1-\frac{4}{5}p\right)^2. \]
  • Logical fidelity with post-selection
  • The logical state fidelity (conditioned on acceptance) is
\[ \]
  • Equivalently, expanded as a rational function:
\[ F_{\text{logical}}(p)=\frac{1-\frac{8}{5}p+\frac{16}{25}p^2}{1-\frac{8}{5}p+\frac{64}{75}p^2}. \]
  • Small-\(p\) expansion (showing distance-2 detection eliminates all first-order logical error):
\[ F_{\text{logical}}(p)=1-\frac{16}{75}p^2+O(p^3). \]
  1. Mathematical Typesetting

All expressions are written in LaTeX above.

  1. Conventions and Units
  • Qubits labeled 0,1,2,3; logical qubits A,B.
  • Stabilizers: \(XXXX\), \(ZZZZ\).
  • Logical operators: \(X_A=XIXI\), \(X_B=XXII\), \(Z_A=ZZII\), \(Z_B=ZIZI\).
  • Two-qubit depolarizing noise of strength \(p\) acts after each CNOT.
  • Fidelity is \(F=\langle\psi_{\text{ideal}}|\rho|\psi_{\text{ideal}}\rangle\); post-selection keeps only runs with no stabilizer flips.
  1. Final Answer: \(F_{\text{logical}}(p)=\dfrac{\left(1-\frac{4}{5}p\right)^2}{\left(1-\frac{4}{5}p\right)^2+3\left(\frac{4}{15}p\right)^2} =\dfrac{1-\frac{8}{5}p+\frac{16}{25}p^2}{1-\frac{8}{5}p+\frac{64}{75}p^2} =1-\dfrac{16}{75}p^2+O(p^3).\)
User #7

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 #8
Tool Call (code to execute)
from fractions import Fraction
import itertools

# Define Pauli multiplication ignoring phase
mult_table = {
    ('I','I'):'I', ('I','X'):'X', ('I','Y'):'Y', ('I','Z'):'Z',
    ('X','I'):'X', ('X','X'):'I', ('X','Y'):'Z', ('X','Z'):'Y',
    ('Y','I'):'Y', ('Y','X'):'Z', ('Y','Y'):'I', ('Y','Z'):'X',
    ('Z','I'):'Z', ('Z','X'):'Y', ('Z','Y'):'X', ('Z','Z'):'I',
}

def pauli_mult(a,b):
    return mult_table[(a,b)]

# Conjugation of a Pauli string by CNOT(c,t)
# Mapping on single-qubit letters ignoring phase:
# X_c -> X_c X_t; Z_t -> Z_c Z_t; others unchanged except Y derived from X/Z

single_to_pair = {
    'I': ('I','I'),
    'X': ('X','I'),
    'Y': ('Y','I'),  # treat as XZ product
    'Z': ('Z','I'),
}

# We'll implement conjugation by directly applying rules per letters on c and t

def conj_cnot(P, c, t):
    P = list(P)
    a = P[c]
    b = P[t]
    # We consider effect on c and t jointly by factoring Pauli letters into X^x Z^z, applying known mapping, then multiply
    # Represent letters as (x,z) bits
    def xz(letter):
        if letter=='I': return (0,0)
        if letter=='X': return (1,0)
        if letter=='Y': return (1,1)
        if letter=='Z': return (0,1)
    def letter_from_xz(x,z):
        if x==0 and z==0: return 'I'
        if x==1 and z==0: return 'X'
        if x==0 and z==1: return 'Z'
        if x==1 and z==1: return 'Y'
    xc, zc = xz(a)
    xt, zt = xz(b)
    # Under CNOT(c->t):
    # X_c -> X_c X_t => x_t ^= x_c
    # Z_t -> Z_c Z_t => z_c ^= z_t
    xt ^= xc
    zc ^= zt
    P[c] = letter_from_xz(xc, zc)
    P[t] = letter_from_xz(xt, zt)
    return ''.join(P)

# Build list of gates in forward order
# Gates: CNOT_12, CNOT_10, CNOT_23, CNOT_34, CNOT_04
# Using qubit indices 0..4

gates = [(1,2),(1,0),(2,3),(3,4),(0,4)]

# Build all 2-qubit Pauli errors per pair as strings of length 5
paulis = ['I','X','Y','Z']

# Helper to embed pair paulis on given qubits (i,j) into 5-qubit string

def embed_pair(i,j, a, b):
    P=['I']*5
    P[i]=a
    P[j]=b
    return ''.join(P)

# Precompute propagation of each pair Pauli inserted after gate k through subsequent gates to the end
E_final = [[None]*16 for _ in range(5)]
label_list = []
for idx, a in enumerate(paulis):
    for b_idx, b in enumerate(paulis):
        label_list.append((a,b))
# label_list order consistent: (I,I),(I,X),... But we want index mapping 0..15

for k,(ci,ti) in enumerate(gates):
    for idx,(a,b) in enumerate(label_list):
        P = embed_pair(ci,ti,a,b)
        # Propagate through gates k+1..end
        for (c,t) in gates[k+1:]:
            P = conj_cnot(P, c, t)
        E_final[k][idx] = P

# Probability polynomials for each 2-qubit error on a gate
# Represent polynomial as list of coefficients [c0..c5] for sum c_k p^k
class Poly:
    def __init__(self, deg=5):
        self.c = [Fraction(0) for _ in range(deg+1)]
    def add(self, other):
        for i in range(len(self.c)):
            self.c[i] += other.c[i]
    def mul(self, other):
        r = Poly(len(self.c)-1)
        for i,a in enumerate(self.c):
            for j,b in enumerate(other.c):
                if i+j < len(r.c):
                    r.c[i+j] += a*b
        return r
    def __repr__(self):
        return ' + '.join(f'{self.c[i]} p^{i}' for i in range(len(self.c)) if self.c[i]) or '0'

# For a single gate error option: II -> (1-p), non-identity -> p/15
single_probs = []
for idx,(a,b) in enumerate(label_list):
    P = Poly(5)
    if a=='I' and b=='I':
        P.c[0] = Fraction(1)
        P.c[1] = Fraction(-1)
    else:
        P.c[1] = Fraction(1,15)
    single_probs.append(P)

# Function: classify acceptance and logical goodness from total final Pauli
# Acceptance if: ancilla (qubit 4) letter in {I,Z} and code commutes with XXXX and ZZZZ.
# For logical goodness: data Pauli in the stabilizer of |00>_AB generated by XXXX, ZZZZ, ZZII, ZIZI

# Build group of stabilizers of |00>_AB as set of strings length 4 (data only)
# We'll generate by multiplying generators ignoring phase.

def pauli_mult_letter(a,b):
    return mult_table[(a,b)]

def mult_str4(A,B):
    return ''.join(pauli_mult_letter(a,b) for a,b in zip(A,B))

# 4 data qubits order [0,1,2,3]; ancilla 4 index 4.

# Generators:
G1 = 'XXXX'
G2 = 'ZZZZ'
G3 = 'ZZII'  # Z_A
G4 = 'ZIZI'  # Z_B

# Generate stabilizer group size 16
stab_set = set()
for a in [0,1]:
    for b in [0,1]:
        for c in [0,1]:
            for d in [0,1]:
                P = 'IIII'
                if a: P = mult_str4(P,G1)
                if b: P = mult_str4(P,G2)
                if c: P = mult_str4(P,G3)
                if d: P = mult_str4(P,G4)
                stab_set.add(P)

# Function to get x,z parity sums on data
XZ = {'I':(0,0),'X':(1,0),'Y':(1,1),'Z':(0,1)}

def commute_with_checks(P4):
    xsum = sum(XZ[ch][0] for ch in P4) % 2
    zsum = sum(XZ[ch][1] for ch in P4) % 2
    return (xsum==0) and (zsum==0)

# Evaluate
P_acc = Poly(5)
P_good = Poly(5)

# We will iterate over all patterns using nested loops; However for efficiency, we can apply dynamic programming: combine two gates at a time and convolve polynomials while tracking resulting Pauli. But let's keep nested loops; 16^5 ~ 1e6 iterations ok.

# We'll precompute for each gate the list of (final Pauli string, prob poly) for the 16 options
opts_per_gate = []
for k in range(5):
    lst=[]
    for idx in range(16):
        lst.append((E_final[k][idx], single_probs[idx]))
    opts_per_gate.append(lst)

# Multiply Pauli strings of 5 contributions ignoring phase

def mult5(Ps):
    Q = ['I']*5
    for P in Ps:
        for i in range(5):
            Q[i] = pauli_mult(Q[i], P[i])
    return ''.join(Q)

# Now enumerate
count=0
for opt0 in opts_per_gate[0]:
    for opt1 in opts_per_gate[1]:
        for opt2 in opts_per_gate[2]:
            for opt3 in opts_per_gate[3]:
                for opt4 in opts_per_gate[4]:
                    Ps = [opt0[0],opt1[0],opt2[0],opt3[0],opt4[0]]
                    Pfin = mult5(Ps)
                    # Acceptance conditions
                    anc_ok = (Pfin[4] in ('I','Z'))
                    if not anc_ok:
                        # no acceptance for ancilla=0
                        pass
                    dataP = Pfin[:4]
                    code_ok = commute_with_checks(dataP)
                    if anc_ok and code_ok:
                        # compute probability polynomial product
                        prob = opt0[1].mul(opt1[1]).mul(opt2[1]).mul(opt3[1]).mul(opt4[1])
                        P_acc.add(prob)
                        if dataP in stab_set:
                            P_good.add(prob)
                    count += 1

print('Enumerated patterns:', count)
print('P_acc coefficients:', P_acc.c)
print('P_good coefficients:', P_good.c)
Tool #9
Enumerated patterns: 1048576
P_acc coefficients: [Fraction(1, 1), Fraction(-68, 15), Fraction(704, 75), Fraction(-32768, 3375), Fraction(253952, 50625), Fraction(-262144, 253125)]
P_good coefficients: [Fraction(1, 1), Fraction(-68, 15), Fraction(656, 75), Fraction(-29312, 3375), Fraction(223232, 50625), Fraction(-229376, 253125)]
Assistant #10
Tool Call (code to execute)
from fractions import Fraction

# Precomputation reused: E_final, label_list, single_probs, etc.

# Build list of all patterns with at most 1 non-identity error
single_acc = 0
single_good = 0
single_total = 0
for k in range(5):
    for idx,(a,b) in enumerate(label_list):
        if a=='I' and b=='I':
            continue
        # pattern: gate k uses (a,b), others identity
        Ps = ['IIIII']*5
        Ps[k] = E_final[k][idx]
        Pfin = Ps[0]
        for j in range(1,5):
            # multiply
            Q=['I']*5
            for i in range(5):
                Q[i] = mult_table[(Pfin[i], Ps[j][i])]
            Pfin=''.join(Q)
        anc_ok = (Pfin[4] in ('I','Z'))
        dataP = Pfin[:4]
        code_ok = commute_with_checks(dataP)
        if anc_ok and code_ok:
            single_acc += 1
            if dataP in stab_set:
                single_good += 1
        single_total += 1

print('Single-error cases per gate accepted:', single_acc, 'out of', single_total)
print('Single-error accepted and good (should equal accepted):', single_good)
Tool #11
Traceback (most recent call last):
  File "<stdin>", line 10, in <module>
NameError: name 'label_list' is not defined
Assistant #12
# Recreate needed variables from previous cell
paulis=['I','X','Y','Z']
label_list = [(a,b) for a in paulis for b in paulis]

# Use existing E_final, mult_table, commute_with_checks, stab_set from earlier cell? Re-create if needed.

def multP(A,B):
    return ''.join(mult_table[(a,b)] for a,b in zip(A,B))

# If E_final not present, reconstruct
try:
    _ = E_final
except NameError:
    def xz(letter):
        if letter=='I': return (0,0)
        if letter=='X': return (1,0)
        if letter=='Y': return (1,1)
        if letter=='Z': return (0,1)
    def letter_from_xz(x,z):
        if x==0 and z==0: return 'I'
        if x==1 and z==0: return 'X'
        if x==0 and z==1: return 'Z'
        if x==1 and z==1: return 'Y'
    def conj_cnot(P, c, t):
        P = list(P)
        xc, zc = xz(P[c]); xt, zt = xz(P[t])
        xt ^= xc
        zc ^= zt
        P[c] = letter_from_xz(xc, zc)
        P[t] = letter_from_xz(xt, zt)
        return ''.join(P)
    gates = [(1,2),(1,0),(2,3),(3,4),(0,4)]
    def embed_pair(i,j,a,b):
        P=['I']*5; P[i]=a; P[j]=b; return ''.join(P)
    E_final = [[None]*16 for _ in range(5)]
    for k,(ci,ti) in enumerate(gates):
        for idx,(a,b) in enumerate(label_list):
            P = embed_pair(ci,ti,a,b)
            for (c,t) in gates[k+1:]:
                P = conj_cnot(P, c, t)
            E_final[k][idx] = P
    # Define commute and stabilizer set again
    XZ={'I':(0,0),'X':(1,0),'Y':(1,1),'Z':(0,1)}
    def commute_with_checks(P4):
        xsum = sum(XZ[ch][0] for ch in P4) % 2
        zsum = sum(XZ[ch][1] for ch in P4) % 2
        return (xsum==0) and (zsum==0)
    def mult_str4(A,B):
        return ''.join(mult_table[(a,b)] for a,b in zip(A,B))
    G1='XXXX'; G2='ZZZZ'; G3='ZZII'; G4='ZIZI'
    stab_set=set()
    for a in [0,1]:
        for b in [0,1]:
            for c in [0,1]:
                for d in [0,1]:
                    P='IIII'
                    if a: P=mult_str4(P,G1)
                    if b: P=mult_str4(P,G2)
                    if c: P=mult_str4(P,G3)
                    if d: P=mult_str4(P,G4)
                    stab_set.add(P)

single_acc = 0
single_good = 0
single_total = 0
for k in range(5):
    for idx,(a,b) in enumerate(label_list):
        if a=='I' and b=='I':
            continue
        Ps = ['IIIII']*5
        Ps[k] = E_final[k][idx]
        Pfin = 'IIIII'
        for P in Ps:
            Pfin = multP(Pfin, P)
        anc_ok = (Pfin[4] in ('I','Z'))
        dataP = Pfin[:4]
        code_ok = commute_with_checks(dataP)
        if anc_ok and code_ok:
            single_acc += 1
            if dataP in stab_set:
                single_good += 1
        single_total += 1

print('Single-error cases per gate accepted:', single_acc, 'out of', single_total)
print('Single-error accepted and good (should equal accepted):', single_good)
Tool #13
Traceback (most recent call last):
  File "<stdin>", line 12, in <module>
NameError: name 'E_final' is not defined

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 60, in <module>
  File "<stdin>", line 49, in mult_str4
  File "<stdin>", line 49, in <genexpr>
NameError: name 'mult_table' is not defined
Assistant #14
# Rebuild everything fresh in one cell
from fractions import Fraction
import itertools

# Pauli multiplication table ignoring phase
mult_table = {
    ('I','I'):'I', ('I','X'):'X', ('I','Y'):'Y', ('I','Z'):'Z',
    ('X','I'):'X', ('X','X'):'I', ('X','Y'):'Z', ('X','Z'):'Y',
    ('Y','I'):'Y', ('Y','X'):'Z', ('Y','Y'):'I', ('Y','Z'):'X',
    ('Z','I'):'Z', ('Z','X'):'Y', ('Z','Y'):'X', ('Z','Z'):'I',
}

def multP(A,B):
    return ''.join(mult_table[(a,b)] for a,b in zip(A,B))

# Conjugation by CNOT(c->t)

def xz(letter):
    if letter=='I': return (0,0)
    if letter=='X': return (1,0)
    if letter=='Y': return (1,1)
    if letter=='Z': return (0,1)

def letter_from_xz(x,z):
    if x==0 and z==0: return 'I'
    if x==1 and z==0: return 'X'
    if x==0 and z==1: return 'Z'
    if x==1 and z==1: return 'Y'

def conj_cnot(P, c, t):
    P=list(P)
    xc,zc = xz(P[c]); xt,zt = xz(P[t])
    xt ^= xc
    zc ^= zt
    P[c] = letter_from_xz(xc,zc)
    P[t] = letter_from_xz(xt,zt)
    return ''.join(P)

# Gates list
gates = [(1,2),(1,0),(2,3),(3,4),(0,4)]

# Embedding pair Pauli
paulis=['I','X','Y','Z']
label_list=[(a,b) for a in paulis for b in paulis]

def embed_pair(i,j,a,b):
    P=['I']*5
    P[i]=a; P[j]=b
    return ''.join(P)

# Precompute E_final
E_final = [[None]*16 for _ in range(5)]
for k,(ci,ti) in enumerate(gates):
    for idx,(a,b) in enumerate(label_list):
        P = embed_pair(ci,ti,a,b)
        for (c,t) in gates[k+1:]:
            P = conj_cnot(P,c,t)
        E_final[k][idx]=P

# Checks and stabilizer conditions
XZ={'I':(0,0),'X':(1,0),'Y':(1,1),'Z':(0,1)}

def commute_with_checks(P4):
    xsum=sum(XZ[ch][0] for ch in P4)%2
    zsum=sum(XZ[ch][1] for ch in P4)%2
    return (xsum==0) and (zsum==0)

def mult_str4(A,B):
    return ''.join(mult_table[(a,b)] for a,b in zip(A,B))
G1='XXXX'; G2='ZZZZ'; G3='ZZII'; G4='ZIZI'
stab_set=set()
for a in [0,1]:
    for b in [0,1]:
        for c in [0,1]:
            for d in [0,1]:
                P='IIII'
                if a: P=mult_str4(P,G1)
                if b: P=mult_str4(P,G2)
                if c: P=mult_str4(P,G3)
                if d: P=mult_str4(P,G4)
                stab_set.add(P)

# Count single-error accepted and good
single_acc=0; single_good=0; single_total=0
for k in range(5):
    for idx,(a,b) in enumerate(label_list):
        if a=='I' and b=='I':
            continue
        Ps=['IIIII']*5
        Ps[k] = E_final[k][idx]
        Pfin='IIIII'
        for P in Ps:
            Pfin = multP(Pfin,P)
        anc_ok = (Pfin[4] in ('I','Z'))
        dataP = Pfin[:4]
        code_ok = commute_with_checks(dataP)
        if anc_ok and code_ok:
            single_acc += 1
            if dataP in stab_set:
                single_good += 1
        single_total += 1

print('Single non-identity error patterns per gate options total:', single_total)
print('Accepted singles count:', single_acc)
print('Among accepted, good count:', single_good)
Tool #15
Single non-identity error patterns per gate options total: 75
Accepted singles count: 7
Among accepted, good count: 7
Assistant #16
Tool Call (code to execute)
from fractions import Fraction
from decimal import Decimal, getcontext
getcontext().prec=30

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

def poly_eval(coeffs, x):
    s = Decimal(0)
    xp=Decimal(1)
    for c in coeffs:
        s += Decimal(c.numerator)/Decimal(c.denominator) * xp
        xp *= Decimal(x)
    return s

for p in [0.0, 1e-3, 1e-2, 0.05, 0.1]:
    Pa = poly_eval(acc, p)
    Pg = poly_eval(good, p)
    F = Pg/Pa if Pa!=0 else Decimal('NaN')
    print(p, 'P_acc', Pa, 'P_good', Pg, 'F', F)
Tool #17
0.0 P_acc 1.00000000000000000000000000000 P_good 1.00000000000000000000000000000 F 1
0.001 P_acc 0.995476043629311596374169383647 P_good 0.995475404652704911013155147873 F 0.999999358119554182562878414705
0.01 P_acc 0.955595674356091258354047375152 P_good 0.955532692300888492919347519380 F 0.999934091314043248873287537997
0.05 P_acc 0.795617398834567891062125457289 P_good 0.794141646696296285966703554089 F 0.998145148484141613029937056966
0.1 P_acc 0.631315573570370354118239711281 P_good 0.625880186627160477021902666528 F 0.991390380388574375766674030585
Assistant #18
  1. Step-by-Step Derivation
  • Target logical state and circuit
  • The circuit H1 → CNOT12 → CNOT10 → CNOT23 prepares a 4-qubit GHZ state on data qubits 0–3:
\[ |\mathrm{GHZ}_4\rangle=\frac{|0000\rangle+|1111\rangle}{\sqrt{2}}, \]
which is stabilized by the code checks $XXXX$ and $ZZZZ$ and has logical eigenvalues $Z_A=+1$, $Z_B=+1$ (hence $|00\rangle_{AB}$).
  • The two final CNOTs CNOT34 and CNOT04 couple an ancilla qubit 4 to the parity \(Z_0Z_3\); in the ideal case, ancilla measurement \(M_4\) is deterministically \(|0\rangle\).

  • Noise model and post-selection

  • After each of the five CNOTs, an independent two-qubit depolarizing channel acts:
\[ \mathcal{E}(\rho)=(1-p)\rho+\frac{p}{15}\!\!\sum_{P\in\mathcal{P}_2\setminus\{II\}}\!\!P\rho P. \]
  • We post-select on: 1) ancilla outcome \(|0\rangle\), and 2) “no detectable code error,” i.e., the resulting data Pauli \(P_{\text{data}}\) commutes with both \(XXXX\) and \(ZZZZ\).

  • Clifford-Pauli propagation and acceptance conditions

  • Let \(E_k\) be the Pauli error (possibly identity) inserted after the k-th CNOT. Propagate each \(E_k\) forward through the remaining CNOTs using the standard CNOT conjugation rules on Paulis:
    • \(X_c \mapsto X_cX_t\), \(Z_t \mapsto Z_cZ_t\), \(X_t \mapsto X_t\), \(Z_c \mapsto Z_c\), and \(Y=iXZ\) accordingly (phases are irrelevant for acceptance and fidelity).
  • The net Pauli at the end is \(E_{\mathrm{tot}}=\prod_k E_k'\), where \(E_k'\) is \(E_k\) propagated to the end.
  • Acceptance (post-selection) is equivalent to the following purely commutation-based conditions on \(E_{\mathrm{tot}}\):

    • Ancilla accept (outcome \(|0\rangle\)): the ideal final 5-qubit state has \(Z_4\) in its stabilizer with \(+1\) eigenvalue, so the measurement is deterministic; we accept iff \(E_{\mathrm{tot}}\) commutes with \(Z_4\), i.e., its letter on qubit 4 is in \(\{I,Z\}\).
    • Code accept (no detectable code error): \(P_{\text{data}}=E_{\mathrm{tot}}|_{0\ldots 3}\) commutes with \(XXXX\) and \(ZZZZ\). In terms of \(x\)/\(z\) parities of \(P_{\text{data}}\), this is the condition that both the total \(x\)-parity and total \(z\)-parity on data are even.
  • Logical correctness among accepted events

  • Within the code space, the prepared logical state \(|00\rangle_{AB}\) is the unique +1 eigenstate of the four commuting generators
\[ \{XXXX,\ ZZZZ,\ ZZII(=Z_A),\ ZIZI(=Z_B)\}. \]
Hence an accepted data error $P_{\text{data}}$ preserves the logical state iff
\[ P_{\text{data}}\in\langle XXXX,\ ZZZZ,\ ZZII,\ ZIZI\rangle. \]
  • Exact counting (Clifford-Pauli enumeration)
  • We enumerate all \(16^5\) Pauli fault patterns (II or one of the 15 non-identity two-qubit Paulis after each of the five CNOTs), propagate them to the end, enforce the acceptance conditions above, and sum their probabilities as polynomials in \(p\).
  • Let
    • \(P_{\text{acc}}(p)\) be the total acceptance probability (ancilla \(0\) and code-accept),
    • \(P_{\text{good}}(p)\) be the total probability of accepted-and-logically-correct events.
  • The exact polynomials are:
\[ P_{\text{acc}}(p) &= 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,\\[4pt] P_{\text{good}}(p) &= 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. \]
  • As a useful check, all accepted single-fault patterns (7 out of the 75 possible single non-identity faults across the five CNOTs) are logically benign: the linear coefficients of \(P_{\text{acc}}\) and \(P_{\text{good}}\) match.

  • Logical fidelity conditioned on acceptance

  • The logical (post-selected) fidelity is
\[ F_{\text{logical}}(p)=\frac{P_{\text{good}}(p)}{P_{\text{acc}}(p)}. \]
  • Equivalently,
\[ = 1-\frac{P_{\text{acc}}(p)-P_{\text{good}}(p)}{P_{\text{acc}}(p)} \]
  • Small-\(p\) expansion:
\[ F_{\text{logical}}(p)=1-\frac{16}{25}p^2+O(p^3), \]
showing first-order fault tolerance (no $O(p)$ logical contamination).
  1. Mathematical Typesetting

All probabilities and fidelities are expressed above in LaTeX.

  1. Conventions and Units
  • Qubits 0–3: data; qubit 4: ancilla.
  • Stabilizers: \(XXXX\), \(ZZZZ\); logicals: \(Z_A=ZZII\), \(Z_B=ZIZI\).
  • Two-qubit depolarizing noise of strength \(p\) after each CNOT; \(H\) and \(M\) are perfect.
  • Fidelity is conditioned on post-selection (ancilla \(|0\rangle\) and no code-syndrome).
  1. Final Answer:
\[ =\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}\\[6pt] \]
User #19

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 #20
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 ------------------
    P_acc = (
        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
    )

    P_good = (
        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
    )

    F_logical = P_good / P_acc
    # ---------------------------------------------------------------

    return F_logical