transport_census.py
11.1 kB · python · 272 lines
1import os2import sys3import time45import mpmath as mp67HERE = os.path.dirname(os.path.abspath(__file__))8sys.path.insert(0, os.path.join(HERE, "..", "design-zeta"))9sys.path.insert(0, os.path.join(HERE, "..", "zeta-locus"))1011import design_zeta as dz12import zeta_locus as zl1314# DESIGNS1516LOCUS = zl.Q3 + zl.Q4 + zl.Q5 + zl.HALF + zl.CONTROL + zl.WIDE17SHADOW = [(20, tuple(range(19))), (50, tuple(range(49)))]18RUNGS = [(5, (0, 1, 2, 3)), (10, tuple(range(8)))]19FULL = [(2, (0, 1)), (3, (0, 1, 2)), (4, (0, 1, 2, 3))]20SETS = {"locus": LOCUS, "shadow": SHADOW, "ctl": FULL, "rungs": RUNGS,21 "b20": SHADOW[:1], "b50": SHADOW[1:], "all": LOCUS + RUNGS}2223HALF = mp.mpf("5e-5")24EDGE = mp.mpf(10) ** -625YMAX = mp.mpf(40)2627def line(*a):28 print(" ".join(str(x) for x in a))2930def down(x, n=7):31 return mp.nstr(mp.floor(x * mp.mpf(10) ** n) / mp.mpf(10) ** n, n + 2)3233def build(q, F):34 return zl.build(q, F, dps=25, tol=mp.mpf(10) ** -10)3536# THE ZERO FREE EDGE3738def edge(d):39 amin = min(a for a in d.F if a != 0)40 s = d.alpha + mp.mpf("0.05")41 while s < mp.mpf(14):42 v, e = d.zeta(s)43 if mp.power(amin, s) * (v + e) < 2:44 return s, v, e45 s += mp.mpf("0.05")46 return None, None, None4748# THE POLES4950def poledist(d, x0, x1, y0, y1):51 per = 2 * mp.pi / mp.log(d.q)52 best = mp.inf53 for m in range(0, 3):54 xp = d.alpha - m55 for j in range(int(mp.floor(y0 / per)) - 1, int(mp.ceil(y1 / per)) + 2):56 yp = j * per57 dx = max(x0 - xp, xp - x1, mp.mpf(0))58 dy = max(y0 - yp, yp - y1, mp.mpf(0))59 best = min(best, mp.sqrt(dx * dx + dy * dy))60 return best6162# THE SWEEP6364def sweep(q, F, ymax, nsub):65 d = build(q, F)66 dp = zl.build(q, F, dps=32, tol=mp.mpf(10) ** -18)67 s1, v1, e1 = edge(d)68 x0, x1 = d.alpha + EDGE, s169 f = dz.Cache(d, cof=True)70 t0 = time.time()71 res = dz.strip(f, x0, x1, mp.mpf("0.02"), ymax, nsub, 20)72 tot = sum(r[2] for r in res)73 mx = max(r[3] for r in res)74 zs = []75 for a, b, w, m in res:76 want = int(mp.nint(abs(w)))77 if want == 0:78 continue79 hit = 080 for z0 in zl.seek(f, x0, x1, a, b, 2 * want + 8, 15, 11):81 if hit >= want:82 break83 z, val = zl.polish(d, z0, (b - a) / 40, x1 - x0, dp)84 if z is None or mp.re(z) < x0 or mp.re(z) > x1:85 continue86 if mp.im(z) < a - mp.mpf("0.5") or mp.im(z) > b + mp.mpf("0.5"):87 continue88 if any(abs(z - y) < mp.mpf("1e-9") for y in zs):89 continue90 hit += 191 zs.append(z)92 return {"d": d, "s1": s1, "v1": v1, "e1": e1, "x0": x0, "x1": x1, "res": res,93 "n": int(mp.nint(tot)), "wind": tot, "mx": mx, "zs": zs,94 "evals": f.n, "sec": time.time() - t0}9596# THE BOX9798def certify(q, F, z, half=HALF, n=16):99 d = build(q, F)100 f = dz.Cache(d)101 x0, x1 = mp.re(z) - half, mp.re(z) + half102 y0, y1 = mp.im(z) - half, mp.im(z) + half103 w, mx = dz.box_phase(f, x0, x1, y0, y1, n)104 lo = min(abs(v) for v in f.m.values())105 return {"x0": x0, "x1": x1, "y0": y0, "y1": y1, "w": w, "mx": mx, "lo": lo,106 "e": f.emax, "pole": poledist(d, x0, x1, y0, y1), "alpha": d.alpha}107108VERBS = {}109110# CENSUS111112def census(which=None, ymax=YMAX):113 which = which or LOCUS114 line("TRANSPORT CENSUS: zeros of zeta_F right of alpha, and the Mertens exponent")115 line("of the design's own Mobius nu_F they force through the transport theorem")116 line("sigma1 is a proved zero free edge: a_min^sigma zeta_F(sigma) < 2 there, so the")117 line("census over alpha < Re s < sigma1 is complete in Re and cut only in height;")118 line("the count is the winding and is exact, the rightmost is the rightmost LOCATED")119 got = {}120 for q, F in which:121 r = sweep(q, F, ymax, int(ymax / 2))122 d = r["d"]123 zs = sorted(r["zs"], key=lambda z: -mp.re(z))124 line(" ", zl.tag(q, F), "alpha", mp.nstr(d.alpha, 10), "k/q",125 mp.nstr(mp.mpf(d.k) / q, 8), "sigma1", mp.nstr(r["s1"], 6),126 "a_min^s zeta_F", mp.nstr(mp.power(min(a for a in d.F if a != 0), r["s1"])127 * r["v1"], 8), "bound", mp.nstr(r["e1"], 3))128 line(" strip Re", mp.nstr(r["x0"], 10), mp.nstr(r["x1"], 8), "Im to",129 mp.nstr(ymax, 6), "Z winding", mp.nstr(r["wind"], 8), "zeros",130 r["n"], "located", len(zs), "complete", len(zs) == r["n"],131 "max phase step", mp.nstr(r["mx"], 4), "evals", r["evals"],132 "sec", round(r["sec"], 1))133 if not zs:134 line(" NO ZERO RIGHT OF alpha to height", mp.nstr(ymax, 6),135 "- no transport bound on theta(nu_F) from this census")136 got[(q, F)] = None137 continue138 for z in zs:139 line(" ZERO", zl.tag(q, F), "Re", mp.nstr(mp.re(z), 12), "Im",140 mp.nstr(mp.im(z), 12), "Re-alpha", mp.nstr(mp.re(z) - d.alpha, 10))141 z = zs[0]142 b = certify(q, F, z)143 line(" BOX Re [", mp.nstr(b["x0"], 12), ",", mp.nstr(b["x1"], 12), "] Im [",144 mp.nstr(b["y0"], 12), ",", mp.nstr(b["y1"], 12), "] winding",145 mp.nstr(b["w"], 8), "step", mp.nstr(b["mx"], 4), "min |zeta_F|",146 mp.nstr(b["lo"], 4), "engine bound", mp.nstr(b["e"], 4),147 "pole distance", mp.nstr(b["pole"], 6))148 ok = abs(b["w"] - 1) < mp.mpf("0.05") and b["lo"] > b["e"]149 line(" THETA", zl.tag(q, F), "theta(nu_F) >=", down(b["x0"]), "alpha",150 mp.nstr(d.alpha, 10), "gain", down(b["x0"] - d.alpha), "above 1",151 b["x0"] > 1, "certified", ok)152 got[(q, F)] = (z, b, len(zs), r["n"])153 return got154155VERBS["census"] = census156157# LAW158159def nullbox(q, F, j, half=HALF):160 d = build(q, F)161 s0 = d.alpha + 2 * mp.pi * 1j * j / mp.log(q)162 out = []163 for cof in (True, False):164 f = dz.Cache(d, cof=cof)165 w, mx = dz.box_phase(f, mp.re(s0) - half, mp.re(s0) + half,166 mp.im(s0) - half, mp.im(s0) + half, 16)167 out.append((w, mx, min(abs(v) for v in f.m.values()), f.emax))168 return s0, out169170LANDED = [(3, (0, 1), "0.72074", "0.72084", "28.60563", "28.60573", 1),171 (10, tuple(range(9)), "1.00150", "1.00168", "2.73915", "2.73925", 1),172 (10, tuple(range(9)), "0.99900", "1.00050", "2.73810", "2.74030", 0)]173174def repro():175 line("REPRODUCTION of the two landed boxes of lab/py/mrly-pairing and its control,")176 line("same edges, same argument principle, evaluated here")177 for q, F, x0, x1, y0, y1, want in LANDED:178 d = build(q, F)179 f = dz.Cache(d)180 w, mx = dz.box_phase(f, mp.mpf(x0), mp.mpf(x1), mp.mpf(y0), mp.mpf(y1), 16)181 lo = min(abs(v) for v in f.m.values())182 line(" ", zl.tag(q, F), "Re [", x0, ",", x1, "] Im [", y0, ",", y1,183 "] winding", mp.nstr(w, 8), "expected", want, "step", mp.nstr(mx, 4),184 "min |zeta_F|", mp.nstr(lo, 4), "engine bound", mp.nstr(f.emax, 4),185 "pole distance", mp.nstr(poledist(d, mp.mpf(x0), mp.mpf(x1),186 mp.mpf(y0), mp.mpf(y1)), 6),187 "reproduced", abs(w - want) < mp.mpf("0.05"))188189def law(which=None, ymax=YMAX):190 which = which or LOCUS191 got = census(which, ymax)192 line("LAW: the columns, no fit")193 line(" DESIGN alpha k/q sigma1 zeros-right-of-alpha rightmost-Re gain=Re-alpha above-1 1-in-F")194 rows = []195 for q, F in which:196 g = got[(q, F)]197 d = build(q, F)198 one = 1 in F199 if g is None:200 rows.append((q, F, d.alpha, mp.mpf(d.k) / q, 0, None, None, one))201 continue202 z, b, nloc, ntot = g203 rows.append((q, F, d.alpha, mp.mpf(d.k) / q, ntot, mp.re(z),204 mp.re(z) - d.alpha, one))205 for r in sorted(rows, key=lambda t: (t[3], t[2])):206 q, F, a, kq, n, re, gain, one = r207 line(" ROW", zl.tag(q, F), "alpha", mp.nstr(a, 10), "k/q", mp.nstr(kq, 8),208 "zeros", n, "rightmost located", "-" if re is None else mp.nstr(re, 12),209 "gain", "-" if gain is None else mp.nstr(gain, 10),210 "above 1", "-" if re is None else (re > 1), "1 in F", one)211 line(" BY k/q, then by alpha: the gain column above is printed in k/q order and")212 line(" again in alpha order below; equal-key pairs with unequal gain kill a law")213 for r in sorted(rows, key=lambda t: (t[2], t[3])):214 q, F, a, kq, n, re, gain, one = r215 line(" ALPHAORDER", zl.tag(q, F), "alpha", mp.nstr(a, 10), "k/q",216 mp.nstr(kq, 8), "gain", "-" if gain is None else mp.nstr(gain, 10))217 keys = {}218 for r in rows:219 keys.setdefault((mp.nstr(r[2], 10), mp.nstr(r[3], 8)), []).append(r)220 for key, fam in sorted(keys.items()):221 if len(fam) < 2:222 continue223 gs = [r[6] for r in fam if r[6] is not None]224 line(" TIE alpha", key[0], "k/q", key[1], "designs",225 " ".join(zl.tag(r[0], r[1]) for r in fam), "gains",226 " ".join("-" if r[6] is None else mp.nstr(r[6], 8) for r in fam),227 "spread", "-" if len(gs) < 2 else mp.nstr(max(gs) - min(gs), 8))228 ab = [r for r in rows if r[5] is not None and r[5] > 1]229 line(" ABOVE ONE: designs whose rightmost censused zero has Re rho > 1, so the")230 line(" Mertens of nu_F outruns x itself:", " ".join(zl.tag(r[0], r[1]) for r in ab),231 "| their k/q", " ".join(mp.nstr(r[3], 8) for r in ab),232 "| their alpha", " ".join(mp.nstr(r[2], 10) for r in ab))233 line(" COROLLARY: 1 in F and a certified zero rho with Re rho > alpha give")234 line(" sigma_c(N_F) >= Re rho > alpha >= alpha/2, so sum_(n <= x) nu_F(n) is not")235 line(" O(x^(alpha/2 + eps)) and not O(x^(alpha - eps)): no square-root-shaped bound")236 for r in sorted(rows, key=lambda t: t[2]):237 q, F, a, kq, n, re, gain, one = r238 if re is None:239 line(" NOBOUND", zl.tag(q, F), "no zero right of alpha to height",240 mp.nstr(ymax, 6))241 elif not one:242 line(" NOINVERSE", zl.tag(q, F), "1 not in F, so 1 not in S_F and the",243 "Dirichlet inverse nu_F does not exist; the zero stands, the",244 "transport does not")245 else:246 b = got[(q, F)][1]247 line(" BOUND", zl.tag(q, F), "theta(nu_F) >=", down(b["x0"]), ">",248 "alpha", mp.nstr(a, 10), ">= alpha/2", mp.nstr(a / 2, 10),249 "square-root shape refuted for nu_F", True)250 line(" CONTROLS: the cofactor only teeth of the full sets at Re s = alpha = 1")251 for q, F in FULL:252 s0, out = nullbox(q, F, 1)253 (wz, mz, lz, ez), (wf, mf, lf, ef) = out254 line(" NULLBOX", zl.tag(q, F), "pole", mp.nstr(s0, 12), "winding of Z",255 mp.nstr(wz, 8), "winding of zeta_F", mp.nstr(wf, 8), "min |Z|",256 mp.nstr(lz, 4), "min |zeta_F|", mp.nstr(lf, 4), "steps",257 mp.nstr(mz, 4), mp.nstr(mf, 4))258 repro()259260VERBS["law"] = law261262def main():263 argv = sys.argv[1:]264 verb = argv[0] if argv else "census"265 ymax = mp.mpf(argv[1]) if len(argv) > 1 else YMAX266 which = SETS[argv[2]] if len(argv) > 2 else LOCUS267 t0 = time.time()268 VERBS[verb](which, ymax)269 line("seconds", round(time.time() - t0, 1))270271if __name__ == "__main__":272 main()