""" 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()