slices.py
8.7 kB · python · 233 lines
1import numpy as np2from itertools import product34CARPET = frozenset([(0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)])5NET = frozenset([(1, 1, 0), (1, 0, 1), (0, 1, 1), (1, 1, 1)])6TREE = frozenset([(0, 0, 0), (0, 0, 1)])78def corners(P, q):9 return np.array([v for v in product(range(q), repeat=3)10 if (v[0] % 2, v[1] % 2, v[2] % 2) in P], dtype=np.int64)1112def level_points(F, q, n):13 pts = np.zeros((1, 3), dtype=np.int64)14 for _ in range(n):15 pts = (pts[:, None, :] * q + F[None, :, :]).reshape(-1, 3)16 return pts1718def prime_factors(m):19 ps, d = [], 220 while d * d <= m:21 if m % d == 0:22 ps.append(d)23 while m % d == 0:24 m //= d25 d += 126 if m > 1:27 ps.append(m)28 return ps2930def squarefree_upto(limit):31 out = []32 for d in range(1, limit + 1):33 ps, prod = prime_factors(d), 134 for p in ps:35 prod *= p36 if prod == d:37 out.append((d, (-1) ** len(ps)))38 return out3940def squarefree_divisors(m):41 out = [(1, 1)]42 for p in prime_factors(m):43 out = out + [(d * p, -mu) for d, mu in out]44 return out4546def height_counts(F, q, n):47 weights = {}48 for x in F.sum(axis=1).tolist():49 weights[x] = weights.get(x, 0) + 150 top, arr = max(weights), np.array([1], dtype=np.int64)51 for _ in range(n):52 L = len(arr)53 new = np.zeros(q * (L - 1) + top + 1, dtype=np.int64)54 for x, c in weights.items():55 new[x:x + q * L:q] += c * arr56 arr = new57 return arr5859def aggregate_local(F, q, n, p):60 vs, cur = [tuple(v) for v in F.tolist()], {(0, 0, 0): 1}61 for _ in range(n):62 nxt = {}63 for (a, b, c), t in cur.items():64 for v in vs:65 k = ((q * a + v[0]) % p, (q * b + v[1]) % p, (q * c + v[2]) % p)66 nxt[k] = nxt.get(k, 0) + t67 cur = nxt68 tot = sum(t for r, t in cur.items() if sum(r) % p == 0)69 return cur.get((0, 0, 0), 0), tot7071def slice_divisor_counts(F, q, n, s, ds):72 m = n // 273 lo, hi = level_points(F, q, m), level_points(F, q, n - m)74 scale = q ** m75 slo, shi = lo.sum(axis=1).tolist(), hi.sum(axis=1).tolist()76 lol, hil, out = lo.tolist(), hi.tolist(), {}77 for d in ds:78 tab = {}79 for j, v in enumerate(lol):80 k = (slo[j], v[0] % d, v[1] % d, v[2] % d)81 tab[k] = tab.get(k, 0) + 182 tot = 083 for i, v in enumerate(hil):84 need = s - shi[i] * scale85 if need >= 0:86 tot += tab.get((need, (-scale * v[0]) % d, (-scale * v[1]) % d,87 (-scale * v[2]) % d), 0)88 out[d] = tot89 return out9091def mobius_and_prime_slices(P, q, n):92 F = corners(P, q)93 pts = level_points(F, q, n)94 s = pts.sum(axis=1)95 smax = 3 * (q ** n - 1)96 N = np.bincount(s, minlength=smax + 1)97 A = np.bincount(s[np.gcd.reduce(pts, axis=1) == 1], minlength=smax + 1)98 total = np.zeros(smax + 1, dtype=np.int64)99 for d, mu in squarefree_upto(q ** n - 1):100 total += mu * np.bincount(s[(pts % d == 0).all(axis=1)], minlength=smax + 1)101 hidden = max([int(N[h] - A[h]) for h in range(2, smax + 1)102 if N[h] and prime_factors(h) == [h]] + [0])103 return int(np.count_nonzero(total[1:] != A[1:])), hidden104105def peel_mismatches(P, q, n):106 F = corners(P, q)107 a, b = level_points(F, q, n), level_points(F, q, n - 1)108 smax = 3 * (q ** n - 1)109 lhs = np.bincount(a.sum(axis=1)[(a % q == 0).all(axis=1)], minlength=smax + 1)110 Nb = np.bincount(b.sum(axis=1), minlength=3 * (q ** (n - 1) - 1) + 1)111 origin, bad = (0, 0, 0) in P, 0112 for h in range(smax + 1):113 r = int(Nb[h // q]) if (origin and h % q == 0 and h // q < len(Nb)) else 0114 bad += int(lhs[h]) != r115 return bad116117def parity_dichotomy(q, n):118 F = corners(TREE, q)119 base = level_points(F, q, n - 1)120 ev = od = seen = evengcd = 0121 for v in F.tolist():122 ch = base * q + np.array(v, dtype=np.int64)123 s, g = ch.sum(axis=1), np.gcd.reduce(ch, axis=1)124 pos = s > 0125 e, o = pos & (s % 2 == 0), pos & (s % 2 == 1)126 ev += int(np.count_nonzero(e))127 od += int(np.count_nonzero(o))128 seen += int(np.count_nonzero(e & (g == 1)))129 evengcd += int(np.count_nonzero(o & (g % 2 == 0)))130 return ev, od, seen, evengcd131132def walk_lambdas(P, q):133 F = corners(P, q).tolist()134 return {t: sum((-1) ** (t[0] * v[0] + t[1] * v[1] + t[2] * v[2]) for v in F) / len(F)135 for t in product((0, 1), repeat=3)}136137def central_line(P, q, n):138 F = corners(P, q)139 sc = 3 * (q ** n - 1) // 2140 ds = squarefree_divisors(sc)141 cnt = slice_divisor_counts(F, q, n, sc, [d for d, _ in ds])142 ps = prime_factors(sc)143 loc = {p: cnt[p] / cnt[1] for p in ps}144 ind = 1.0145 for p in ps:146 ind *= 1 - loc[p]147 return sc, ps, cnt[1], sum(mu * cnt[d] for d, mu in ds), loc, ind148149def order(p, q):150 o, x = 1, q % p151 while x != 1:152 x, o = x * q % p, o + 1153 return o154155print("DOMAIN carpet q=3 to n=6, net q=3 to n=7, tree q=3 to n=7, carpet q=4 to n=3, carpet q=5 to n=4")156print()157print("EXACT LAWS AT EVERY HEIGHT")158for name, P, q, nmax in [("carpet3", CARPET, 3, 4), ("net3", NET, 3, 4),159 ("carpet4", CARPET, 4, 3), ("carpet5", CARPET, 5, 3)]:160 for n in range(1, nmax + 1):161 bad, hidden = mobius_and_prime_slices(P, q, n)162 peel = peel_mismatches(P, q, n) if q in (3, 5) else "not a prime base"163 print(f"{name} n={n} mobius mismatches={bad} peel mismatches={peel} "164 f"max hidden on a prime slice={hidden}")165pts = level_points(corners(NET, 3), 3, 7)166print(f"net3 n=7 points={len(pts)} with 3 dividing the gcd="167 f"{int(np.count_nonzero(np.gcd.reduce(pts, axis=1) % 3 == 0))}")168print()169170print("PARITY DICHOTOMY, TREE q=3")171for n in (6, 7):172 ev, od, seen, evengcd = parity_dichotomy(3, n)173 print(f"tree3 n={n} even-height points={ev} visible among them={seen} "174 f"odd-height points={od} even gcds among them={evengcd}")175print()176177print("AGGREGATED LOCAL FACTOR OVER THE HEIGHTS DIVISIBLE BY p, CARPET q=3 n=6")178F = corners(CARPET, 3)179for p in (5, 7, 11, 13):180 div, tot = aggregate_local(F, 3, 6, p)181 print(f"p={p}: {div}/{tot} = {div / tot:.6f} vs 1/p^2 = {1 / p ** 2:.6f}")182e3 = level_points(F, 3, 3)183bad = sum(aggregate_local(F, 3, 3, p) != (int(np.count_nonzero((e3 % p == 0).all(axis=1))),184 int(np.count_nonzero(e3.sum(axis=1) % p == 0)))185 for p in (2, 5, 7, 11, 13))186print(f"transfer against enumeration at n=3 over p=2,5,7,11,13: mismatches={bad}")187lam = walk_lambdas(CARPET, 3)188num = sum(v ** 6 for v in lam.values()) / 8 * 20 ** 6189den = (1 + lam[(1, 1, 1)] ** 6) / 2 * 20 ** 6190div, tot = aggregate_local(F, 3, 6, 2)191print(f"p=2 counted {div}/{tot} = {div / tot:.7f}")192print(f"p=2 walk formula {round(num)}/{round(den)} = {num / den:.7f}")193print()194195print("CENTRAL SLICE")196for q, nmax in ((3, 6), (5, 4)):197 for n in range(1, nmax + 1):198 sc, ps, N, A, loc, ind = central_line(CARPET, q, n)199 print(f"q={q} n={n} s*={sc}={'*'.join(map(str, ps))} N={N} A={A} delta={A / N:.5f} "200 f"independence-product={ind:.5f} locals "201 + " ".join(f"{p}:{loc[p]:.5f}" for p in ps))202print()203204print("CENTRAL COUNT AND ITS PEEL, CARPET q=3")205hc = [height_counts(corners(CARPET, 3), 3, n) for n in range(15)]206cen = [int(hc[n][3 * (3 ** n - 1) // 2]) for n in range(15)]207pel = [int(hc[n - 1][(3 ** n - 1) // 2]) for n in range(2, 15)]208print("N(s*_n) n=1..8:", ", ".join(str(cen[n]) for n in range(1, 9)))209print("N^(3)(s*_n) n=2..8:", ", ".join(str(pel[n - 2]) for n in range(2, 9)))210for n in range(2, 8):211 direct = slice_divisor_counts(corners(CARPET, 3), 3, n, 3 * (3 ** n - 1) // 2, [3])[3]212 print(f"n={n} peel {direct} vs level {n - 1} at R_n={(3 ** n - 1) // 2}: {pel[n - 2]} "213 f"equal={direct == pel[n - 2]}")214off = sum(cen[n] != 9 * cen[n - 1] - 12 * cen[n - 2] for n in range(2, 15))215off += sum(pel[i] != 9 * pel[i - 1] - 12 * pel[i - 2] for i in range(2, len(pel)))216print(f"(9,-12) recurrence exceptions on both sequences to n=14: {off}")217print(f"peel ratio n=14 = {pel[12] / cen[14]:.9f} vs (sqrt(33)-5)/8 = {(33 ** 0.5 - 5) / 8:.10f}")218print()219220print("THE CENTRAL BILL")221bad = 0222for q in (3, 5, 7):223 for n in range(1, 9):224 ps = prime_factors(3 * (q ** n - 1) // 2)225 bad += 3 not in ps226 bad += (2 in ps) != (q % 4 == 1 or n % 2 == 0)227 for p in ps:228 if p not in (2, 3) and q % p:229 bad += n % order(p, q) != 0230print(f"bill rule exceptions over q=3,5,7 and n=1..8: {bad}")231print(f"R_7(3) = {(3 ** 7 - 1) // 2}, foreign bill at q=3 n=7 = "232 f"{[p for p in prime_factors(3 * (3 ** 7 - 1) // 2) if p != 3]}")233print(f"2^1092 mod 1093^2 = {pow(2, 1092, 1093 ** 2)}")