verify.py
8.9 kB · python · 262 lines
1# MENGER23import cmath4from decimal import Decimal, getcontext5from fractions import Fraction6from itertools import product7from math import comb, factorial, gcd, log89getcontext().prec = 1001011DESIGN = [v for v in product(range(3), repeat=3) if sum(d == 1 for d in v) <= 1]12CENSUS = [0, 60, 1434, 32268, 721524, 15141288]13MENGER = 0.25162086845125514FULL = 0.28674742843447915FULL_DECIMALS = "0.286747428434478734107892712789"16MENGER_DECIMALS = "0.251620868451255089179675855473"1718def fibre(coords):19 best = 020 for w in product(range(3), repeat=len(coords)):21 best = max(best, sum(1 for v in DESIGN if tuple(v[i] for i in coords) == w))22 return best2324def points(level):25 pts = [(0, 0, 0)]26 for _ in range(level):27 pts = [(3 * x + a, 3 * y + b, 3 * z + c) for (x, y, z) in pts for (a, b, c) in DESIGN]28 return pts2930def split(level):31 low = level // 232 return points(low), points(level - low), 3 ** low3334def census(level):35 lows, highs, shift = split(level)36 total = 037 for hx, hy, hz in highs:38 bx, by, bz = hx * shift, hy * shift, hz * shift39 for lx, ly, lz in lows:40 x = bx + lx41 y = by + ly42 if gcd(x, y) != 1:43 continue44 z = bz + lz45 if gcd(x, z) == 1 and gcd(y, z) == 1:46 total += 147 return total4849def base_census(level):50 lows, highs, shift = split(level)51 total = 052 for hx, hy, hz in highs:53 bx, by, bz = hx * shift, hy * shift, hz * shift54 for lx, ly, lz in lows:55 zeros = ((bx + lx) % 3 == 0) + ((by + ly) % 3 == 0) + ((bz + lz) % 3 == 0)56 if zeros <= 1:57 total += 158 return total5960def parity_census(level):61 lows, highs, shift = split(level)62 total = 063 for hx, hy, hz in highs:64 bx, by, bz = hx * shift, hy * shift, hz * shift65 for lx, ly, lz in lows:66 odd = ((bx + lx) & 1) + ((by + ly) & 1) + ((bz + lz) & 1)67 if odd >= 2:68 total += 169 return total7071def parity_transfer(level):72 state = {(0, 0, 0): Fraction(1)}73 for _ in range(level):74 nxt = {}75 for s, w in state.items():76 for v in DESIGN:77 t = tuple((s[i] + (v[i] == 1)) % 2 for i in range(3))78 nxt[t] = nxt.get(t, Fraction(0)) + w * Fraction(1, 20)79 state = nxt80 return sum(w for s, w in state.items() if sum(s) >= 2)8182def parity_closed(level):83 return Fraction(1, 2) - Fraction(3, 4) * Fraction(3, 5) ** level + Fraction(1, 4) * Fraction(-1, 5) ** level8485def primes_to(limit):86 sieve = bytearray([1]) * (limit + 1)87 sieve[0:2] = b"\x00\x00"88 for i in range(2, int(limit ** 0.5) + 1):89 if sieve[i]:90 sieve[i * i::i] = bytearray(len(range(i * i, limit + 1, i)))91 return [i for i in range(2, limit + 1) if sieve[i]]9293def euler(primes, skip):94 out = 1.095 for p in primes:96 if p != skip:97 out *= 1.0 - 3.0 / p ** 2 + 2.0 / p ** 398 return out99100def bernoulli(kmax):101 out = [Fraction(0)] * (kmax + 1)102 out[0] = Fraction(1)103 for m in range(1, kmax + 1):104 out[m] = -sum(comb(m + 1, j) * out[j] for j in range(m)) / (m + 1)105 return out106107BERNOULLI = bernoulli(32)108109def decimalise(fr):110 return Decimal(fr.numerator) / Decimal(fr.denominator)111112def zeta(n, cut=100, terms=15):113 total = sum(Decimal(1) / Decimal(j) ** n for j in range(1, cut))114 total += Decimal(cut) ** (1 - n) / Decimal(n - 1)115 total += Decimal(1) / (Decimal(2) * Decimal(cut) ** n)116 for k in range(1, terms + 1):117 rising = 1118 for i in range(2 * k - 1):119 rising *= n + i120 total += decimalise(BERNOULLI[2 * k] * Fraction(rising, factorial(2 * k))) * Decimal(cut) ** (1 - n - 2 * k)121 return total122123ZETA = {}124125def zeta_at(n):126 if n not in ZETA:127 ZETA[n] = zeta(n)128 return ZETA[n]129130def mobius(limit):131 mu = [1] * (limit + 1)132 for p in primes_to(limit):133 for m in range(p, limit + 1, p):134 mu[m] = -mu[m]135 for m in range(p * p, limit + 1, p * p):136 mu[m] = 0137 return mu138139def prime_zeta(n, top, mu):140 total = Decimal(0)141 k = 1142 while k * n <= top:143 if mu[k]:144 total += Decimal(mu[k]) / Decimal(k) * zeta_at(k * n).ln()145 k += 1146 return total147148def constant(bound, nmax, top):149 small = primes_to(bound)150 mu = mobius(top)151 total = Decimal(0)152 for p in small:153 x = Decimal(1) / Decimal(p)154 total += ((1 - x) ** 2 * (1 + 2 * x)).ln()155 for n in range(2, nmax + 1):156 weight = Fraction(-2 + (-1) ** (n + 1) * 2 ** n, n)157 tail = prime_zeta(n, top, mu) - sum(Decimal(1) / Decimal(p) ** n for p in small)158 total += decimalise(weight) * tail159 return total.exp()160161def multiplier(p, t):162 total = sum(cmath.exp(2j * cmath.pi * sum(t[i] * v[i] for i in range(3)) / p) for v in DESIGN)163 return abs(total) / 20.0164165def order_of_three(p):166 k, r = 1, 3 % p167 while r != 1:168 r = (r * 3) % p169 k += 1170 return k171172def contraction(p):173 d = order_of_three(p)174 best = 0.0175 for t in product(range(p), repeat=3):176 if not any(t):177 continue178 run = 1.0179 u = t180 for _ in range(d):181 run *= multiplier(p, u)182 u = tuple((3 * x) % p for x in u)183 best = max(best, run ** (1.0 / d))184 return best185186def main():187 got = len(DESIGN)188 assert got == 20, f"design size: got {got}, want 20"189 got = sum(1 for v in DESIGN if sum(d == 0 for d in v) <= 1)190 assert got == 13, f"vectors with at most one zero: got {got}, want 13"191 print(f"design: {len(DESIGN)} vectors, {got} of them with at most one zero")192 for pair in [(0, 1), (0, 2), (1, 2)]:193 got = fibre(pair)194 assert got == 3, f"pair fibre {pair}: got {got}, want 3"195 for i in range(3):196 got = fibre((i,))197 assert got == 8, f"single fibre {i}: got {got}, want 8"198 alpha = log(20 / 3) / log(3)199 single = log(20 / 8) / log(3)200 assert alpha > 1.0, f"pair exponent: got {alpha}, want above 1"201 assert single < 1.0, f"single exponent: got {single}, want below 1"202 print(f"fibres: pair 3 giving exponent {alpha:.6f}, single 8 giving exponent {single:.6f}")203204 for level in range(1, 7):205 got = census(level)206 want = CENSUS[level - 1]207 assert got == want, f"census level {level}: got {got}, want {want}"208 print(f"level {level}: {got} pairwise-coprime points of {20 ** level}, density {got / 20 ** level:.9f}")209210 for level in range(1, 6):211 got = Fraction(base_census(level), 20 ** level)212 want = Fraction(13, 20)213 assert got == want, f"base factor at level {level}: got {got}, want {want}"214 print("base prime: no coordinate pair divisible by 3 for exactly 13/20 of every level up to 5")215216 for level in range(5):217 got = Fraction(parity_census(level), 20 ** level)218 want = parity_closed(level)219 assert got == want, f"parity census level {level}: got {got}, want {want}"220 for level in range(41):221 got = parity_transfer(level)222 want = parity_closed(level)223 assert got == want, f"parity transfer level {level}: got {got}, want {want}"224 print(f"bias at two: closed form exact to level 40, level 5 value {float(parity_closed(5)):.6f}")225226 high = constant(100, 60, 340)227 got = str(high)[:32]228 assert got == FULL_DECIMALS, f"full constant to thirty decimals: got {got}, want {FULL_DECIMALS}"229 sponge = high * Decimal(351) / Decimal(400)230 got = str(sponge)[:32]231 assert got == MENGER_DECIMALS, f"sponge constant to thirty decimals: got {got}, want {MENGER_DECIMALS}"232 again = constant(200, 45, 300)233 gap = abs(high - again)234 assert gap < Decimal(10) ** -60, f"two truncation settings: got gap {gap}, want below 1e-60"235 print(f"high precision: full {str(high)[:32]}, sponge {str(sponge)[:32]}, settings agree to {gap:.3e}")236237 primes = primes_to(10 ** 6)238 got = euler(primes, None)239 assert abs(got - FULL) < 1e-6, f"truncated full product: got {got}, want {FULL} within 1e-6"240 got = 0.65 * euler(primes, 3)241 assert abs(got - MENGER) < 1e-6, f"truncated sponge product: got {got}, want {MENGER} within 1e-6"242 print(f"products to 1e6: full {euler(primes, None):.9f}, sponge {got:.9f}")243244 local = Fraction(1) - 3 * Fraction(1, 9) + 2 * Fraction(1, 27)245 assert local == Fraction(20, 27), f"local factor at 3: got {local}, want 20/27"246 ratio = Fraction(13, 20) / local247 assert ratio == Fraction(351, 400), f"ratio: got {ratio}, want 351/400"248 loss = Fraction(1) - ratio249 assert loss == Fraction(49, 400), f"loss: got {loss}, want 49/400"250 print(f"exact rationals: 20/27, {ratio}, {loss} = {float(loss) * 100:.2f} percent")251252 for p in [2, 5, 7, 11, 13, 17, 19]:253 got = contraction(p)254 assert got <= 0.6 + 1e-9, f"contraction at {p}: got {got}, want at most 3/5"255 if p == 2:256 assert abs(got - 0.6) < 1e-9, f"contraction at 2: got {got}, want 3/5"257 print(f"contraction at {p}: {got:.6f}")258259 print("all green")260261if __name__ == "__main__":262 main()