#!/usr/bin/env python3 """ test_326 -- THE REALIZABILITY SYMBOL MODULE IS THE IMMERSIONS' MODULE, AT EVERY SPECIAL SHAPE OPERATOR TESTED; AND THE TWO LEMMAS BEHIND THE SYMBOL THEOREM, EXACTLY For MA section 4 (the paper this script is deposited with). test_325 ran Cartan's test at two generic K. Involutivity of F = 0 is a pointwise claim, and the round sphere (K umbilic) is exactly where special things happen, so this runs it at the special shape operators too, and identifies the symbol module. Set-up as test_325: at a point, orthonormal frame, K = K~(g) invertible; tau_q the order-q prolonged symbol in jet coordinates (columns = d^M h_w); g_q = ker tau_q. A linearised immersion delta x = X + phi N changes the metric by h = L_X g - 2 phi K; at order q its symbol is M_K = {xi (.) omega + phi K : omega a 1-form-valued polynomial of degree q+1, phi a polynomial of degree q} (constants absorbed). (A first scan of 15 shape operators was run in a scratch script before this test was written; it motivated P1 and P3.) PRE-REGISTERED: P1 Lemma (the factors), exactly. For unit xi, P = 1 - xi xi^T, and adj_2(Y) = (tr Y) P - Y on xi-perp: (a) the Einstein symbol is sigma_2(DM)(xi) h = -(1/2) adj_2(P h P), and it vanishes on xi (.) v; (b) D adj_K(X) = KX + XK - sigma_1 X - (tr X) K + (sigma_1 tr X - tr KX) 1 for EVERY symmetric K (the derivative of adj K = K^2 - sigma_1 K + sigma_2 1), and D adj_K(xi xi^T) = adj_2(P K P). Symbolic, with xi = e_1 and K, h general symmetric (rotation invariance covers every unit xi). P2 M_K has dimension (q+2)(2q+5) = 3 dim S^{q+1} + dim S^q for q = 3, 4, 5 at every K below (the map (omega, phi) -> xi (.) omega + phi K is injective), and tau_q annihilates it: M_K is inside g_q. P3 At fifteen shape operators, in an exact rational frame (Cayley rotation), covering umbilic (1,1,1), two equal eigenvalues on both branches, the TT-characteristic (2,3,-3), the boundary k1 + k2 = 0, and near-degenerate (1,1,1/10): dim g_3 = 55, dim g_{3,1} = 19, dim g_{3,2} = 4, dim g_4 = 78 = 55 + 19 + 4 (Cartan's test with equality), dim g_5 = 105. So g_q = M_K for q = 3, 4, 5 at every K tested: the formal solutions of the symbol are exactly the linearised immersions', and F = 0 is involutive at each of these points, the round sphere's included. P4 (Added after P1-P3 ran; registered before its own run.) The injectivity of (omega, phi) -> d(.)omega + phi K for q >= 2 rests on: in dimension three the Kulkarni-Nomizu map A -> A (o) K on Sym^2 has determinant 2 (det K)^2 (symbolic, general symmetric K), so it is injective iff K is invertible. BUNDLE EDIT (29 October): the archive's [M] labels were removed from the claims; nothing else changed. """ import sys import itertools import numpy as np 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) SYM = [(0, 0), (1, 1), (2, 2), (0, 1), (0, 2), (1, 2)] CODIDX = [(a, b, c) for (a, b) in ((0, 1), (0, 2), (1, 2)) for c in range(3)] def symmat(vals): S = sp.zeros(3, 3) for v, (i, j) in zip(vals, SYM): S[i, j] = S[j, i] = v return S def sigma_M(xi, h): x2 = (xi.T * xi)[0] hx = h * xi Ric = (xi * hx.T + hx * xi.T - x2 * h - xi * xi.T * h.trace()) / 2 R = (xi.T * h * xi)[0] - x2 * h.trace() return -(Ric - R * sp.eye(3) / 2) def Dadj_poly(K, X): s1 = K.trace() return K * X + X * K - s1 * X - X.trace() * K + (s1 * X.trace() - (K * X).trace()) * sp.eye(3) # ---------------------------------------------------------------- P1 print("P1 the two lemmas behind the symbol theorem, exactly") K = symmat(sp.symbols("k0:6")) H = symmat(sp.symbols("h0:6")) e1 = sp.Matrix([1, 0, 0]) P = sp.eye(3) - e1 * e1.T adj2 = lambda Y: Y.trace() * P - Y Y = P * H * P okA = sp.expand(sigma_M(e1, H) + adj2(Y) / 2) == sp.zeros(3, 3) v = sp.Matrix(sp.symbols("v0:3")) okA &= sp.expand(sigma_M(e1, (e1 * v.T + v * e1.T) / 2)) == sp.zeros(3, 3) t = sp.symbols("t") X = symmat(sp.symbols("x0:6")) okB = sp.expand(sp.diff((K + t * X).adjugate(), t).subs(t, 0) - Dadj_poly(K, X)) == sp.zeros(3, 3) okB &= sp.expand(Dadj_poly(K, e1 * e1.T) - adj2(P * K * P)) == sp.zeros(3, 3) check("sigma_2(DM)(xi) h = -1/2 adj_2(PhP), zero on gauge; D adj_K polynomial for every K, D adj_K(xi xi^T) = adj_2(PKP)", okA and okB) # ---------------------------------------------------------------- symbol machinery (exact rational K) def dadj_inv(Kr, Yr): xs = sp.symbols("z0:6") Xs = symmat(xs) sol = sp.solve([(Dadj_poly(Kr, Xs) - Yr)[i, j] for (i, j) in SYM], xs, dict=True)[0] return Xs.subs(sol) def symbol_poly(Kr): Xv = sp.Matrix(sp.symbols("X0:3")) cols = [] for w in range(6): E = sp.zeros(3, 3) i, j = SYM[w] E[i, j] = E[j, i] = 1 L = dadj_inv(Kr, sigma_M(Xv, E)) cols.append([sp.expand(Xv[a] * L[b, c] - Xv[b] * L[a, c]) for (a, b, c) in CODIDX]) return sp.Matrix(cols).T, list(Xv) def monomials(deg): return [m for m in itertools.product(range(deg + 1), repeat=3) if sum(m) == deg] def tau(Spoly, Xv, q): cols = {(M, w): i for i, (M, w) in enumerate((M, w) for M in monomials(q) for w in range(6))} polys = [[sp.Poly(Spoly[u, w], *Xv) for w in range(6)] for u in range(9)] rows = [] for m in monomials(q - 3): for u in range(9): row = np.zeros(len(cols)) for w in range(6): for mon, coef in polys[u][w].terms(): if coef == 0: continue row[cols[(tuple(a + b for a, b in zip(mon, m)), w)]] += float(coef) rows.append(row) return np.array(rows), cols def rank(Mx, tol=1e-8): s = np.linalg.svd(Mx, compute_uv=False) return int((s > tol * s.max()).sum()) if s.size else 0 def immersion_module(Kr, q, cols): """columns: jet vectors (d^M h_w = M! * coefficient) of h = x (.) omega + phi K, for monomial omega, phi.""" xv = sp.symbols("y0:3") fact = lambda M: int(np.prod([sp.factorial(m) for m in M])) gens = [] def jetvec(hmat): vec = np.zeros(len(cols)) for w, (i, j) in enumerate(SYM): p = sp.Poly(sp.expand(hmat[i, j]), *xv) for mon, coef in p.terms(): if coef != 0: vec[cols[(mon, w)]] += float(coef) * fact(mon) return vec # tangent part: h_ab = d_a om_b + d_b om_a, om a 1-form with homogeneous degree q+1 components for M in monomials(q + 1): mono = sp.prod([xv[i] ** M[i] for i in range(3)]) for b in range(3): hm = sp.zeros(3, 3) for a in range(3): hm[a, b] += sp.diff(mono, xv[a]) hm[b, a] += sp.diff(mono, xv[a]) gens.append(jetvec(hm)) for M in monomials(q): mono = sp.prod([xv[i] ** M[i] for i in range(3)]) gens.append(jetvec(mono * Kr)) return np.array(gens).T def rat_rot(a, b, c): A = sp.Matrix([[0, -c, b], [c, 0, -a], [-b, a, 0]]) return (sp.eye(3) - A) * (sp.eye(3) + A).inv() R = rat_rot(sp.Rational(2, 7), sp.Rational(-3, 5), sp.Rational(5, 11)) assert sp.simplify(R * R.T - sp.eye(3)) == sp.zeros(3, 3) CASES = [(1, 1, 1), (1, 1, 2), (1, 1, -1), (2, 3, -3), (-1, 1, 2), (1, 2, 3), (1, 1, -2), (-1, 2, 2), (2, 2, -1), (1, -2, -2), (3, -1, -1), (1, 2, -1), (5, -2, 1), (1, 1, sp.Rational(1, 10)), (1, -1, 1)] ok2, ok3, det2, det3 = True, True, [], [] for kd in CASES: Kr = R * sp.diag(*kd) * R.T Sp_, Xv = symbol_poly(Kr) dims = {} for q in (3, 4, 5): Tq, cols = tau(Sp_, Xv, q) dims[q] = 6 * len(monomials(q)) - rank(Tq) G = immersion_module(Kr, q, cols) want = (q + 2) * (2 * q + 5) rG = rank(G) inside = np.abs(Tq @ G).max() / max(1.0, np.abs(G).max()) < 1e-9 ok2 &= (rG == want and inside) if q == 3: gk = [] for kfree in (1, 2): idx = [i for (M, w), i in cols.items() if all(M[j] == 0 for j in range(kfree))] gk.append(len(idx) - rank(Tq[:, idx])) if kd == (1, 1, 1): det2.append(f"q=3: rank M_K {rG} = {want}, tau_3 M_K = 0: {inside}") good = (dims[3] == 55 and gk == [19, 4] and dims[4] == 78 == dims[3] + gk[0] + gk[1] and dims[5] == 105) ok3 &= good det3.append(f"{kd}:{'ok' if good else f'dims {dims}, gk {gk}'}") print("P2 the immersions' symbol module M_K: dimension (q+2)(2q+5), inside g_q") check("rank M_K = (q+2)(2q+5) and tau_q M_K = 0 for q = 3, 4, 5 at all fifteen K", ok2, "; ".join(det2)) print("P3 Cartan's test at fifteen special shape operators; g_q = M_K") check("dim g_3 = 55, g_{3,1} = 19, g_{3,2} = 4, dim g_4 = 78 = Cartan sum, dim g_5 = 105 at every K: g_q = M_K, involutive", ok3, ", ".join(det3)) print("P4 the Kulkarni-Nomizu map with K is injective iff det K != 0") qk = sp.symbols("w0:6") Kq = symmat(qk) PAIRS = [(0, 1), (0, 2), (1, 2)] rows = [] for (a, b) in PAIRS: for (c, d) in PAIRS: if (a, b) <= (c, d): row = [] for w in range(6): E = sp.zeros(3, 3) i, j = SYM[w] E[i, j] = E[j, i] = 1 row.append(E[a, c] * Kq[b, d] + E[b, d] * Kq[a, c] - E[a, d] * Kq[b, c] - E[b, c] * Kq[a, d]) rows.append(row) KNdet = sp.Matrix(rows).det() check("det(A -> A (o) K on Sym^2) = 2 (det K)^2", sp.expand(KNdet - 2 * Kq.det() ** 2) == 0, f"{sp.factor(KNdet)}") print() print(f"test_326: {'ALL PASS' if not FAILURES else 'FAILURES: ' + ', '.join(FAILURES)}") sys.exit(1 if FAILURES else 0)