version_h.py
3.9 kB · python · 113 lines
1import mpmath as mp2import numpy as np3import sympy as sp45TOP = 76MC_DIMS = (3, 4, 5)7MC_SAMPLES = 10**88BATCH = 10**6910def beta_sym(x, y):11 return sp.gamma(x) * sp.gamma(y) / sp.gamma(x + y)1213def exact_miss(d):14 th, ps = sp.symbols("th ps", positive=True)15 alpha = sp.Rational(d * d - 1, 2)16 inner = sp.integrate(sp.sin(th) ** (d * d), (th, 0, sp.pi / 2 - ps))17 body = sp.expand_trig(sp.expand(inner * sp.sin(ps) ** (d - 2)))18 num = 2 * sp.integrate(body, (ps, 0, sp.pi / 2))19 denom = beta_sym(sp.Rational(1, 2), sp.Rational(d - 1, 2)) * beta_sym(20 sp.Rational(1, 2), alpha + 121 )22 return sp.radsimp(sp.expand(sp.nsimplify(sp.simplify(2**d * num / denom))))2324def quad_miss(d, dps):25 saved = mp.mp.dps26 mp.mp.dps = dps27 alpha = mp.mpf(d * d - 1) / 228 half = mp.mpf(1) / 22930 def shell(h):31 return mp.betainc(mp.mpf(d - 1) / 2, half, 0, h * h, regularized=True)3233 num = mp.quad(lambda h: (1 - h * h) ** alpha * shell(h), [0, half, 1])34 out = mp.mpf(2) ** d * num / mp.beta(half, alpha + 1)35 mp.mp.dps = saved36 return out3738def half_ball(rng, count, dim):39 pts = rng.standard_normal((count, dim, dim))40 pts /= np.linalg.norm(pts, axis=2, keepdims=True)41 pts *= rng.random((count, dim, 1)) ** (1.0 / dim)42 pts[:, :, dim - 1] = np.abs(pts[:, :, dim - 1])43 return pts4445def flat_face_rate(rng, dim, samples):46 hits = 047 done = 048 while done < samples:49 take = min(BATCH, samples - done)50 pts = half_ball(rng, take, dim)51 mat = np.concatenate(52 [pts[:, :, : dim - 1], np.ones((take, dim, 1))], axis=253 )54 rhs = pts[:, :, dim - 1][:, :, None]55 sol = np.linalg.solve(mat, rhs)[:, :, 0]56 slope = np.linalg.norm(sol[:, : dim - 1], axis=1)57 hits += int(np.count_nonzero(np.abs(sol[:, dim - 1]) <= slope))58 done += take59 return hits / samples6061def main():62 mp.mp.dps = 3063 print("VERSION H: d uniform points, the hyperplane through them misses the flat face")64 print(" reduction: unoriented normal n with n_d = t >= 0 and signed offset h")65 print(" the section is a (d-1)-ball of radius sqrt(1-h^2) and stays above the")66 print(" flat face exactly when h >= sqrt(1-t^2)")67 print(" the within-hyperplane simplex integral scales as radius^(d^2-1)")68 print(" the half-ball Cartesian d-tuple measure is 2^-d of the full-ball one")6970 forms = {}71 print(" exact values by symbolic reduction")72 for d in range(2, TOP + 1):73 p = exact_miss(d)74 forms[d] = p75 print(f" d = {d} {p}")7677 print(" decimals to ten places")78 for d in range(2, TOP + 1):79 print(f" d = {d} {float(sp.N(forms[d], 30)):.10f}")8081 print(" quadrature against the exact values, 60 and 80 working digits")82 for d in range(2, TOP + 1):83 lo = quad_miss(d, 60)84 hi = quad_miss(d, 80)85 mp.mp.dps = 8086 target = mp.mpf(str(sp.N(forms[d], 90)))87 print(88 f" d = {d} 60 against 80 digits {mp.nstr(abs(lo - hi), 3)}"89 f" quadrature against exact {mp.nstr(abs(hi - target), 3)}"90 )91 mp.mp.dps = 309293 print(" parity read off the exact values")94 for d in range(2, TOP + 1):95 powers = sorted(96 int(term.as_coeff_exponent(sp.pi)[1]) for term in sp.Add.make_args(forms[d])97 )98 shape = " + ".join("Q" if e == 0 else f"Q Pi^{e}" for e in powers)99 print(f" d = {d} pi powers {powers} {shape}")100101 rng = np.random.default_rng(20250828)102 print(f" random-point check, {MC_SAMPLES} samples per dimension")103 for d in MC_DIMS:104 flat_exact = 1 - mp.mpf(str(sp.N(forms[d], 30)))105 est = mp.mpf(flat_face_rate(rng, d, MC_SAMPLES))106 se = mp.sqrt(est * (1 - est) / MC_SAMPLES)107 print(108 f" d = {d} flat-face estimate {mp.nstr(est, 8)}"109 f" exact {mp.nstr(flat_exact, 8)}"110 f" deviation {mp.nstr(est - flat_exact, 3)} one sigma {mp.nstr(se, 3)}"111 )112113main()