e0e61d6b

By: ValentinHirschi <valentin.hirschi@gmail.com>

Add binary64 bubble and tadpole tensor kernels

NativeTensorEvaluator<f64> (B11, B111, An) now runs a binary64
specialization of the OneLOop bub0-bub111/bnlog/solabc/tadpn port
(src/native/tensor/binary64.rs). DoubleFloat and Float keep the generic
code, which f64 can still select with the hidden set_binary64(false) for
validation. Same sectors, exact zero tests, mass ordering, lips, root pair
and series radius; what changes:

- real kinematics stay in real arithmetic: one ln_1p per real root, the
  lip as +-pi(1-x^(n+1)) on a real direct formula, and every real root and
  -1/x one quotient of the inputs (complex masses keep the generic roots);
- complex-conjugate roots of real masses below threshold take one bnlog:
  bnlog(x1) + bnlog(x2) = 2 Re bnlog(x1), exactly real;
- the bnlog series is evaluated by Estrin's scheme on 8 (|y|_1 <= 0.01),
  16 (<= 0.2) or all (<= 24, zero padded) coefficients, each meeting the
  generic 2^-57 truncation bound (checked at construction), instead of a
  Horner chain of up to 21 complex multiply-adds per rank.

Each rank depends only on the row, so B111's first nine outputs are
bitwise B11's.

Timing (in-process, interleaved with OneLOop 3.6 gfortran -O3 through
Ninja's _cm wrappers, CPU 52, medians of 9 blocks >= 20/25 ms, two
rounds, before = d10eaa0) on Ninja's 27 recorded master streams:
- every distinct bubble call (285 rows): worst 1.31x -> 0.57x OneLOop
  (syn-n2-r1/r2, syn-n4-r4, syn-n6-r6: one massive, one massless line
  below threshold, 1.05-1.31x -> 0.39-0.57x); median 0.23x;
- bubble group per case 0.15-1.22x -> 0.08-0.52x; bubbles + tadpoles
  0.19-1.05x -> 0.10-0.55x; syn-n2 totals 0.97-1.08x -> 0.32-0.55x;
- An vs olo_a0 0.46-0.62x -> 0.42-0.58x; 1,200 random rows over every
  mass sector: worst 1.28x -> 0.90x (a massless An, ~12 ns).
D0/C0/A0 code is unchanged.

Accuracy: tests/native_tensor.rs passes unchanged (worst binary64 error
against 320 bits 2.1e-14 -> 1.2e-14, Feynman-parameter rows 2.1e-14 ->
1.8e-14). New tests/native_tensor_binary64.rs: 8,000 random rows over
every sector against the generic binary64 code and 320 bits (error
quantiles 50/90/99/99.9/100% 0/1.6e-16/1.4e-15/9.8e-15/1.2e-12 vs generic
0/1.6e-16/1.5e-15/9.4e-15/1.1e-12; never 16x worse above 1e-14), An
bitwise equal to the generic code, every bubble/tadpole call of the 27
streams (tests/data/ninja_tensor_rows.txt, generated by
tests/generate_ninja_tensor_rows.py) against OneLOop (<= 9.4e-14) and
320 bits (<= 3.4e-14, as the generic code), and 2,090 regression rows
against the symbolic b0/b1/b11/b111/an expressions at 256 bits (worst
4.4e-14; 3 random outputs at the generic code's own 5e-14-2.5e-13). The
expressions of this commit put opposite-sign real masses at p^2 = 0 on the
wrong lip and lose finiteness for m1^2 = 0 where a root rounds to 1-ulp;
the test reports both, and both are empty with claude/expression-fixes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GitHub
configure