enumerate_maxwell.py โ€” src/27-fields

The file src/27-fields/enumerate_maxwell.py of the validation package of the paper 27-fld, rendered with an anchor per line; the file itself is beside this page.

27-fields ledgersrc/27-fields/enumerate_maxwell.py ยท 10.8 KBenumerate_maxwell.py
1# framed-rational status: MIXED -- exact framed-rational core; continuum constructs are confined to clearly-labelled [approx] / degenerate-idealisation comparisons (Tier 2/3/5 or measured-data), never to an exact claim.2"""3O1: uniqueness of the lattice Maxwell functional.45Determines the space of quadratic functionals  S[A] = A^T Q A  of a U(1) lattice6gauge field on a periodic cubic lattice that are simultaneously7  (T) translation invariant,8  (L) range-R local         (kernel supported on |x-y|_inf <= R),9  (C) hypercubic invariant   (the order-48 signed-permutation point group),10  (G) gauge invariant        (Q B = 0, B the lattice gradient A -> A + d.lambda).1112The point group acts directly on oriented links as signed permutations (a13reflection reverses a link, flipping sign and shifting base), so conjugation14P_g^T Q P_g is a flat index-permutation-with-sign on vec(Q).  We intersect (G)15and (C) via the Gram matrix on the (T,L) parameter space and report the16dimension of the admissible Q-space.  Expectation: dim = 1 (Maxwell) at R = 1.17"""18import numpy as np19import itertools2021d = 3222324def build(L):25    sites = list(itertools.product(range(L), repeat=d))26    sidx = {s: i for i, s in enumerate(sites)}27    def add(x, z): return tuple((x[i] + z[i]) % L for i in range(d))28    links = [(s, mu) for s in sites for mu in range(d)]29    lidx = {l: i for i, l in enumerate(links)}30    nlink = len(links); nsite = len(sites)3132    B = np.zeros((nlink, nsite))33    for (x, mu) in links:34        e = tuple(1 if k == mu else 0 for k in range(d))35        B[lidx[(x, mu)], sidx[add(x, e)]] += 1.036        B[lidx[(x, mu)], sidx[x]] -= 1.03738    group = []39    for pi in itertools.permutations(range(d)):40        for eps in itertools.product((1, -1), repeat=d):41            R = np.zeros((d, d), dtype=int)42            for a in range(d):43                R[pi[a], a] = eps[pi[a]]44            group.append(R)4546    # link signed-permutation for each isometry:  A'[k] = s[k]*A[sigma[k]]47    def link_perm(R):48        sigma = np.empty(nlink, dtype=int); s = np.empty(nlink)49        for (x, mu) in links:50            k = lidx[(x, mu)]51            col = R[:, mu]; mup = int(np.nonzero(col)[0][0]); sgn = int(col[mup])52            Rx = tuple(int(sum(R[i, j] * x[j] for j in range(d))) % L for i in range(d))53            if sgn == 1:54                src = lidx[(Rx, mup)]; sg = 1.055            else:56                base = tuple((Rx[i] - (1 if i == mup else 0)) % L for i in range(d))57                src = lidx[(base, mup)]; sg = -1.058            sigma[k] = src; s[k] = sg59        return sigma, s6061    # flat (i,j)->(t[i],t[j]) permutation and sign for conjugation P_g^T Q P_g62    flatperm, flatsign = [], []63    for R in group:64        sigma, s = link_perm(R)65        t = np.empty(nlink, dtype=int); t[sigma] = np.arange(nlink); st = s[t]66        fp = (t[:, None] * nlink + t[None, :]).reshape(-1)67        fs = (st[:, None] * st[None, :]).reshape(-1)68        flatperm.append(fp); flatsign.append(fs)6970    return dict(sites=sites, add=add, links=links, lidx=lidx, nlink=nlink,71                nsite=nsite, B=B, flatperm=flatperm, flatsign=flatsign, group=group)727374def param_basis(g, R):75    sites, add, lidx, nlink = g['sites'], g['add'], g['lidx'], g['nlink']76    disp = [z for z in itertools.product(range(-R, R + 1), repeat=d)77            if max(abs(c) for c in z) <= R]78    seen, Qs = set(), []79    for mu in range(d):80        for nu in range(d):81            for z in disp:82                if (mu, nu, z) in seen: continue83                seen.add((mu, nu, z)); seen.add((nu, mu, tuple(-c for c in z)))84                Q = np.zeros((nlink, nlink))85                for x in sites:86                    Q[lidx[(x, mu)], lidx[(add(x, z), nu)]] += 1.087                Qs.append(0.5 * (Q + Q.T))88    return Qs899091def maxwell_Q(g):92    sites, add, lidx, nlink = g['sites'], g['add'], g['lidx'], g['nlink']93    Q = np.zeros((nlink, nlink))94    for x in sites:95        for mu in range(d):96            for nu in range(mu + 1, d):97                emu = tuple(1 if k == mu else 0 for k in range(d))98                enu = tuple(1 if k == nu else 0 for k in range(d))99                terms = [(+1, lidx[(x, mu)]), (+1, lidx[(add(x, emu), nu)]),100                         (-1, lidx[(add(x, enu), mu)]), (-1, lidx[(x, nu)])]101                for s1, i in terms:102                    for s2, j in terms:103                        Q[i, j] += 0.5 * s1 * s2104    return Q105106107_P = (1 << 61) - 1   # Mersenne prime; exact finite-field linear algebra (framed-rational)108109def _nullspace_modp(M, p=_P):110    """Exact rank and nullspace basis of an integer matrix over F_p (Gaussian111    elimination in F_p, python ints, no float).  Used for the exact admissible112    dimension; p > all entry magnitudes, so the F_p corank equals the rational corank."""113    A = [[int(x) % p for x in row] for row in M.tolist()]114    rows = len(A); cols = len(A[0]) if rows else 0115    r = 0; pivots = []116    for c in range(cols):117        piv = next((i for i in range(r, rows) if A[i][c] % p), None)118        if piv is None:119            continue120        A[r], A[piv] = A[piv], A[r]121        inv = pow(A[r][c], p - 2, p)122        A[r] = [(x * inv) % p for x in A[r]]123        for i in range(rows):124            if i != r and A[i][c] % p:125                f = A[i][c]; A[i] = [(A[i][j] - f * A[r][j]) % p for j in range(cols)]126        pivots.append(c); r += 1127        if r == rows:128            break129    free = [c for c in range(cols) if c not in pivots]130    basis = []131    for fc in free:132        v = [0] * cols; v[fc] = 1133        for ri, pc in enumerate(pivots):134            v[pc] = (-A[ri][fc]) % p135        basis.append([x - p if x > p // 2 else x for x in v])   # balanced representative136    return r, basis137138139def admissible(L, R):140    g = build(L)141    Qs = param_basis(g, R); n = len(Qs); nlink = g['nlink']142    Qstack = np.array([Q.reshape(-1) for Q in Qs])              # (n, nlink^2)143    # gauge residual block:  vec(Q_a B)144    QB = np.array([(Q @ g['B']).reshape(-1) for Q in Qs])       # (n, nlink*nsite)145    # Exact admissible dimension (no float eigenvalues). Build the same Gram form as146    # before, but in INTEGER arithmetic (2*Q_a is integral, entries +-1), then take its147    # exact rational corank: a combination sum_a c_a Q_a is admissible iff c is in the148    # nullspace of the (integer) Gram = sum of the gauge- and cubic-constraint Grams.149    # dim = n - rank(Gram), computed exactly, not by a float eigenvalue threshold.150    Qint = [np.rint(2 * Q).astype(np.int64) for Q in Qs]             # integral (entries +-1)151    Bint = g['B'].astype(np.int64)152    QBi = np.array([(Qint[a] @ Bint).reshape(-1) for a in range(n)]) # gauge block (n, nlink*nsite)153    Gram = QBi @ QBi.T                                               # integer n x n (PSD)154    Qstk = np.array([Q.reshape(-1) for Q in Qint])155    for fp, fs in zip(g['flatperm'], g['flatsign']):156        D = Qstk[:, fp] * fs - Qstk                                  # integer (n, nlink^2)157        Gram = Gram + D @ D.T158    _, nb = _nullspace_modp(Gram)                                    # exact F_p corank (Tier-1 claim)159    dim = len(nb)160    # reps span the same admissible space and feed ONLY the [approx] continuum-symbol161    # reading below; a well-conditioned real basis is taken for that, and its count is162    # cross-checked against the exact framed-rational dimension above.163    w, V = np.linalg.eigh(Gram.astype(float))164    null = V[:, w < 1e-7 * max(1.0, w.max())]165    assert null.shape[1] == dim, "float/exact corank mismatch"166    reps = [sum(null[a, c] * Qs[a] for a in range(n)) for c in range(dim)]167    return dim, reps, g168169170def kernel_of(Q, g):171    """Read the translation-invariant kernel M_{mu,nu}(z) from the origin rows."""172    x0 = (0,) * d; lidx, addf = g['lidx'], g['add']173    disp = [z for z in itertools.product(range(-2, 3), repeat=d)174            if max(abs(c) for c in z) <= 2]175    K = {}176    for mu in range(d):177        for nu in range(d):178            for z in disp:179                v = Q[lidx[(x0, mu)], lidx[(addf(x0, z), nu)]]180                if abs(v) > 1e-12:181                    K[(mu, nu, z)] = v182    return K183184185def symbol(K, k):186    S = np.zeros((d, d), complex)187    for (mu, nu, z), v in K.items():188        S[mu, nu] += v * np.exp(1j * np.dot(k, z))189    return 0.5 * (S + S.conj().T)190191192def relevant_split(reps, g):193    """Split the admissible space into relevant (O(k^2)) and irrelevant (O(k^4))194       by the rank of the leading small-k symbol signature; check the relevant195       operator equals the Maxwell transverse projector |k|^2 delta - k k."""196    Ks = [kernel_of(r, g) for r in reps]197    dirs = [(1, 0, 0), (0, 1, 0), (0, 0, 1), (1, 1, 0), (1, 0, 1),198            (0, 1, 1), (1, 1, 1), (1, -1, 0), (2, 1, 0)]199    t = 1e-3200    sig = []201    for K in Ks:202        row = []203        for dv in dirs:204            kk = t * np.array(dv) / np.linalg.norm(dv)205            row.append(symbol(K, kk).real[np.triu_indices(d)] / t**2)206        sig.append(np.concatenate(row))207    sig = np.array(sig)208    s = np.linalg.svd(sig.T, full_matrices=False)[1]209    nrel = int(np.sum(s > 1e-6)); nirr = len(reps) - nrel210    # relevant generator and its deviation from the Maxwell projector211    crel = np.linalg.svd(sig.T, full_matrices=False)[2][0]212    Krel = {}213    for c, K in zip(crel, Ks):214        for key, v in K.items():215            Krel[key] = Krel.get(key, 0.0) + c * v216    dev = []217    for dv in [(1, 0, 0), (1, 1, 0), (1, 1, 1), (2, 1, 0), (3, 1, 1)]:218        kk = np.array(dv); S = symbol(Krel, t * kk).real / t**2219        P = np.dot(kk, kk) * np.eye(d) - np.outer(kk, kk)220        dev.append(np.linalg.norm(S / np.trace(S @ P) * np.trace(P @ P) - P)221                   / np.linalg.norm(P))222    return nrel, nirr, max(dev)223224225if __name__ == "__main__":226    print("=" * 68)227    print(f"O1  uniqueness of the lattice Maxwell functional   (d={d})")228    print("=" * 68)229230    g5 = build(5); QM = maxwell_Q(g5)231    gres = np.abs(QM @ g5['B']).max()232    cub = all(np.allclose((QM.reshape(-1)[fp] * fs).reshape(g5['nlink'], g5['nlink']), QM)233              for fp, fs in zip(g5['flatperm'], g5['flatsign']))234    print(f"Maxwell plaquette action: ||Q_M B||_inf = {gres:.1e} (gauge-inv {gres<1e-9});"235          f"  hypercubic-inv {cub}")236    print("-" * 68)237238    for L in (4, 5, 6):239        dim, reps, g = admissible(L=L, R=1)240        nrel, nirr, dev = relevant_split(reps, g)241        print(f"range R=1 (L={L}): dim(T&L&C&G) = {dim}  ="242              f"  {nrel} relevant (O(k^2)) + {nirr} irrelevant (O(k^4));"243              f"  relevant vs Maxwell projector: {dev:.1e}")244    print("-" * 68)245    print("Conclusion: the range-1 admissible space is 2-dimensional, and splits")246    print("into exactly ONE relevant operator -- the Maxwell action, whose symbol")247    print("is the transverse projector |k|^2 d_{mn} - k_m k_n -- plus one")248    print("irrelevant O(k^4) higher-derivative lattice artifact.  Maxwell is the")249    print("unique relevant adjacency-local gauge+hypercubic quadratic functional.")250    print("=" * 68)251