splitpart.py
Cited on: PRINCIPAL.
To run it: save splitpart.py, with localtype.py, module_law.py, principal.py beside it; needs numpy.
"""splitpart.py -- which split terms carry the rise of PI_1 - PI_k0?
QUESTION. Over the real fields with the class k0 to |D| <= 48000, the
split places carry the rise of d = PI_1 - PI_k0 with log D
(localtype.py: +0.258 of +0.290 per unit at the cut 250, each field
weighted by its h+/2 odd characters). Which split terms carry it: the
first powers or the higher, and inside Lagrange's zone p < sqrt(D)/2
or outside it?
THE DERIVATION (before code). A split P, Pbar in narrow classes c,
c^-1 adds (2/e)([c^e = 1] - [c^e = k0]) at each power with p^e < x.
(a) e = 1, S1: a uniform c has mean 0 here for every field, so a
slope in S1 is a bias of 1 over k0. TRANSPLANT: the uniform c at
fixed p as D grows is Chebotarev carried from p -> infinity.
(b) e >= 2, S2: a uniform c gives E2 = sum (2/e)(#{g: g^e = 1} -
#{g: g^e = k0})/h+, 0 at odd e (k0 = k0^e), 1/|Cl+^2| at e = 2
when k0 is not a square and 0 when it is.
(c) Lagrange (known): |m| < sqrt(D)/2 represented primitively by the
principal form leads a reduced form of its cycle. So for split
p < sqrt(D)/2, P in 1 iff +p leads a form in the cycle of
(1, b0, .), P in k0 iff -p does.
(d) S1 splits at sqrt(D)/2 into S1lo (the zone) and S1hi. At 250 the
zone's prime count grows with D to 48000.
PREDICTIONS, frozen before the engine (cut 250, weighted h+/2).
C1 Lagrange on the engine: [P in 1] = [+p leads a principal-cycle
form], [P in k0] = [-p does], over every split p < sqrt(D)/2;
0 exceptions.
C2 S1lo + S1hi + S2 = d_S per field to 1e-12; s(S1lo + c log D)
reads s(S1lo) + c to 1e-9, c = 0.29.
P1 s(S2) < +0.05, and the mean of S2 - E2 within 0.1 of 0.
P2 the per-prime bias inside the zone, mean of S1lo / #zone primes
split, is positive in every |D| band; outside it, within 0.02 of 0.
KILL ZONE (printed "S1lo share of s(d_S) at 250"): fires if
s(S1lo) < 0.5 s(d_S); Lagrange's zone then does not carry the
rise. At 10^4 the split prints, no verdict.
FINDINGS, the 10323 real fields with k0 to |D| <= 48000, weighted h+/2.
F0 CONTROLS. Every principal-cycle form lies in class 1 (by
construction); Lagrange read on the engine with 0 exceptions, so
+p in the cycle is P in 1 and -p is P in k0; the parts sum to d_S
to 1.42e-14; the planted c log D reads c to the tenth digit. P2's
"mean of S1lo / #zone primes" is read in the engine as a ratio of
means, Sum S1 / Sum primes, both weighted h+/2; a mean of
per-field ratios is undefined at a field with no zone prime.
F1 KILL ZONE FIRED, and P2 MISSED with the sign reversed: at 250
s(d_S) +0.2581 = S1lo -0.0657 +- 0.0206 + S1hi +0.4204 +- 0.0398
+ S2 -0.0966 +- 0.0185 (E2 -0.1471). The first powers lean to k0,
not to 1, inside the zone and outside it, and the lean fades with
D on both sides: per split prime, Sum S1 / Sum primes reads
-0.1374, -0.0750, -0.0707, -0.0542, -0.0506 by band inside the
zone and -0.0784, -0.0445, -0.0330, -0.0314, -0.0234 outside. The
zone holds 3.26 to 11.83 split primes by band: as sqrt(D)/2
rises, primes cross from S1hi into S1lo, each carrying a negative
lean, so S1lo reads -0.448, -0.394, -0.499, -0.521, -0.598 by band
and S1hi -1.740 to -0.320; the S1lo, S1hi split tracks the zone's
moving edge. At 10^4: +0.8078 = -0.0657 + 1.5815 - 0.7079.
F2 P1 half held: s(S2) -0.0966 < 0.05, but S2 sits below the uniform
class's E2 by 0.3891 at 250 (1.2183 at 10^4); band means S2
+1.299 to +0.980 against E2 +1.810 to +1.334. d_S by band -0.890,
-0.152, -0.023, +0.006, +0.061: the uniform class's value of d_S
is E2, which d_S approaches from below.
Not derived: why the first powers lean to k0, and why the lean
relaxes as D grows at a fixed cut.
TIERS: F0 a property (Lagrange, known); F1, F2
observations over this population.
RUN RECORD. A rehearsal at --d 3000 (3.0 s). Full: 5/8, the three
misses P1's second half and P2's two, 51.4 s, peak commit 55.8 MB under
a memory guard (512 MB). P1's second half and P2 are now prints, since
a verdict is read, never asserted, and a missed one is the finding:
5/5, 51.5 s, every printed number unchanged.
"""
import os
os.environ.setdefault("OPENBLAS_NUM_THREADS", "1")
import sys
import time
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)
import numpy as np # noqa: E402
import principal as P # noqa: E402
import localtype as L # noqa: E402
from module_law import check, section, CHECKS # noqa: E402
D_MAX = 48000
if "--d" in sys.argv:
D_MAX = int(sys.argv[sys.argv.index("--d") + 1])
J250, J10K = 0, len(P.CUTS) - 1
def split_terms(G, avals):
"""Per cut: S1lo, S1hi, S2, E2, the zone's and the outside's split
prime counts; and the Lagrange exceptions."""
nc = len(P.CUTS)
out = np.zeros((6, nc))
bad = 0
pw = {}
def npow(e):
if e not in pw:
vals = [G.power(g, e) for g in range(G.h)]
pw[e] = (vals.count(G.e), vals.count(G.k0))
return pw[e]
for p in P.SMALL:
if P.kron(G.D, p) != 1:
continue
c = G.place(p)
cb = G.inv(c)
zone = 4 * p * p < G.D
j = P.bucket(p)
v = 2 * ((c == G.e) - (c == G.k0))
out[0 if zone else 1, j:] += v
out[4 if zone else 5, j:] += 1
if zone:
bad += ((c == G.e) != (p in avals)) + \
((c == G.k0) != (-p in avals))
e, pe = 2, p * p
while pe < P.TOP:
j = P.bucket(pe)
for g in (c, cb):
ge = G.power(g, e)
out[2, j:] += ((ge == G.e) - (ge == G.k0)) / e
n1, nk = npow(e)
out[3, j:] += 2 * (n1 - nk) / (e * G.h)
e, pe = e + 1, pe * p
return out, bad
def read():
rows, bad, worst, offcyc = [], 0, 0.0, 0
for D in range(5, D_MAX + 1):
if not P.fundamental(D):
continue
G = P.Group(D)
if G.h < 2 or G.k0 == G.e:
continue
cyc = P.Real(D).cycle(G.one_form)
offcyc += sum(G.idx[f] != G.e for f in cyc)
t, b = split_terms(G, {f[0] for f in cyc})
bad += b
dS = L.parts(G)[2]
worst = max(worst, float(np.max(np.abs(t[0] + t[1] + t[2] - dS))))
rows.append((D, G.h // 2, t, dS))
return rows, bad, worst, offcyc
def main():
t0 = time.time()
section("A LAGRANGE AND THE SPLIT")
rows, bad, worst, offcyc = read()
n = len(rows)
Ds = np.array([r[0] for r in rows])
x = np.log(Ds.astype(float))
wc = np.array([r[1] for r in rows], float)
print(f" {n} real fields with k0, |D| <= {D_MAX}"
f" [{time.time() - t0:.0f} s]")
check("the principal cycle's forms all lie in class 1", offcyc == 0,
f"{offcyc} off")
check("C1 Lagrange: in the zone P in 1 / k0 iff +p / -p leads a"
" principal-cycle form", bad == 0, f"{bad} exceptions")
check("C2 S1lo + S1hi + S2 = d_S per field", worst < 1e-12,
f"worst {worst:.2e}")
def col(k, j):
return np.array([r[2][k, j] for r in rows])
s1lo = col(0, J250)
sa = L.wslope(x, s1lo, wc)[0]
sb = L.wslope(x, s1lo + 0.29 * x, wc)[0]
check("C2 s(S1lo + c log D) reads s(S1lo) + c", abs(sb - sa - 0.29)
< 1e-9, f"{sb - sa:+.10f}")
if not all(CHECKS):
print("CONTROL FAILED: the kill is not read.")
raise SystemExit(1)
section("B THE SLOPES, WEIGHTED h+/2")
for j in (J250, J10K):
dS = np.array([r[3][j] for r in rows])
s, se = L.wslope(x, dS, wc)
parts = [L.wslope(x, col(k, j), wc) for k in range(4)]
print(f" cut {P.CUTS[j]:5d}: s(d_S) {s:+.4f} +- {se:.4f} ="
f" S1lo {parts[0][0]:+.4f} +- {parts[0][1]:.4f}"
f" + S1hi {parts[1][0]:+.4f} +- {parts[1][1]:.4f}"
f" + S2 {parts[2][0]:+.4f} +- {parts[2][1]:.4f}"
f" (E2 {parts[3][0]:+.4f})")
resid = np.sum(wc * (col(2, j) - col(3, j))) / np.sum(wc)
print(f" mean S2 - E2 {resid:+.4f}")
if j == J250:
check("P1 s(S2) < +0.05", parts[2][0] < 0.05,
f"{parts[2][0]:+.4f}")
print(f" P1 mean S2 - E2 within 0.1 of 0: "
f"{'HELD' if abs(resid) <= 0.1 else 'MISSED'}"
f" ({resid:+.4f})")
share = parts[0][0] / s
print(f" S1lo share of s(d_S) at 250: {share:.3f}")
print(f" KILL ZONE (share < 0.5): "
f"{'FIRED' if share < 0.5 else 'MISSED'}")
section("C BY |D| BAND, WEIGHTED h+/2: PER-PRIME BIAS, MEANS")
print(" band n zone primes bias in bias out"
" S1lo S1hi S2 E2 d_S")
p2in, p2out = [], []
for i, (lo, hi) in enumerate(L.BANDS):
m = np.array([L.band_of(int(D)) == i for D in Ds])
w = wc[m]
def mean(v):
return np.sum(w * v[m]) / np.sum(w)
bin_ = np.sum(w * col(0, J250)[m]) / max(np.sum(w * col(4, J250)[m]),
1e-300)
bout = np.sum(w * col(1, J250)[m]) / np.sum(w * col(5, J250)[m])
p2in.append(bin_)
p2out.append(bout)
dS = np.array([r[3][J250] for r in rows])
print(f" {lo:5d}-{min(hi, D_MAX):5d} {int(m.sum()):5d}"
f" {mean(col(4, J250)):6.2f} {bin_:+.4f} {bout:+.4f}"
f" {mean(col(0, J250)):+.3f} {mean(col(1, J250)):+.3f}"
f" {mean(col(2, J250)):+.3f} {mean(col(3, J250)):+.3f}"
f" {mean(dS):+.3f}")
print(f" P2 the per-prime bias in the zone is positive in every band: "
f"{'HELD' if all(v > 0 for v in p2in) else 'MISSED'}")
print(f" P2 outside the zone within 0.02 of 0: "
f"{'HELD' if all(abs(v) <= 0.02 for v in p2out) else 'MISSED'}")
print(f"\n{sum(CHECKS)}/{len(CHECKS)} checks PASS,"
f" {time.time() - t0:.1f} s")
raise SystemExit(0 if all(CHECKS) else 1)
if __name__ == "__main__":
main()