import sys
from collections import defaultdict
import mpmath as mp
mp.mp.dps = 80
MIN_MAGNITUDE = mp.mpf("1e-300")
MAX_MAGNITUDE = mp.mpf("1e300")
def log_ncdf(x):
if x > 0:
return mp.log1p(-mp.erfc(x / mp.sqrt(2)) / 2)
return mp.log(mp.erfc(-x / mp.sqrt(2)) / 2)
def ref_bessel_ratio(x):
return mp.besseli(1, x) / mp.besseli(0, x)
def ref_bessel_d2(x):
r = ref_bessel_ratio(x)
return -x + x * x * (1 - r * r)
def mills(x):
return mp.npdf(x) / mp.ncdf(x)
def ref_logcdf_derivative(order):
def inner(x):
if order == 0:
return log_ncdf(x)
lam = mills(x)
if order == 1:
return lam
if order == 2:
return -lam * (x + lam)
if order == 3:
return lam * (x * x - 1 + 3 * x * lam + 2 * lam * lam)
return -lam * (
(x**3 - 3 * x)
+ (7 * x * x - 4) * lam
+ 12 * x * lam**2
+ 6 * lam**3
)
return inner
def ref_normal_quantile(p):
target = mp.log(mp.mpf(p))
guess = -mp.sqrt(2 * -target) if target < -20 else mp.mpf(0)
return mp.findroot(lambda z: log_ncdf(z) - target, guess, solver="mnewton")
def ref_normal_quantile_from_log(log_p):
target = mp.mpf(log_p)
guess = -mp.sqrt(2 * -target) if target < -20 else mp.mpf(0)
return mp.findroot(lambda z: log_ncdf(z) - target, guess, solver="mnewton")
CHANNELS = {
"bessel_centered_log": lambda x: mp.log(mp.besseli(0, x)) - x,
"bessel_ratio": ref_bessel_ratio,
"bessel_d1": lambda x: x * (ref_bessel_ratio(x) - 1),
"bessel_d2": ref_bessel_d2,
"digamma": lambda x: mp.psi(0, x),
"trigamma": lambda x: mp.psi(1, x),
"tetragamma": lambda x: mp.psi(2, x),
"pentagamma": lambda x: mp.psi(3, x),
"normal_pdf": mp.npdf,
"normal_cdf": mp.ncdf,
"normal_sf": lambda x: mp.erfc(x / mp.sqrt(2)) / 2,
"normal_logcdf": log_ncdf,
"normal_logsf": lambda x: log_ncdf(-x),
"probit_logcdf": log_ncdf,
"probit_mills": mills,
"erfcx": lambda x: mp.exp(x * x) * mp.erfc(x),
"log1mexp": lambda a: mp.log(1 - mp.exp(-a)),
"normal_quantile": ref_normal_quantile,
"normal_quantile_from_log": ref_normal_quantile_from_log,
"logcdf_d0": ref_logcdf_derivative(0),
"logcdf_d1": ref_logcdf_derivative(1),
"logcdf_d2": ref_logcdf_derivative(2),
"logcdf_d3": ref_logcdf_derivative(3),
"logcdf_d4": ref_logcdf_derivative(4),
}
BESSEL_CAP = 1e11
DOMAIN_CAP = {
"bessel_centered_log": lambda x: x < BESSEL_CAP,
"bessel_ratio": lambda x: x < BESSEL_CAP,
"bessel_d1": lambda x: x < BESSEL_CAP,
"bessel_d2": lambda x: x < BESSEL_CAP,
}
def legendre_refine(n, seed):
z = mp.mpf(seed)
if z == 0:
derivative = n * (z * mp.legendre(n, z) - mp.legendre(n - 1, z)) / (z * z - 1)
return z, 2 / ((1 - z * z) * derivative * derivative)
for _ in range(200):
value = mp.legendre(n, z)
derivative = n * (z * value - mp.legendre(n - 1, z)) / (z * z - 1)
step = value / derivative
z = z - step
if abs(step) < mp.mpf(10) ** (-mp.mp.dps + 5):
break
value = mp.legendre(n, z)
derivative = n * (z * value - mp.legendre(n - 1, z)) / (z * z - 1)
return z, 2 / ((1 - z * z) * derivative * derivative)
def errors(got, want):
got = mp.mpf(got)
absolute = abs(got - want)
relative = absolute / abs(want) if want != 0 else absolute
return relative, absolute
def beta_quantile_reference(a, b, p):
A, B, P = mp.mpf(a), mp.mpf(b), mp.mpf(p)
low, high = mp.mpf(-1490), mp.mpf(0)
for _ in range(230):
middle = (low + high) / 2
value = mp.betainc(A, B, 0, mp.e ** middle, regularized=True)
value = mp.mpf(value.real) if not isinstance(value, mp.mpf) else value
if value < P:
low = middle
else:
high = middle
root = mp.e ** ((low + high) / 2)
return mp.mpf(0) if root < mp.mpf("5e-324") else root
def main():
path = sys.argv[1]
only = set(sys.argv[2:]) or None
worst_relative = defaultdict(lambda: (mp.mpf(0), None))
worst_absolute = defaultdict(lambda: (mp.mpf(0), None))
gl_rows = defaultdict(dict)
binomial_rows = []
beta_rows = []
t_rows = []
skipped = defaultdict(int)
with open(path) as handle:
for line in handle:
parts = line.rstrip("\n").split("\t")
channel = parts[0]
if only and channel not in only and not channel.startswith("gl_"):
continue
if channel in ("gl_node", "gl_weight"):
if only and "gl" not in only:
continue
n, index, value = int(parts[1]), int(parts[2]), float(parts[3])
gl_rows[n].setdefault(channel, {})[index] = value
continue
if channel == "binomial":
binomial_rows.append((int(parts[1]), int(parts[2]), float(parts[3])))
continue
if channel == "beta_shape":
continue
if channel == "students_t_sf":
if only and channel not in only:
continue
nu, t, value = (float(x) for x in parts[1:4])
half = mp.betainc(
mp.mpf(nu) / 2, mp.mpf(1) / 2, 0, mp.mpf(nu) / (mp.mpf(nu) + mp.mpf(t) ** 2),
regularized=True,
) / 2
want = half if t >= 0 else 1 - half
if want == 0:
if value != 0.0:
t_rows.append((float("inf"), (nu, t)))
continue
if not (MIN_MAGNITUDE < abs(want) < MAX_MAGNITUDE):
skipped[channel] += 1
continue
t_rows.append((float(abs((mp.mpf(value) - want) / want)), (nu, t)))
continue
if channel == "beta_quantile":
if only and channel not in only:
continue
a, b, p, value = (float(t) for t in parts[1:5])
want = beta_quantile_reference(a, b, p)
if want == 0:
if value != 0.0:
beta_rows.append((float("inf"), (a, b, p)))
continue
if not (MIN_MAGNITUDE < abs(want) < MAX_MAGNITUDE):
skipped[channel] += 1
continue
beta_rows.append((float(abs((mp.mpf(value) - want) / want)), (a, b, p)))
continue
reference = CHANNELS.get(channel)
if reference is None:
continue
arg, value = float(parts[1]), float(parts[2])
cap = DOMAIN_CAP.get(channel)
if cap is not None and not cap(arg):
continue
try:
want = reference(mp.mpf(arg))
except (ValueError, ZeroDivisionError, OverflowError):
skipped[channel] += 1
continue
if not mp.isfinite(want):
skipped[channel] += 1
continue
if want != 0 and not (MIN_MAGNITUDE < abs(want) < MAX_MAGNITUDE):
skipped[channel] += 1
continue
relative, absolute = errors(value, want)
if relative > worst_relative[channel][0]:
worst_relative[channel] = (relative, arg)
if absolute > worst_absolute[channel][0]:
worst_absolute[channel] = (absolute, arg)
for channel in sorted(worst_relative):
relative, rel_arg = worst_relative[channel]
absolute, abs_arg = worst_absolute[channel]
note = f" (skipped {skipped[channel]})" if skipped[channel] else ""
print(
f"{channel:26s} rel={mp.nstr(relative, 3):>9s} @ {rel_arg!r:<24s}"
f" abs={mp.nstr(absolute, 3):>9s} @ {abs_arg!r}{note}"
)
if t_rows:
t_rows.sort(reverse=True)
worst, where = t_rows[0]
median = sorted(row[0] for row in t_rows)[len(t_rows) // 2]
print(
f"{'students_t_sf':26s} rel={mp.nstr(worst, 3):>9s} @ "
f"(nu,t)={where!r} median={median:.3e} n={len(t_rows)}"
)
for relative, where in t_rows[1:4]:
print(f"{'':26s} {relative:.6e} @ {where!r}")
if beta_rows:
beta_rows.sort(reverse=True)
worst, where = beta_rows[0]
median = sorted(row[0] for row in beta_rows)[len(beta_rows) // 2]
print(
f"{'beta_quantile':26s} rel={mp.nstr(worst, 3):>9s} @ "
f"(a,b,p)={where!r} median={median:.3e} n={len(beta_rows)}"
)
for relative, where in beta_rows[1:4]:
print(f"{'':26s} {mp.nstr(relative, 3):>9s} @ (a,b,p)={where!r}")
for n in sorted(gl_rows):
nodes = gl_rows[n]["gl_node"]
weights = gl_rows[n]["gl_weight"]
node_error = mp.mpf(0)
weight_error = mp.mpf(0)
weight_arg = None
for i in range(n):
ref_node, ref_weight = legendre_refine(n, nodes[i])
node_error = max(node_error, errors(nodes[i], ref_node)[1])
this_weight = errors(weights[i], ref_weight)[0]
if this_weight > weight_error:
weight_error, weight_arg = this_weight, i
weight_sum_error = abs(sum(weights.values()) - 2)
print(
f"gauss_legendre n={n:<5d} node_abs={mp.nstr(node_error, 3):>9s} "
f"weight_rel={mp.nstr(weight_error, 3):>9s} @ i={weight_arg} "
f"sum-2={weight_sum_error:.3e}"
)
bad = [
(n, k, got)
for n, k, got in binomial_rows
if mp.mpf(got) != mp.binomial(n, k) and mp.binomial(n, k) < 2**53
]
if binomial_rows:
print(f"binomial exact-within-2^53 failures: {len(bad)}")
for row in bad[:10]:
print(" ", row, "want", mp.binomial(row[0], row[1]))
if __name__ == "__main__":
main()