jump_q.py
Checks: OBSERVATORY: The jump set as the class maxima theorem.
Cited on: OBSERVATORY.
To run it: save jump_q.py, with clock.py, module_law.py, rung.py, staircase.py, tame.py, tame_q.py, triangle.py, weld.py, wild.py beside it; needs numpy.
"""
jump_q.py -- the jump set as the class maxima at f > 1, where the
bend's kernel splits each class. Question, derivation, predictions,
kill and controls frozen before code; the engine and grid below.
QUESTION. At odd p over the unramified base of degree f > 1, zeta_p
in K (N(-ubar) = 1): is the jump set still the staircase of the class
maxima H(m), proved at f = 1 (staircase.py, weld.py), now that each
class Gamma_m = (s/p^m, m) splits into units off the kernel (landing e*
exactly) and on it (tame_q.py)? Over 2 the wild readout is unread at
f > 1: odd p only.
DERIVATION (by hand; every use of f = 1 in the f = 1 proof).
(F1) "zeta_p in K forces ubar = -1, so y^p - 1 sits past e* at every
leading digit": FALSE at f > 1 (TRANSPLANT). Repair: off the kernel y
lands at e* (excess 0); on it y^p - 1 sits past e*, and the f = 1 step
runs verbatim (above the bend p-th powers map U_(s+j) onto U_(e*+j),
j >= 1, the graded map x -> ubar x bijective on F_(p^f)), so a kernel
landing is e* + v(zeta_p^(-a) y - 1) - s <= e* + G(m) - s. The witness
y = h^(-1), v(zeta_p h - 1) = G(m) > s, has level s and residue that
of zeta_p, which is on the kernel. So H(m) = G(m) + e at f > 1,
attained on the kernel, no outside theorem.
(F2) Pagano's presentation "U_1 = Lambda/Z_p V, one phi_i per weight":
f = 1 only. His Theorem 1.5 (read in his text): U_1 = M^(f-1) (+)
M*/Z_p v as filtered Z_p-modules, the direct sum filtered by the min
(his 3.2.1). Torsion, so zeta_p, lies in the second summand; adding a
p^m-th power with a nonzero first component cannot raise the weight,
so G(m) is computed in M*/Z_p v, which is the f = 1 module: the
Proposition 3.35 step and the staircase formula run unchanged.
(F3) The bracket: the class's landings are {e*} (off the kernel, never
empty at f > 1: q - 1 > p - 1) together with the kernel's, which lie
in [min(H(m), e* + p^m), H(m)], both ends attained (the kernel's
least landing is e* + min(l, p^m), tame_q). "Rigid" moves to the
kernel: kernel-rigid iff l < p^m, the kernel then one value e* + l.
(F4) No kernel-rigid class below N, so zeta_(p^N) in K forces
l >= p^(N-1): f-free once (F3) holds.
(F5) The rigid-seat closed form (i_2, beta_2) from dep = l = q e + r:
f-free (pull-back through rho). The N = 1 bound H(M) - e* <= e: f-free
(U_(e*+1) is all p-th powers at every f).
(F6) The jump set reads M e digits of u: its proof's Hensel and
O/pi^A steps are f-free; the Haar count (Pagano's 1.7/1.8 and Serre's
mass) is f = 1 as written. OUT of this question.
PREDICTIONS. (P1) H(m) by brute force over kernel units equals G(m) + e
by search over U_1^(p^m), and units off the kernel land at e*, at every
field and class. (P2) The staircase of H is a valid extended jump set
(beta down, weights up, first point (c_0, M + 1) at e*, last beta = N),
and equals the set rebuilt from the echelon's weights of zeta_p modulo
p^n-th powers. (P3) Kernel landings inside the bracket, both ends met;
l >= p^(N-1). (P4) The closed form at every kernel-rigid seat.
DESIGN. tame_q.py's Ring at p = 3, f = 2, e = 2, 4, 6, 12, 18 and p =
5, f = 2, e = 4, 8: random digits with N(-ubar) = 1 and designed
departures 1 .. s + 2, plus x^e - p T(dbar) twisted toward zeta_9
(Phi_9-type designs over Z_9) for N = 2. weld.py's Echelon generalized
to f elements per level (F_p-linear algebra on F_(p^f) leading
coefficients), giving U_1^(p^m) mod U_L and G(m) exactly; brute H(m)
over kernel units z = 1 + T(a) pi^c + ... to the depth G sets (the
brute arm and the grid as run: THE ENGINE AND GRID below).
KILL (printed). Fields off (P1), off (P2), off (P3) or off (P4), summed
over the grid: nonzero fires. Bar on the kernel arm (the arm a split
would break); the off-kernel count printed beside it.
CONTROLS. C1 f = 1: the generalized echelon reproduces weld.py's g and
jump sets on its own population rows. C2 plant: the bracket's lower end
read as the class's least landing (e*) instead of the kernel's at a
kernel-rigid field: (P3)'s count nonzero. C3 the zeta_p found has
order p and residue on the kernel.
THE ENGINE AND GRID, set before the run. O_K mod p^N as int64 vectors
over Z_p, multiplied through a structure tensor read off tame_q.py's
Ring. The echelon files up to f elements per level, leading residues
in echelon form over F_p, generators 1 + omega(beta_j) pi^i over the
F_p-basis beta_j; zeta_p starts at a residue with a^(p-1) = -ubar and
each correction solves x -> x lead((1 + pi^t)^p - 1) over F_q. The
brute arm needs no G: every unit of the class (c, m), all a != 0, a
prefix landing below the next digit's floor rho^(m+1)(c + k) landing
there for every extension and only the rest extended, so the class's
landings are read exhaustively, kernel and off. L = e* + (M + 1) e + 3.
Grid widened before the run: p = 3, f = 2, e = 2, 4, 6, 12, 18 at
the four ubar with N(-ubar) = 1, and p = 5, f = 2, e = 4, 8, 20 and
p = 3, f = 3, e = 6, 12 at three ubar each (-1 first, then off F_p), l = 1 .. s + 2,
none and one random tail per ubar. e = 2, 4 at p = 3 and 4, 8 at p = 5
have M = 0, so they read (P1)'s off-kernel arm and the first point
alone. N = 2: cofactor.py's design B^6 Phi_9(A/B), B = 1 + 3 Omega -
omega(a) x^i, A = B + omega(th) x^k over Z_9, k = 1, 2, 3, th = 1, g,
g^2.
FINDINGS (copied from the printed run). Controls: C1 30 fields of
weld.py's population at (p, e) = (3, 6), (3, 12), (3, 18), 0 off; C3
and the echelon's closure 0 off at 311 fields (C3's level and residue
hold by construction, its order is the reading); every designed
departure read back; C2 the class's landings read for the kernel's
break (P3) at 311 of 311 fields, and C2' the departure read as l + 1
at 105 of the 105 fields with a kernel-rigid class (117 classes). (P1) to
(P4): 0 fields off of 311, 0 classes off on the kernel, 0 off-kernel
landings off e*, at every grid point: (3, 2, 2) 20 fields, (3, 2, 4)
24, (3, 2, 6) 34 with 28 second points and 12 kernel-rigid seats, (3,
2, 12) 52, 40 and 12, (3, 2, 18) 70, 70 and 48, (5, 2, 4) 15, (5, 2,
8) 18, (5, 2, 20) 27, 27 and 15, (3, 3, 6) 21, 21 and 9, (3, 3, 12)
30, 30 and 9; 275 fields at N = 1 and 36 at N = 2, ubar off F_p at
168; the widest live branch count 6; 0 of 105 kernel-rigid seats off
the closed form. At N = 2 every field reads l >= 3 = p^(N-1): l = 3 i
at the designs with a != 0 (seats (1, 3), (4, 2) at l = 3 and (1, 3),
(5, 2) at l = 6, every th), l = 9 and the second point (19, 2) at a =
0, k = 3; the 18 fields at k = 1, 2 have M = 1 = N - 1, every class
holding a root, so they read the first point (c_0, 2) and the
bracket's ends alone. KILL MISSES.
VERDICT. On this grid (f = 2 and 3 at p = 3, f = 2 at p = 5, M <= 2,
M = 2 only at (3, 2, 18); N = 2 only at p = 3, f = 2, from one design
family) the derivation's predictions
hold: the class maxima sit on the bend's kernel, H(m) = G(m) + e
(F1), units off the kernel land at e*, the kernel's landings (not the
class's: off-kernel units sit at e*, below it) lie in [min(H(m), e* +
p^m), H(m)] with both ends attained (F3), l >= p^(N-1) (F4), and the
rigid-seat closed form holds with "rigid" read on the kernel, l < p^m
(F5's first half; its N = 1 bound H(M) - e* <= e is not read). The
staircase is checked against G alone: given (P1) its
equality with the set rebuilt from G is pullback arithmetic, and that
G's staircase is Pagano's jump set at f > 1 rests on (F2), his Theorem
1.5 as read in his text, which nothing here computes. So the run is a
toy instance of (F1), (F3), (F4) and the closed form of (F5), and the
claim at every odd p and f is the derivation's.
CHANGED AFTER THE FIRST RUN: the plant C2 now runs as
frozen, the class's landings put through (P3)'s comparison where it
reads the kernel's (the first run compared e* with the kernel's least
landing outside it, which cannot fail), with C2', the departure read
as l + 1, beside it; the kill counts fields as frozen, its bar on the
kernel arm, classes and off-kernel landings printed beside; a pullback
outside T* is counted against (P2) instead of raising, the H side and
the G side apart; C1 prints its (p, e). Every later run reads as the
first.
RUN RECORD. First run 10/10, 100.9 s; second 10/10, 100.1 s; third
11/11, 100.5 s, peak commit 22.6 MB; on moving to code, 11/11, 99.5 s.
"""
import os
os.environ.setdefault("OPENBLAS_NUM_THREADS", "1")
import random
import time
import numpy as np # noqa: E402
from module_law import check, section, CHECKS # noqa: E402
from tame_q import (Ring, build, fq_all, norm_is_one, u_of, # noqa: E402
departure, designed_digits)
from weld import rho, pullback, jump_set, weight # noqa: E402
import weld # noqa: E402
from staircase import staircase, second_closed, second_point # noqa: E402
INF = 10 ** 9
SEED = 1701
# ------------------------------------------------------------ the engine
class Units:
"""U_1/U_L of O_K = Z_q[pi]/(F) mod p^N as int64 vectors of length
e f (index i f + j for y^j pi^i), multiplied through a structure
tensor built from the tuple Ring R."""
def __init__(self, R, L):
p, f, e = R.p, R.f, R.e
self.R, self.p, self.f, self.e, self.q = R, p, f, e, R.q
self.N, self.P, self.L = R.N, R.P, L
assert e * R.N > L
n = self.n = e * f
# y^m pi^k for k <= 2e - 2, m <= 2f - 2, by the Ring
def mono(k, m):
x = R.zero()
x[min(k, e - 1)] = tuple(int(t == min(m, f - 1)) for t in range(f))
z = R.zero()
z[k - min(k, e - 1)] = tuple(int(t == m - min(m, f - 1))
for t in range(f))
return self.vec(R.mul(x, z))
tab = {(k, m): mono(k, m) for k in range(2 * e - 1)
for m in range(2 * f - 1)}
T = np.zeros((n, n, n), dtype=np.int64)
for i in range(e):
for j in range(f):
for i2 in range(e):
for j2 in range(f):
T[i * f + j, i2 * f + j2] = tab[(i + i2, j + j2)]
self.T2 = T.reshape(n, n * n)
self.one = np.zeros(n, dtype=np.int64)
self.one[0] = 1
idx = np.arange(self.P)
vp = np.zeros(self.P, dtype=np.int64)
for k in range(1, R.N):
vp[idx % p ** k == 0] = k
vp[0] = INF
self.vpt = vp
self.pk = np.array([p ** k for k in range(R.N + 1)], dtype=np.int64)
self.pis = [self.one]
x = np.zeros(n, dtype=np.int64)
x[f] = 1
self.piv = x
def vec(self, z):
return np.array([c for a in z for c in a], dtype=np.int64) % self.P
def mul(self, x, y):
M = ((x @ self.T2) % self.P).reshape(self.n, self.n)
return (y @ M) % self.P
def pw(self, x, k):
r = self.one
while k:
if k & 1:
r = self.mul(r, x)
k >>= 1
if k:
x = self.mul(x, x)
return r
def pi(self, i):
while len(self.pis) <= i:
self.pis.append(self.mul(self.pis[-1], self.piv))
return self.pis[i]
def scal(self, c, x):
"""omega-free scalar: the Z_q tuple c times x."""
w = np.zeros(self.n, dtype=np.int64)
w[:self.f] = c
return self.mul(w, x)
def gen(self, i, a):
"""1 + omega(a) pi^i."""
return (self.one + self.scal(self.R.omega(a), self.pi(i))) % self.P
def lead(self, z):
"""(level, leading residue tuple) of z - 1; level L means z = 1."""
w = z.copy()
w[0] = (w[0] - 1) % self.P
w = w.reshape(self.e, self.f)
v = self.vpt[w].min(axis=1)
lev = self.e * v + np.arange(self.e)
i = int(np.argmin(lev))
r = int(lev[i])
if r >= self.L:
return self.L, None
k = int(v[i])
return r, tuple(int(c) for c in (w[i] // self.pk[k]) % self.p)
def inv(self, z):
"""z^(-1) for a principal unit: z^(p^k - 1) once U_1^(p^k) is
past the precision."""
k, lv = 0, 1
while lv < self.e * self.N:
lv = rho(self.p, self.e, lv)
k += 1
return self.pw(z, self.p ** k - 1)
class Echelon:
"""At each level the subgroup reaches, up to f filed elements whose
leading residues are in echelon form over F_p (pivot coordinate 1,
zero at every earlier pivot)."""
def __init__(self, U):
self.U, self.at = U, {}
def reduce(self, z):
"""z times filed elements, to the first level where its leading
residue leaves the filed span: (z, level, residue)."""
U, p = self.U, self.U.p
while True:
r, c = U.lead(z)
if r >= U.L:
return z, U.L, None
c = list(c)
for (_, vec, piv, invs) in self.at.get(r, []):
k = c[piv]
if k:
c = [(x - k * y) % p for x, y in zip(c, vec)]
z = U.mul(z, invs[k])
if any(c):
return z, r, tuple(c)
def insert(self, z):
U, p = self.U, self.U.p
todo = [z]
while todo:
z, r, c = self.reduce(todo.pop())
if r >= U.L:
continue
piv = next(j for j, x in enumerate(c) if x)
k = pow(c[piv], -1, p)
if k != 1:
z = U.pw(z, k)
vec = tuple(k * x % p for x in c)
assert U.lead(z) == (r, vec)
assert len(self.at.get(r, [])) < U.f
bi = U.inv(z)
invs = [U.one]
for _ in range(p - 1):
invs.append(U.mul(invs[-1], bi))
self.at.setdefault(r, []).append((z, vec, piv, invs))
todo.append(U.pw(z, p))
def closed(self, rng, gens):
U = self.U
ok = all(self.reduce(U.pw(b, U.p))[1] >= U.L
for ents in self.at.values() for b, _, _, _ in ents)
for _ in range(4):
z = U.one
for g in gens:
z = U.mul(z, U.pw(g, rng.randrange(U.p ** 2)))
ok &= self.reduce(z)[1] >= U.L
return ok
def basis(p, f):
return [tuple(int(t == j) for t in range(f)) for j in range(f)]
def power_subgroup(U, n):
"""H_n = U_1^(p^n) mod U_L from the (1 + omega(beta_j) pi^i)^(p^n)."""
gens = []
for i in range(1, U.L):
lv = i
for _ in range(n):
lv = rho(U.p, U.e, lv)
if lv >= U.L:
continue
for b in basis(U.p, U.f):
gens.append(U.pw(U.gen(i, b), U.p ** n))
E = Echelon(U)
for g in gens:
E.insert(g)
return E, gens
def zeta_p(U, s, ubar):
"""A p-th root of unity of level s: residue a with a^(p-1) = -ubar,
each correction solving the graded map over F_q at a level past s."""
R, p, e = U.R, U.p, U.e
mu = tuple((-x) % p for x in ubar)
a = next(a for a in fq_all(p, U.f)[1:] if R.fq_pow(a, p - 1) == mu)
one = basis(p, U.f)[0]
y = U.gen(s, a)
while True:
r, c = U.lead(U.pw(y, p))
if r >= U.L:
return y, a
t = r - e
assert t > s, "the candidate's p-th power did not cancel at e*"
_, cw = U.lead(U.pw(U.gen(t, one), p))
x = R.fq_mul(tuple((-v) % p for v in c), R.fq_inv(cw))
y = U.mul(y, U.gen(t, x))
assert U.lead(U.pw(y, p))[0] > r
def on_kernel(R, a, m, ubar):
mu = tuple((-x) % R.p for x in ubar)
return R.fq_pow(R.fq_pow(a, R.p ** m), R.p - 1) == mu
def floor_at(p, e, c, k, m, L):
x = c + k
for _ in range(m + 1):
x = rho(p, e, x)
return min(x, L)
def brute_class(U, c, m, ubar):
"""Every landing v(z^(p^(m+1)) - 1) of the class (c, m), exhaustively:
z = 1 + omega(a) pi^c + omega(t_1) pi^(c+1) + ..., a prefix whose
landing sits below the next digit's floor rho^(m+1)(c + k) lands
there for every extension, and only the rest is extended. Returns
the off-kernel landings, the kernel landings (L for a branch still
alive at the precision) and the largest live branch count."""
R, p, e, L = U.R, U.p, U.e, U.L
F = fq_all(p, U.f)
om = {x: R.omega(x) for x in F}
P = p ** (m + 1)
off, ker = [], []
live = []
for a in F[1:]:
z = U.gen(c, a)
live.append((z, on_kernel(R, a, m, ubar)))
k, widest = 1, 0
while live:
fl = floor_at(p, e, c, k, m, L)
nxt = []
for z, onk in live:
lv = U.lead(U.pw(z, P))[0]
if lv < fl:
(ker if onk else off).append(lv)
elif fl >= L:
(ker if onk else off).append(L)
else:
nxt.append((z, onk))
widest = max(widest, len(nxt))
if len(nxt) > 400:
raise RuntimeError("brute: too many live branches")
pc = U.pi(c + k)
live = [((z + U.scal(om[t], pc)) % U.P, onk)
for z, onk in nxt for t in F]
k += 1
return off, ker, widest
# ------------------------------------------------------------ a field
def vp_int(n, p):
k = 0
while n % p == 0:
n //= p
k += 1
return k
def shape(p, e, s, M):
es = p * s
L = es + (M + 1) * e + 3
return L, (L + e) // e + 2
def read_field(R, rng):
"""G(0..M+2), N, the brute landings of every class, the controls."""
p, f, e = R.p, R.f, R.e
s = e // (p - 1)
M = vp_int(s, p)
c0, es = s // p ** M, p * s
L, _ = shape(p, e, s, M)
u = u_of(R)
ubar = tuple(a % p for a in u[0])
l = departure(R, u)
U = Units(R, L + e)
z, a = zeta_p(U, s, ubar)
U.L = L
ok_zeta = (U.lead(U.pw(z, p))[0] >= L and U.lead(z)[0] == s
and on_kernel(R, a, 0, ubar))
G, closed = {0: L}, True
for n in range(1, M + 3):
E, gens = power_subgroup(U, n)
closed &= E.closed(rng, gens[:6])
G[n] = E.reduce(z)[1]
N = min(n for n in range(1, M + 3) if G[n] < L)
br = {}
for m in range(M + 1):
off, ker, wide = brute_class(U, s // p ** m, m, ubar)
br[m] = dict(off=off, ker=ker, hi=max(off + ker), wide=wide)
return dict(s=s, M=M, c0=c0, es=es, L=L, l=l, ubar=ubar, G=G, N=N,
br=br, ok_zeta=ok_zeta, closed=closed)
def verdicts(p, e, rec, dl=0, whole=False):
"""Counts off (P1) .. (P4) for one field, and what each read. The
plants C2: dl shifts the departure the predictions use; whole reads
the class's landings, kernel and off, where (P3) reads the kernel's."""
s, M, es, L, N = (rec[k] for k in ("s", "M", "es", "L", "N"))
l = rec["l"] + dl
G, br = rec["G"], rec["br"]
out = dict(p1=0, p1off=0, p2=0, p3=0, p4=0, seats=0, rigid=0, lN=0)
H = {}
for m in range(M + 1):
want = G[m] + e if G[m] < L else L
b = br[m]
out["p1"] += b["hi"] != want
out["p1off"] += sum(v != es for v in b["off"])
H[m] = b["hi"] if b["hi"] < L else INF
ker = b["ker"] + (b["off"] if whole else [])
lo = min(b["hi"], es + p ** m)
ok = min(ker) == lo and max(ker) == b["hi"]
ok &= min(ker) == es + min(l, p ** m)
if l < p ** m and m >= N:
out["rigid"] += 1
ok &= set(ker) == {es + l}
out["p3"] += not ok
if N >= 2:
out["lN"] = l < p ** (N - 1)
out["p3"] += out["lN"]
try:
pts = staircase(p, e, s, M, H)
except TypeError:
pts = None
try:
fromG = jump_set(p, e, G, M + 1)
except TypeError:
fromG = None
by = sorted(pts or [(None, 0)], key=lambda t: -t[1])
ok = (pts is not None
and by[0] == (rec["c0"], M + 1)
and weight(p, e, *by[0]) == es
and by[-1][1] == N
and all(by[k][1] > by[k + 1][1]
and weight(p, e, *by[k]) < weight(p, e, *by[k + 1])
for k in range(len(by) - 1))
and pts == fromG)
out["p2"] = not ok
if M >= N and l < p ** M:
out["seats"] = 1
out["p4"] = not pts or len(pts) < 2 or second_point(
pts) != second_closed(
p, e, l)
out["pts"] = pts
return out
# ------------------------------------------------------------ populations
def ubars(p, f, R0, rng, k):
"""-1 first, then up to k - 1 others with N(-ubar) = 1, off F_p first."""
F = fq_all(p, f)
good = [x for x in F[1:] if norm_is_one(R0, tuple((-a) % p for a in x))]
m1 = (p - 1,) + (0,) * (f - 1)
rest = [x for x in good if x != m1]
rest.sort(key=lambda x: all(c == 0 for c in x[1:]))
return [m1] + rest[:k - 1]
GRID = [(3, 2, 2, 4), (3, 2, 4, 4), (3, 2, 6, 4), (3, 2, 12, 4),
(3, 2, 18, 4), (5, 2, 4, 3), (5, 2, 8, 3), (5, 2, 20, 3),
(3, 3, 6, 3), (3, 3, 12, 3)]
def population(rng):
"""(label, Ring) over GRID: per ubar with N(-ubar) = 1, departures
1 .. s + 2 and none, and one random tail."""
out = []
for (p, f, e, k) in GRID:
s = e // (p - 1)
L, N = shape(p, e, s, vp_int(s, p))
F = fq_all(p, f)
R0 = Ring(p, f, e, [None] * e, None, N)
top = 2 * e + s
for ub in ubars(p, f, R0, rng, k):
dbar = R0.fq_inv(ub)
for l in list(range(1, s + 3)) + [INF]:
dg = (designed_digits(rng, F, l, top) if l < INF
else [F[0]] * top)
out.append((f"l={'inf' if l >= INF else l}",
build(p, f, e, dbar, dg, N), l))
out.append(("random", build(p, f, e, dbar,
[rng.choice(F) for _ in range(top)],
N), None))
return out
def cyclo9(R0, k, th, a, i, om):
"""B^6 Phi_9(A/B) over Z_q made monic, B = 1 + 3 Omega(x) - omega(a)
x^i, A = B + omega(th) x^k (i < k, deg Omega < k): the (b, d) of an
Eisenstein polynomial of degree 6k whose root pi has A/B = zeta_9,
cofactor.py's design with Teichmuller residues th and a."""
p, f = R0.p, R0.f
zero, one = (0,) * f, (1,) + (0,) * (f - 1)
def pmul(x, y):
out = [zero] * (len(x) + len(y) - 1)
for i_, u in enumerate(x):
for j, w in enumerate(y):
out[i_ + j] = R0.zadd(out[i_ + j], R0.zmul(u, w))
return out
def padd(x, y):
n = max(len(x), len(y))
x = x + [zero] * (n - len(x))
y = y + [zero] * (n - len(y))
return [R0.zadd(u, w) for u, w in zip(x, y)]
B = [zero] * k
for j, c in enumerate(om):
B[j] = R0.zmul((p,) + (0,) * (f - 1), c)
B[0] = R0.zadd(B[0], one)
if any(a):
B[i] = R0.zadd(B[i], R0.zneg(R0.omega(a)))
A = padd(B, [zero] * k + [R0.omega(th)])
A3 = pmul(pmul(A, A), A)
B3 = pmul(pmul(B, B), B)
tot = padd(padd(pmul(B3, B3), pmul(A3, B3)), pmul(A3, A3))
while not any(tot[-1]):
tot.pop()
lc = R0.omega(R0.fq_inv(R0.fq_pow(th, 6)))
tot = [R0.zmul(lc, c) for c in tot]
e = 6 * k
assert tot[e] == one and len(tot) == e + 1
assert all(all(x % p == 0 for x in c) for c in tot[:e])
assert any(x % p ** 2 for x in tot[0])
b = [zero] + [tuple(x // p for x in c) for c in tot[1:e]]
d = tuple((-x // p) % R0.P for x in tot[0])
return b, d
def zeta9_population(rng):
"""Fields over W(F_9) holding zeta_9: k = 1, 2, 3 (e = 6, 12, 18),
th = 1, a generator g and g^2; per th, Omega none or random, and at
k >= 2 a random a at each i < k."""
p, f = 3, 2
out = []
F = fq_all(p, f)
for k in (1, 2, 3):
e = 6 * k
s = e // 2
L, N = shape(p, e, s, vp_int(s, p))
R0 = Ring(p, f, e, [None] * e, None, N + 2)
g = next(x for x in F[1:] if all(
R0.fq_pow(x, j) != F[1] for j in range(1, 8)))
designs = [(F[0], 0, 0), (F[0], 0, 1)]
designs += [(rng.choice(F[1:]), i, t) for i in range(1, k)
for t in (0, 1)]
for th in (F[1], g, R0.fq_mul(g, g)):
for a, i, t in designs:
om = ([] if t == 0 else
[tuple(rng.randrange(p ** 2) for _ in range(f))
for _ in range(k)])
b, d = cyclo9(R0, k, th, a, i, om)
P = p ** N
R = Ring(p, f, e, [tuple(x % P for x in c) for c in b],
tuple(x % P for x in d), N)
out.append((f"Phi_9 k={k} th={th} a={a} i={i} t={t}", R,
None))
return out
# ------------------------------------------------------------ sections
def section_c1(rng):
section("C1 f = 1: the engine against weld.py on its own population")
bad = n = 0
seen = set()
wrng = random.Random(weld.SEED)
for label, p, e, b, d, dep in weld.population(wrng):
if p == 2 or e > 18:
continue
seen.add((p, e))
rec = weld.read_field(p, e, b, d)
s, M = rec["s"], rec["M"]
L, N = shape(p, e, s, M)
P = p ** N
R = Ring(p, 1, e, [(x % P,) for x in b], (d % P,), N)
U = Units(R, L + e)
u = u_of(R)
z, _ = zeta_p(U, s, tuple(a % p for a in u[0]))
U.L = L
Lw = rec["L"]
mine = {n_: min(power_subgroup(U, n_)[0].reduce(z)[1], Lw)
for n_ in range(1, M + 3)}
theirs = {n_: rec["g"][n_] for n_ in range(1, M + 3)}
mine[0] = theirs[0] = Lw
n += 1
off = (mine != theirs
or jump_set(p, e, mine, M + 2) != jump_set(p, e, theirs, M + 2))
bad += off
if off:
print(f" OFF p={p} e={e} {label}: mine {mine} weld {theirs}")
check("C1 f = 1 echelon reproduces weld.py's g and jump sets", bad == 0,
f"{n} fields at (p, e) in {sorted(seen)}, {bad} off")
def section_fields(rng):
section("P1-P4 THE JUMP SET AS THE CLASS MAXIMA AT f > 1")
rows = population(rng) + zeta9_population(rng)
tot = dict(p1=0, p1off=0, p2=0, p3=0, p4=0, seats=0, rigid=0, lN=0)
foff = dict(p1=0, p2=0, p3=0, p4=0)
plant = rigid_fields = plant_w = 0
bad_z = bad_c = 0
lmiss = 0
by = {}
Ns = {}
wide = 0
nonfp = 0
for label, R, dl in rows:
p, f, e = R.p, R.f, R.e
rec = read_field(R, rng)
if dl is not None and rec["l"] != (dl if dl < INF else e * R.N):
lmiss += 1
bad_z += not rec["ok_zeta"]
bad_c += not rec["closed"]
v = verdicts(p, e, rec)
for k_ in tot:
tot[k_] += v[k_]
foff["p1"] += bool(v["p1"])
plant_w += verdicts(p, e, rec, whole=True)["p3"] > 0
for k_ in ("p2", "p3", "p4"):
foff[k_] += bool(v[k_])
if v["rigid"]:
rigid_fields += 1
plant += verdicts(p, e, rec, dl=1)["p3"] > 0
key = (p, f, e)
by.setdefault(key, [0, 0, 0, 0])
by[key][0] += 1
by[key][1] += v["p1"] + v["p1off"] + v["p2"] + v["p3"] + v["p4"]
by[key][2] += v["seats"]
by[key][3] += len(v["pts"] or []) >= 2
Ns[rec["N"]] = Ns.get(rec["N"], 0) + 1
nonfp += any(rec["ubar"][1:])
wide = max(wide, max(b["wide"] for b in rec["br"].values()))
if v["p1"] + v["p1off"] + v["p2"] + v["p3"] + v["p4"]:
print(f" OFF {key} {label} N={rec['N']} l={rec['l']} "
f"G={rec['G']} H={ {m: b['hi'] for m, b in rec['br'].items()} } "
f"pts={v['pts']} {v}")
if rec["N"] >= 2:
print(f" N={rec['N']} {key} {label}: l={rec['l']} "
f"ubar={rec['ubar']} G={rec['G']} pts={v['pts']}")
for key, (n, off, seats, two) in by.items():
print(f" {key}: {n} fields, {two} with a second point, "
f"{seats} kernel-rigid seats, off {off}")
print(f" fields by N: {Ns}; ubar off F_p at {nonfp}; widest live "
f"branch count {wide}; designed l missed {lmiss}")
check("C3 zeta_p: order p, level s, residue on the kernel",
bad_z == 0, f"{bad_z} off")
check("Cb the echelon closed under p-th powers and random products",
bad_c == 0, f"{bad_c} off")
check("designed departures read back", lmiss == 0, f"{lmiss} missed")
check("C2 plant: the class's landings read for the kernel's break P3 "
"at every field", plant_w == len(rows),
f"{plant_w} of {len(rows)} fields")
check("C2' plant: the departure read as l + 1 breaks P3 at every field "
"with a kernel-rigid class", plant == rigid_fields > 0,
f"{plant} of {rigid_fields} fields ({tot['rigid']} kernel-rigid "
f"classes)")
check("P1 H(m) = G(m) + e on the kernel", tot["p1"] == 0,
f"{tot['p1']} classes off; off the kernel {tot['p1off']} "
f"landings off e*")
check("P1' units off the kernel land at e*", tot["p1off"] == 0,
f"{tot['p1off']} off")
check("P2 the staircase of H is an extended jump set equal to G's",
tot["p2"] == 0, f"{tot['p2']} fields off")
check("P3 kernel landings lie in [min(H, e* + p^m), H], both ends "
"attained, least e* + min(l, p^m); l >= p^(N-1)", tot["p3"] == 0,
f"{tot['p3']} off ({tot['lN']} fields with l < p^(N-1))")
check("P4 the closed form at every kernel-rigid seat", tot["p4"] == 0,
f"{tot['p4']} of {tot['seats']} seats off")
kill = sum(foff.values())
print(f"\nKILL: fields off (P1) {foff['p1']}, (P2) {foff['p2']}, "
f"(P3) {foff['p3']}, (P4) {foff['p4']} of {len(rows)}; the "
f"kernel arm's classes off (P1) {tot['p1']}, (P3) {tot['p3']}, "
f"off-kernel landings off e* {tot['p1off']}: "
f"{'FIRES' if kill else 'MISSES'}")
def main():
t0 = time.time()
rng = random.Random(SEED)
section_c1(rng)
if not all(CHECKS):
print("\ncontrol failed: nothing below is read")
raise SystemExit(1)
section_fields(rng)
print(f"\n{sum(CHECKS)}/{len(CHECKS)} checks PASS "
f"[{time.time() - t0:.1f} s]")
if not all(CHECKS):
raise SystemExit(1)
if __name__ == "__main__":
main()