validate_fp_gauge.py — src/21-gravity

The script src/21-gravity/validate_fp_gauge.py of the validation package of the paper 21-grav, rendered with an anchor per line. ▶ run the predicates on Google Colab.

21-gravity ledgersrc/21-gravity/validate_fp_gauge.py · 3.5 KBvalidate_fp_gauge.pyColab
1"""Discrete Fierz-Pauli gauge invariance: exact (3+1) integer identity.23Validates the manuscript's claim (Eq. dfp, Prop. coupling/uniqueness, Prop. spacetime)4that the discrete Fierz-Pauli functional is exactly invariant under the discrete gauge5orbit6  h_mn -> h_mn + D_m xi_n + D_n xi_m7on the (3+1) periodic chart -- the same four-frame the uniqueness script works in.89The test is exact, not floating point. Fields are integer-valued and the functional is10evaluated in integer arithmetic with the dfp coefficients cleared to integers (1,-1,-2,+2)11and the undivided central difference D f = roll(f,-1) - roll(f,+1), which is integer and12anti-self-adjoint on the periodic cycle. Gauge invariance is then the exact integer identity13E[h + Dxi-symmetrised] - E[h] == 0. By the Schwartz-Zippel lemma a nonzero polynomial14identity in the field components fails on random integer points with negligible probability,15so an exact zero on random fields certifies the algebraic identity. Forward differences are16not anti-self-adjoint and the identity fails, retained as a control.17"""18import numpy as np1920rng = np.random.default_rng(7)21d, N = 4, 6                       # (3+1) periodic lattice (t,x,y,z), N^d sites22eta = np.diag([-1] + [1]*(d-1))   # integer Minkowski signature2324def D(f, mu, kind):25    if kind == "central":26        return np.roll(f, -1, axis=mu) - np.roll(f, 1, axis=mu)   # undivided central: integer, D^T = -D27    return np.roll(f, -1, axis=mu) - f                            # forward: integer, not anti-self-adjoint2829def E_FP(h, kind):30    """Integer Fierz-Pauli functional (dfp coefficients x4 -> +1,-1,-2,+2). h symmetric in (m,n)."""31    hu = np.einsum('ma,nb,ab...->mn...', eta, eta, h)             # indices raised32    tr = np.einsum('mn,mn...->...', eta, h)                       # trace h33    E = 034    for lam in range(d):35        dh  = np.stack([[D(h[m, n],  lam, kind) for n in range(d)] for m in range(d)])36        dhu = np.stack([[D(hu[m, n], lam, kind) for n in range(d)] for m in range(d)])37        E += int(eta[lam, lam]) * int(np.sum(dh * dhu))          # +1 D_l h_mn D^l h^mn38        dtr = D(tr, lam, kind)39        E -= int(eta[lam, lam]) * int(np.sum(dtr * dtr))         # -1 D_l h  D^l h40    divu = np.stack([sum(D(hu[m, n], m, kind) for m in range(d)) for n in range(d)])  # D_m h^mn41    divd = np.einsum('na,a...->n...', eta, divu)42    E -= 2 * int(np.sum(divu * divd))                            # -2 D_m h^mn D^l h_ln43    dtrv = np.stack([D(tr, n, kind) for n in range(d)])44    E += 2 * int(np.sum(divu * dtrv))                            # +2 D_m h^mn D_n h45    return int(E)4647def gauge_shift(xi, kind):48    return np.stack([[D(xi[n], m, kind) + D(xi[m], n, kind) for n in range(d)] for m in range(d)])4950h = rng.integers(-3, 4, size=(d, d) + (N,) * d)51h = h + h.transpose(1, 0, *range(2, 2 + d))                      # symmetric integer field52xi = rng.integers(-3, 4, size=(d,) + (N,) * d)5354ok = True55for kind in ("central", "forward"):56    E0 = E_FP(h, kind)57    E1 = E_FP(h + gauge_shift(xi, kind), kind)58    inv = (E1 == E0)59    if inv:60        print(f"  {kind:8s} differences: gauge invariance exact (integer identity, D^T = -D)")61    else:62        print(f"  {kind:8s} differences: gauge invariance broken (E1 - E0 = {E1 - E0})")63    if kind == "central" and not inv:64        ok = False65    if kind == "forward" and inv:66        print("    (unexpected: forward differences invariant)")6768print("\nPASS" if ok else "\nFAIL",69      "- discrete (3+1) FP gauge invariance: exact integer identity for central differences")70