Expand description
The runtime Bessel order, and the cost class it selects.
The const-order Bessel entry point (bessel_n) takes a
whole-number order known at compile time. The runtime form
(bessel) takes BesselOrder instead of a bare order
value, because “what order is this?” and “what does that order cost?” are different
questions and only the caller can answer the second one cheaply.
The family markers (J, Y, I, K, Scaled, and the Airy selectors) live
in this module too. See BesselFamily.
§Why a tagged order rather than a plain $\nu$
$J_\nu$ at whole-number $\nu$ is a table lookup plus a recurrence. At half-integer
$\nu$ it is elementary: sines and cosines. At arbitrary real $\nu$ it is Steed’s
method: two continued fractions and a Temme series, one of which needs $O(x)$
iterations. Those are three genuinely different algorithms with costs an order of
magnitude apart, and under SIMD the whole packet pays for whichever is selected. A
per-lane choice would make every lane pay every arm.
So the class is carried as a scalar tag and the order values as a vector. One packet runs one algorithm, and the caller can see in the type which one they asked for.
§Exact by construction
Each variant stores a numerator, not a rounded $\nu$: HalfInteger(k) means
$\nu = k/2$ and Thirds(k) means $\nu = k/3$. This is not decoration. 1/3 is not
representable in binary, so a design that stored $\nu$ as a float and tagged it
separately could not distinguish Thirds(1) from a nearby real order, and the tag would
be a promise the caller could break. Here the tag cannot disagree with the payload.
§Downgrading
simplify narrows a value to the cheapest variant its data
actually needs, so a caller who reaches for Real and happens to
pass whole numbers gets the fast path anyway. The runtime forms call it themselves. It’s
public so a caller with a hot loop can hoist it out and pay the check once.
The checks run in cost order and cost proportionally to how general the claim was, so
Integer checks nothing at all. Downgrading requires the
condition to hold in every lane. One odd lane keeps the whole packet on the general
path. A caller with a genuinely mixed packet can recover the fast path with
group_by_value, which turns a
divergent packet into uniform sub-packets.
§Real does not downgrade to Thirds, deliberately
Real -> Integer and Real -> HalfInteger are exact: whole numbers and halves are both
representable, so the downgrade cannot change which function is evaluated.
Real -> Thirds would not be. fl(1.0/3.0) is not $1/3$, so snapping it to
Thirds(1) would silently evaluate a different function than the caller asked for.
Close, but wrong, and wrong in a way no test of Thirds itself would catch. A caller who
wants an exact third writes Thirds(1), which is the whole reason the variant carries a
numerator.
Structs§
- Ai
$\mathrm{Ai}(x)$.- AiPrime
$\mathrm{Ai}'(x)$.- Bi
$\mathrm{Bi}(x)$.- BiPrime
$\mathrm{Bi}'(x)$.- I
- Modified Bessel function of the first kind,
$I_\nu$. Grows like$e^x$. - J
- Bessel function of the first kind,
$J_\nu$. The oscillating minimal solution. - K
- Modified Bessel function of the second kind,
$K_\nu$. Decays like$e^{-x}$. - Scaled
- The exponentially scaled form of a family or Airy function.
- Y
- Bessel function of the second kind,
$Y_\nu$(Neumann). The oscillating dominant solution.
Enums§
- Bessel
Order - The order
$\nu$for the runtime-order Bessel functions, tagged with the class of order it carries. See the module documentation for why the class is part of the value.
Traits§
- AiryFn
- An Airy selector:
Ai,AiPrime,Bi,BiPrime, or one of them underScaled. Each asks the kernel for exactly its own output, so the cost is one Bessel pass, the same as the long-form single entries. - Bessel
Family - A Bessel family marker:
J,Y,I,K, or one of them underScaled. - Bessel
Ratio Family - A family with a ratio kernel
$F_\nu / F_{\nu-1}$and its inverse, forbessel_ratioand its three companions. OnlyItoday (the von Mises-Fisher quantities). AKratio would be the next member.