Challenge

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\).

Challenge

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

# Represent Pauli on n=5 qubits as (x[], z[]) bit arrays, ignoring global phase
n = 5

# Utility functions

def pauli_mult(p, q):
    # Multiply two Paulis (x,z pairs), ignoring overall phase
    x1, z1 = p
    x2, z2 = q
    x = [(a ^ b) for a,b in zip(x1,x2)]
    z = [(a ^ b) for a,b in zip(z1,z2)]
    return (x, z)

# Conjugations

def conj_H(p, q):
    # Hadamard on qubit q: X <-> Z, Y-> -Y (phase ignored)
    x, z = [r[:] for r in p]
    x[q], z[q] = z[q], x[q]
    return (x, z)

def conj_CNOT(p, c, t):
    # Conjugate Pauli by CNOT(c->t):
    # X_c -> X_c X_t (i.e., x_t ^= x_c)
    # Z_t -> Z_c Z_t (i.e., z_c ^= z_t)
    x, z = [r[:] for r in p]
    # new x_t = x_t ^ x_c
    x[t] ^= x[c]
    # new z_c = z_c ^ z_t
    z[c] ^= z[t]
    return (x, z)

# Build single-qubit Pauli basis
single_labels = ['I','X','Y','Z']

# Map two-qubit pauli on given pair (a,b) from labels like 'XI', 'XZ', etc.

def twoq_pauli(label, a, b):
    # label is two chars over {I,X,Y,Z}
    x = [0]*n
    z = [0]*n
    for ch, q in zip(label, [a,b]):
        if ch == 'I':
            pass
        elif ch == 'X':
            x[q] = 1
        elif ch == 'Z':
            z[q] = 1
        elif ch == 'Y':
            x[q] = 1
            z[q] = 1
        else:
            raise ValueError('bad')
    return (x, z)

# Gates in order g1..g5 (right-to-left application order in the problem statement):
# We'll label in the chronological forward order they are applied.
# Sequence: H1, CNOT_12, CNOT_10, CNOT_23, CNOT_34, CNOT_04, then measure ancilla
# Errors happen AFTER each CNOT, so after g1=CNOT_12, g2=CNOT_10, g3=CNOT_23, g4=CNOT_34, g5=CNOT_04

gates = [
    ('CNOT', 1, 2),  # g1
    ('CNOT', 1, 0),  # g2
    ('CNOT', 2, 3),  # g3
    ('CNOT', 3, 4),  # g4
    ('CNOT', 0, 4),  # g5
]

# We also have an initial H on qubit 1, but since errors are after gates, it only matters for state prep,
# not for error propagation to the end (we only propagate errors forward through subsequent CNOTs and measure).

# Precompute forward conjugation of an error inserted after gate k, to the end.
# That is, apply successive conjugations of subsequent gates g_{k+1}..g5 to the error.

# Build list of 15 non-identity 2-qubit labels
labels_2q = []
for a in 'IXYZ':
    for b in 'IXYZ':
        if a == 'I' and b == 'I':
            continue
        labels_2q.append(a+b)
labels_2q = [l for l in labels_2q if l != 'II']
assert len(labels_2q) == 15

# Map from gate idx to qubit pair
pairs = [(1,2),(1,0),(2,3),(3,4),(0,4)]

# function to propagate an inserted 2q pauli at gate index k (1..5) with label lab to the end

def propagate_error_to_end(k, lab):
    # initial Pauli right after gate k
    a, b = pairs[k-1]
    p = twoq_pauli(lab, a, b)
    # propagate through g_{k+1}..g5
    for j in range(k, len(gates)):
        name, c, t = gates[j]
        if name == 'CNOT':
            p = conj_CNOT(p, c, t)
        else:
            raise
    return p

# Utility: check ancilla acceptance: ancilla (qubit 4) must be I or Z (i.e., x4==0)

def ancilla_accepts(p):
    x, z = p
    return x[4] == 0  # if x4==1 then ancilla has X or Y, flips Z measurement

# Utility: code acceptance: commute with XXXX and ZZZZ -> even parity of Z and X respectively on data 0..3

def code_accepts(p):
    x, z = p
    px = sum(x[q] for q in range(4)) % 2
    pz = sum(z[q] for q in range(4)) % 2
    return (px == 0) and (pz == 0)

# Logical X presence test using commutation with logical Z_A and Z_B.
# Z_A = Z on qubits 0 and 1; Z_B = Z on qubits 0 and 2? Wait from problem: Z_A=ZZII (0,1), Z_B=ZIZI (0,2)

# Build these as Paulis on 5 qubits (ancilla unaffected)
def make_multiZ(indices):
    x = [0]*n
    z = [0]*n
    for q in indices:
        z[q] = 1
    return (x, z)

Z_A = make_multiZ([0,1])
Z_B = make_multiZ([0,2])

# Commutator tester: p anticommutes with q if dot(x_p, z_q)+dot(z_p, x_q) mod 2 == 1

def anticommutes(p, q):
    x1, z1 = p
    x2, z2 = q
    s = 0
    for i in range(n):
        s ^= (x1[i] & z2[i])
        s ^= (z1[i] & x2[i])
    return s == 1

# Data-only part of a Pauli (zeroing ancilla bits)

def data_part(p):
    x, z = p
    xd = x[:4] + [0]
    zd = z[:4] + [0]
    return (xd, zd)

# Does P_data include a logical X on A or B?

def has_logical_X(p):
    # Only data part matters for logical op; ancilla bits ignore
    d = data_part(p)
    return anticommutes(d, Z_A) or anticommutes(d, Z_B)

# Lets enumerate single and double faults contributions

# Precompute errors-to-end for each gate and label
E_end = {}
for k in range(1,6):
    for lab in labels_2q:
        E_end[(k, lab)] = propagate_error_to_end(k, lab)

# Single fault stats
single_stats = {
    'total': 0,
    'acc_anc': 0,
    'acc_code': 0,
    'acc_both': 0,
    'acc_good': 0,
    'acc_bad': 0,  # accepted but logical X present
}

for k in range(1,6):
    for lab in labels_2q:
        p_end = E_end[(k, lab)]
        single_stats['total'] += 1
        if ancilla_accepts(p_end):
            single_stats['acc_anc'] += 1
            if code_accepts(p_end):
                single_stats['acc_both'] += 1
                if has_logical_X(p_end):
                    single_stats['acc_bad'] += 1
                else:
                    single_stats['acc_good'] += 1
        # also count code_accepts marginally
        if code_accepts(p_end):
            single_stats['acc_code'] += 1

single_stats
Tool #3
''
Assistant #4
Tool Call (code to execute)
print('Single-fault stats:')
print(single_stats)
Tool #5
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
NameError: name 'single_stats' is not defined

Single-fault stats:
Assistant #6
Tool Call (code to execute)
import itertools
n=5

def pauli_mult(p, q):
    x1,z1=p
    x2,z2=q
    x=[a^b for a,b in zip(x1,x2)]
    z=[a^b for a,b in zip(z1,z2)]
    return (x,z)

def conj_H(p,q):
    x,z=[r[:] for r in p]
    x[q],z[q]=z[q],x[q]
    return (x,z)

def conj_CNOT(p,c,t):
    x,z=[r[:] for r in p]
    x[t]^=x[c]
    z[c]^=z[t]
    return (x,z)

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

def twoq_pauli(label,a,b):
    x=[0]*n
    z=[0]*n
    for ch,q in zip(label,[a,b]):
        if ch=='I':
            pass
        elif ch=='X':
            x[q]=1
        elif ch=='Z':
            z[q]=1
        elif ch=='Y':
            x[q]=1; z[q]=1
        else:
            raise ValueError
    return (x,z)

gates=[('CNOT',1,2),('CNOT',1,0),('CNOT',2,3),('CNOT',3,4),('CNOT',0,4)]
labels_2q=[a+b for a in 'IXYZ' for b in 'IXYZ' if not (a=='I' and b=='I')]
assert len(labels_2q)==15
pairs=[(1,2),(1,0),(2,3),(3,4),(0,4)]

def propagate_error_to_end(k,lab):
    a,b=pairs[k-1]
    p=twoq_pauli(lab,a,b)
    for j in range(k,len(gates)):
        name,c,t=gates[j]
        p=conj_CNOT(p,c,t)
    return p


def ancilla_accepts(p):
    x,z=p
    return x[4]==0

def code_accepts(p):
    x,z=p
    px=sum(x[q] for q in range(4))%2
    pz=sum(z[q] for q in range(4))%2
    return (px==0) and (pz==0)


def make_multiZ(indices):
    x=[0]*n; z=[0]*n
    for q in indices: z[q]=1
    return (x,z)

Z_A=make_multiZ([0,1])
Z_B=make_multiZ([0,2])

def anticommutes(p,q):
    x1,z1=p; x2,z2=q
    s=0
    for i in range(n):
        s^=(x1[i] & z2[i])
        s^=(z1[i] & x2[i])
    return s==1

def data_part(p):
    x,z=p
    xd=x[:4]+[0]
    zd=z[:4]+[0]
    return (xd,zd)

def has_logical_X(p):
    d=data_part(p)
    return anticommutes(d,Z_A) or anticommutes(d,Z_B)

E_end={}
for k in range(1,6):
    for lab in labels_2q:
        E_end[(k,lab)]=propagate_error_to_end(k,lab)

single_stats={'total':0,'acc_anc':0,'acc_code':0,'acc_both':0,'acc_good':0,'acc_bad':0}
for k in range(1,6):
    for lab in labels_2q:
        p_end=E_end[(k,lab)]
        single_stats['total']+=1
        if ancilla_accepts(p_end):
            single_stats['acc_anc']+=1
            if code_accepts(p_end):
                single_stats['acc_both']+=1
                if has_logical_X(p_end):
                    single_stats['acc_bad']+=1
                else:
                    single_stats['acc_good']+=1
        if code_accepts(p_end):
            single_stats['acc_code']+=1

print('Single-fault stats:', single_stats)

# Enumerate double faults across distinct gates (k1<k2)

double_stats={'total_pairs':0,'acc_both':0,'acc_good':0,'acc_bad':0}
malignant_list=[]
for k1 in range(1,6):
    for k2 in range(k1+1,6):
        for lab1 in labels_2q:
            for lab2 in labels_2q:
                p1=E_end[(k1,lab1)]
                p2=E_end[(k2,lab2)]
                p=pauli_mult(p1,p2)
                double_stats['total_pairs']+=1
                if ancilla_accepts(p) and code_accepts(p):
                    double_stats['acc_both']+=1
                    if has_logical_X(p):
                        double_stats['acc_bad']+=1
                        malignant_list.append((k1,lab1,k2,lab2))
                    else:
                        double_stats['acc_good']+=1

print('Double-fault stats:', double_stats)
print('Number of malignant ordered pairs recorded:', len(malignant_list))

# As a sanity check, count how many accepted single faults are benign (should be all or some?)
Tool #7
Single-fault stats: {'total': 75, 'acc_anc': 35, 'acc_code': 19, 'acc_both': 7, 'acc_good': 7, 'acc_bad': 0}
Double-fault stats: {'total_pairs': 2250, 'acc_both': 282, 'acc_good': 138, 'acc_bad': 144}
Number of malignant ordered pairs recorded: 144
Assistant #8
Tool Call (code to execute)
# Recompute and list the 7 accepted single faults and classify their logical action
n=5

def format_pauli(p):
    s=''
    x,z=p
    for i in range(n):
        if x[i]==0 and z[i]==0:
            s+='I'
        elif x[i]==1 and z[i]==0:
            s+='X'
        elif x[i]==0 and z[i]==1:
            s+='Z'
        else:
            s+='Y'
    return s

# replicate computation quickly

pairs=[(1,2),(1,0),(2,3),(3,4),(0,4)]
labels_2q=[a+b for a in 'IXYZ' for b in 'IXYZ' if not (a=='I' and b=='I')]

def twoq_pauli(label,a,b):
    x=[0]*n; z=[0]*n
    for ch,q in zip(label,[a,b]):
        if ch=='I':
            pass
        elif ch=='X':
            x[q]=1
        elif ch=='Z':
            z[q]=1
        elif ch=='Y':
            x[q]=1; z[q]=1
    return (x,z)

def conj_CNOT(p,c,t):
    x,z=[r[:] for r in p]
    x[t]^=x[c]
    z[c]^=z[t]
    return (x,z)

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

def propagate_error_to_end(k,lab):
    a,b=pairs[k-1]
    p=twoq_pauli(lab,a,b)
    for j in range(k,len(gates)):
        name,c,t=gates[j]
        p=conj_CNOT(p,c,t)
    return p


def ancilla_accepts(p):
    x,z=p
    return x[4]==0

def code_accepts(p):
    x,z=p
    px=sum(x[q] for q in range(4))%2
    pz=sum(z[q] for q in range(4))%2
    return (px==0) and (pz==0)

Z_A=([0,0,0,0,0],[1,1,0,0,0])
Z_B=([0,0,0,0,0],[1,0,1,0,0])

def anticommutes(p,q):
    x1,z1=p; x2,z2=q
    s=0
    for i in range(n):
        s^=(x1[i] & z2[i])
        s^=(z1[i] & x2[i])
    return s==1


def data_part(p):
    x,z=p
    return (x[:4]+[0], z[:4]+[0])


def has_logical_X(p):
    d=data_part(p)
    return anticommutes(d,Z_A) or anticommutes(d,Z_B)

accepted_singles=[]
for k in range(1,6):
    for lab in labels_2q:
        p=propagate_error_to_end(k,lab)
        if ancilla_accepts(p) and code_accepts(p):
            accepted_singles.append((k,lab,format_pauli(p)))

print(len(accepted_singles))
for item in accepted_singles:
    print(item)
Tool #9
7
(1, 'XX', 'XXXXI')
(1, 'YY', 'XYYXI')
(1, 'ZZ', 'IZZII')
(2, 'ZZ', 'ZZIII')
(3, 'ZZ', 'IIZZI')
(4, 'ZZ', 'ZIIZZ')
(5, 'IZ', 'IIIIZ')
Assistant #10
Tool Call (code to execute)
# Exact coefficient extraction by enumerating all 16^5 error patterns (including identity per gate)
# Precompute E_end for each gate and 16 labels
n=5

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

def twoq_pauli(label,a,b):
    x=[0]*n; z=[0]*n
    for ch,q in zip(label,[a,b]):
        if ch=='I':
            pass
        elif ch=='X':
            x[q]=1
        elif ch=='Z':
            z[q]=1
        elif ch=='Y':
            x[q]=1; z[q]=1
    return (x,z)

def conj_CNOT(p,c,t):
    x,z=[r[:] for r in p]
    x[t]^=x[c]
    z[c]^=z[t]
    return (x,z)

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

labels_2q = [a+b for a in 'IXYZ' for b in 'IXYZ']  # includes 'II'

# identity
I5=([0]*n,[0]*n)

# propagate error after gate k with label lab

def propagate_error_to_end(k, lab):
    a,b=pairs[k-1]
    if lab=='II':
        return I5
    p=twoq_pauli(lab,a,b)
    for j in range(k, len(gates)):
        name,c,t=gates[j]
        p=conj_CNOT(p,c,t)
    return p

# Build map
E_end_map = {}
for k in range(1,6):
    for lab in labels_2q:
        E_end_map[(k,lab)] = propagate_error_to_end(k,lab)

# acceptance and logical checks

def ancilla_accepts(p):
    x,z=p
    return x[4]==0

def code_accepts(p):
    x,z=p
    px=sum(x[q] for q in range(4))%2
    pz=sum(z[q] for q in range(4))%2
    return (px==0) and (pz==0)

Z_A=([0,0,0,0,0],[1,1,0,0,0])
Z_B=([0,0,0,0,0],[1,0,1,0,0])

def anticommutes(p,q):
    x1,z1=p; x2,z2=q
    s=0
    for i in range(n):
        s^=(x1[i] & z2[i])
        s^=(z1[i] & x2[i])
    return s==1

def data_part(p):
    x,z=p
    return (x[:4]+[0], z[:4]+[0])


def has_logical_X(p):
    d=data_part(p)
    return anticommutes(d,Z_A) or anticommutes(d,Z_B)

# Enumerate all 16^5 patterns; accumulate counts by k faults
from itertools import product
choices = 'II IX IY IZ XI XX XY XZ YI YX YY YZ ZI ZX ZY ZZ'.split()
assert len(choices)==16

counts_acc=[0]*6
counts_good=[0]*6
counts_tot=[0]*6

# Precompute identity to speed multiply

def pauli_mult(p, q):
    x1,z1=p; x2,z2=q
    x=[a^b for a,b in zip(x1,x2)]
    z=[a^b for a,b in zip(z1,z2)]
    return (x,z)

# We'll iterate; to reduce Python overhead, we expand nested loops instead of product
cnt=0
for lab1 in choices:
    p1=E_end_map[(1,lab1)]
    k1 = 0 if lab1=='II' else 1
    for lab2 in choices:
        p12 = pauli_mult(p1, E_end_map[(2,lab2)])
        k2 = k1 + (0 if lab2=='II' else 1)
        for lab3 in choices:
            p123 = pauli_mult(p12, E_end_map[(3,lab3)])
            k3 = k2 + (0 if lab3=='II' else 1)
            for lab4 in choices:
                p1234 = pauli_mult(p123, E_end_map[(4,lab4)])
                k4 = k3 + (0 if lab4=='II' else 1)
                for lab5 in choices:
                    p = pauli_mult(p1234, E_end_map[(5,lab5)])
                    k = k4 + (0 if lab5=='II' else 1)
                    counts_tot[k]+=1
                    if ancilla_accepts(p) and code_accepts(p):
                        counts_acc[k]+=1
                        if not has_logical_X(p):
                            counts_good[k]+=1
                    cnt+=1

print('Total patterns:', cnt)
print('counts_tot by k faults:', counts_tot)
print('counts_acc by k faults:', counts_acc)
print('counts_good by k faults:', counts_good)

# Build exact polynomials P_acc(p) and P_good(p)
from fractions import Fraction

N=5
coeff_acc=[Fraction(0,1)]*(N+1)
coeff_good=[Fraction(0,1)]*(N+1)

# P_acc = sum_k counts_acc[k] * (p/15)^k * (1-p)^(N-k) ; same for good counts
# Expand (1-p)^(N-k) as sum_{j=0..N-k} C(N-k, j) (-1)^j p^j
# Then coefficient at p^m equals sum over k and j with k+j=m of counts * (1/15)^k * C(N-k, j) * (-1)^j

from math import comb

for k in range(N+1):
    for j in range(N-k+1):
        m = k + j
        c = Fraction(counts_acc[k], 15**k) * Fraction(comb(N-k, j) * ((-1)**j), 1)
        coeff_acc[m] += c
        c2 = Fraction(counts_good[k], 15**k) * Fraction(comb(N-k, j) * ((-1)**j), 1)
        coeff_good[m] += c2

print('P_acc coeffs [p^0..p^5]:', coeff_acc)
print('P_good coeffs [p^0..p^5]:', coeff_good)

# Derive F = P_good/P_acc as series up to p^5 via formal series division

max_deg=5
# Convert to Fractions lists (a0..a5)
a=coeff_acc
b=coeff_good

# Series division b/a to degree 5
f=[Fraction(0,1)]*(max_deg+1)
# f0 = b0/a0 ; a0 should be 1
f[0]=b[0]/a[0]
for m in range(1, max_deg+1):
    # b[m] = sum_{i=0..m} a[i] f[m-i]
    s=b[m]
    for i in range(1, m+1):
        s -= a[i]*f[m-i]
    f[m] = s/a[0]

print('Fidelity series coeffs [p^0..p^5]:', f)
print('Check p^2 coeff numeric:', float(f[2]))
Tool #11
Total patterns: 1048576
counts_tot by k faults: [1, 75, 2250, 33750, 253125, 759375]
counts_acc by k faults: [1, 7, 282, 4222, 31637, 94923]
counts_good by k faults: [1, 7, 138, 1198, 7397, 24027]
P_acc coeffs [p^0..p^5]: [Fraction(1, 1), Fraction(-68, 15), Fraction(704, 75), Fraction(-32768, 3375), Fraction(253952, 50625), Fraction(-262144, 253125)]
P_good coeffs [p^0..p^5]: [Fraction(1, 1), Fraction(-68, 15), Fraction(656, 75), Fraction(-29312, 3375), Fraction(223232, 50625), Fraction(-229376, 253125)]
Fidelity series coeffs [p^0..p^5]: [Fraction(1, 1), Fraction(0, 1), Fraction(-16, 25), Fraction(-704, 375), Fraction(-10496, 3375), Fraction(-216064, 84375)]
Check p^2 coeff numeric: -0.64
Assistant #12
  1. Step-by-Step Derivation
  • Setup, noise model and acceptance conditions

  • The circuit (executed right-to-left) is

\[ H_1 \;\xrightarrow{\;CNOT_{1\to 2}\;}\;\xrightarrow{\;CNOT_{1\to 0}\;}\;\xrightarrow{\;CNOT_{2\to 3}\;}\;\xrightarrow{\;CNOT_{3\to 4}\;}\;\xrightarrow{\;CNOT_{0\to 4}\;}\; M_4, \]
with data qubits 0–3 and ancilla 4. A two-qubit depolarizing channel acts after each CNOT. Thus there are five independent error locations, one after each CNOT. After a given CNOT on qubits $(i,j)$, the error is one of the 15 non-identity two-qubit Paulis on $(i,j)$, each with probability $p/15$; with probability $1-p$ it is the identity.
  • Because all gates are Clifford, any sequence of Pauli faults through the circuit is equivalent to a single Pauli \(P_{\text{tot}}\) on the 5 qubits immediately before measurement.

  • Acceptance (post-selection) consists of: 1) Ancilla measurement in \(Z\) with outcome “0”: this occurs if and only if the ancilla component of \(P_{\text{tot}}\) is in \(\{I,Z\}\) (since \(X\) or \(Y\) on the ancilla flips the \(Z\) measurement outcome). 2) “No code-syndrome”: the data (qubits 0–3) component of \(P_{\text{tot}}\) must commute with both stabilizers \(XXXX\) and \(ZZZZ\). In Pauli-vector terms, this is equivalent to requiring that the total number of \(X/Y\) on data is even and the total number of \(Z/Y\) on data is even.

  • Among accepted events, the data operator lies in the normalizer of the stabilizer, i.e. it is equivalent to some logical Pauli (times possibly a stabilizer). With the given logicals

\[ X_A=X\,I\,X\,I,\quad X_B=X\,X\,I\,I,\quad Z_A=Z\,Z\,I\,I,\quad Z_B=Z\,I\,Z\,I, \]
the prepared logical state $|00\rangle_{AB}$ is stabilized by $Z_A$ and $Z_B$; hence any accepted error equivalent to a logical $Z$ (or product of logical $Z$’s, possibly times stabilizers) leaves the state unchanged, while any accepted error whose logical part contains $X_A$ and/or $X_B$ maps $|00\rangle_{AB}$ to an orthogonal logical basis state.
  • Therefore, the logical fidelity conditioned on acceptance equals the conditional probability that the net (accepted) logical operator contains no \(X\) on either \(A\) or \(B\):
\[ F_L(p)=\frac{P\{\text{accepted and no logical }X\}}{P\{\text{accepted}\}}. \]
  • Propagation and classification via Pauli analysis

  • Conjugation rules for CNOT\((c\to t)\) on Paulis:

\[ X_c\mapsto X_c X_t,\quad Z_t\mapsto Z_c Z_t,\quad X_t\mapsto X_t,\quad Z_c\mapsto Z_c, \]
and $Y=iXZ$ follows from these.
  • Ancilla acceptance depends only on the ancilla component of \(P_{\text{tot}}\):
\[ \text{accept ancilla} \iff P_{\text{tot}}|_4\in\{I,Z\}. \]
  • Code acceptance depends on parities on data qubits:
\[ \text{accept code} \iff \#(X/Y)\equiv 0\ (\text{mod }2)\ \text{and}\ \#(Z/Y)\equiv 0\ (\text{mod }2)\ \text{on qubits }0\text{–}3. \]
  • Presence of a logical \(X\) component can be detected by commutation with \(Z_A\) and \(Z_B\). For any accepted Pauli \(P\) on data, \(P\) has a logical \(X\) on \(A\) iff it anticommutes with \(Z_A\); similarly for \(B\):
\[ \text{$X_A$ present} \iff \{P,Z_A\}=0,\qquad \text{$X_B$ present} \iff \{P,Z_B\}=0. \]
  • First-order (single-fault) analysis

  • We enumerated all 75 single-fault cases: 5 locations × 15 two-qubit Paulis. Exactly 7 are accepted by both ancilla and code. They are

\[ \begin{aligned} &(1,\ XX)\ \to\ XXXX I,\\ &(1,\ YY)\ \to\ XYYX I,\\ &(1,\ ZZ)\ \to\ IZZII,\\ &(2,\ ZZ)\ \to\ ZZIII,\\ &(3,\ ZZ)\ \to\ IIZZI,\\ &(4,\ ZZ)\ \to\ ZIIZZ,\\ &(5,\ IZ)\ \to\ IIIIZ, \end{aligned} \]
where the string shows the resulting $P_{\text{tot}}$ on qubits 0–4. All 7 correspond to a stabilizer or a product of logical $Z$’s (possibly times stabilizers), hence are benign for $|00\rangle_{AB}$. Therefore there are no accepted malignant single faults:
\[ \Rightarrow\ \text{no }O(p)\text{ logical infidelity}. \]
  • Second order (two-fault) analysis and beyond

  • Two-fault patterns (errors on two distinct CNOTs) occur with probability \(\sim (p/15)^2\) and can combine to produce accepted logical-\(X\) errors. We exhaustively enumerated all \(10\times 15^2=2250\) ordered two-fault patterns and found:

\[ \#\{\text{accepted}\}=282,\qquad \#\{\text{accepted and benign}\}=138,\qquad \#\{\text{accepted and malignant}\}=144. \]
Thus the leading (quadratic) logical infidelity is governed by the 144 malignant, accepted pairs.
  • To obtain the exact dependence on \(p\) (not only to leading order), we counted, for \(k=0,\dots,5\), the number of \(k\)-fault patterns that are accepted, and among those, how many are benign. Denote these counts by c_acc[k] and c_good[k]. The exhaustive enumeration yields:
\[ \begin{aligned} &\text{Total patterns at }k\text{ faults: }[1,75,2250,33750,253125,759375],\\ &\text{Accepted counts }c_{\text{acc}}[k]=[1,7,282,4222,31637,94923],\\ &\text{Accepted and benign }c_{\text{good}}[k]=[1,7,138,1198,7397,24027].\\ \end{aligned} \]
  • From these counts, the acceptance probability and the “accepted-and-benign” probability are exact polynomials of degree 5 in \(p\):
\[ \begin{aligned} P_{\text{acc}}(p) &=\sum_{k=0}^{5} c_{\text{acc}}[k]\,(1-p)^{5-k}\Big(\frac{p}{15}\Big)^k\\ &=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) &=\sum_{k=0}^{5} c_{\text{good}}[k]\,(1-p)^{5-k}\Big(\frac{p}{15}\Big)^k\\ &=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.\end{aligned} \]
  • The logical state fidelity conditioned on acceptance is therefore the exact rational function
\[ F_L(p)=\frac{P_{\text{good}}(p)}{P_{\text{acc}}(p)}. \]
  • Small-\(p\) expansion: Since there are no accepted malignant single faults, the linear term cancels in the ratio. Expanding,
\[ \begin{aligned} F_L(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)\\ &=1-0.64\,p^2-1.877\overline{3}\,p^3-3.11037\,p^4-2.56076\,p^5+O(p^6). \end{aligned} \]
In particular, the leading coefficient is
\[ \frac{16}{25}=\frac{\#\{\text{malignant accepted pairs}\}}{15^2\binom{5}{2}}=\frac{144}{225}. \]
  • Physical interpretation

  • The ancilla parity check (CNOTs from qubits 3 and 0 into the ancilla) “flags” the dangerous single “hook” faults that would otherwise produce undetected weight-2 \(X\)-type errors on the cat state used for \(|00\rangle_{AB}\) preparation. Consequently, all accepted single faults are purely \(Z\)-type (logical \(Z\)’s or stabilizers), leaving \(|00\rangle_{AB}\) unchanged. The first harmful events require two faults, so the conditional logical infidelity scales as \(O(p^2)\) with coefficient \(16/25\).

  1. Mathematical Typesetting

All equations above are in LaTeX.

  1. Conventions and Units
  • Code qubits indexed 0–3, ancilla 4.
  • Stabilizers: \(XXXX\) and \(ZZZZ\).
  • Logical operators: \(X_A=X\,I\,X\,I\), \(X_B=X\,X\,I\,I\), \(Z_A=Z\,Z\,I\,I\), \(Z_B=Z\,I\,Z\,I\).
  • Two-qubit depolarizing error after each CNOT: non-identity two-qubit Pauli with probability \(p/15\).
  1. Final Answer:

The logical state fidelity of the post-selected output (conditioning on both “no code syndrome” and ancilla outcome \(|0\rangle\)) is the exact rational function

\[ F_L(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}. \]

For small \(p\) this expands to

\[ F_L(p)=1-\frac{16}{25}p^2+O(p^3)=1-0.64\,p^2+O(p^3). \]

In particular, there is no \(O(p)\) logical error (the circuit is fault-tolerant for this state preparation), and the leading logical infidelity is \(\frac{16}{25}p^2\).

User #13

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 #14
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 ------------------
    num = (
        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
    )

    den = (
        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 = num / den  # a SymPy expression of inputs
    # ---------------------------------------------------------------

    return F_logical