#!/usr/bin/env python3 """ test_343 -- GAUSS AND CODAZZI AS THE FLATNESS OF TWO su(2) CONNECTIONS Let (Sigma^3, g) carry a symmetric endomorphism K. In an orthonormal frame with coframe theta^i and Levi-Civita forms omega^i_j, put k^i = K^i_j theta^j and Omega = [[omega^i_j, -k^i], [k_j, 0]], an so(4)-valued 1-form. so(4) splits into the commuting ideals su(2)_+ and su(2)_- (self-dual and anti-self-dual: M -> (M +- *M)/2, *M_ab = (1/2) eps_abcd M_cd). A+- = the projections of Omega. PRE-REGISTERED: P1 su(2)_+ and su(2)_- each close under the bracket, have dimension 3, and commute; P+ + P- = 1 on so(4). P2 (Theorem 9.1) On a generic diagonal metric with a generic K: the curvature of each half, computed from its own 1-form (d A + A ^ A), equals the projection of the curvature of Omega; and that curvature has tangential block R - k ^ k (Riemann computed separately) and mixed block the Codazzi tensor of K (computed separately). So F(A+) = F(A-) = 0 iff Gauss and Codazzi hold. P3 On the paraboloid of revolution in R^4 (g = (1 + r^2) dr^2 + r^2 (dth^2 + sin^2 th dph^2), K = diag(kmer, ksph, ksph)) both halves are flat. With K replaced by 1.1 K the Gauss block is nonzero and the Codazzi block is zero, and both halves are curved. P4 (Corollary 9.2) On the non-realizable metric g = dx^2 + (1 + x/2 + x^2/3)^2 dy^2 + (1 + x/3 - x^2/5)^2 dz^2 (frame components depending on x only) with K = K~(g) = sqrt(det M) M^{-1}: the Gauss block of the curvature of Omega vanishes identically, the Codazzi block equals Cod K~ computed separately and is nonzero, and the two halves' curvatures are equal and opposite (as 3-vectors of 2-forms in the su(2) identification). P5 (Proposition 9.3) Replacing K by -K (reversing the normal) leaves the Gauss block unchanged and reverses the Codazzi block, so the two halves are exchanged; equivalently Omega -> R Omega R, R = diag(1, 1, 1, -1). FIRST RUN: P1-P3 and P5 passed; P4 failed in its implementation. It compared the normal (mixed) parts of the two projected curvatures, which are equal for the two projections of a pure mixed element; the claim concerns their su(2) labels (the dual of the tangential block), which are opposite. Re-run comparing labels: label(F+) = -label(F-), nonzero wherever the Codazzi block is, and equal to +-1/2 the Codazzi vector. Claim unchanged. This script replaces test_295 for the paper's verification bundle. (The spinor form is test_334.) """ import sys import itertools import sympy as sp from sympy import Rational as R 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) def hodge(M): return sp.Matrix(4, 4, lambda a, b: sum(sp.LeviCivita(a, b, c, d) * M[c, d] for c in range(4) for d in range(4)) / 2) def proj(M, sgn): return (M + sgn * hodge(M)) / 2 def is_zero(M): return all(sp.simplify(x) == 0 for x in M) def basis_so4(): 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 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: the curvature of Omega, the curvatures of its two projected halves, 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) # the two chiral halves as connections in their own right: curvature computed from the projected 1-forms Fhalf = {} for sgn in (1, -1): Ah = [proj(Om[mu], sgn) for mu in range(3)] for mu, nu in ((0, 1), (0, 2), (1, 2)): Fhalf[(sgn, mu, nu)] = (sp.diff(Ah[nu], X[mu]) - sp.diff(Ah[mu], X[nu]) + Ah[mu] * Ah[nu] - Ah[nu] * Ah[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, Fhalf, Rf, Cf, kk # ---------------------------------------------------------------- P1 print("P1 so(4) = su(2)+ (+) su(2)-") B = basis_so4() ok1 = True for sgn in (1, -1): half = [proj(M, sgn) for M in B] rank = sp.Matrix([[h[i, j] for (i, j) in itertools.combinations(range(4), 2)] for h in half]).rank() ok1 &= rank == 3 for a in half: for b in half: br = a * b - b * a ok1 &= is_zero(br - proj(br, sgn)) for a in B: for b in B: ok1 &= is_zero(proj(a, 1) * proj(b, -1) - proj(b, -1) * proj(a, 1)) ok1 &= is_zero(proj(a, 1) + proj(a, -1) - a) check("each half closes and has dimension 3; the halves commute and sum to so(4)", ok1) # ---------------------------------------------------------------- P2 print("P2 Theorem 9.1 on a generic metric and 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 = (R(1, 3), R(-1, 5), R(2, 7)) FOm, Fh, Rf, Cf, kk = frame_data(a_gen, K_gen, pt) ok2 = all(is_zero(Fh[(sgn, mu, nu)] - proj(FOm[(mu, nu)], sgn)) for sgn in (1, -1) for (mu, nu) in FOm) ok2 &= all(is_zero(FOm[k][:3, :3] - (Rf[k] - kk[k])) and is_zero(FOm[k][3, :3].T - Cf[k]) for k in FOm) ok2 &= not all(is_zero(Fh[key]) for key in Fh) check("curvature of each half = projection of the curvature of Omega; blocks = Gauss and Codazzi residuals", ok2) # ---------------------------------------------------------------- P3 print("P3 a realizable hypersurface, and a scaled K") r, th, ph = X a_par = [sp.sqrt(1 + r**2), r, r * sp.sin(th)] K_par = sp.diag((1 + r**2)**R(-3, 2), (1 + r**2)**R(-1, 2), (1 + r**2)**R(-1, 2)) pt_par = (R(1, 2), R(2, 3), R(1, 5)) _, Fh_a, _, _, _ = frame_data(a_par, K_par, pt_par) FOm_b, Fh_b, _, _, _ = frame_data(a_par, R(11, 10) * K_par, pt_par) ok3 = all(is_zero(Fh_a[key]) for key in Fh_a) ok3 &= (not all(is_zero(FOm_b[k][:3, :3]) for k in FOm_b)) and all(is_zero(FOm_b[k][3, :3]) for k in FOm_b) ok3 &= all(not all(is_zero(Fh_b[(sgn, mu, nu)]) for (mu, nu) in FOm_b) for sgn in (1, -1)) check("paraboloid in R^4: both halves flat; 1.1 K: Gauss block nonzero, Codazzi block zero, both halves curved", ok3) # ---------------------------------------------------------------- P4 print("P4 Corollary 9.2: K = K~(g) on a non-realizable metric") x = X[0] a4 = [sp.Integer(1), 1 + x / 2 + x**2 / 3, 1 + x / 3 - x**2 / 5] g4 = sp.diag(*[a**2 for a in a4]) gi4 = g4.inv() Gm4 = [[[sum(gi4[i, m] * (sp.diff(g4[m, j], X[k]) + sp.diff(g4[m, k], X[j]) - sp.diff(g4[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)] Rm4 = lambda i, j, mu, nu: (sp.diff(Gm4[i][nu][j], X[mu]) - sp.diff(Gm4[i][mu][j], X[nu]) + sum(Gm4[i][mu][m] * Gm4[m][nu][j] - Gm4[i][nu][m] * Gm4[m][mu][j] for m in range(3))) Ric4 = sp.Matrix(3, 3, lambda j, nu: sp.simplify(sum(Rm4(i, j, i, nu) for i in range(3)))) Rs4 = sp.simplify(sum(gi4[i, j] * Ric4[i, j] for i in range(3) for j in range(3))) Mend = sp.simplify(-gi4 * (Ric4 - Rs4 * g4 / 2)) Mframe = sp.Matrix(3, 3, lambda i, j: sp.simplify(a4[i] * Mend[i, j] / a4[j])) # frame components pt4 = (R(1, 2), R(1, 3), R(1, 5)) assert Mframe == sp.diag(*[Mframe[i, i] for i in range(3)]), "M not diagonal" assert Mframe.det().subs(X[0], pt4[0]) > 0, "det M <= 0 at the point" Kt4 = sp.diag(*[sp.sqrt(Mframe.det()) / Mframe[i, i] for i in range(3)]) FOm4, Fh4, Rf4, Cf4, kk4 = frame_data(a4, Kt4, pt4) gauss0 = all(is_zero(FOm4[k][:3, :3]) for k in FOm4) codnz = not all(is_zero(FOm4[k][3, :3]) for k in FOm4) codsame = all(is_zero(FOm4[k][3, :3].T - Cf4[k]) for k in FOm4) label = lambda M: sp.Matrix([M[1, 2], M[2, 0], M[0, 1]]) # the su(2) label: the tangential block's dual codvec = lambda M: sp.Matrix([M[3, 0], M[3, 1], M[3, 2]]) eqopp = all(is_zero(label(Fh4[(1, mu, nu)]) + label(Fh4[(-1, mu, nu)])) and not is_zero(label(Fh4[(1, mu, nu)])) or is_zero(codvec(FOm4[(mu, nu)])) for (mu, nu) in FOm4) eqopp &= all(is_zero(label(Fh4[(1, mu, nu)]) - codvec(FOm4[(mu, nu)]) / 2) or is_zero(label(Fh4[(1, mu, nu)]) + codvec(FOm4[(mu, nu)]) / 2) for (mu, nu) in FOm4) check("Gauss block 0; Codazzi block = Cod K~ != 0; the halves are equal and opposite (su(2) labels opposite)", gauss0 and codnz and codsame and eqopp, f"Gauss 0 {gauss0}, Codazzi != 0 {codnz}, = Cod K~ {codsame}, opposite {eqopp}") # ---------------------------------------------------------------- P5 print("P5 Proposition 9.3: reversing the normal") FOmn, Fhn, _, _, _ = frame_data(a_gen, -K_gen, pt) Rr = sp.diag(1, 1, 1, -1) ok5 = all(is_zero(FOmn[k][:3, :3] - FOm[k][:3, :3]) and is_zero(FOmn[k][3, :3] + FOm[k][3, :3]) for k in FOm) ok5 &= all(is_zero(FOmn[k] - Rr * FOm[k] * Rr) for k in FOm) ok5 &= all(is_zero(Fhn[(1, mu, nu)] - Rr * Fh[(-1, mu, nu)] * Rr) for (mu, nu) in FOm) check("K -> -K: Gauss block unchanged, Codazzi block reversed, Omega -> R Omega R, the halves exchanged", ok5) print() print(f"test_343: {'ALL PASS' if not FAILURES else 'FAILURES: ' + ', '.join(FAILURES)}") sys.exit(1 if FAILURES else 0)