#!/usr/bin/env python3 """ test_334 -- GENERALIZED KILLING SPINORS AND THE TWO HALVES OF THE so(4)-CONNECTION Let (Sigma^3, g) carry a symmetric endomorphism K. In an orthonormal frame (E_1, E_2, E_3) with coframe theta^i and Levi-Civita forms omega^i_j, let k^i = K^i_j theta^j and Omega = [[omega^i_j, -k^i], [k_j, 0]] (an so(4)-valued 1-form on T Sigma (+) R), whose curvature dOmega + Omega ^ Omega has tangential block R^i_j - k^i ^ k_j (the Gauss residual) and mixed block d k_j + k_i ^ omega^i_j (the Codazzi residual). so(4) = su(2)_+ (+) su(2)_-, and each half-spin representation of so(4) is the 3-dimensional spinor representation on C^2 twisted by the normal part: rho_s(M) = c (1/4 sum_{i,j<=3} M_ij e_i e_j + s (1/2) sum_i M_i4 e_i), s = +1 or -1, with e_k = i sigma_k (so e_i e_j + e_j e_i = -2 delta_ij and e_1 e_2 e_3 = 1). The spinor connection rho_s(Omega) is nabla_X - s c (1/2) K(X). on spinors. PRE-REGISTERED: P1 Algebra. For the overall sign c that makes rho_s a Lie algebra homomorphism so(4) -> End(C^2) (c is found, not assumed, and must be the same for s = +1 and s = -1): rho_+ and rho_- are both homomorphisms; each has a 3-dimensional kernel, and the two kernels are complementary (they are su(2)_- and su(2)_+). Every nonzero element of the image is invertible (its square is -|v|^2/4 times the identity), so a nonzero element of the image annihilates no nonzero spinor. P2 Dirac. For symmetric K, sum_i e_i K(e_i) = -(tr K) 1, so a spinor with nabla_X psi = (eps/2) K(X).psi has D psi = -(eps/2)(tr K) psi under this Clifford convention (symbolic). P3 End to end, at a point of a diagonal metric g = diag(a1^2, a2^2, a3^2) with polynomial a_i and an arbitrary polynomial symmetric K (frame components): (i) the curvature of the spinor connection rho_s(Omega), computed directly as d + commutator, equals rho_s of the curvature of Omega; (ii) the tangential block of the curvature of Omega equals the frame components of the Riemann tensor (computed separately, from Christoffel symbols) minus k ^ k; (iii) the mixed block equals the Codazzi tensor of K computed separately with Christoffel symbols. P4 Consequences, at points of explicit metrics: (a) the paraboloid of revolution z = |x|^2/2 in R^4, g = (1 + r^2) dr^2 + r^2 (dtheta^2 + sin^2 theta dphi^2), K = diag(kmer, ksph, ksph) with kmer = (1+r^2)^(-3/2), ksph = (1+r^2)^(-1/2): the spinor connection is flat for BOTH s (a realizable, non-umbilic hypersurface carries generalized Killing spinors of both signs); (b) the same metric with K replaced by 1.1 K (Gauss fails, Codazzi fails): curvature nonzero for both s; (c) the round S^3 (stereographic, a_i = 1/(1 + |x|^2/4)) with K = Id: flat for both s (Killing spinors); with K = 2 Id: not flat; (d) one chirality and Codazzi force Gauss: on the generic metric of P3 with K = Id (Codazzi, Gauss failing), the curvature of rho_s(Omega) is rho_s(Gauss residual) and is nonzero for both s; the s-dependence sits only in the Codazzi block, which vanishes. So with K Codazzi, flatness for one s is flatness for both. FIRST RUN: all passed (c = -1, so rho_s(Omega) = nabla + (s/2) K(X). and parallel spinors satisfy nabla_X psi = -(s/2) K(X).psi). Tidied before logging, no check changed: a dead variable removed, and P3(iii)'s comparison, which accepted either sign of the mixed block, made one-sided (the block is +Codazzi in the row [k_j]). Second run identical. """ import sys import itertools import sympy as sp FAILURES = [] def check(name, ok, detail=""): print(f" [{'PASS' if ok else 'FAIL'}] {name}" + (f" -- {detail}" if detail else "")) if not ok: FAILURES.append(name) I = sp.I sig = [sp.Matrix([[0, 1], [1, 0]]), sp.Matrix([[0, -I], [I, 0]]), sp.Matrix([[1, 0], [0, -1]])] e = [I * s for s in sig] Id2 = sp.eye(2) def rho(M, s, c): """M: 4x4 antisymmetric (sympy). Indices 0..2 tangential, 3 normal.""" out = sp.zeros(2, 2) for i in range(3): for j in range(3): out += sp.Rational(1, 4) * M[i, j] * e[i] * e[j] for i in range(3): out += s * sp.Rational(1, 2) * M[i, 3] * e[i] return c * out def so4_basis(): B = [] for (i, j) in itertools.combinations(range(4), 2): M = sp.zeros(4, 4) M[i, j], M[j, i] = 1, -1 B.append(M) return B # ---------------------------------------------------------------- P1 print("P1 the two half-spin representations") B = so4_basis() found = None for c in (1, -1): ok = True for s in (1, -1): for X in B: for Y in B: if sp.simplify(rho(X * Y - Y * X, s, c) - (rho(X, s, c) * rho(Y, s, c) - rho(Y, s, c) * rho(X, s, c))) != sp.zeros(2, 2): ok = False if ok: found = c break ok1 = found is not None kers = {} if ok1: for s in (1, -1): A = sp.Matrix([[x for x in rho(X, s, found)] for X in B]).T # 4 x 6 (complex entries) ns = A.nullspace() kers[s] = [sum((v[k] * B[k] for k in range(6)), sp.zeros(4, 4)) for v in ns] ok1 &= len(kers[1]) == 3 and len(kers[-1]) == 3 stack = sp.Matrix([[M[i, j] for (i, j) in itertools.combinations(range(4), 2)] for M in kers[1] + kers[-1]]) ok1 &= stack.rank() == 6 v = sp.symbols("v0:3", real=True) Mv = sum((v[k] * e[k] for k in range(3)), sp.zeros(2, 2)) / 2 ok1 &= sp.simplify(Mv * Mv + (v[0]**2 + v[1]**2 + v[2]**2) / 4 * Id2) == sp.zeros(2, 2) check("rho_+ and rho_- are homomorphisms for one overall sign c; kernels 3 + 3 complementary; image elements square to " "-|v|^2/4", ok1, f"c = {found}") C = found # ---------------------------------------------------------------- P2 print("P2 the Dirac operator on a generalized Killing spinor") q = sp.symbols("q0:6", real=True) SYM = [(0, 0), (1, 1), (2, 2), (0, 1), (0, 2), (1, 2)] Ks = sp.zeros(3, 3) for val, (i, j) in zip(q, SYM): Ks[i, j] = Ks[j, i] = val tot = sp.zeros(2, 2) for i in range(3): for j in range(3): tot += Ks[j, i] * e[i] * e[j] # sum_i e_i . K(e_i), K(e_i) = sum_j K_ji e_j check("sum_i e_i K(e_i) = -(tr K) 1 for symmetric K", sp.expand(tot + Ks.trace() * Id2) == sp.zeros(2, 2)) # ---------------------------------------------------------------- machinery for P3, P4 X = sp.symbols("x0:3", real=True) def frame_data(avec, Kfun, pt): """Diagonal metric with g_ii = a_i^2; frame E_i = d_i / a_i. Kfun: 3x3 symmetric (frame components, functions of X). Returns at pt: Omega curvature blocks, spinor curvature for s = +-1, independent Riemann (frame) and Codazzi.""" g = sp.diag(*[a**2 for a in avec]) gi = sp.diag(*[1 / a**2 for a in avec]) Gam = [[[sum(gi[i, m] * (sp.diff(g[m, j], X[k]) + sp.diff(g[m, k], X[j]) - sp.diff(g[j, k], X[m])) for m in range(3)) / 2 for k in range(3)] for j in range(3)] for i in range(3)] # Gam[i][j][k] = Gamma^i_{jk} # connection forms in the frame: omega^i_j(d_mu) = theta^i(nabla_mu E_j) om = [[[avec[i] * (sp.diff(1 / avec[j], X[mu]) * (1 if i == j else 0) + Gam[i][mu][j] / avec[j]) for mu in range(3)] for j in range(3)] for i in range(3)] # om[i][j][mu] k1 = [[sum(Kfun[i, j] * (avec[j] if j == mu else 0) for j in range(3)) for mu in range(3)] for i in range(3)] # k^i(d_mu) Om = [sp.zeros(4, 4) for _ in range(3)] # Omega(d_mu) for mu in range(3): for i in range(3): for j in range(3): Om[mu][i, j] = om[i][j][mu] Om[mu][i, 3] = -k1[i][mu] Om[mu][3, i] = k1[i][mu] sub = dict(zip(X, pt)) FOm = {} for mu, nu in ((0, 1), (0, 2), (1, 2)): FOm[(mu, nu)] = (sp.diff(Om[nu], X[mu]) - sp.diff(Om[mu], X[nu]) + Om[mu] * Om[nu] - Om[nu] * Om[mu]).subs(sub) Fsp = {} for s in (1, -1): Rho = [rho(Om[mu], s, C) for mu in range(3)] for mu, nu in ((0, 1), (0, 2), (1, 2)): Fsp[(s, mu, nu)] = (sp.diff(Rho[nu], X[mu]) - sp.diff(Rho[mu], X[nu]) + Rho[mu] * Rho[nu] - Rho[nu] * Rho[mu]).subs(sub) # independent Riemann R^i_{j mu nu} (coordinates) and Codazzi (d_mu K)(nu) - (d_nu K)(mu) via Christoffels def Riem(i, j, mu, nu): return (sp.diff(Gam[i][nu][j], X[mu]) - sp.diff(Gam[i][mu][j], X[nu]) + sum(Gam[i][mu][m] * Gam[m][nu][j] - Gam[i][nu][m] * Gam[m][mu][j] for m in range(3))) # coordinate components of K as a (1,1) tensor: K^i_j(coord) = a_i^{-1} K_ij(frame) a_j Kc = sp.Matrix(3, 3, lambda i, j: Kfun[i, j] * avec[j] / avec[i]) def covK(mu, i, j): # (nabla_mu K)^i_j return sp.diff(Kc[i, j], X[mu]) + sum(Gam[i][mu][m] * Kc[m, j] - Gam[m][mu][j] * Kc[i, m] for m in range(3)) Rf, Cf = {}, {} for mu, nu in ((0, 1), (0, 2), (1, 2)): Rf[(mu, nu)] = sp.Matrix(3, 3, lambda i, j: (avec[i] / avec[j] * Riem(i, j, mu, nu)).subs(sub)) # Codazzi: (nabla_mu K)(d_nu) - (nabla_nu K)(d_mu), as frame vector: component i -> a_i * coord^i Cf[(mu, nu)] = sp.Matrix(3, 1, lambda i, _: (avec[i] * (covK(mu, i, nu) - covK(nu, i, mu))).subs(sub)) kk = {} for mu, nu in ((0, 1), (0, 2), (1, 2)): kk[(mu, nu)] = sp.Matrix(3, 3, lambda i, j: (k1[i][mu] * k1[j][nu] - k1[i][nu] * k1[j][mu]).subs(sub)) return FOm, Fsp, Rf, Cf, kk def is_zero(M, tol=None): return all(sp.simplify(x) == 0 for x in M) # ---------------------------------------------------------------- P3 print("P3 end to end on a generic diagonal metric and generic K") a_gen = [1 + X[0]**2 / 5 + X[1] * X[2] / 7, 1 + X[1] / 3 + X[0] * X[2] / 4, 2 + X[2]**2 / 6 - X[0] * X[1] / 5] K_gen = sp.Matrix([[1 + X[0] / 2, X[1] / 3, X[2] / 5], [X[1] / 3, 2 - X[2] / 4, X[0] * X[1] / 6], [X[2] / 5, X[0] * X[1] / 6, -1 + X[1]**2 / 7]]) pt = (sp.Rational(1, 3), sp.Rational(-1, 5), sp.Rational(2, 7)) FOm, Fsp, Rf, Cf, kk = frame_data(a_gen, K_gen, pt) ok3a = all(is_zero(Fsp[(s, mu, nu)] - rho(FOm[(mu, nu)], s, C)) for s in (1, -1) for (mu, nu) in FOm) ok3b = all(is_zero(FOm[k][:3, :3] - (Rf[k] - kk[k])) for k in FOm) ok3c = all(is_zero(FOm[k][3, :3].T - Cf[k]) for k in FOm) nonzero = not all(is_zero(Fsp[(s, mu, nu)]) for s in (1, -1) for (mu, nu) in FOm) check("spinor curvature = rho_s(curvature of Omega); tangential block = Riemann - k^k; mixed block = Codazzi of K", ok3a and ok3b and ok3c and nonzero, f"(i) {ok3a}, (ii) {ok3b}, (iii) {ok3c}") # ---------------------------------------------------------------- P4 print("P4 consequences") r, th, ph = X a_par = [sp.sqrt(1 + r**2), r, r * sp.sin(th)] kmer, ksph = (1 + r**2)**sp.Rational(-3, 2), (1 + r**2)**sp.Rational(-1, 2) K_par = sp.diag(kmer, ksph, ksph) pt_par = (sp.Rational(1, 2), sp.Rational(2, 3), sp.Rational(1, 5)) _, Fsp_a, _, _, _ = frame_data(a_par, K_par, pt_par) okA = all(is_zero(Fsp_a[key]) for key in Fsp_a) _, Fsp_b, _, _, _ = frame_data(a_par, sp.Rational(11, 10) * K_par, pt_par) okB = all(not all(is_zero(Fsp_b[(s, mu, nu)]) for (mu, nu) in ((0, 1), (0, 2), (1, 2))) for s in (1, -1)) check("(a) paraboloid in R^4, both chiralities flat; (b) 1.1 K, both curved", okA and okB, f"(a) {okA}, (b) {okB}") rr = X[0]**2 + X[1]**2 + X[2]**2 a_s3 = [1 / (1 + rr / 4)] * 3 _, Fsp_c1, _, _, _ = frame_data(a_s3, sp.eye(3), pt) _, Fsp_c2, _, _, _ = frame_data(a_s3, 2 * sp.eye(3), pt) okC = all(is_zero(Fsp_c1[key]) for key in Fsp_c1) and not all(is_zero(Fsp_c2[key]) for key in Fsp_c2) check("(c) round S^3: K = Id flat for both s (Killing spinors), K = 2 Id not flat", okC) FOm_d, Fsp_d, _, Cf_d, _ = frame_data(a_gen, sp.eye(3), pt) codazzi_zero = all(is_zero(Cf_d[k]) for k in Cf_d) gauss_only = all(is_zero(Fsp_d[(s, mu, nu)] - rho(sp.Matrix(sp.BlockMatrix([[FOm_d[(mu, nu)][:3, :3], sp.zeros(3, 1)], [sp.zeros(1, 3), sp.zeros(1, 1)]])), s, C)) for s in (1, -1) for (mu, nu) in FOm_d) curved = all(not all(is_zero(Fsp_d[(s, mu, nu)]) for (mu, nu) in FOm_d) for s in (1, -1)) check("(d) K = Id on the generic metric: Codazzi holds, spinor curvature = rho_s(Gauss residual) != 0 for both s", codazzi_zero and gauss_only and curved, f"Codazzi {codazzi_zero}, Gauss-only {gauss_only}, curved {curved}") print() print(f"test_334: {'ALL PASS' if not FAILURES else 'FAILURES: ' + ', '.join(FAILURES)}") sys.exit(1 if FAILURES else 0)