ad84124c

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

cli: numerator templates in positional slots

The CLI's prepare bound the external carriers of the numerator template to
fresh symbols (ninjars_cli_components::p<generation>_<index>). Plans over
that template (export-bench, `reduce`/`bench --numerator-evaluator
symjit-o2[-factored]`, the fast runtime) therefore paid the positional
renaming in PolynomialCoefficientPlan::exact_evaluator, which re-normalizes
every coefficient head. Their exact factoring (`export-bench --factor`,
symjit-o2-factored) also still depended on the process history, because
Symbolica orders polynomial variables by symbol: after 300 unrelated
symbols and two other fixtures, 6 of 684 values of the gg -> hh top-box-2
heads differed.

prepare now binds the carrier components to ninjars_positional::slots, as
PhysicalTensorNumerator does since 561117c, so the template is expanded
directly in the evaluators' positional parameters. Fresh names, as before,
take their place if the slots are functions (after tens of thousands of
registrations) or occur in the bound integrand. Parameter names appear only
in `reduce --diagnostics` (`numerator_template`, `numerator_parameters`),
now as ninjars_slot::s<n>.

src/determinism_tests.rs (a unit test of the binary, so it can call
prepare) evaluates the gg -> hh top-box-2 pool fixture's heads, plain and
exactly factored, in Float(256) and binary64 at its exact samples. It does
so in a fresh child process, then in a process that first registers 300
symbols and prepares two other fixtures, in this thread and in two
concurrent threads, and requires bitwise-equal results. It fails without
this change (6 of 684 values, all from factoring).

Results, `reduce` against 561117c on 50 configurations (generic engine in
f64, double-float and Float; SymJIT plain and factored; fast engine; FDH;
scalar, parameterized, rank-8 heptagon and all seven physical pool
fixtures): bitwise identical except the factored four-photon top box, whose
factors now come out in positional order:
- generic engine: 22 of 30 components move by <= 1.9e-14 of the lane's
  largest component; errors against Float(256) go from 6e-15/2e-14/2.4e-14
  to 1.2e-14/3e-14/2.4e-14 (unfactored: 1.1e-14 to 9.9e-14);
- fast engine: same components, <= 8.4e-13 of the lane maximum; distance
  to the unfactored fast result from <= 9.7e-13 to <= 5.8e-13.
The committed bench cases are byte-identical (export_bench.rs), and neither
the rank-n2-s0 bitwise fixture's inputs nor ninjars-symbolica change.

Timing:
- Same-core interleaved A/B against 561117c (8 rounds, medians): SymJIT
  coefficient compilation (evaluator construction and JIT) -4.4% for the
  gg -> hhh top pentagon (843 -> 806 ms), -4.8% factored, -6.2% for the
  factored gg -> hh top box; export-bench Float(256) evaluation -3% and -5%
  (--factor). Coefficient planning and factoring are unchanged.
- export-bench's prepare stage takes 1253 ms instead of 1208 ms. The slot
  pool, previously registered by the first evaluator, now registers there.
  In a full CLI process, its ~4100 symbols cost ~90M instructions and grow
  Symbolica's symbol table by ~10^4 page faults (~35 ms of system time). A
  build with the pool but fresh names prepares in 1255 ms; expanding in slot
  order adds ~40M instructions.
- Whole processes: user-space instructions -0.1% to -1.2% (pentagon
  symjit-o2 -1.2%, export-bench -0.6%); task-clock +0.3% to +1.1% (export-
  bench: user +0.4%, system +10 ms), generic hgg -6.8%.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GitHub
Time to Start Worker time Duration Time to finish Idle
Config - - - - 26s
Eval - - - - -
Build - - - - -
Suite - - - - 26s