8c92672b

Author: Kaapo Seppänen <118552719+kaapos@users.noreply.github.com>

Committer: GitHub <noreply@github.com>

Add finite-temperature and finite-density equilibrium amplitudes (#138)

* disable CI during thermal branch integration

* preserve panic output during streamed integration

* handle empty external basis in lmb multichanneling

* cover model parameter updates after saved-state reload

Extend the fresh-process reload regression to check saved parameter
values, subsequent coupling updates, and dual layouts while retaining
the UFO custom-printer checks.

* simplify the dangling slot check for clippy

* update symjit to 2.27.0

* fix symbolica callback resolution for numeric wrappers

Preserve wrapper precision when resolving native callbacks and include the corrected hyperbolic precision assertion.

* add hyperbolic evaluator coverage

Exercise eager, arbitrary-precision, C++ and SymJIT evaluation of
hyperbolic functions with complex and real arguments, with improved
diagnostics and the current numeric API. Eager Arb on real arguments is the
required stability level; the other backends are ignored as stable enough
for current purposes.

* add direct translation setting for JIT compilation

GammaloopCompileOptions::jit_direct_translation, enabled by default,
translates Symbolica instructions directly to SymJIT IR, including when JIT
compilation is the fallback for external artifacts. Frozen SymJIT modes now
carry the full CompilationOptionsSnapshot, which replaces
ExternalCompilationOptionsSnapshot.

* preserve native precision through UV profile weighting and fitting

Keep scaling weights, contribution sums, magnitudes, and logarithms in
native precision, converting only regression inputs to f64. Include
profile weights in stability checks and add range-preservation tests.

* add chemical potentials to bundled models and UFO imports

Represent particle chemical potentials consistently in UFO and JSON model
data so finite-medium generation can resolve them at runtime, and preserve
them through UFO imports with ufo-model-loader 1.0. Align the built-in model
defaults and add the sm-thermal restriction.

Particle charges stay exact rationals through import and export. Older
exports wrote fractional charges such as 2/3 as their nearest binary64
value; numeric charges are rounded to the simplest rational within one
binary64 epsilon.

A loader checkout without distribution metadata now reports the missing
metadata and how to install it instead of a misleading version error. UFO
import tests run only where Symbolica publishes a wheel, and the loader no
longer needs the Nix source patch.

* add a thermal SM restriction with a massive strange quark

The bundled SM fixed the strange quark massless, so no restriction card
could give it a mass. Add an external MS (MASS block, code 3) to the UFO
and JSON models, without a Yukawa coupling. Every existing card sets
MS = 0 explicitly, because the loader otherwise falls back to the UFO
default and would change those restrictions.

Add sm-thermal_s_mass, which matches sm-thermal but keeps MS = 0.095
adjustable. The unrestricted model retains a symbolic MS, so the full-SM
two-loop vacuum generation test no longer groups strange-quark graphs with
massless down-quark graphs.

* select nix test groups for the host system

rust-workspace.nix imported ci.nix without system, so hasUfoWheel was
always evaluated for x86_64-linux. On aarch64-linux this enabled the UFO
loader and failed evaluation on the missing wheel. Host checks now use
their own system, while the committed nix-ci.nix is still generated from
the x86_64-linux schedule.

Without a UFO wheel, the core group skips the two tests that import UFO
models. Shared Python stub generation still enables ufo-import, so
loader-version validation keeps running. Archive filtersets reject test()
predicates, so the exclusion applies only when running nextest.

Update nextest to 0.9.137.

* add finite-medium cff and vacuum subtraction

Implement finite-temperature and zero-temperature equilibrium across CFF
generation and thermal distribution evaluation, including higher
derivatives, numerator energy powers, cyclic chains with equal and unequal
masses, and shared scalar sectors. Graph parameter builders persist the
selected medium and register only its distribution functions; warm-up
validates the temperature, chemical potentials and bosonic mass domain.

Vacuum subtraction acts on the completed thermal weights instead of the UV
forests, so it commutes with local and integrated UV counterterms. The UV
counterterms covering all loops are omitted, also when bridges remain in
the graph. Thermal CFF contractions require a connected virtual
complement, and an orientation without an admissible contraction fails
generation instead of being dropped. Thermal modes reject threshold
subtraction, and generation settings are validated once per process
stage, for every process of a batch before any of them is preprocessed.

Include vanishing-limit UV profiling, thermal and cold-dense vacuum
inspections up to four loops, inverse-propagator cancellation coverage,
schemas and architecture documentation. State version 13 stores the medium
in graph parameter builders.

* support zero-temperature Fermi-surface localization in thermal amplitudes

Zero-temperature distribution derivatives are separated from the completed
UV-subtracted amplitude into Fermi sectors. Each sector localizes its delta
derivatives along an adapted loop basis with a normalized positive-scale
profile and differentiates the smooth coefficient with hyperdual jets.
Sectors evaluate in the current precision, use the existing precision
retry path, and are stored with the process state.

- Preserve independent fermion momenta when completing localization bases.
- Add one-loop triple-pole and non-aligned two-loop convergence tests.
- Strengthen analytic normalization and UV composition checks.
- Enable cold-dense tennis-ball inspection with profile-dependence checks.
- Document the independent two-loop reference in the run card.

* unify vacuum and thermal CFF generation while preserving contour prescriptions

- Remove graph splitting and retain connected graph incidence.
- Preserve inherited contact poles and existing vacuum normalization.
- Share tadpole handling with thermal weights for both energy orientations.
- Add regressions for odd numerators, chemical potentials, and contact terms.

* add option to hide numerator in CFF display

* harden Fermi-surface localization

- Validate the h-function profile before localization with the checks that
  LU-h sampling applies, so unsupported profiles and a nonpositive sigma
  are rejected instead of panicking or flipping the sign.
- Treat zero chemical potentials as empty Fermi seas, and use |m| for
  negative masses.
- Drop boson Fermi surfaces at zero temperature, where warm-up already
  makes their occupation constant on E > 0.
- Reject repeated distributions after polynomial expansion, since a squared
  delta has no defined value.
- Require real fermion masses for zero-temperature steps during warm-up.
- Cover every evaluator method with interpreted and symjit backends at both
  signs of mu, and a product of two independent Fermi deltas.
- Archive the 20k-sample estimate within one integration instead of
  repeating the first samples of the 100k-sample run.
- Document the thermal distribution call convention and these Fermi limits.

* compute h-function normalizations in native precision

The poly_exponential and poly_left_right_exponential normalizations were
binary64 constants, which limited the f128/Arb rescue path of Fermi-surface
localization to about 1e-16 relative accuracy. They are Bessel integrals:
sigma e^2 K_{(1-p)/2}(2) and 2 sigma e^2 K_{1-p}(2).
HFunctionSettings::normalization now evaluates them in the active precision.
Half-integer orders are an exact rational times sqrt(pi)/2. Integer orders
use the K_0(2) series, whose only transcendental input is Euler's constant,
the Wronskian for K_1 and the stable upward recurrence. h and h_dual share
this one definition; the supported powers are unchanged.

Tests compare the normalizations in f64, f128, 256-bit and Arb precision
against Symbolica's 1200-bit bessel_k, anchor the orders to the previous
tabulated values, and check that every profile integrates to one.

* keep exponentially small thermal occupations representable

Order-zero distributions are now written as the vacuum weight (1 + s)/2
plus a separately evaluated occupation, so a vanishing directed weight is
the occupation itself rather than a difference that rounds away.

At zero temperature the step is theta(sigma*mu - E). Above the Fermi
surface its numerator |x| + x is exactly zero, while the evaluator's
x * inv(x) for theta(E - sigma*mu) is often one ulp off one. That ulp
survived vacuum subtraction as noise times the vacuum integrand, which
the UV profiler fitted with the vacuum power counting.

At finite temperature, s + tanh(a) lost the occupation once tanh(a)
rounded to one. With a = beta*(E - sigma*mu)/2, the bodies factor
exp(-(a + |a|)) out of n_F = 1/(exp(2a) + 1) and n_B = 1/(exp(2a) - 1),
leaving nonpositive exponents and a remainder free of cancellation; coth
keeps n_B accurate in the soft limit. Quad and Arb now resolve the UV
tail. f64 still loses weights whose vacuum value cancels numerically.

The native distribution test checks the tail relative to the occupation.
The symmetric-terminal test checks dependence through E - sigma*mu only,
and the derivative chain starts from the documented hyperbolic form,
since |a| has no symbolic derivative.

Building a distribution body is fallible: a zero-temperature derivative
with a chemical potential needs Fermi-surface localization, so it reports
an error naming the edge and order instead of being skipped silently.

* cancel the thermal vacuum constant symbolically

Vacuum subtraction formed W - V(W) numerically. For weights whose vacuum
value is nonzero, factors 1 - n rounded to one once the occupation fell
below the f64 epsilon, so their exponentially small UV contribution was
lost while the vacuum-free weights kept theirs. f64 then returned a tail
of the right size but the wrong sign, which rotations cannot detect.

An order-zero factor differs from its vacuum weight by its occupation,
N(s) - (1 + s)/2 = N(-1). The subtraction now applies the product rule
D(a*b) = D(a)*V(b) + a*D(b) over the completed weight, so every term
keeps an occupation or a derivative and the result stays linear in the
number of factors. Weights with V(W) = 0 are unchanged, which keeps the
map idempotent and leaves calls downstream code already handles.

Along a UV ray of the thermal quark sunrise, f64 now agrees with Quad
and Arb to 14-16 digits up to beta*E ~ 640, and bulk f64 accuracy
improves from 4e-12 to 7e-16 at s = 10. For the three-loop quark
Mercedes, evaluator ops (-1%), evaluator time per sample (-3%) and
generation time (-2%) are unchanged within noise.

OCCUPATION_FORM rewrites N(s) as (1 + s)/2 + N(-1), so tests can compare
subtracted weights with W - V(W). The thermal weight test checks that
every term keeps a factor vanishing in vacuum.

* register thermal bodies per contour offset and test tail precision

Thermal function-map entries took the contour offset s as a runtime
argument. Evaluator optimization could then group (1 + s)/2 + n so that
the vacuum weight met an exponentially small occupation before s, and
the generic evaluator lost the gluon tail: 2e-8 relative error at
a = 10 and an exact zero at a = 40. Each body is now registered once per
offset, with s as a tag. The vacuum weight folds symbolically, and the
s = -1 body is the bare occupation, independent of evaluation order.

Regression tests cover the precision fixes at both temperatures:

- zero_temperature_steps_are_exact_outside_the_fermi_sea requires the
  order-zero body to equal its vacuum weight exactly above the Fermi
  surface, in real and complex f64, compiled C++ and Arb. A step written
  as theta(E - sigma*mu) fails on about a fifth of the sampled energies.
- vacuum_subtracted_thermal_weights_keep_uv_tails_and_exact_zeros
  evaluates vacuum-subtracted weights with a nonzero vacuum value through
  the production evaluator. Eager f64, symjit and Quad must match an Arb
  oracle from the bulk to occupations near binary64 underflow, and zero
  temperature must give exact zeros outside the Fermi sea.
- thermal_sunrise_f64_keeps_uv_tails_and_exact_zeros generates the quark
  sunrise with UV subtraction and the assembly backend. Raw f64 must match
  Arb along a UV ray at finite temperature and vanish exactly at zero
  temperature and density.

Reverting the hyperbolic bodies, the numerical vacuum subtraction, the
zero-temperature step or the tagged offset each fails at least one test.

* reject thermal denominators that vanish for every loop momentum

Generation turns coinciding poles into derivatives only for edges sharing a
mass parameter and momentum routing. Distinct mass parameters with equal
values, or exceptional external momenta, leave a mixed-sign denominator that
is zero at every sample, which every precision evaluates to NaN and the
stability checks then report as a zero weight.

Amplitude terms now record the H-surfaces in the denominators of their
selected orientations, and warm-up rejects one whose energies pair up into
edges with masses equal up to sign and equal spatial momenta, with a zero
external energy shift. The comparisons are exact; near coincidences stay
finite and remain with the stability checks. UV counterterm expressions and
cross sections are not covered.

* accept exponentially suppressed UV profile rays as vanishing

Exponentially small thermal occupations now survive vacuum subtraction,
so the overall UV limit of a vacuum-subtracted thermal integrand decays
like exp(-beta*E) instead of vanishing exactly. On the thermal sunrise,
Arb samples fall from 1e-4e7 to 1e-3e8 between scales 1e8 and 7.5e8 and
then underflow to exact zero. The log-log fit of the few nonzero samples
reported slopes near -3e8 with r^2 of 0.80-0.97, so 17 of 36 limits
failed as unstable fits.

A ray without an asymptotic power law is now accepted as vanishing,
when vanishing limits are allowed, if a high-scale suffix as long as
the fit window has at most one nonzero sample and ends in an exact
zero, or its nonzero samples decay along their least concave log-log
majorant. That majorant must fall faster than the threshold power from
its first sample, and its last segment must be the final sampled step
unless only exact zeros follow. Zeros may only trail the nonzero
samples. The majorant bridges the dips where Arb resolves an exact
cancellation of a leading occupation, while a tail which flattens,
rises or fluctuates above the threshold-power line still fails. An
asymptotic fit keeps precedence.

thermal_sunrise_uv now profiles 36/36 limits as passing. Over the
default 1e3..1e6 range 34/36 pass. The remaining orientation reaches
its 1/lambda asymptote only beyond 1e6 and fails as before.

Profile JSON stores the majorant's initial slope as a conservative decay
bound, writing -inf for a sparse window ending in zero, and saved
profiles render their per-orientation tables from the serialized entries.

* document finite-medium settings and thermal media

Add a thermal-media chapter on selecting a medium, setting the inverse
temperature and chemical potentials, loading the thermal models, Fermi-surface
localization and the medium-mode restrictions, and document the EoS phase
convention. Regenerate the GammaLoop reference for the medium settings, fix
the VacuumLimit spelling, and record the reviewed verified-scope digests.

* add NLO cool quark matter EoS example and acceptance test

The example card integrates the O(aS) pressure of cold quark matter with
quark masses, following Graf, Schaffner-Bielich and Fraga (arXiv:1507.08941),
with a massive strange quark from sm-thermal_s_mass. The acceptance test runs
the card and checks the result against the cited target.

* compare inspect references with a relative tolerance

The finite-medium CFF generation assembles the vacuum orientation sum in
a different order. The integrand is unchanged up to the order of its
sums and products, but photons_1l_inspect and photons_2l_inspect move by
one ulp, which their exact snapshots reject. test_epem_dd_dt compiles its
evaluator, so its last digits also depend on the host compiler.
Compare all three with assert_complex_approx_eq instead.

* clarify summed function-map evaluator documentation

Describe the per-orientation Symbolica aliases in the setting comment
and generated API reference.

* canonicalize masses fixed to zero by model restrictions

* complete light-quark masses and Yukawa interactions

* consolidate CFF source-frame sign metadata

* restore signed cut-alias surface coverage

* centralize thermal distribution call encoding

* cover standalone evaluation rejection of thermal weights

* restrict each Fermi sector to its own orientation catalogue

* treat literal zero chemical potentials as absent

* prioritize occupied fermion routes when completing Fermi bases

* reject non-real fermion masses in finite-temperature occupations

* validate conjugate chemical potentials during medium warm-up

* cover vacuum cube sampling with empty externals

* share UFO wheel platform metadata

* recreate generated test states before saving

* adapt thermal branch to updated main APIs

* reject thermal amplitudes in vacuum analytical evaluation

* require accelerating decay for nonzero UV tail exemptions

* update ARM filter for renamed UFO model test

* canonicalize restriction-fixed zero chemical potentials

* refresh medium and UV profile schema descriptions

* recommend custom restrictions for adjustable chemical potentials

* restore finite charm mass and Yukawa defaults in the full JSON model

* enable CI for final review of thermal amplitudes

* load the builtin SM in paired performance benchmarks

The SM JSON model now stores rational and optional hypercharges, which the
baseline binary cannot parse from the candidate's shared model files. The
GL15 and GL1 cards import sm-default so each revision loads its own
embedded model; results must still agree between revisions.

* disable CI while the benchmark overhead fix lands on main

* enable CI for final review of thermal amplitudes
gammaloop-clippygammaloop-fmtgammaloop-guppy-workspace-graphgammaloop-nextest-binaries-clinnetgammaloop-nextest-binaries-coregammaloop-nextest-binaries-docsgammaloop-nextest-binaries-integrationgammaloop-nextest-binaries-linnetgammaloop-nextest-binaries-python-apigammaloop-nextest-binaries-spensogammaloop-nextest-binaries-vakintconfiguredeploy:nix-ci-passedalphal00p-docs-cargo-artifactscargoArtifactscargoCheckArtifactscrate-deps-gammaloop-workspace-hackcrate-test-binaries-alphal00p-docs-buildercrate-test-binaries-alphal00p-docs-catalogscrate-test-binaries-alphal00p-docs-examplescrate-test-binaries-alphal00p-docs-macroscrate-test-binaries-alphal00p-docs-python-exportercrate-test-binaries-alphal00p-docs-schemacrate-test-binaries-clinnetcrate-test-binaries-gammaloop-apicrate-test-binaries-gammaloop-integration-testscrate-test-binaries-gammaloop-tracing-filtercrate-test-binaries-gammaloop-tracing-filter-macroscrate-test-binaries-gammalooprscrate-test-binaries-idensocrate-test-binaries-kurvstcrate-test-binaries-linnestcrate-test-binaries-linnetcrate-test-binaries-linnet-pycrate-test-binaries-spensocrate-test-binaries-spenso-hep-libcrate-test-binaries-spenso-macroscrate-test-binaries-spynso3crate-test-binaries-symbolica-utilscrate-test-binaries-three-dimensional-repscrate-test-binaries-vakintcrate-test-dependencies-alphal00p-docs-buildercrate-test-dependencies-alphal00p-docs-catalogscrate-test-dependencies-alphal00p-docs-examplescrate-test-dependencies-alphal00p-docs-macroscrate-test-dependencies-alphal00p-docs-python-exportercrate-test-dependencies-alphal00p-docs-schemacrate-test-dependencies-clinnetcrate-test-dependencies-gammaloop-apicrate-test-dependencies-gammaloop-integration-testscrate-test-dependencies-gammaloop-tracing-filtercrate-test-dependencies-gammaloop-tracing-filter-macroscrate-test-dependencies-gammalooprscrate-test-dependencies-idensocrate-test-dependencies-kurvstcrate-test-dependencies-linnestcrate-test-dependencies-linnetcrate-test-dependencies-linnet-pycrate-test-dependencies-spensocrate-test-dependencies-spenso-hep-libcrate-test-dependencies-spenso-macroscrate-test-dependencies-spynso3crate-test-dependencies-symbolica-utilscrate-test-dependencies-three-dimensional-repscrate-test-dependencies-vakintgammaloop-python-modulegammaloop-ufo-python-runtimenix-ci-check-alphal00p-docsnix-ci-check-gammaloop-doctestnix-ci-check-gammaloop-nextest-clinnetnix-ci-check-gammaloop-nextest-corenix-ci-check-gammaloop-nextest-docsnix-ci-check-gammaloop-nextest-integrationnix-ci-check-gammaloop-nextest-linnetnix-ci-check-gammaloop-nextest-python-apinix-ci-check-gammaloop-nextest-spensonix-ci-check-gammaloop-nextest-vakintnix-ci-passedx86_64-linuxtest:nix-ci-check-alphal00p-docstest:nix-ci-check-gammaloop-doctesttest:nix-ci-check-gammaloop-nextest-clinnettest:nix-ci-check-gammaloop-nextest-coretest:nix-ci-check-gammaloop-nextest-docstest:nix-ci-check-gammaloop-nextest-integrationtest:nix-ci-check-gammaloop-nextest-linnettest:nix-ci-check-gammaloop-nextest-python-apitest:nix-ci-check-gammaloop-nextest-spensotest:nix-ci-check-gammaloop-nextest-vakint