210 lines
7.7 KiB
Python
210 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()
|