g_math 0.6.8

Multi-domain fixed-point arithmetic with geometric extension: Lie groups, manifolds, ODE solvers, tensors, fiber bundles — pure Rust, zero-float, deterministic
Documentation
#!/usr/bin/env python3
"""References for the realtime profile's transcendental gates
(tests/data/fasc_ulp_refs_q16.rs), one table per supported split.

Each row: (input literal, input raw = nearest(x * 2^F), expected raw =
nearest(f(x) * 2^F), function), f evaluated by mpmath at 60 digits on the
EXACT decimal input (the canonical parser reads "0.1" exactly). The F=16 table
reproduces the original hand-generated data byte for byte; F=10 is Q22.10.
`REALTIME_REFS` selects the table for the build's GMATH_FRAC_BITS (every
split the build accepts, 2..30); rows whose input or result does not fit a
split's range are left out of its table.

Usage: python3 scripts/generate_realtime_ulp_refs.py
"""
from mpmath import mp, mpf, nint
import mpmath as M

mp.dps = 60
SPLITS = list(range(2, 31))   # every GMATH_FRAC_BITS the realtime build accepts
OUT = "tests/data/fasc_ulp_refs_q16.rs"
FUNCS = {
    "exp": M.exp, "ln": M.log, "sqrt": M.sqrt, "sin": M.sin, "cos": M.cos,
    "tan": M.tan, "atan": M.atan, "asin": M.asin, "acos": M.acos, "sinh": M.sinh,
    "cosh": M.cosh, "tanh": M.tanh, "asinh": M.asinh, "acosh": M.acosh, "atanh": M.atanh,
}
# The original table's inputs, per function, in its row order.
INPUTS = [
    ("exp", ["0.5", "1.0", "-0.5", "2.0", "-1.0", "0.1"]),
    ("ln", ["0.5", "1.0", "2.0", "3.0", "0.1", "10.0"]),
    ("sqrt", ["0.25", "1.0", "2.0", "4.0", "9.0", "0.5"]),
    ("sin", ["0.0", "0.5", "1.0", "-1.0", "1.5", "3.0"]),
    ("cos", ["0.0", "0.5", "1.0", "-1.0", "1.5", "2.0"]),
    ("tan", ["0.0", "0.5", "1.0", "-0.5", "0.75", "-0.75"]),
    ("atan", ["0.0", "0.5", "1.0", "-1.0", "0.1", "100.0"]),
    ("asin", ["0.0", "0.5", "-0.5", "0.9", "-0.9", "0.1"]),
    ("acos", ["0.0", "0.5", "-0.5", "0.9", "-0.9", "0.1"]),
    ("sinh", ["0.0", "0.5", "1.0", "-1.0", "2.0", "-0.5"]),
    ("cosh", ["0.0", "0.5", "1.0", "-1.0", "2.0", "-0.5"]),
    ("tanh", ["0.0", "0.5", "1.0", "-1.0", "2.0", "-0.5"]),
    ("asinh", ["0.0", "0.5", "1.0", "-1.0", "2.0", "10.0"]),
    ("acosh", ["1.0", "1.5", "2.0", "3.0", "10.0", "1.1"]),
    ("atanh", ["0.0", "0.5", "-0.5", "0.9", "-0.9", "0.1"]),
]


def nearest(v):
    return int(nint(v))


def table(rows, F):
    """Rows whose input and result fit the split's i32 range (and whose input
    is not rounded to zero: ln(0.1) at F = 2 has no input)."""
    lo, hi = -(1 << 31), (1 << 31) - 1
    out = []
    for s, f in rows:
        x = mpf(s)
        ir, er = nearest(x * 2**F), nearest(FUNCS[f](x) * 2**F)
        if not (lo <= ir <= hi and lo <= er <= hi):
            continue
        if ir == 0 and x != 0:
            continue
        out.append((s, ir, er, f))
    return out


def main():
    rows = [(s, f) for f, inputs in INPUTS for s in inputs]
    lines = [
        "// GENERATED by scripts/generate_realtime_ulp_refs.py - do not edit.",
        "// mpmath 60 digits. Per row: (input literal, nearest input raw, nearest raw of",
        "// f at the EXACT decimal input, function), one table per realtime split.",
        "",
    ]
    for F in SPLITS:
        name = "Q16_REFS" if F == 16 else f"Q{32 - F}_{F}_REFS"
        lines.append(f"/// Realtime at GMATH_FRAC_BITS={F}.")
        lines.append(f"pub const {name}: &[(&str, i32, i32, &str)] = &[")
        for s, ir, er, f in table(rows, F):
            lines.append(f'    ("{s}", {ir}_i32, {er}_i32, "{f}"),')
        lines.append("];")
        lines.append("")
    arms = " ".join(f"{F} => Q{32 - F}_{F}_REFS," for F in SPLITS if F != 16)
    lines.append("/// The table for the build's split (every GMATH_FRAC_BITS the build accepts).")
    lines.append("pub const REALTIME_REFS: &[(&str, i32, i32, &str)] =")
    lines.append(f"    match g_math::fixed_point::frac_config::FRAC_BITS {{ {arms} _ => Q16_REFS }};")
    lines.append("/// The split `REALTIME_REFS` was generated for (equals FRAC_BITS for 2..=30).")
    lines.append("pub const REALTIME_REFS_FRAC_BITS: u32 =")
    lines.append("    match g_math::fixed_point::frac_config::FRAC_BITS { f @ 2..=30 => f, _ => 16 };")
    open(OUT, "w").write("\n".join(lines) + "\n")


if __name__ == "__main__":
    main()