quantum-shadow-maps_v2/scripts/ghz3_perturbation_symbolic.py
2026-07-26 14:09:49 +02:00

209 lines
7.7 KiB
Python

"""
ghz3_perturbation_symbolic.py
Exact symbolic (sympy) first-order degenerate perturbation theory for the
GHZ3 shadow map under single-qubit dephasing noise on party B.
Background / formalism
-----------------------
At rho0 = GHZ3, the normalized combined shadow map M_A(rho0) has an exactly
threefold-degenerate singular value sigma = sqrt(2/3), with orthonormal
bases U0 (15x3, target side) and V0 = I_3 (3x3, source side) -- see
ghz3_shadow_map_symbolic.py.
For a perturbation rho(eps) = rho0 + eps * delta_rho, the map itself is
exactly linear: M_A(rho(eps)) = M_A(rho0) + eps * M_A(delta_rho).
To first order in eps, the perturbed singular values within the degenerate
block are
sigma_i(eps) = sigma + eps * lambda_i(K) + O(eps^2),
where K is the symmetrized projection of the perturbing map onto the
degenerate subspace:
K = (1/2) * ( U0^T M_A(delta_rho) V0
+ V0^T M_A(delta_rho)^T U0 ).
This is the direct analogue, for singular values, of ordinary degenerate
perturbation theory for Hermitian eigenvalues. K is automatically real
symmetric here because U0, V0 are real orthonormal bases.
Physical perturbation studied here: single-qubit dephasing on party B,
i.e. the (unnormalized, direction-only) Lindbladian jump direction
delta_rho^(P) = P_B rho0 P_B - rho0, P in {X, Y, Z}.
P = Z models T2-type dephasing in the computational (stabilizer) basis --
the dominant error channel on most physical qubit platforms. P = X, Y model
dephasing along an axis that does not commute with the GHZ3 stabilizer
group.
We show, exactly:
- Z-dephasing on B: eigenvalues of K are {-2*sqrt(6)/3 (x2), 0 (x1)}
-> the "z" target-response channel is exactly protected to first order,
while the "x","y" channels decay at twice the generic rate.
- X- or Y-dephasing on B: eigenvalues of K are {-sqrt(6)/3 (x3)}
-> fully isotropic decay, no protected direction.
The physical reason: Z_A Z_B is a stabilizer generator of GHZ3 and commutes
with Z_B, so the z-channel survives Z_B-dephasing unchanged to first order;
X_AX_B, Y_AY_B do not commute with Z_B and decay.
A numeric finite-difference cross-check (against the singular values of the
exactly perturbed matrix, not just the leading-order K prediction) is
included at the end.
Run: python3 ghz3_perturbation_symbolic.py
"""
import sympy as sp
from sympy import sqrt, I, simplify, Matrix, eye, zeros, re, Rational, N
# ---------------------------------------------------------------------
# 1. Rebuild the same primitives as in ghz3_shadow_map_symbolic.py
# (kept self-contained so this script can be run standalone)
# ---------------------------------------------------------------------
X = Matrix([[0, 1], [1, 0]])
Y = Matrix([[0, -I], [I, 0]])
Z = Matrix([[1, 0], [0, -1]])
I2 = eye(2)
PAULIS = {'x': X, 'y': Y, 'z': Z}
def kron(A, B):
mA, nA = A.shape
mB, nB = B.shape
out = zeros(mA * mB, nA * nB)
for i in range(mA):
for j in range(nA):
out[i * mB:(i + 1) * mB, j * nB:(j + 1) * nB] = A[i, j] * B
return out
def kron3(a, b, c): return kron(kron(a, b), c)
def op_A(P): return kron3(P, I2, I2)
def op_B(P): return kron3(I2, P, I2)
def op_C(P): return kron3(I2, I2, P)
def ghz3_state():
psi = zeros(8, 1)
psi[0, 0] = 1 / sqrt(2)
psi[7, 0] = 1 / sqrt(2)
return simplify(psi * psi.H)
def entry(rho, ops):
M = None
for op in ops:
M = op if M is None else M * op
return simplify(re(simplify((rho * M).trace())))
def build_M(rho):
"""Same 15x3 unnormalized shadow-map matrix as in the companion script."""
rows = []
for pb in ['x', 'y', 'z']:
rows.append([entry(rho, [op_A(PAULIS[pa]), op_B(PAULIS[pb])])
for pa in ['x', 'y', 'z']])
for pc in ['x', 'y', 'z']:
rows.append([entry(rho, [op_A(PAULIS[pa]), op_C(PAULIS[pc])])
for pa in ['x', 'y', 'z']])
for pb in ['x', 'y', 'z']:
for pc in ['x', 'y', 'z']:
rows.append([entry(rho, [op_A(PAULIS[pa]), op_B(PAULIS[pb]), op_C(PAULIS[pc])])
for pa in ['x', 'y', 'z']])
return Matrix(rows)
NORM_CONST = 1 / sqrt(3) # combined-map normalization for n=3 qubits
# ---------------------------------------------------------------------
# 2. Degenerate-perturbation-theory machinery
# ---------------------------------------------------------------------
def Kmatrix(delta_rho, U0, V0):
"""Symmetrized first-order splitting matrix for the degenerate block
spanned by (U0, V0), given a perturbation direction delta_rho."""
Md = simplify(NORM_CONST * build_M(delta_rho))
A = simplify(U0.T * Md * V0)
return simplify(Rational(1, 2) * (A + A.T))
def main():
rho0 = ghz3_state()
M0 = build_M(rho0)
Mn0 = simplify(NORM_CONST * M0)
# Degenerate subspace bases (see companion script for derivation):
# Gram matrix Mn0^T Mn0 = (2/3) I_3 exactly, so V0 = I_3 and
# U0 = Mn0 rescaled to unit-norm columns.
sigma = sqrt(Rational(2, 3))
U0 = simplify(Mn0 / sigma)
V0 = eye(3)
print(f"Unperturbed degenerate singular value: sigma = {sigma} "
f"= {float(sigma):.6f} (should be sqrt(6)/3, threefold)\n")
ops_B = {
'Z (T2-type, computational-basis dephasing)': op_B(Z),
'X': op_B(X),
'Y': op_B(Y),
}
K_store = {}
for label, OB in ops_B.items():
delta_rho = simplify(OB * rho0 * OB - rho0)
K = Kmatrix(delta_rho, U0, V0)
K_store[label] = (K, delta_rho)
print("=" * 70)
print(f"Dephasing on qubit B along {label}")
print("K =")
sp.pprint(K)
eigs = K.eigenvals()
print("Exact eigenvalues of K (first-order singular-value shifts):")
for ev, mult in eigs.items():
print(f" {sp.nsimplify(ev)} (multiplicity {mult}) "
f"= {float(ev):.6f}")
print(f"trace(K) = {simplify(sp.trace(K))} "
f"= {float(sp.trace(K)):.6f} "
f"(this is d/d(eps) ||M_A(rho(eps))||_* at eps=0)\n")
# -------------------------------------------------------------
# 3. Numeric finite-difference cross-check (independent of the
# symbolic K-matrix machinery): compute the *exact* singular
# values of M_A(rho0 + eps*delta_rho) for small eps and compare
# to sigma + eps*lambda_i(K).
# -------------------------------------------------------------
print("=" * 70)
print("Finite-difference cross-check for Z-dephasing on B")
print("(exact singular values of the perturbed matrix vs. first-order "
"prediction from K)\n")
K_Z, delta_rho_Z = K_store['Z (T2-type, computational-basis dephasing)']
eig_list = sorted(K_Z.eigenvals().keys(), reverse=True) # e.g. [0, -2sqrt6/3, -2sqrt6/3]
# build the multiset of 3 eigenvalues (respecting multiplicity)
eig_multiset = []
for ev, mult in K_Z.eigenvals().items():
eig_multiset += [ev] * mult
eig_multiset = sorted(eig_multiset, reverse=True)
for eps_val in [sp.Rational(1, 100), sp.Rational(1, 1000)]:
rho_eps = rho0 + eps_val * delta_rho_Z
M_eps = simplify(NORM_CONST * build_M(rho_eps))
G_eps = simplify(M_eps.T * M_eps)
sv_exact = sorted([sp.sqrt(ev) for ev in G_eps.eigenvals().keys()
for _ in range(G_eps.eigenvals()[ev])],
key=lambda v: float(v), reverse=True)
sv_predicted = sorted([sigma + eps_val * ev for ev in eig_multiset],
key=lambda v: float(v), reverse=True)
print(f"eps = {eps_val} :")
print(" exact singular values:", [f"{float(v):.6f}" for v in sv_exact])
print(" 1st-order prediction :", [f"{float(v):.6f}" for v in sv_predicted])
print()
if __name__ == "__main__":
main()