Your free trial has ended.

Try the live demo or log in .

09b2c749

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

Evaluate the divided difference's dilogarithms once in the interpreter

NinjaRS's end-to-end Float pentagon (700 and 1000 bits) became 1.4 to 1.9
times slower at 3cbe08d. It evaluates oneloopmaster's exact expressions
with Symbolica's interpreter, where the interpreted D0 and C0 of the
pentagon's calls went from 8.2e8 and 2.3e8 retired instructions per call at
700 bits before 3cbe08d to 1.8e9 and 5.3e8 (native code was unaffected).

3cbe08d's divided difference tests whether the direct difference cancels,
|upper - lower|² 256 < |upper|², before choosing near_difference. Symbolica
caches common subexpressions while linearizing, but returns before caching
a call of a non-inlined registered function, so every occurrence of such a
call is evaluated again: the test made the interpreter evaluate each
dilogarithm two or three times per divided difference (the instruction
listing of the divided difference called the sheet dilogarithm five times
for its two arguments). The native emitter shares the calls, which is why
only the interpreter slowed down. The test and the choice now live in a
registered selection (sheet_exact::select_body) that takes the two
dilogarithms and the gap as arguments, evaluated once; the complements'
difference of 3023fa6 shares it.

At 700 bits the interpreted D0 and C0 now take 7.9e8 and 2.2e8 instructions
per call (4% below the commit before 3cbe08d; at 1000 bits 6% and 7%
below). Building the interpreted D0 with its first call still costs 1.8
times what it did before 3cbe08d (0.6 s at 700 bits, once per precision),
from the larger contour-box and collinear expressions since. Binary64 and
256-bit results are bitwise unchanged on every route (native, generated,
interpreted, SymJIT) over 1,145 collinear and near-threshold rows.

tests/expression_construction.rs checks that no interpreted D0 or C0 body
(nested bodies included) calls a dilogarithm, or a divided difference of
them, twice with the same arguments where the first call dominates (it
fails on the previous form).

Tests: full suite with generated evaluators passes (231 passed, 7 ignored).
Native code regenerated (C0 and D0), C0 and D0 SymJIT assets rebuilt.
Oracle sweeps (D0, four seeds; C0, two seeds; native, generated and
expression routes, against 149ddff): no row fixed or regressed. Retired
instructions per binary64 call on the recorded NinjaRS streams against
149ddff: D0 unchanged, C0 -0.09% (geomean; at most -0.65%).

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