import struct
import mpmath as mp
mp.mp.dps = 60
NS = (8, 16, 24, 32, 40)
def coefficients(n):
m = 2 * n
m2 = 2 * m
ell = mp.sqrt(mp.mpf(n) / mp.sqrt(2))
f = [mp.mpf(0)]
for k in range(-m + 1, m):
t = ell * mp.tan((mp.mpf(k) * mp.pi / m) / 2)
f.append(mp.exp(-(t**2)) * (ell**2 + t**2))
assert len(f) == m2
shifted = f[m2 // 2 :] + f[: m2 // 2]
a = [
mp.fsum(x * mp.cos(-2 * mp.pi * i * j / m2) for i, x in enumerate(shifted)) / m2
for j in range(1, n + 1)
]
a.reverse() return ell, a
def as_f32(v):
return struct.unpack("f", struct.pack("f", float(v)))[0]
def fmt(v, f32):
target = as_f32(v) if f32 else float(v)
round_trip = as_f32 if f32 else float
for p in range(1, 18):
s = "%.*g" % (p, target)
if round_trip(s) == target:
break
if "." not in s and "e" not in s:
s += ".0"
return s
HEADER = '''//! Weideman coefficient tables for the Faddeeva function.
//!
//! **Generated by `tables.py`, in this directory. Do not edit by hand.**
//!
//! Leading-term-first (MATLAB `polyval` order), which is what
//! [`poly_rev`](thermite::math::specialized::SpecializedCoreMath::poly_rev) expects.
//! Reordering them silently yields a different polynomial that agrees at `Z = 1`.
//!
//! The f32 tables are the same values rounded, not a separate fit.
//! `Weideman<24|32|40> for f32` exist only to satisfy the
//! [`WeidemanTables`](super::WeidemanTables) supertrait bound - f32 clamps at
//! `MAX_N = 16` and never instantiates them.
use super::Weideman;
'''
FOOTER = """
impl super::WeidemanTables for f64 {
const MAX_N: usize = 40;
// sqrt(f64::MAX) is ~1.34e154; back off for the (L + y)^2 headroom.
const HUGE: Self = 1.0e150;
// Measured crossover: inside this box the correction is never worse than the direct
// evaluation and up to 4 orders better; outside it, the direct one wins.
const REAL_AXIS_Y: Self = 1.0e-5;
const REAL_AXIS_X: Self = 1.0e3;
}
impl super::WeidemanTables for f32 {
// f32 Horner roundoff floors at ~5e-7 (N = 16); more terms measure no better.
const MAX_N: usize = 16;
// sqrt(f32::MAX) is ~1.84e19.
const HUGE: Self = 1.0e17;
const REAL_AXIS_Y: Self = 1.0e-3;
const REAL_AXIS_X: Self = 1.0e2;
}
"""
def main():
out = [HEADER]
tables = {n: coefficients(n) for n in NS}
for ty in ("f64", "f32"):
f32 = ty == "f32"
for n in NS:
ell, a = tables[n]
out.append(f"impl Weideman<{n}> for {ty} {{")
out.append(f" const L: Self = {fmt(ell, f32)};")
out.append(f" const A: [Self; {n}] = [")
out.extend(f" {fmt(v, f32)}," for v in a)
out.append(" ];")
out.append("}")
out.append("")
out.append(FOOTER.strip())
print("\n".join(out))
if __name__ == "__main__":
main()