#[repr(C)]pub struct Compensated<V> {
pub value: V,
pub error: V,
}Expand description
Compensated arithmetic number type.
This type represents a number as the sum of two components: a high-order value and a low-order error term. Using these, it can effectively double the mantissa precision of standard floating-point types, providing significantly improved accuracy for a wide range of numerical computations.
Compensated<f32 | f64> have some functionality required for use as an Element
in vectorized types, but cannot use the math library. Use Vector<f32> or Vector<f64>
as the inner type for full functionality.
Fields§
§value: V§error: VImplementations§
Source§impl<V: ScalarValue> Compensated<V>
impl<V: ScalarValue> Compensated<V>
Sourcepub fn value(self) -> V
pub fn value(self) -> V
Returns the normalized value (value + error).
Strict, so a re-bracketing into surrounding arithmetic cannot discard the second word instead of rounding it in.
Sourcepub const fn uncompensated(self) -> V
pub const fn uncompensated(self) -> V
Returns the uncompensated value, with no error term applied.
pub fn normalize(self) -> Self
Source§impl<V: CompensatedFloatVector> Compensated<V>
impl<V: CompensatedFloatVector> Compensated<V>
pub fn splat_value(value: V::Element) -> Self
Source§impl<V: ScalarValue> Compensated<V>
impl<V: ScalarValue> Compensated<V>
Sourcepub fn accumulate_unnormalized(&mut self, rhs: Self)
pub fn accumulate_unnormalized(&mut self, rhs: Self)
Accumulate rhs into self without renormalization.
This should only be used in specific scenarios where renormalization is not desired, such as within iterative series expansions.
Sourcepub fn reduce_unnormalized(&mut self, rhs: Self)
pub fn reduce_unnormalized(&mut self, rhs: Self)
Reduce rhs from self without renormalization.
This should only be used in specific scenarios where renormalization is not desired, such as within iterative series expansions.
Source§impl<V: ScalarValue> Compensated<V>
impl<V: ScalarValue> Compensated<V>
pub fn div_scalar(num: V, denom: Self) -> Self
Source§impl<V: ScalarValue> Compensated<V>
impl<V: ScalarValue> Compensated<V>
Sourcepub fn from_fraction(numerator: V, denominator: V) -> Self
pub fn from_fraction(numerator: V, denominator: V) -> Self
Creates a compensated number from a fraction numerator / denominator,
dividing with compensation.
Trait Implementations§
Source§impl<V: ScalarValue> Add for Compensated<V>
impl<V: ScalarValue> Add for Compensated<V>
Source§impl<V: ScalarValue> Add<V> for Compensated<V>
impl<V: ScalarValue> Add<V> for Compensated<V>
Source§impl<V: Copy, T> AddAssign<T> for Compensated<V>where
Self: Add<T, Output = Self>,
impl<V: Copy, T> AddAssign<T> for Compensated<V>where
Self: Add<T, Output = Self>,
Source§fn add_assign(&mut self, rhs: T)
fn add_assign(&mut self, rhs: T)
+= operation. Read moreSource§impl<V: CompensatedFloatVector, Rhs> AddAssignMasked<<V as GenericVector>::Mask, Rhs> for Compensated<V>where
Compensated<V>: Add<Rhs, Output = Self>,
impl<V: CompensatedFloatVector, Rhs> AddAssignMasked<<V as GenericVector>::Mask, Rhs> for Compensated<V>where
Compensated<V>: Add<Rhs, Output = Self>,
Source§fn add_assign_c(&mut self, mask: V::Mask, rhs: Rhs)
fn add_assign_c(&mut self, mask: V::Mask, rhs: Rhs)
Source§fn add_assign_m(&mut self, src: Self, mask: V::Mask, rhs: Rhs)
fn add_assign_m(&mut self, src: Self, mask: V::Mask, rhs: Rhs)
Source§fn add_assign_z(&mut self, mask: V::Mask, rhs: Rhs)
fn add_assign_z(&mut self, mask: V::Mask, rhs: Rhs)
AddAssign masked (zeroed where mask is false).Source§impl<V: CompensatedFloatVector, Rhs> AddMasked<<V as GenericVector>::Mask, Rhs> for Compensated<V>where
Compensated<V>: Add<Rhs, Output = Self>,
impl<V: CompensatedFloatVector, Rhs> AddMasked<<V as GenericVector>::Mask, Rhs> for Compensated<V>where
Compensated<V>: Add<Rhs, Output = Self>,
Source§impl<V: CompensatedFloatVector> AddSubExt for Compensated<V>
impl<V: CompensatedFloatVector> AddSubExt for Compensated<V>
Source§impl<V: CompensatedFloatVector> AddSubExtMasked<<V as GenericVector>::Mask> for Compensated<V>
impl<V: CompensatedFloatVector> AddSubExtMasked<<V as GenericVector>::Mask> for Compensated<V>
Source§fn fmaddsub_c(self, mask: V::Mask, b: Self, c: Self) -> Self
fn fmaddsub_c(self, mask: V::Mask, b: Self, c: Self) -> Self
Source§fn fmaddsub_m(self, src: Self, mask: V::Mask, b: Self, c: Self) -> Self
fn fmaddsub_m(self, src: Self, mask: V::Mask, b: Self, c: Self) -> Self
Source§fn fmaddsub_z(self, mask: V::Mask, b: Self, c: Self) -> Self
fn fmaddsub_z(self, mask: V::Mask, b: Self, c: Self) -> Self
fmaddsub where mask is true, else zero.Source§fn fmsubadd_c(self, mask: V::Mask, b: Self, c: Self) -> Self
fn fmsubadd_c(self, mask: V::Mask, b: Self, c: Self) -> Self
Source§fn fmsubadd_m(self, src: Self, mask: V::Mask, b: Self, c: Self) -> Self
fn fmsubadd_m(self, src: Self, mask: V::Mask, b: Self, c: Self) -> Self
Source§fn fmsubadd_z(self, mask: V::Mask, b: Self, c: Self) -> Self
fn fmsubadd_z(self, mask: V::Mask, b: Self, c: Self) -> Self
fmsubadd where mask is true, else zero.Source§impl BernoulliNumbers for Compensated<f32>
Available on crate feature special only.
impl BernoulliNumbers for Compensated<f32>
special only.Source§impl BernoulliNumbers for Compensated<f64>
Available on crate feature special only.
impl BernoulliNumbers for Compensated<f64>
special only.Source§impl<V: CompensatedFloatVector> Bounded for Compensated<V>
impl<V: CompensatedFloatVector> Bounded for Compensated<V>
Source§impl<FROM, TO> CastVector<Compensated<FROM>> for Compensated<TO>
impl<FROM, TO> CastVector<Compensated<FROM>> for Compensated<TO>
Source§fn cast_into(self) -> Compensated<FROM>
fn cast_into(self) -> Compensated<FROM>
FROM, lane-by-lane.Source§fn cast_from(from: Compensated<FROM>) -> Self
fn cast_from(from: Compensated<FROM>) -> Self
FROM into Self, lane-by-lane, using as
semantics on each element. See the trait docs for what float-to-int
does with NaN and out-of-range lanes.Source§fn saturating_cast_from(from: FROM) -> Self
fn saturating_cast_from(from: FROM) -> Self
Self’s element
range rather than wrapping (integers) or producing a backend-defined
value (float to int). Read moreSource§fn fast_cast_from(from: FROM) -> Self
fn fast_cast_from(from: FROM) -> Self
cast_from, but may take a faster path that
relaxes IEEE corner cases. See GenericVector::fast_cast. Read moreSource§fn fast_cast_into(self) -> FROM
fn fast_cast_into(self) -> FROM
cast_into, but may take a faster path that
relaxes IEEE corner cases. See GenericVector::fast_cast.Source§impl<V: Clone> Clone for Compensated<V>
impl<V: Clone> Clone for Compensated<V>
Source§fn clone(&self) -> Compensated<V>
fn clone(&self) -> Compensated<V>
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl<V: ScalarValue> ConstDefault for Compensated<V>
impl<V: ScalarValue> ConstDefault for Compensated<V>
impl<V: Copy> Copy for Compensated<V>
Source§impl<V: Debug> Debug for Compensated<V>
impl<V: Debug> Debug for Compensated<V>
Source§impl<V: Default> Default for Compensated<V>
impl<V: Default> Default for Compensated<V>
Source§fn default() -> Compensated<V>
fn default() -> Compensated<V>
Source§impl<V: PrettyPrintScalar> Display for Compensated<V>
impl<V: PrettyPrintScalar> Display for Compensated<V>
Source§impl<V: ScalarValue> Div for Compensated<V>
impl<V: ScalarValue> Div for Compensated<V>
Source§impl<V: ScalarValue> Div<V> for Compensated<V>
impl<V: ScalarValue> Div<V> for Compensated<V>
Source§impl<V: Copy, T> DivAssign<T> for Compensated<V>where
Self: Div<T, Output = Self>,
impl<V: Copy, T> DivAssign<T> for Compensated<V>where
Self: Div<T, Output = Self>,
Source§fn div_assign(&mut self, rhs: T)
fn div_assign(&mut self, rhs: T)
/= operation. Read moreSource§impl<V: CompensatedFloatVector, Rhs> DivAssignMasked<<V as GenericVector>::Mask, Rhs> for Compensated<V>where
Compensated<V>: Div<Rhs, Output = Self>,
impl<V: CompensatedFloatVector, Rhs> DivAssignMasked<<V as GenericVector>::Mask, Rhs> for Compensated<V>where
Compensated<V>: Div<Rhs, Output = Self>,
Source§fn div_assign_c(&mut self, mask: V::Mask, rhs: Rhs)
fn div_assign_c(&mut self, mask: V::Mask, rhs: Rhs)
Source§fn div_assign_m(&mut self, src: Self, mask: V::Mask, rhs: Rhs)
fn div_assign_m(&mut self, src: Self, mask: V::Mask, rhs: Rhs)
Source§fn div_assign_z(&mut self, mask: V::Mask, rhs: Rhs)
fn div_assign_z(&mut self, mask: V::Mask, rhs: Rhs)
DivAssign masked (zeroed where mask is false).Source§impl<V: CompensatedFloatVector, Rhs> DivMasked<<V as GenericVector>::Mask, Rhs> for Compensated<V>where
Compensated<V>: Div<Rhs, Output = Self>,
impl<V: CompensatedFloatVector, Rhs> DivMasked<<V as GenericVector>::Mask, Rhs> for Compensated<V>where
Compensated<V>: Div<Rhs, Output = Self>,
Source§impl<E: ScalarValue + Element> Element for Compensated<E>
impl<E: ScalarValue + Element> Element for Compensated<E>
const ONE: Self
const ZERO: Self
Source§const ORDER_MAX: Self
const ORDER_MAX: Self
Source§const HAS_UNORDERED: bool = E::HAS_UNORDERED
const HAS_UNORDERED: bool = E::HAS_UNORDERED
PartialOrd -
float NaN. false for every integer type. Read moreSource§const IS_FLOAT: bool = E::IS_FLOAT
const IS_FLOAT: bool = E::IS_FLOAT
HAS_UNORDERED because some float types may
not have NaN and so are ordered, but still have some special
properties of floats.Source§type Signed = <E as Element>::Signed
type Signed = <E as Element>::Signed
Source§type Unsigned = <E as Element>::Unsigned
type Unsigned = <E as Element>::Unsigned
fn from_i8(value: i8) -> Self
fn from_u8(value: u8) -> Self
fn from_u16(value: u16) -> Self
Source§impl EllipticConsts for Compensated<f64>
Available on crate feature special only.
impl EllipticConsts for Compensated<f64>
special only.Source§const CARLSON_THRESH: Self
const CARLSON_THRESH: Self
(3 * 2^-104)^(1/8)
Source§const RC_SERIES_THRESH: Self
const RC_SERIES_THRESH: Self
2^-14: the R_C series tail t^8/17 is then 1e-34. Below this the ln arm of
the closed form loses about eps/s with s ~ sqrt(t) = 0.008, a few units of 1e-30,
which is the accuracy floor of R_J on this type.
Source§impl EllipticConsts for Compensated<f32>
Available on crate feature special only.
impl EllipticConsts for Compensated<f32>
special only.Source§const CARLSON_THRESH: Self
const CARLSON_THRESH: Self
(3 * 2^-46)^(1/8)
Source§const RC_SERIES_THRESH: Self
const RC_SERIES_THRESH: Self
1/128 still: the tail 1.5e-17 is below this type’s 2^-46.
Source§impl<V: CompensatedFloatVector> ExpIntDetails<Compensated<<V as GenericVector>::Element>, Compensated<V>> for Compensated<V>
Available on crate feature special only.Double-double is still real arithmetic, so the regime and domain rules apply
unchanged - but the Lentz sentinel does not.
impl<V: CompensatedFloatVector> ExpIntDetails<Compensated<<V as GenericVector>::Element>, Compensated<V>> for Compensated<V>
special only.Double-double is still real arithmetic, so the regime and domain rules apply unchanged - but the Lentz sentinel does not.
Source§fn cf_tiny() -> Compensated<V>
fn cf_tiny() -> Compensated<V>
The default, MIN_POSITIVE, is reciprocated on the first Lentz step, and
1/2.2e-308 = 4.5e307 is past the ~1.3e300 where compensated multiplication’s
Dekker 2^27+1 splitter overflows to infinity - so every continued-fraction lane
came back NaN. expint takes the fraction for x >= 1, which is exactly where it
failed.
Same defect and same fix as Complex, and as the erf/erfc tail in this crate:
a sentinel only has to be negligible as a floor, but this one also has to
survive being inverted.
Source§fn use_series(z: V) -> <V as GenericVector>::Mask
fn use_series(z: V) -> <V as GenericVector>::Mask
Source§impl<V: ScalarValue> FloatConsts for Compensated<V>
impl<V: ScalarValue> FloatConsts for Compensated<V>
Source§const EULER_GAMMA: Self
const EULER_GAMMA: Self
Source§const PI_SQUARED: Self
const PI_SQUARED: Self
$\pi^2$Source§const FRAC_1_SQRT_2: Self
const FRAC_1_SQRT_2: Self
$1/\sqrt{2}$Source§const FRAC_1_SQRT_3: Self
const FRAC_1_SQRT_3: Self
$1/\sqrt{3}$Source§const FRAC_1_SQRT_5: Self
const FRAC_1_SQRT_5: Self
$1/\sqrt{5}$Source§const FRAC_1_SQRT_PI: Self
const FRAC_1_SQRT_PI: Self
$1/\sqrt{\pi}$Source§const FRAC_1_SQRT_SQRT_PI: Self
const FRAC_1_SQRT_SQRT_PI: Self
$\pi^{-1/4}$, the normalization of the Hermite functionsSource§const FRAC_2_SQRT_PI: Self
const FRAC_2_SQRT_PI: Self
$2/\sqrt{\pi}$Source§const FRAC_SQRT_PI_2: Self
const FRAC_SQRT_PI_2: Self
$\sqrt{\pi}/2$Source§const FRAC_1_SQRT_TAU: Self
const FRAC_1_SQRT_TAU: Self
$1/\sqrt{2\pi}$Source§const FRAC_PI_180: Self
const FRAC_PI_180: Self
$\pi/180$Source§const FRAC_180_PI: Self
const FRAC_180_PI: Self
$180/\pi$Source§const NINE_LN_9_HI: Self
const NINE_LN_9_HI: Self
$9 \ln 9$, the high word of the two-word split used by the Stirling shift in
thermite-special’s Poisson kernel. $9 \ln 9 \approx 19.8$ sits in an exponent, where
one rounding of it is about 8 ulp of the density.Source§const NINE_LN_9_LO: Self
const NINE_LN_9_LO: Self
$9 \ln 9$ minus NINE_LN_9_HI, exactly. Format-dependent like the epsilons.Source§const FRAC_LN_PI_2: Self
const FRAC_LN_PI_2: Self
$\frac{1}{2}\ln \pi$Source§const FRAC_LN_TAU_2: Self
const FRAC_LN_TAU_2: Self
$\frac{1}{2}\ln 2\pi$, the constant term of the Stirling series for $\ln \Gamma$Source§const SQRT_EPSILON: Self
const SQRT_EPSILON: Self
$\sqrt{\varepsilon}$)Source§const FOURTH_ROOT_EPSILON: Self
const FOURTH_ROOT_EPSILON: Self
$\sqrt[4]{\varepsilon}$)Source§const SQRT_FRAC_PI_2: Self
const SQRT_FRAC_PI_2: Self
$\sqrt{\pi/2}$Source§const FRAC_1_PHI: Self
const FRAC_1_PHI: Self
$1/\varphi = \varphi - 1$, the 1D golden-ratio low-discrepancy incrementSource§const FRAC_1_PHI_SQUARED: Self
const FRAC_1_PHI_SQUARED: Self
$1/\varphi^2$Source§const GOLDEN_ANGLE: Self
const GOLDEN_ANGLE: Self
$2\pi/\varphi^2 = \pi(3 - \sqrt{5})$ in radians Read moreSource§const FRAC_NEG_1_E: Self
const FRAC_NEG_1_E: Self
$-1/e$Source§const FRAC_2PI_3: Self
const FRAC_2PI_3: Self
$2\pi/3$Source§const FRAC_3PI_4: Self
const FRAC_3PI_4: Self
$3\pi/4$Source§const FRAC_4PI_3: Self
const FRAC_4PI_3: Self
$4\pi/3$, the volume of the unit sphereSource§const FRAC_1_4PI: Self
const FRAC_1_4PI: Self
$1/(4\pi)$, the density of the uniform distribution on the sphereSource§const FRAC_1_TAU: Self
const FRAC_1_TAU: Self
$1/(2\pi)$Source§const PI_MINUS_3: Self
const PI_MINUS_3: Self
$\pi - 3$Source§const FOUR_MINUS_PI: Self
const FOUR_MINUS_PI: Self
$4 - \pi$Source§const FRAC_1_CBRT_PI: Self
const FRAC_1_CBRT_PI: Self
$1/\sqrt[3]{\pi}$Source§const FRAC_1_SQRT_E: Self
const FRAC_1_SQRT_E: Self
$1/\sqrt{e} = e^{-1/2}$Source§const GELFOND_SCHNEIDER: Self
const GELFOND_SCHNEIDER: Self
$2^{\sqrt{2}}$, the Gelfond-Schneider constant (also called Hilbert’s number)Source§const FRAC_1_LN_PHI: Self
const FRAC_1_LN_PHI: Self
$1/\ln \varphi$Source§const FRAC_1_EULER_GAMMA: Self
const FRAC_1_EULER_GAMMA: Self
$1/\gamma$Source§const EULER_GAMMA_SQUARED: Self
const EULER_GAMMA_SQUARED: Self
$\gamma^2$Source§const EXTREME_VALUE_SKEWNESS: Self
const EXTREME_VALUE_SKEWNESS: Self
$12\sqrt{6}\,\zeta(3)/\pi^3$, the skewness of the extreme value distributionSource§const RAYLEIGH_SKEWNESS: Self
const RAYLEIGH_SKEWNESS: Self
$2\sqrt{\pi}(\pi-3)/(4-\pi)^{3/2}$, the skewness of the Rayleigh distributionSource§const RAYLEIGH_KURTOSIS_EXCESS: Self
const RAYLEIGH_KURTOSIS_EXCESS: Self
$-(6\pi^2 - 24\pi + 16)/(4-\pi)^2$, the excess kurtosis of the Rayleigh distributionSource§const RAYLEIGH_KURTOSIS: Self
const RAYLEIGH_KURTOSIS: Self
$3 - (6\pi^2 - 24\pi + 16)/(4-\pi)^2$, the kurtosis of the Rayleigh distribution Read moreSource§const FEIGENBAUM_DELTA: Self
const FEIGENBAUM_DELTA: Self
$\delta$Source§const PLASTIC_RATIO: Self
const PLASTIC_RATIO: Self
$\rho$, the real root of $x^3 = x + 1$Source§const FRAC_1_PLASTIC_RATIO: Self
const FRAC_1_PLASTIC_RATIO: Self
$1/\rho$, the first increment of the 2D R2 low-discrepancy sequence Read moreSource§const FRAC_1_PLASTIC_RATIO_SQUARED: Self
const FRAC_1_PLASTIC_RATIO_SQUARED: Self
$1/\rho^2$, the second R2 incrementSource§const LEMNISCATE: Self
const LEMNISCATE: Self
$\varpi = \pi G = 2\int_0^1 dt/\sqrt{1-t^4}$Source§const OMEGA: Self
const OMEGA: Self
$\Omega$, the solution of $\Omega e^{\Omega} = 1$, i.e. $W(1)$Source§const LAPLACE_LIMIT: Self
const LAPLACE_LIMIT: Self
$x e^{\sqrt{1+x^2}} / (1 + \sqrt{1+x^2}) = 1$Source§const ERDOS_BORWEIN: Self
const ERDOS_BORWEIN: Self
$E = \sum_{k=1}^{\infty} 1/(2^k - 1)$Source§const NIVEN: Self
const NIVEN: Self
$1 + \sum_{k=2}^{\infty} (1 - 1/\zeta(k))$, the average maximum prime exponentSource§const SOLDNER: Self
const SOLDNER: Self
$\mu$, the positive root of the logarithmic integral $\mathrm{li}(x)$Source§const FRANSEN_ROBINSON: Self
const FRANSEN_ROBINSON: Self
$\int_0^{\infty} dx/\Gamma(x)$Source§const GOLOMB_DICKMAN: Self
const GOLOMB_DICKMAN: Self
$\lambda = \int_0^1 e^{\mathrm{li}(t)}\,dt$Source§const TWIN_PRIME: Self
const TWIN_PRIME: Self
$C_2 = \prod_{p \ge 3} (1 - 1/(p-1)^2)$Source§impl<E: ScalarValue + FloatElement> FloatElement for Compensated<E>
impl<E: ScalarValue + FloatElement> FloatElement for Compensated<E>
Source§const HAS_INFINITY: bool = E::HAS_INFINITY
const HAS_INFINITY: bool = E::HAS_INFINITY
Source§const HAS_SIGNED_ZERO: bool = E::HAS_SIGNED_ZERO
const HAS_SIGNED_ZERO: bool = E::HAS_SIGNED_ZERO
Source§const HAS_SUBNORMALS: bool = E::HAS_SUBNORMALS
const HAS_SUBNORMALS: bool = E::HAS_SUBNORMALS
Source§type ConstInt<const N: LargeInt> = <E as ScalarValue>::CompensatedConstInt<N>
type ConstInt<const N: LargeInt> = <E as ScalarValue>::CompensatedConstInt<N>
Source§type ConstRatio<const N: LargeInt, const D: LargeInt> = <E as ScalarValue>::CompensatedConstRatio<N, D>
type ConstRatio<const N: LargeInt, const D: LargeInt> = <E as ScalarValue>::CompensatedConstRatio<N, D>
fn sqrt(this: Self) -> Self
fn floor(this: Self) -> Self
fn ceil(this: Self) -> Self
fn trunc(this: Self) -> Self
fn next_up(this: Self) -> Self
fn next_down(this: Self) -> Self
Source§fn try_from_int(value: LargeInt) -> Option<Self>
fn try_from_int(value: LargeInt) -> Option<Self>
fn try_from_ratio(n: LargeInt, d: LargeInt) -> Option<Self>
fn from_int(value: i64) -> Self
fn from_ratio(n: i64, d: i64) -> Self
fn fract(value: Self) -> Self
Source§impl<V: CompensatedFloatVector> FloatVector for Compensated<V>
impl<V: CompensatedFloatVector> FloatVector for Compensated<V>
Source§type ExtendedPrecision = Compensated<V>
type ExtendedPrecision = Compensated<V>
Don’t use Compensated if you need to go higher precision than it provides.
If you absolutely must, use CastVector to convert to a higher-precision type.
Source§const NEG_INFINITY: Self
const NEG_INFINITY: Self
-infinity represented in this vector type.Source§const HAS_APPROX_RCP: bool = false
const HAS_APPROX_RCP: bool = false
true if the backend has a hardware approximate-reciprocal
instruction (e.g. rcpps on x86). When false, rcp
falls back to a full IEEE division and provides no speed advantage
over Self::ONE / self.Source§const HAS_APPROX_RSQRT: bool = false
const HAS_APPROX_RSQRT: bool = false
true if the backend has a hardware approximate-reciprocal-square-root
instruction (e.g. rsqrtps on x86). When false, rsqrt
falls back to Self::ONE / self.sqrt().Source§fn is_infinite(self) -> Self::Mask
fn is_infinite(self) -> Self::Mask
Source§fn is_finite(self) -> Self::Mask
fn is_finite(self) -> Self::Mask
Source§fn is_zero_or_subnormal(self) -> Self::Mask
fn is_zero_or_subnormal(self) -> Self::Mask
Source§fn is_normal(self) -> Self::Mask
fn is_normal(self) -> Self::Mask
Source§fn is_subnormal(self) -> Self::Mask
fn is_subnormal(self) -> Self::Mask
Source§fn floor(self) -> Self
fn floor(self) -> Self
Source§fn ceil(self) -> Self
fn ceil(self) -> Self
Source§fn round(self) -> Self
fn round(self) -> Self
Source§fn trunc(self) -> Self
fn trunc(self) -> Self
Source§fn mul_sign(self, sign: Self) -> Self
fn mul_sign(self, sign: Self) -> Self
self * sign.signum(), multiplying the sign bits.Source§fn signed_zero(self) -> Self
fn signed_zero(self) -> Self
self, i.e.: only the sign bit is set.Source§fn next_up(self) -> Self
fn next_up(self) -> Self
Source§fn next_down(self) -> Self
fn next_down(self) -> Self
Source§unsafe fn block_autovectorization(&mut self)
unsafe fn block_autovectorization(&mut self)
Source§fn sqrt_m(self, src: Self, mask: Self::Mask) -> Self
fn sqrt_m(self, src: Self, mask: Self::Mask) -> Self
Source§fn rsqrt_c(self, mask: Self::Mask) -> Self
fn rsqrt_c(self, mask: Self::Mask) -> Self
Source§fn rsqrt_m(self, src: Self, mask: Self::Mask) -> Self
fn rsqrt_m(self, src: Self, mask: Self::Mask) -> Self
Source§fn rsqrt_z(self, mask: Self::Mask) -> Self
fn rsqrt_z(self, mask: Self::Mask) -> Self
Source§fn rcp_c(self, mask: Self::Mask) -> Self
fn rcp_c(self, mask: Self::Mask) -> Self
1 / self. Read moreSource§fn rcp_m(self, src: Self, mask: Self::Mask) -> Self
fn rcp_m(self, src: Self, mask: Self::Mask) -> Self
1 / self. Read moreSource§fn rcp_z(self, mask: Self::Mask) -> Self
fn rcp_z(self, mask: Self::Mask) -> Self
1 / self. Read moreSource§fn floor_c(self, mask: Self::Mask) -> Self
fn floor_c(self, mask: Self::Mask) -> Self
Source§fn floor_m(self, src: Self, mask: Self::Mask) -> Self
fn floor_m(self, src: Self, mask: Self::Mask) -> Self
Source§fn floor_z(self, mask: Self::Mask) -> Self
fn floor_z(self, mask: Self::Mask) -> Self
Source§fn ceil_c(self, mask: Self::Mask) -> Self
fn ceil_c(self, mask: Self::Mask) -> Self
ceil when mask is true, returns self where false.Source§fn ceil_m(self, src: Self, mask: Self::Mask) -> Self
fn ceil_m(self, src: Self, mask: Self::Mask) -> Self
ceil with src using mask.Source§fn ceil_z(self, mask: Self::Mask) -> Self
fn ceil_z(self, mask: Self::Mask) -> Self
ceil masked (zeroed where mask is false).Source§fn round_c(self, mask: Self::Mask) -> Self
fn round_c(self, mask: Self::Mask) -> Self
Source§fn round_m(self, src: Self, mask: Self::Mask) -> Self
fn round_m(self, src: Self, mask: Self::Mask) -> Self
Source§fn round_z(self, mask: Self::Mask) -> Self
fn round_z(self, mask: Self::Mask) -> Self
Source§fn trunc_c(self, mask: Self::Mask) -> Self
fn trunc_c(self, mask: Self::Mask) -> Self
trunc when mask is true, returns self where false.Source§fn trunc_m(self, src: Self, mask: Self::Mask) -> Self
fn trunc_m(self, src: Self, mask: Self::Mask) -> Self
trunc with src using mask.Source§fn trunc_z(self, mask: Self::Mask) -> Self
fn trunc_z(self, mask: Self::Mask) -> Self
trunc masked (zeroed where mask is false).Source§fn fract_c(self, mask: Self::Mask) -> Self
fn fract_c(self, mask: Self::Mask) -> Self
self - self.trunc(). Read moreSource§fn fract_m(self, src: Self, mask: Self::Mask) -> Self
fn fract_m(self, src: Self, mask: Self::Mask) -> Self
self - self.trunc(). Read moreSource§fn fract_z(self, mask: Self::Mask) -> Self
fn fract_z(self, mask: Self::Mask) -> Self
self - self.trunc(). Read moreSource§fn signed_zero_c(self, mask: Self::Mask) -> Self
fn signed_zero_c(self, mask: Self::Mask) -> Self
self, i.e.: only the sign bit is set.
Computes signed_zero when mask is true, returns self where false.Source§fn signed_zero_m(self, src: Self, mask: Self::Mask) -> Self
fn signed_zero_m(self, src: Self, mask: Self::Mask) -> Self
self, i.e.: only the sign bit is set.
Merges signed_zero with src using mask.Source§fn signed_zero_z(self, mask: Self::Mask) -> Self
fn signed_zero_z(self, mask: Self::Mask) -> Self
self, i.e.: only the sign bit is set.
Computes signed_zero masked (zeroed where mask is false).Source§fn next_up_c(self, mask: Self::Mask) -> Self
fn next_up_c(self, mask: Self::Mask) -> Self
next_up when mask is true, returns self where false.Source§fn next_up_m(self, src: Self, mask: Self::Mask) -> Self
fn next_up_m(self, src: Self, mask: Self::Mask) -> Self
next_up with src using mask.Source§fn next_up_z(self, mask: Self::Mask) -> Self
fn next_up_z(self, mask: Self::Mask) -> Self
next_up masked (zeroed where mask is false).Source§fn next_down_c(self, mask: Self::Mask) -> Self
fn next_down_c(self, mask: Self::Mask) -> Self
next_down when mask is true, returns self where false.Source§fn next_down_m(self, src: Self, mask: Self::Mask) -> Self
fn next_down_m(self, src: Self, mask: Self::Mask) -> Self
next_down with src using mask.Source§fn next_down_z(self, mask: Self::Mask) -> Self
fn next_down_z(self, mask: Self::Mask) -> Self
next_down masked (zeroed where mask is false).Source§fn mul_sign_c(self, mask: Self::Mask, rhs: Self) -> Self
fn mul_sign_c(self, mask: Self::Mask, rhs: Self) -> Self
self * sign.signum(), multiplying the sign bits.
Computes mul_sign when mask is true, returns self where false.Source§fn mul_sign_m(self, src: Self, mask: Self::Mask, rhs: Self) -> Self
fn mul_sign_m(self, src: Self, mask: Self::Mask, rhs: Self) -> Self
Source§fn mul_sign_z(self, mask: Self::Mask, rhs: Self) -> Self
fn mul_sign_z(self, mask: Self::Mask, rhs: Self) -> Self
self * sign.signum(), multiplying the sign bits.
Computes mul_sign masked (zeroed where mask is false).Source§fn mix(self, a: Self, b: Self) -> Self
fn mix(self, a: Self, b: Self) -> Self
a and b by self, where self is typically in the range [0, 1]. Read moreSource§fn one_minus_sq(self) -> Self
fn one_minus_sq(self) -> Self
$1 - x^2$ accurately, avoiding the cancellation a naive 1 - self * self
suffers as self approaches ±1 (where the result is small but self * self is near 1). Read moreSource§fn with_bits<const N: usize, K>(
_values: [Self; N],
_kernel: K,
) -> Option<<K as AsFloatVectorWithBitsKernel<Self, N>>::Output>where
K: AsFloatVectorWithBitsKernel<Self, N>,
fn with_bits<const N: usize, K>(
_values: [Self; N],
_kernel: K,
) -> Option<<K as AsFloatVectorWithBitsKernel<Self, N>>::Output>where
K: AsFloatVectorWithBitsKernel<Self, N>,
Source§impl<V: CompensatedFloatVector> GenericSelectable for Compensated<V>
impl<V: CompensatedFloatVector> GenericSelectable for Compensated<V>
Source§type SelectableMask = <V as GenericSelectable>::SelectableMask
type SelectableMask = <V as GenericSelectable>::SelectableMask
Self.Source§fn select<M>(mask: M, t: Self, f: Self) -> Selfwhere
Self::SelectableMask: CastMask<M>,
fn select<M>(mask: M, t: Self, f: Self) -> Selfwhere
Self::SelectableMask: CastMask<M>,
t where mask is true, otherwise
from f.Source§impl<V: CompensatedFloatVector> GenericVector for Compensated<V>
impl<V: CompensatedFloatVector> GenericVector for Compensated<V>
Source§unsafe fn load_deinterleaved<const M: usize>(
ptr: *const Self::Element,
) -> [Self; M]
unsafe fn load_deinterleaved<const M: usize>( ptr: *const Self::Element, ) -> [Self; M]
A Compensated element is #[repr(C)] over two floats (value, error),
so M interleaved Compensated streams are exactly 2 * M
interleaved float streams, precisely a grouped problem with TAIL = 1
(see StreamGroup). This hands M straight to the inner vector’s
GenericVector::load_deinterleaved_grouped (a NEON LD2/LD3/LD4,
or a shuffle network on x86), for any M: no dispatch ladder, no
scalar fallback.
Source§unsafe fn store_interleaved<const M: usize>(
ptr: *mut Self::Element,
values: [Self; M],
)
unsafe fn store_interleaved<const M: usize>( ptr: *mut Self::Element, values: [Self; M], )
The exact inverse of load_deinterleaved.
Source§const HAS_NATIVE_ALIGN: bool = V::HAS_NATIVE_ALIGN
const HAS_NATIVE_ALIGN: bool = V::HAS_NATIVE_ALIGN
align is a native cross-register instruction rather
than the generic shuffle-and-blend fallback, forwarded from
Register::HAS_NATIVE_ALIGN. Read moreSource§type Element = Compensated<<V as GenericVector>::Element>
type Element = Compensated<<V as GenericVector>::Element>
Source§type Lanes = <V as GenericVector>::Lanes
type Lanes = <V as GenericVector>::Lanes
Source§type Unsigned = <V as GenericVector>::Unsigned
type Unsigned = <V as GenericVector>::Unsigned
Source§type Signed = <V as GenericVector>::Signed
type Signed = <V as GenericVector>::Signed
Source§type Mask = <V as GenericVector>::Mask
type Mask = <V as GenericVector>::Mask
Source§fn swizzle(self, other: Self, indices: Self::Unsigned) -> Self
fn swizzle(self, other: Self, indices: Self::Unsigned) -> Self
[self, other] by a live index
vector: index i < LANES takes self[i], LANES <= i < 2*LANES
takes other[i - LANES]. Read moreSource§fn new<const N: usize>(value: [Self::Element; N]) -> Self
fn new<const N: usize>(value: [Self::Element; N]) -> Self
Source§fn into_array(self) -> GenericArray<Self::Element, Self::Lanes>
fn into_array(self) -> GenericArray<Self::Element, Self::Lanes>
GenericArray. Read moreSource§fn splat(value: Self::Element) -> Self
fn splat(value: Self::Element) -> Self
Source§fn single(value: Self::Element) -> Self
fn single(value: Self::Element) -> Self
Source§unsafe fn load(ptr: *const Self::Element) -> Self
unsafe fn load(ptr: *const Self::Element) -> Self
Source§fn interleave_by<const GROUP: usize>(self, other: Self) -> (Self, Self)
fn interleave_by<const GROUP: usize>(self, other: Self) -> (Self, Self)
GROUP consecutive elements move
as a unit and are never split. GROUP == 1 is interleave; GROUP == 2
is the complex interleave - lo == [a.c0, b.c0, a.c1, b.c1, ...] over the low half of the
groups, hi over the high half - which lowers to the doubled-element unpack (unpacklo_pd +
permute2f128 on AVX2, zip on NEON) rather than a general permute. The primitive for
complex FFT transposes and any group-structured SIMD. GROUP must divide LANES. Read moreSource§fn deinterleave_by<const GROUP: usize>(self, other: Self) -> (Self, Self)
fn deinterleave_by<const GROUP: usize>(self, other: Self) -> (Self, Self)
interleave_by - group-granularity de-interleave.Source§fn interleave_radix<const N: usize>(inputs: [Self; N]) -> [Self; N]
fn interleave_radix<const N: usize>(inputs: [Self; N]) -> [Self; N]
N interleave: the generic sibling of interleave
(N == 2). Treats the N inputs as one contiguous N * LANES span and
gives out with concat(out)[q * N + r] == inputs[r].extract(q). Read moreSource§fn deinterleave_radix<const N: usize>(inputs: [Self; N]) -> [Self; N]
fn deinterleave_radix<const N: usize>(inputs: [Self; N]) -> [Self; N]
interleave_radix - radix-N
de-interleave: out[r].extract(q) == concat(inputs)[q * N + r].Source§fn deinterleave_radix_by<const N: usize, const GROUP: usize>(
inputs: [Self; N],
) -> [Self; N]
fn deinterleave_radix_by<const N: usize, const GROUP: usize>( inputs: [Self; N], ) -> [Self; N]
N de-interleave: the two-axis unification of
deinterleave_radix (GROUP == 1) and
deinterleave_by (N == 2). Each vector is viewed
as LANES / GROUP groups of GROUP consecutive elements; out[r] group q
is the (q * N + r)-th group of the concatenated input sequence, each group
moving as a unit. Read moreSource§fn interleave_radix_by<const N: usize, const GROUP: usize>(
inputs: [Self; N],
) -> [Self; N]
fn interleave_radix_by<const N: usize, const GROUP: usize>( inputs: [Self; N], ) -> [Self; N]
deinterleave_radix_by -
group-granularity radix-N interleave. For the square case it is the same
(self-inverse) register-array transpose.Source§unsafe fn load_m(src: Self, mask: Self::Mask, ptr: *const Self::Element) -> Self
unsafe fn load_m(src: Self, mask: Self::Mask, ptr: *const Self::Element) -> Self
Source§unsafe fn load_z(mask: Self::Mask, ptr: *const Self::Element) -> Self
unsafe fn load_z(mask: Self::Mask, ptr: *const Self::Element) -> Self
Source§unsafe fn load_unaligned(ptr: *const Self::Element) -> Self
unsafe fn load_unaligned(ptr: *const Self::Element) -> Self
Source§unsafe fn load_streaming(ptr: *const Self::Element) -> Self
unsafe fn load_streaming(ptr: *const Self::Element) -> Self
Source§unsafe fn store(self, ptr: *mut Self::Element)
unsafe fn store(self, ptr: *mut Self::Element)
Source§unsafe fn store_masked(self, mask: Self::Mask, ptr: *mut Self::Element)
unsafe fn store_masked(self, mask: Self::Mask, ptr: *mut Self::Element)
true.
For lanes where the mask is false, the store is suppressed without panicking. Read moreSource§unsafe fn store_unaligned(self, ptr: *mut Self::Element)
unsafe fn store_unaligned(self, ptr: *mut Self::Element)
Source§unsafe fn store_streaming(self, ptr: *mut Self::Element)
unsafe fn store_streaming(self, ptr: *mut Self::Element)
Source§unsafe fn lookup_unchecked(
values: &[Self::Element],
indices: Self::Unsigned,
) -> Self
unsafe fn lookup_unchecked( values: &[Self::Element], indices: Self::Unsigned, ) -> Self
Source§fn broadcast<const I: usize>(self) -> Self
fn broadcast<const I: usize>(self) -> Self
Source§fn broadcastv(self, idx: usize) -> Self
fn broadcastv(self, idx: usize) -> Self
Source§fn extract<const I: usize>(self) -> Self::Element
fn extract<const I: usize>(self) -> Self::Element
I. Read moreSource§fn extractv(self, idx: usize) -> Self::Element
fn extractv(self, idx: usize) -> Self::Element
idx. Read moreSource§fn insert<const I: usize>(self, value: Self::Element) -> Self
fn insert<const I: usize>(self, value: Self::Element) -> Self
I. Read moreSource§fn insertv(self, idx: usize, value: Self::Element) -> Self
fn insertv(self, idx: usize, value: Self::Element) -> Self
idx. Read moreSource§fn swap_bytes(self) -> Self
fn swap_bytes(self) -> Self
Source§fn zz(self, mask: Self::Mask) -> Self
fn zz(self, mask: Self::Mask) -> Self
Source§fn nz(self, mask: Self::Mask) -> Self
fn nz(self, mask: Self::Mask) -> Self
Source§fn compress(self, mask: Self::Mask) -> Self
fn compress(self, mask: Self::Mask) -> Self
compress): gather the lanes where mask is true
into the low lanes, preserving their relative order. The unselected lanes
are kept (not zeroed) and packed into the high lanes, also in order - a
stable partition of the vector by mask. Read moreSource§fn compress_z(self, mask: Self::Mask) -> Self
fn compress_z(self, mask: Self::Mask) -> Self
Source§fn compress_m(self, src: Self, mask: Self::Mask) -> Self
fn compress_m(self, src: Self, mask: Self::Mask) -> Self
Source§fn align<const OFFSET: usize>(self, other: Self) -> Self
fn align<const OFFSET: usize>(self, other: Self) -> Self
palignr family): the window of LANES
lanes starting at lane OFFSET of the concatenation [self, other]
(self’s lanes first, then other’s). OFFSET == 0 returns self,
OFFSET == LANES returns other; in between, lanes spill from the tail
of self into the head of other. Read moreSource§fn map<F>(self, f: F) -> Self
fn map<F>(self, f: F) -> Self
Source§fn fold<F>(self, init: Self::Element, f: F) -> Self::Element
fn fold<F>(self, init: Self::Element, f: F) -> Self::Element
Source§fn reduce<F>(self, f: F) -> Self::Element
fn reduce<F>(self, f: F) -> Self::Element
Source§fn splat_m(src: Self, mask: Self::Mask, value: Self::Element) -> Self
fn splat_m(src: Self, mask: Self::Mask, value: Self::Element) -> Self
splat with src using mask.Source§fn splat_z(mask: Self::Mask, value: Self::Element) -> Self
fn splat_z(mask: Self::Mask, value: Self::Element) -> Self
splat masked (zeroed where mask is false).Source§fn broadcast_c<const I: usize>(self, mask: Self::Mask) -> Self
fn broadcast_c<const I: usize>(self, mask: Self::Mask) -> Self
broadcast when mask is true, returns self where false.Source§fn broadcast_m<const I: usize>(self, src: Self, mask: Self::Mask) -> Self
fn broadcast_m<const I: usize>(self, src: Self, mask: Self::Mask) -> Self
broadcast with src using mask.Source§fn broadcast_z<const I: usize>(self, mask: Self::Mask) -> Self
fn broadcast_z<const I: usize>(self, mask: Self::Mask) -> Self
broadcast masked (zeroed where mask is false).Source§fn broadcastv_c(self, mask: Self::Mask, idx: usize) -> Self
fn broadcastv_c(self, mask: Self::Mask, idx: usize) -> Self
Source§fn broadcastv_m(self, src: Self, mask: Self::Mask, idx: usize) -> Self
fn broadcastv_m(self, src: Self, mask: Self::Mask, idx: usize) -> Self
Source§fn broadcastv_z(self, mask: Self::Mask, idx: usize) -> Self
fn broadcastv_z(self, mask: Self::Mask, idx: usize) -> Self
Source§fn reverse_c(self, mask: Self::Mask) -> Self
fn reverse_c(self, mask: Self::Mask) -> Self
Source§fn reverse_m(self, src: Self, mask: Self::Mask) -> Self
fn reverse_m(self, src: Self, mask: Self::Mask) -> Self
Source§fn reverse_z(self, mask: Self::Mask) -> Self
fn reverse_z(self, mask: Self::Mask) -> Self
Source§fn swap_bytes_c(self, mask: Self::Mask) -> Self
fn swap_bytes_c(self, mask: Self::Mask) -> Self
Source§fn swap_bytes_m(self, src: Self, mask: Self::Mask) -> Self
fn swap_bytes_m(self, src: Self, mask: Self::Mask) -> Self
Source§fn swap_bytes_z(self, mask: Self::Mask) -> Self
fn swap_bytes_z(self, mask: Self::Mask) -> Self
Source§fn concat<INTO>(self, hi: Self) -> INTOwhere
INTO: ConcatVector<Self, Element = Self::Element>,
fn concat<INTO>(self, hi: Self) -> INTOwhere
INTO: ConcatVector<Self, Element = Self::Element>,
self as the lower half and hi as the upper half.Source§fn split<INTO>(self) -> (INTO, INTO)
fn split<INTO>(self) -> (INTO, INTO)
Source§fn extend<INTO>(self) -> INTOwhere
INTO: ExtendVector<Self, Element = Self::Element>,
fn extend<INTO>(self) -> INTOwhere
INTO: ExtendVector<Self, Element = Self::Element>,
Source§fn narrow<INTO>(self) -> INTO
fn narrow<INTO>(self) -> INTO
Source§fn align_slice(
slice: &[Self::Element],
) -> (&[Self::Element], &[Self], &[Self::Element])
fn align_slice( slice: &[Self::Element], ) -> (&[Self::Element], &[Self], &[Self::Element])
Source§fn align_slice_mut(
slice: &mut [Self::Element],
) -> (&mut [Self::Element], &mut [Self], &mut [Self::Element])
fn align_slice_mut( slice: &mut [Self::Element], ) -> (&mut [Self::Element], &mut [Self], &mut [Self::Element])
Source§fn from_slice(slice: &[Self::Element]) -> Self
fn from_slice(slice: &[Self::Element]) -> Self
Source§fn copy_to_slice(self, slice: &mut [Self::Element])
fn copy_to_slice(self, slice: &mut [Self::Element])
Source§fn iter_unaligned<'a>(
values: &'a [Self::Element],
) -> (Unaligned<'a, Self>, &'a [Self::Element])
fn iter_unaligned<'a>( values: &'a [Self::Element], ) -> (Unaligned<'a, Self>, &'a [Self::Element])
Source§fn iter_mut_unaligned<'a>(
values: &'a mut [Self::Element],
) -> (UnalignedMut<'a, Self>, &'a mut [Self::Element])
fn iter_mut_unaligned<'a>( values: &'a mut [Self::Element], ) -> (UnalignedMut<'a, Self>, &'a mut [Self::Element])
Source§fn stream_aligned_slice<'a>(
values: &'a [Self::Element],
) -> impl DoubleEndedIterator
fn stream_aligned_slice<'a>( values: &'a [Self::Element], ) -> impl DoubleEndedIterator
Source§fn stream_aligned_slice_mut<'a>(
values: &'a mut [Self::Element],
) -> impl DoubleEndedIterator
fn stream_aligned_slice_mut<'a>( values: &'a mut [Self::Element], ) -> impl DoubleEndedIterator
Source§fn gather<I>(slice: &[Self::Element], indices: I) -> Selfwhere
I: VectorIndices<Self>,
fn gather<I>(slice: &[Self::Element], indices: I) -> Selfwhere
I: VectorIndices<Self>,
Source§fn gather_or<I>(slice: &[Self::Element], indices: I, or: Self) -> Self
fn gather_or<I>(slice: &[Self::Element], indices: I, or: Self) -> Self
or if the index is out of bounds. Read moreSource§fn gather_or_zero<I>(slice: &[Self::Element], indices: I) -> Self
fn gather_or_zero<I>(slice: &[Self::Element], indices: I) -> Self
Source§fn gather_if<I>(
slice: &[Self::Element],
enable: Self::Mask,
indices: I,
or: Self,
) -> Selfwhere
I: VectorIndices<Self>,
Self::Mask: CastMask<<I as GenericVector>::Mask>,
Self::Element: Default,
fn gather_if<I>(
slice: &[Self::Element],
enable: Self::Mask,
indices: I,
or: Self,
) -> Selfwhere
I: VectorIndices<Self>,
Self::Mask: CastMask<<I as GenericVector>::Mask>,
Self::Element: Default,
or if the enable mask is
false OR if any index is out of bounds. Read moreSource§fn scatter<I>(self, slice: &mut [Self::Element], indices: I)
fn scatter<I>(self, slice: &mut [Self::Element], indices: I)
Source§fn scatter_if<I>(
self,
slice: &mut [Self::Element],
enable: Self::Mask,
indices: I,
)
fn scatter_if<I>( self, slice: &mut [Self::Element], enable: Self::Mask, indices: I, )
enable mask is true.
If the index is outside of the bounds of the provided slice, the write is suppressed without panicking.Source§unsafe fn load_deinterleaved_arrays<const M: usize, const C: usize>(
ptr: *const Self::Element,
) -> [[Self; C]; M]
unsafe fn load_deinterleaved_arrays<const M: usize, const C: usize>( ptr: *const Self::Element, ) -> [[Self; C]; M]
M interleaved AoS records of C components each and de-interleave
them: reads M * C * LANES contiguous elements, and out[j][c] holds
component c of record j
(out[j][c].extract(lane) == ptr[lane * M * C + j * C + c]). Read moreSource§unsafe fn store_interleaved_arrays<const M: usize, const C: usize>(
ptr: *mut Self::Element,
values: [[Self; C]; M],
)
unsafe fn store_interleaved_arrays<const M: usize, const C: usize>( ptr: *mut Self::Element, values: [[Self; C]; M], )
M records of C components and store them contiguously - the
exact inverse of
load_deinterleaved_arrays, with the
same lane-wise default. Read moreSource§unsafe fn load_deinterleaved_grouped<const M: usize, const TAIL: usize>(
ptr: *const Self::Element,
) -> [StreamGroup<Self, TAIL>; M]
unsafe fn load_deinterleaved_grouped<const M: usize, const TAIL: usize>( ptr: *const Self::Element, ) -> [StreamGroup<Self, TAIL>; M]
M interleaved composite streams of 1 + TAIL components each and
de-interleave them into M StreamGroups: reads
M * (TAIL + 1) * LANES contiguous elements, and group j’s
head/tail[c - 1] hold the de-interleaved components of composite
stream j. See StreamGroup for why the component count is a
separate const generic, and
Register::load_deinterleaved_grouped
for the register-level strategy. Read moreSource§unsafe fn store_interleaved_grouped<const M: usize, const TAIL: usize>(
ptr: *mut Self::Element,
values: [StreamGroup<Self, TAIL>; M],
)
unsafe fn store_interleaved_grouped<const M: usize, const TAIL: usize>( ptr: *mut Self::Element, values: [StreamGroup<Self, TAIL>; M], )
M StreamGroups and store them as a contiguous
array-of-structures - the exact inverse of
load_deinterleaved_grouped, with
the same lane-wise default and the same override expectations. Read moreSource§fn lookup(values: &[Self::Element], indices: Self::Unsigned) -> Self
fn lookup(values: &[Self::Element], indices: Self::Unsigned) -> Self
Source§fn first_element(self) -> Self::Element
fn first_element(self) -> Self::Element
Source§fn last_element(self) -> Self::Element
fn last_element(self) -> Self::Element
Source§fn prefix_mask(n: usize) -> Self::Mask
fn prefix_mask(n: usize) -> Self::Mask
Source§fn suffix_mask(n: usize) -> Self::Mask
fn suffix_mask(n: usize) -> Self::Mask
Source§fn cast<INTO>(self) -> INTOwhere
INTO: CastVector<Self>,
fn cast<INTO>(self) -> INTOwhere
INTO: CastVector<Self>,
as operator on the underlying scalar elements for in-range, finite
inputs. Read moreSource§fn fast_cast<INTO>(self) -> INTOwhere
INTO: CastVector<Self>,
fn fast_cast<INTO>(self) -> INTOwhere
INTO: CastVector<Self>,
Source§fn into_bits<INTO>(self) -> INTOwhere
INTO: BitCastVector<Self>,
fn into_bits<INTO>(self) -> INTOwhere
INTO: BitCastVector<Self>,
Source§fn saturating_cast<INTO>(self) -> INTOwhere
INTO: CastVector<Self>,
fn saturating_cast<INTO>(self) -> INTOwhere
INTO: CastVector<Self>,
Source§impl<V: HasIsa> HasIsa for Compensated<V>
impl<V: HasIsa> HasIsa for Compensated<V>
Source§impl<V: CompensatedFloatVector> Interleave for Compensated<V>
impl<V: CompensatedFloatVector> Interleave for Compensated<V>
Source§fn interleave(self, other: Self) -> (Self, Self)
fn interleave(self, other: Self) -> (Self, Self)
Source§fn deinterleave(self, other: Self) -> (Self, Self)
fn deinterleave(self, other: Self) -> (Self, Self)
interleave. Read moreSource§impl<V: ScalarValue> Mul for Compensated<V>
impl<V: ScalarValue> Mul for Compensated<V>
Source§impl<V: ScalarValue> Mul<V> for Compensated<V>
impl<V: ScalarValue> Mul<V> for Compensated<V>
Source§impl<V: Copy, A, B> MulAddAssignExt<A, B> for Compensated<V>where
Self: MulAddExt<A, B, Output = Self>,
impl<V: Copy, A, B> MulAddAssignExt<A, B> for Compensated<V>where
Self: MulAddExt<A, B, Output = Self>,
Source§fn mul_add_assign(&mut self, a: A, b: B)
fn mul_add_assign(&mut self, a: A, b: B)
Source§fn mul_sub_assign(&mut self, a: A, b: B)
fn mul_sub_assign(&mut self, a: A, b: B)
Source§fn nmul_add_assign(&mut self, a: A, b: B)
fn nmul_add_assign(&mut self, a: A, b: B)
Source§fn nmul_sub_assign(&mut self, a: A, b: B)
fn nmul_sub_assign(&mut self, a: A, b: B)
Source§fn mul_adde_assign(&mut self, a: A, b: B)
fn mul_adde_assign(&mut self, a: A, b: B)
Source§fn mul_sube_assign(&mut self, a: A, b: B)
fn mul_sube_assign(&mut self, a: A, b: B)
Source§fn nmul_adde_assign(&mut self, a: A, b: B)
fn nmul_adde_assign(&mut self, a: A, b: B)
Source§fn nmul_sube_assign(&mut self, a: A, b: B)
fn nmul_sube_assign(&mut self, a: A, b: B)
Source§impl<V: CompensatedFloatVector, A, B> MulAddAssignExtMasked<<V as GenericVector>::Mask, A, B> for Compensated<V>where
Compensated<V>: MulAddExt<A, B, Output = Self>,
impl<V: CompensatedFloatVector, A, B> MulAddAssignExtMasked<<V as GenericVector>::Mask, A, B> for Compensated<V>where
Compensated<V>: MulAddExt<A, B, Output = Self>,
Source§fn mul_add_assign_c(&mut self, mask: V::Mask, a: A, b: B)
fn mul_add_assign_c(&mut self, mask: V::Mask, a: A, b: B)
Source§fn mul_add_assign_m(&mut self, src: Self, mask: V::Mask, a: A, b: B)
fn mul_add_assign_m(&mut self, src: Self, mask: V::Mask, a: A, b: B)
Source§fn mul_add_assign_z(&mut self, mask: V::Mask, a: A, b: B)
fn mul_add_assign_z(&mut self, mask: V::Mask, a: A, b: B)
Source§fn mul_sub_assign_c(&mut self, mask: V::Mask, a: A, b: B)
fn mul_sub_assign_c(&mut self, mask: V::Mask, a: A, b: B)
Source§fn mul_sub_assign_m(&mut self, src: Self, mask: V::Mask, a: A, b: B)
fn mul_sub_assign_m(&mut self, src: Self, mask: V::Mask, a: A, b: B)
Source§fn mul_sub_assign_z(&mut self, mask: V::Mask, a: A, b: B)
fn mul_sub_assign_z(&mut self, mask: V::Mask, a: A, b: B)
Source§fn nmul_add_assign_c(&mut self, mask: V::Mask, a: A, b: B)
fn nmul_add_assign_c(&mut self, mask: V::Mask, a: A, b: B)
Source§fn nmul_add_assign_m(&mut self, src: Self, mask: V::Mask, a: A, b: B)
fn nmul_add_assign_m(&mut self, src: Self, mask: V::Mask, a: A, b: B)
Source§fn nmul_add_assign_z(&mut self, mask: V::Mask, a: A, b: B)
fn nmul_add_assign_z(&mut self, mask: V::Mask, a: A, b: B)
Source§fn nmul_sub_assign_c(&mut self, mask: V::Mask, a: A, b: B)
fn nmul_sub_assign_c(&mut self, mask: V::Mask, a: A, b: B)
Source§fn nmul_sub_assign_m(&mut self, src: Self, mask: V::Mask, a: A, b: B)
fn nmul_sub_assign_m(&mut self, src: Self, mask: V::Mask, a: A, b: B)
Source§fn nmul_sub_assign_z(&mut self, mask: V::Mask, a: A, b: B)
fn nmul_sub_assign_z(&mut self, mask: V::Mask, a: A, b: B)
Source§fn mul_adde_assign_c(&mut self, mask: V::Mask, a: A, b: B)
fn mul_adde_assign_c(&mut self, mask: V::Mask, a: A, b: B)
Source§fn mul_adde_assign_m(&mut self, src: Self, mask: V::Mask, a: A, b: B)
fn mul_adde_assign_m(&mut self, src: Self, mask: V::Mask, a: A, b: B)
Source§fn mul_adde_assign_z(&mut self, mask: V::Mask, a: A, b: B)
fn mul_adde_assign_z(&mut self, mask: V::Mask, a: A, b: B)
Source§fn mul_sube_assign_c(&mut self, mask: V::Mask, a: A, b: B)
fn mul_sube_assign_c(&mut self, mask: V::Mask, a: A, b: B)
Source§fn mul_sube_assign_m(&mut self, src: Self, mask: V::Mask, a: A, b: B)
fn mul_sube_assign_m(&mut self, src: Self, mask: V::Mask, a: A, b: B)
Source§fn mul_sube_assign_z(&mut self, mask: V::Mask, a: A, b: B)
fn mul_sube_assign_z(&mut self, mask: V::Mask, a: A, b: B)
Source§fn nmul_adde_assign_c(&mut self, mask: V::Mask, a: A, b: B)
fn nmul_adde_assign_c(&mut self, mask: V::Mask, a: A, b: B)
Source§fn nmul_adde_assign_m(&mut self, src: Self, mask: V::Mask, a: A, b: B)
fn nmul_adde_assign_m(&mut self, src: Self, mask: V::Mask, a: A, b: B)
Source§fn nmul_adde_assign_z(&mut self, mask: V::Mask, a: A, b: B)
fn nmul_adde_assign_z(&mut self, mask: V::Mask, a: A, b: B)
Source§fn nmul_sube_assign_c(&mut self, mask: V::Mask, a: A, b: B)
fn nmul_sube_assign_c(&mut self, mask: V::Mask, a: A, b: B)
Source§fn nmul_sube_assign_m(&mut self, src: Self, mask: V::Mask, a: A, b: B)
fn nmul_sube_assign_m(&mut self, src: Self, mask: V::Mask, a: A, b: B)
Source§fn nmul_sube_assign_z(&mut self, mask: V::Mask, a: A, b: B)
fn nmul_sube_assign_z(&mut self, mask: V::Mask, a: A, b: B)
Source§impl<V: ScalarValue> MulAddExt for Compensated<V>
impl<V: ScalarValue> MulAddExt for Compensated<V>
Source§const HAS_NATIVE_FMA: Tribool = tribool::True
const HAS_NATIVE_FMA: Tribool = tribool::True
True = fused single instructions, False =
definitely separate multiply and add, Indeterminate = decided at
runtime (e.g. the wasm relaxed-madd canary). Read moreSource§type Output = Compensated<V>
type Output = Compensated<V>
Source§fn mul_sub(self, b: Self, c: Self) -> Self::Output
fn mul_sub(self, b: Self, c: Self) -> Self::Output
Source§fn nmul_add(self, a: Self, b: Self) -> Self::Output
fn nmul_add(self, a: Self, b: Self) -> Self::Output
Source§fn nmul_sub(self, a: Self, b: Self) -> Self::Output
fn nmul_sub(self, a: Self, b: Self) -> Self::Output
Source§fn mul_adde(self, a: Self, b: Self) -> Self::Output
fn mul_adde(self, a: Self, b: Self) -> Self::Output
Source§fn mul_sube(self, a: Self, b: Self) -> Self::Output
fn mul_sube(self, a: Self, b: Self) -> Self::Output
Source§impl<V: ScalarValue> MulAddExt<Compensated<V>, V> for Compensated<V>
impl<V: ScalarValue> MulAddExt<Compensated<V>, V> for Compensated<V>
Source§const HAS_NATIVE_FMA: Tribool = tribool::True
const HAS_NATIVE_FMA: Tribool = tribool::True
True = fused single instructions, False =
definitely separate multiply and add, Indeterminate = decided at
runtime (e.g. the wasm relaxed-madd canary). Read moreSource§type Output = Compensated<V>
type Output = Compensated<V>
Source§fn mul_add(self, a: Self, b: V) -> Self::Output
fn mul_add(self, a: Self, b: V) -> Self::Output
Source§fn mul_sub(self, b: Self, c: V) -> Self::Output
fn mul_sub(self, b: Self, c: V) -> Self::Output
Source§fn nmul_add(self, a: Self, b: V) -> Self::Output
fn nmul_add(self, a: Self, b: V) -> Self::Output
Source§fn nmul_sub(self, a: Self, b: V) -> Self::Output
fn nmul_sub(self, a: Self, b: V) -> Self::Output
Source§fn mul_adde(self, a: Self, b: V) -> Self::Output
fn mul_adde(self, a: Self, b: V) -> Self::Output
Source§fn mul_sube(self, a: Self, b: V) -> Self::Output
fn mul_sube(self, a: Self, b: V) -> Self::Output
Source§impl<V: ScalarValue> MulAddExt<V> for Compensated<V>
impl<V: ScalarValue> MulAddExt<V> for Compensated<V>
Source§const HAS_NATIVE_FMA: Tribool = tribool::True
const HAS_NATIVE_FMA: Tribool = tribool::True
True = fused single instructions, False =
definitely separate multiply and add, Indeterminate = decided at
runtime (e.g. the wasm relaxed-madd canary). Read moreSource§type Output = Compensated<V>
type Output = Compensated<V>
Source§fn mul_add(self, b: V, c: Self) -> Self::Output
fn mul_add(self, b: V, c: Self) -> Self::Output
Source§fn mul_sub(self, b: V, c: Self) -> Self::Output
fn mul_sub(self, b: V, c: Self) -> Self::Output
Source§fn nmul_add(self, a: V, b: Self) -> Self::Output
fn nmul_add(self, a: V, b: Self) -> Self::Output
Source§fn nmul_sub(self, a: V, b: Self) -> Self::Output
fn nmul_sub(self, a: V, b: Self) -> Self::Output
Source§fn mul_adde(self, a: V, b: Self) -> Self::Output
fn mul_adde(self, a: V, b: Self) -> Self::Output
Source§fn mul_sube(self, a: V, b: Self) -> Self::Output
fn mul_sube(self, a: V, b: Self) -> Self::Output
Source§impl<V: CompensatedFloatVector, A, B> MulAddExtMasked<<V as GenericVector>::Mask, A, B> for Compensated<V>where
Compensated<V>: MulAddExt<A, B, Output = Self>,
impl<V: CompensatedFloatVector, A, B> MulAddExtMasked<<V as GenericVector>::Mask, A, B> for Compensated<V>where
Compensated<V>: MulAddExt<A, B, Output = Self>,
Source§fn mul_add_c(self, mask: V::Mask, a: A, b: B) -> Self
fn mul_add_c(self, mask: V::Mask, a: A, b: B) -> Self
Source§fn mul_add_m(self, src: Self, mask: V::Mask, a: A, b: B) -> Self
fn mul_add_m(self, src: Self, mask: V::Mask, a: A, b: B) -> Self
Source§fn mul_add_z(self, mask: V::Mask, a: A, b: B) -> Self
fn mul_add_z(self, mask: V::Mask, a: A, b: B) -> Self
Source§fn mul_sub_c(self, mask: V::Mask, a: A, b: B) -> Self
fn mul_sub_c(self, mask: V::Mask, a: A, b: B) -> Self
Source§fn mul_sub_m(self, src: Self, mask: V::Mask, a: A, b: B) -> Self
fn mul_sub_m(self, src: Self, mask: V::Mask, a: A, b: B) -> Self
Source§fn mul_sub_z(self, mask: V::Mask, a: A, b: B) -> Self
fn mul_sub_z(self, mask: V::Mask, a: A, b: B) -> Self
Source§fn nmul_add_c(self, mask: V::Mask, a: A, b: B) -> Self
fn nmul_add_c(self, mask: V::Mask, a: A, b: B) -> Self
Source§fn nmul_add_m(self, src: Self, mask: V::Mask, a: A, b: B) -> Self
fn nmul_add_m(self, src: Self, mask: V::Mask, a: A, b: B) -> Self
Source§fn nmul_add_z(self, mask: V::Mask, a: A, b: B) -> Self
fn nmul_add_z(self, mask: V::Mask, a: A, b: B) -> Self
Source§fn nmul_sub_c(self, mask: V::Mask, a: A, b: B) -> Self
fn nmul_sub_c(self, mask: V::Mask, a: A, b: B) -> Self
Source§fn nmul_sub_m(self, src: Self, mask: V::Mask, a: A, b: B) -> Self
fn nmul_sub_m(self, src: Self, mask: V::Mask, a: A, b: B) -> Self
Source§fn nmul_sub_z(self, mask: V::Mask, a: A, b: B) -> Self
fn nmul_sub_z(self, mask: V::Mask, a: A, b: B) -> Self
Source§fn mul_adde_c(self, mask: V::Mask, a: A, b: B) -> Self
fn mul_adde_c(self, mask: V::Mask, a: A, b: B) -> Self
Source§fn mul_adde_m(self, src: Self, mask: V::Mask, a: A, b: B) -> Self
fn mul_adde_m(self, src: Self, mask: V::Mask, a: A, b: B) -> Self
Source§fn mul_adde_z(self, mask: V::Mask, a: A, b: B) -> Self
fn mul_adde_z(self, mask: V::Mask, a: A, b: B) -> Self
Source§fn mul_sube_c(self, mask: V::Mask, a: A, b: B) -> Self
fn mul_sube_c(self, mask: V::Mask, a: A, b: B) -> Self
Source§fn mul_sube_m(self, src: Self, mask: V::Mask, a: A, b: B) -> Self
fn mul_sube_m(self, src: Self, mask: V::Mask, a: A, b: B) -> Self
Source§fn mul_sube_z(self, mask: V::Mask, a: A, b: B) -> Self
fn mul_sube_z(self, mask: V::Mask, a: A, b: B) -> Self
Source§fn nmul_adde_c(self, mask: V::Mask, a: A, b: B) -> Self
fn nmul_adde_c(self, mask: V::Mask, a: A, b: B) -> Self
Source§fn nmul_adde_m(self, src: Self, mask: V::Mask, a: A, b: B) -> Self
fn nmul_adde_m(self, src: Self, mask: V::Mask, a: A, b: B) -> Self
Source§fn nmul_adde_z(self, mask: V::Mask, a: A, b: B) -> Self
fn nmul_adde_z(self, mask: V::Mask, a: A, b: B) -> Self
Source§fn nmul_sube_c(self, mask: V::Mask, a: A, b: B) -> Self
fn nmul_sube_c(self, mask: V::Mask, a: A, b: B) -> Self
Source§fn nmul_sube_m(self, src: Self, mask: V::Mask, a: A, b: B) -> Self
fn nmul_sube_m(self, src: Self, mask: V::Mask, a: A, b: B) -> Self
Source§fn nmul_sube_z(self, mask: V::Mask, a: A, b: B) -> Self
fn nmul_sube_z(self, mask: V::Mask, a: A, b: B) -> Self
Source§impl<V: Copy, T> MulAssign<T> for Compensated<V>where
Self: Mul<T, Output = Self>,
impl<V: Copy, T> MulAssign<T> for Compensated<V>where
Self: Mul<T, Output = Self>,
Source§fn mul_assign(&mut self, rhs: T)
fn mul_assign(&mut self, rhs: T)
*= operation. Read moreSource§impl<V: CompensatedFloatVector, Rhs> MulAssignMasked<<V as GenericVector>::Mask, Rhs> for Compensated<V>where
Compensated<V>: Mul<Rhs, Output = Self>,
impl<V: CompensatedFloatVector, Rhs> MulAssignMasked<<V as GenericVector>::Mask, Rhs> for Compensated<V>where
Compensated<V>: Mul<Rhs, Output = Self>,
Source§fn mul_assign_c(&mut self, mask: V::Mask, rhs: Rhs)
fn mul_assign_c(&mut self, mask: V::Mask, rhs: Rhs)
Source§fn mul_assign_m(&mut self, src: Self, mask: V::Mask, rhs: Rhs)
fn mul_assign_m(&mut self, src: Self, mask: V::Mask, rhs: Rhs)
Source§fn mul_assign_z(&mut self, mask: V::Mask, rhs: Rhs)
fn mul_assign_z(&mut self, mask: V::Mask, rhs: Rhs)
MulAssign masked (zeroed where mask is false).Source§impl<V: CompensatedFloatVector, Rhs> MulMasked<<V as GenericVector>::Mask, Rhs> for Compensated<V>where
Compensated<V>: Mul<Rhs, Output = Self>,
impl<V: CompensatedFloatVector, Rhs> MulMasked<<V as GenericVector>::Mask, Rhs> for Compensated<V>where
Compensated<V>: Mul<Rhs, Output = Self>,
Source§impl<V: ScalarValue> Neg for Compensated<V>
impl<V: ScalarValue> Neg for Compensated<V>
Source§impl<V: CompensatedFloatVector> NegMasked<<V as GenericVector>::Mask> for Compensated<V>
impl<V: CompensatedFloatVector> NegMasked<<V as GenericVector>::Mask> for Compensated<V>
Source§impl<V: CompensatedFloatVector> NewVector<Compensated<<V as GenericVector>::Element>, <V as GenericVector>::Lanes> for Compensated<V>
impl<V: CompensatedFloatVector> NewVector<Compensated<<V as GenericVector>::Element>, <V as GenericVector>::Lanes> for Compensated<V>
Source§type New<T: NewConst<Compensated<V::Element>, V::Lanes>> = CompensatedNewImpl
type New<T: NewConst<Compensated<V::Element>, V::Lanes>> = CompensatedNewImpl
T, the type exposing the constructed vector
constant via VectorValue.Source§impl<V: CompensatedFloatVector> NumericVector for Compensated<V>
impl<V: CompensatedFloatVector> NumericVector for Compensated<V>
Source§fn sort_by<O: SortOrder>(self) -> Self
fn sort_by<O: SortOrder>(self) -> Self
Lane sorts are keyed on the lexicographic (value, error) order, which
is exactly cmp_lt here, so the key IS the comparison and ties are
deterministic. Each compare-exchange derives one routing mask from it
and moves both components through the same permutation and select
(thermite::sort::sort_lanes_by_key).
Source§fn to_signed_integer(self) -> Self::Signed
fn to_signed_integer(self) -> Self::Signed
as semantics -
round toward zero, saturating at the bounds, NaN to zero. Read moreSource§fn from_signed_integer(v: Self::Signed) -> Self
fn from_signed_integer(v: Self::Signed) -> Self
as semantics. Read moreSource§fn to_unsigned_integer(self) -> Self::Unsigned
fn to_unsigned_integer(self) -> Self::Unsigned
as semantics.
See to_signed_integer.Source§fn from_unsigned_integer(v: Self::Unsigned) -> Self
fn from_unsigned_integer(v: Self::Unsigned) -> Self
as semantics.
See from_signed_integer.Source§fn bitonic_clean_by<O: SortOrder>(self) -> Self
fn bitonic_clean_by<O: SortOrder>(self) -> Self
O order - one that rises then
falls, or a rotation of one. Read moreSource§fn is_zero(self) -> Self::Mask
fn is_zero(self) -> Self::Mask
Source§fn is_all_zero(self) -> bool
fn is_all_zero(self) -> bool
Source§fn clamp(self, min: Self, max: Self) -> Self
fn clamp(self, min: Self, max: Self) -> Self
Source§fn min_element(self) -> Self::Element
fn min_element(self) -> Self::Element
Source§fn max_element(self) -> Self::Element
fn max_element(self) -> Self::Element
Source§fn sum_elements(self) -> Self::Element
fn sum_elements(self) -> Self::Element
Source§fn prod_elements(self) -> Self::Element
fn prod_elements(self) -> Self::Element
Source§fn prefix_sum(self) -> Self
fn prefix_sum(self) -> Self
out[i] = self[0] + .. + self[i]. Read moreSource§fn reverse_prefix_sum(self) -> Self
fn reverse_prefix_sum(self) -> Self
out[i] = self[i] + .. + self[LANES-1].Source§fn prefix_min(self) -> Self
fn prefix_min(self) -> Self
out[i] = min(self[0], .., self[i]). Read moreSource§fn prefix_max(self) -> Self
fn prefix_max(self) -> Self
out[i] = max(self[0], .., self[i]). Read moreSource§fn reverse_prefix_min(self) -> Self
fn reverse_prefix_min(self) -> Self
out[i] = min(self[i], .., self[LANES-1]). Read moreSource§fn reverse_prefix_max(self) -> Self
fn reverse_prefix_max(self) -> Self
out[i] = max(self[i], .., self[LANES-1]). Read moreSource§fn indexed() -> Self
fn indexed() -> Self
[0, 1, 2, ..., LANES-1]. Read moreSource§fn min_c(self, mask: Self::Mask, rhs: Self) -> Self
fn min_c(self, mask: Self::Mask, rhs: Self) -> Self
min when mask is true, returns self where false.Source§fn min_z(self, mask: Self::Mask, rhs: Self) -> Self
fn min_z(self, mask: Self::Mask, rhs: Self) -> Self
min masked (zeroed where mask is false).Source§fn max_c(self, mask: Self::Mask, rhs: Self) -> Self
fn max_c(self, mask: Self::Mask, rhs: Self) -> Self
max when mask is true, returns self where false.Source§fn max_z(self, mask: Self::Mask, rhs: Self) -> Self
fn max_z(self, mask: Self::Mask, rhs: Self) -> Self
max masked (zeroed where mask is false).Source§fn scale(self, factor: Self::Element) -> Self
fn scale(self, factor: Self::Element) -> Self
Source§fn scale_c(self, mask: Self::Mask, factor: Self::Element) -> Self
fn scale_c(self, mask: Self::Mask, factor: Self::Element) -> Self
Source§fn scale_m(self, src: Self, mask: Self::Mask, factor: Self::Element) -> Self
fn scale_m(self, src: Self, mask: Self::Mask, factor: Self::Element) -> Self
Source§fn scale_z(self, mask: Self::Mask, factor: Self::Element) -> Self
fn scale_z(self, mask: Self::Mask, factor: Self::Element) -> Self
Source§fn pairwise_sum(lo: Self, hi: Self) -> Self
fn pairwise_sum(lo: Self, hi: Self) -> Self
Source§fn relaxed_pairwise_sum(lo: Self, hi: Self) -> Self
fn relaxed_pairwise_sum(lo: Self, hi: Self) -> Self
pairwise_sum, but may return a relaxed (implementation-defined)
lane ordering for performance. Treat this as if randomly shuffling the result of
pairwise_sum, with better performance than pairwise_sum. Read moreSource§fn min_max_element(self) -> (Self::Element, Self::Element)
fn min_max_element(self) -> (Self::Element, Self::Element)
Source§fn arg_minmax(self) -> (usize, usize)
fn arg_minmax(self) -> (usize, usize)
Source§fn fast_to_signed_integer(self) -> Self::Signed
fn fast_to_signed_integer(self) -> Self::Signed
to_signed_integer, but may relax IEEE corner
cases (out-of-range and NaN inputs) for speed. Defaults to the exact form.Source§fn fast_to_unsigned_integer(self) -> Self::Unsigned
fn fast_to_unsigned_integer(self) -> Self::Unsigned
to_unsigned_integer, but may relax IEEE
corner cases. Defaults to the exact form.Source§fn sort(self) -> Self
fn sort(self) -> Self
sort_by::<Ascending>.Source§fn bitonic_clean(self) -> Self
fn bitonic_clean(self) -> Self
bitonic_clean_by::<Ascending>.Source§impl<V: PartialEq> PartialEq for Compensated<V>
impl<V: PartialEq> PartialEq for Compensated<V>
Source§impl<V: PartialOrd> PartialOrd for Compensated<V>
impl<V: PartialOrd> PartialOrd for Compensated<V>
Source§impl<V: CompensatedFloatVector> PartialOrdVector for Compensated<V>
impl<V: CompensatedFloatVector> PartialOrdVector for Compensated<V>
Source§fn group_by_value(self, valid: Self::Mask) -> ValueGroups<Self> ⓘ
fn group_by_value(self, valid: Self::Mask) -> ValueGroups<Self> ⓘ
valid into groups of equal value. Read moreSource§impl<V: ScalarValue> Product for Compensated<V>
impl<V: ScalarValue> Product for Compensated<V>
Source§impl<V: ScalarValue> Rem for Compensated<V>
impl<V: ScalarValue> Rem for Compensated<V>
Source§impl<V: ScalarValue> Rem<V> for Compensated<V>
impl<V: ScalarValue> Rem<V> for Compensated<V>
Source§impl<V: Copy, T> RemAssign<T> for Compensated<V>where
Self: Rem<T, Output = Self>,
impl<V: Copy, T> RemAssign<T> for Compensated<V>where
Self: Rem<T, Output = Self>,
Source§fn rem_assign(&mut self, rhs: T)
fn rem_assign(&mut self, rhs: T)
%= operation. Read moreSource§impl<V: CompensatedFloatVector, Rhs> RemAssignMasked<<V as GenericVector>::Mask, Rhs> for Compensated<V>where
Compensated<V>: Rem<Rhs, Output = Self>,
impl<V: CompensatedFloatVector, Rhs> RemAssignMasked<<V as GenericVector>::Mask, Rhs> for Compensated<V>where
Compensated<V>: Rem<Rhs, Output = Self>,
Source§fn rem_assign_c(&mut self, mask: V::Mask, rhs: Rhs)
fn rem_assign_c(&mut self, mask: V::Mask, rhs: Rhs)
Source§fn rem_assign_m(&mut self, src: Self, mask: V::Mask, rhs: Rhs)
fn rem_assign_m(&mut self, src: Self, mask: V::Mask, rhs: Rhs)
Source§fn rem_assign_z(&mut self, mask: V::Mask, rhs: Rhs)
fn rem_assign_z(&mut self, mask: V::Mask, rhs: Rhs)
RemAssign masked (zeroed where mask is false).Source§impl<V: CompensatedFloatVector, Rhs> RemMasked<<V as GenericVector>::Mask, Rhs> for Compensated<V>where
Compensated<V>: Rem<Rhs, Output = Self>,
impl<V: CompensatedFloatVector, Rhs> RemMasked<<V as GenericVector>::Mask, Rhs> for Compensated<V>where
Compensated<V>: Rem<Rhs, Output = Self>,
Source§impl<E: ScalarValue + SignedElement> SignedElement for Compensated<E>
impl<E: ScalarValue + SignedElement> SignedElement for Compensated<E>
Source§impl<V: CompensatedFloatVector> SignedVector for Compensated<V>
impl<V: CompensatedFloatVector> SignedVector for Compensated<V>
Source§const MIN_POSITIVE: Self
const MIN_POSITIVE: Self
Source§fn signum(self) -> Self
fn signum(self) -> Self
Source§fn is_positive(self) -> Self::Mask
fn is_positive(self) -> Self::Mask
Source§fn is_negative(self) -> Self::Mask
fn is_negative(self) -> Self::Mask
Source§fn select_negative(self, if_neg: Self, if_pos: Self) -> Self
fn select_negative(self, if_neg: Self, if_pos: Self) -> Self
if_neg and if_pos.Source§fn copysign(self, sign: Self) -> Self
fn copysign(self, sign: Self) -> Self
Source§fn abs_c(self, mask: Self::Mask) -> Self
fn abs_c(self, mask: Self::Mask) -> Self
abs when mask is true, returns self where false.Source§fn abs_z(self, mask: Self::Mask) -> Self
fn abs_z(self, mask: Self::Mask) -> Self
abs masked (zeroed where mask is false).Source§fn copysign_c(self, mask: Self::Mask, sign: Self) -> Self
fn copysign_c(self, mask: Self::Mask, sign: Self) -> Self
copysign when mask is true, returns self where false.Source§fn copysign_m(self, src: Self, mask: Self::Mask, sign: Self) -> Self
fn copysign_m(self, src: Self, mask: Self::Mask, sign: Self) -> Self
copysign with src using mask.Source§fn copysign_z(self, mask: Self::Mask, sign: Self) -> Self
fn copysign_z(self, mask: Self::Mask, sign: Self) -> Self
copysign masked (zeroed where mask is false).Source§impl<V: CompensatedFloatVector> SortKey<Compensated<V>> for Compensated<V>
The lane-sort key: strictly-before under the lexicographic (value, error)
order, i.e. cmp_lt. See thermite::sort::SortKey for why this is a
static trait method and not a closure.
impl<V: CompensatedFloatVector> SortKey<Compensated<V>> for Compensated<V>
The lane-sort key: strictly-before under the lexicographic (value, error)
order, i.e. cmp_lt. See thermite::sort::SortKey for why this is a
static trait method and not a closure.
Source§impl<V> SpecializedCoreMath<Compensated<<V as GenericVector>::Element>> for Compensated<V>where
V: RealMathWithPolicy + CompensatedFloatVector,
impl<V> SpecializedCoreMath<Compensated<<V as GenericVector>::Element>> for Compensated<V>where
V: RealMathWithPolicy + CompensatedFloatVector,
fn inverse_sqrt<P: Policy>(self) -> Self
Source§fn poly_n_primal<P, N>(self, coeffs: &GenericArray<Self::Primal, N>) -> Selfwhere
P: Policy,
N: ArrayLength,
fn poly_n_primal<P, N>(self, coeffs: &GenericArray<Self::Primal, N>) -> Selfwhere
P: Policy,
N: ArrayLength,
CoreMath::poly_n_primal. Read moreSource§fn poly_rev_n_primal<P, N>(self, coeffs: &GenericArray<Self::Primal, N>) -> Selfwhere
P: Policy,
N: ArrayLength,
fn poly_rev_n_primal<P, N>(self, coeffs: &GenericArray<Self::Primal, N>) -> Selfwhere
P: Policy,
N: ArrayLength,
CoreMath::poly_rev_n_primal. Read moreSource§fn poly_primal<P>(self, coeffs: &[Self::Primal]) -> Selfwhere
P: Policy,
fn poly_primal<P>(self, coeffs: &[Self::Primal]) -> Selfwhere
P: Policy,
CoreMath::poly_primal. Read moreSource§fn poly_rev_primal<P>(self, coeffs: &[Self::Primal]) -> Selfwhere
P: Policy,
fn poly_rev_primal<P>(self, coeffs: &[Self::Primal]) -> Selfwhere
P: Policy,
CoreMath::poly_rev_primal. Read moreSource§fn mul_add_primal<P>(self, m: Self, a: Self::Primal) -> Selfwhere
P: Policy,
fn mul_add_primal<P>(self, m: Self, a: Self::Primal) -> Selfwhere
P: Policy,
self * m + a. Read moreSource§fn nmul_add_primal<P>(self, m: Self, a: Self::Primal) -> Selfwhere
P: Policy,
fn nmul_add_primal<P>(self, m: Self, a: Self::Primal) -> Selfwhere
P: Policy,
Source§fn difference_of_products<P>(self, b: Self, c: Self, d: Self) -> Selfwhere
P: Policy,
fn difference_of_products<P>(self, b: Self, c: Self, d: Self) -> Selfwhere
P: Policy,
Source§fn sum_of_products<P>(self, b: Self, c: Self, d: Self) -> Selfwhere
P: Policy,
fn sum_of_products<P>(self, b: Self, c: Self, d: Self) -> Selfwhere
P: Policy,
difference_of_products.fn poly<P>(self, coeffs: &[E]) -> Selfwhere
P: Policy,
fn poly_rev<P>(self, coeffs: &[E]) -> Selfwhere
P: Policy,
fn poly_n<P, const N: usize>(self, coeffs: &[E; N]) -> Selfwhere
P: Policy,
fn poly_rev_n<P, const N: usize>(self, coeffs: &[E; N]) -> Selfwhere
P: Policy,
fn poly_rational_n<P, const N: usize, const D: usize>(
self,
numerator: &[E; N],
denominator: &[E; D],
) -> Selfwhere
P: Policy,
fn approx_reciprocal<P>(self) -> Selfwhere
P: Policy,
fn approx_div<P>(self, rhs: Self) -> Selfwhere
P: Policy,
Source§fn approx_div_sqrt<P>(self, denom: Self) -> Selfwhere
P: Policy,
fn approx_div_sqrt<P>(self, denom: Self) -> Selfwhere
P: Policy,
$a/\sqrt{b}$, spelled a.approx_div_sqrt(b), as one kernel rather than a
divide bolted onto a square root. Read moreSource§fn inv_sum_inv_direct<P, const N: usize>(values: [Self; N], numer: Self) -> Selfwhere
P: Policy,
fn inv_sum_inv_direct<P, const N: usize>(values: [Self; N], numer: Self) -> Selfwhere
P: Policy,
numer / sum(1/x_i) the direct way, backing both
harmonic_mean and inv_sum_inv, which
differ only in whether the numerator is N or 1. Read morefn harmonic_mean_n<P, const N: usize>(values: [Self; N]) -> Selfwhere
P: Policy,
fn inv_sum_inv_n<P, const N: usize>(values: [Self; N]) -> Selfwhere
P: Policy,
Source§fn inv_sum_inv<P>(values: &[Self]) -> Selfwhere
P: Policy,
fn inv_sum_inv<P>(values: &[Self]) -> Selfwhere
P: Policy,
$1/\sum_i 1/x_i$ over a runtime-length slice. Read moreSource§fn harmonic_mean<P>(values: &[Self]) -> Selfwhere
P: Policy,
fn harmonic_mean<P>(values: &[Self]) -> Selfwhere
P: Policy,
$N/\sum_i 1/x_i$ over a runtime-length slice. Read morefn reciprocal_adde<P>(self, a: Self) -> Selfwhere
P: Policy,
fn powi<P>(self, e: i32) -> Selfwhere
P: Policy,
fn powic<P, const N: i32>(self) -> Selfwhere
P: Policy,
fn powiv<P>(self, e: Self::Signed) -> Selfwhere
P: Policy,
impl<V> SpecializedPrimalMath<Compensated<<V as GenericVector>::Element>> for Compensated<V>where
V: RealMathWithPolicy + CompensatedFloatVector,
Source§impl<V> SpecializedRealMath<Compensated<<V as GenericVector>::Element>> for Compensated<V>where
V: RealMathWithPolicy + CompensatedFloatVector,
impl<V> SpecializedRealMath<Compensated<<V as GenericVector>::Element>> for Compensated<V>where
V: RealMathWithPolicy + CompensatedFloatVector,
fn atan2<P: Policy>(self, x: Self) -> Self
fn tolerance<P>() -> Selfwhere
P: Policy,
fn to_degrees<P>(self) -> Selfwhere
P: Policy,
Source§fn entr<P>(self) -> Selfwhere
P: Policy,
fn entr<P>(self) -> Selfwhere
P: Policy,
-x ln x, 0 at zero, -inf below it. The zero case rides the shared guard, since
ln 0 is the same 0 * -inf that xlogy exists to absorb. Only the negative branch
is specific to this one.Source§fn rel_entr<P>(self, y: Self) -> Selfwhere
P: Policy,
fn rel_entr<P>(self, y: Self) -> Selfwhere
P: Policy,
x ln(x/y), the Kullback-Leibler summand, extended by 0 at x = 0, y >= 0 and
+inf everywhere else in the plane.fn to_radians<P>(self) -> Selfwhere
P: Policy,
fn wrap_angle<P>(self) -> Selfwhere
P: Policy,
fn angle_diff<P>(self, other: Self) -> Selfwhere
P: Policy,
fn step<P>(self, t: Self) -> Selfwhere
P: Policy,
fn lerp<P>(self, a: Self, b: Self) -> Selfwhere
P: Policy,
fn rescale<P>(
self,
in_min: Self,
in_max: Self,
out_min: Self,
out_max: Self,
) -> Selfwhere
P: Policy,
fn logaddexp<P>(self, other: Self) -> Selfwhere
P: Policy,
fn logmean<P>(self, other: Self) -> Selfwhere
P: Policy,
fn logsumexp<P>(values: &[Self]) -> Selfwhere
P: Policy,
fn logsumexp_n<P, const N: usize>(values: [Self; N]) -> Selfwhere
P: Policy,
fn logsubexp<P>(self, other: Self) -> Selfwhere
P: Policy,
fn smoothstep<P, const N: usize>(self, edges: Option<(Self, Self)>) -> Selfwhere
P: Policy,
fn smoothstep_derivative<P, const N: usize>(
self,
edges: Option<(Self, Self)>,
) -> Selfwhere
P: Policy,
fn inverse_smoothstep<P, const N: usize>(
y: Self,
edges: Option<(Self, Self)>,
) -> Selfwhere
P: Policy,
fn smooth_interpolator<P>(x: Self, edges: Option<(Self, Self)>, k: Self) -> Selfwhere
P: Policy,
fn smooth_interpolator_inverse<P>(
y: Self,
edges: Option<(Self, Self)>,
k: Self,
) -> Selfwhere
P: Policy,
Source§impl<V> SpecializedRealPrimalMath<Compensated<<V as GenericVector>::Element>> for Compensated<V>
Available on crate feature special only.
impl<V> SpecializedRealPrimalMath<Compensated<<V as GenericVector>::Element>> for Compensated<V>
special only.Source§fn langevin_d<P: Policy>(self) -> (Self, Self)
fn langevin_d<P: Policy>(self) -> (Self, Self)
L(x) and L'(x). The derivative falls out of the value’s own intermediates
on both branches (see generic::langevin), so there is no default here that
would recompute it.Source§fn spherical_harmonics_d_with<P, const L: usize, const N: usize>(
table: &ShTable<Self, N>,
x: Self,
y: Self,
z: Self,
out: &mut [Self; N],
ddx: &mut [Self; N],
ddy: &mut [Self; N],
ddz: &mut [Self; N],
)where
P: Policy,
fn spherical_harmonics_d_with<P, const L: usize, const N: usize>(
table: &ShTable<Self, N>,
x: Self,
y: Self,
z: Self,
out: &mut [Self; N],
ddx: &mut [Self; N],
ddy: &mut [Self; N],
ddz: &mut [Self; N],
)where
P: Policy,
spherical_harmonics_with
plus the ambient Cartesian gradients, from a prebuilt table.Source§fn spherical_harmonics_d<P, const L: usize, const N: usize, const CS: bool>(
x: Self,
y: Self,
z: Self,
out: &mut [Self; N],
ddx: &mut [Self; N],
ddy: &mut [Self; N],
ddz: &mut [Self; N],
)where
P: Policy,
fn spherical_harmonics_d<P, const L: usize, const N: usize, const CS: bool>(
x: Self,
y: Self,
z: Self,
out: &mut [Self; N],
ddx: &mut [Self; N],
ddy: &mut [Self; N],
ddz: &mut [Self; N],
)where
P: Policy,
spherical_harmonics plus the ambient Cartesian
gradient of every harmonic. See sh_d_impl for the gradient semantics.Source§fn zernike_basis_d<P, const L: usize, const NORM: u8, const N: usize>(
x: Self,
y: Self,
out: &mut [Self; N],
ddx: &mut [Self; N],
ddy: &mut [Self; N],
)where
P: Policy,
fn zernike_basis_d<P, const L: usize, const NORM: u8, const N: usize>(
x: Self,
y: Self,
out: &mut [Self; N],
ddx: &mut [Self; N],
ddy: &mut [Self; N],
)where
P: Policy,
zernike_basis plus the Cartesian
gradient of every mode. See zernike_basis_d_impl for the algorithm.fn softplus_d<P>(self, k: Self, rcp_k: Self) -> (Self, Self)where
P: Policy,
fn gelu_d<P>(self, alpha: Self) -> (Self, Self)where
P: Policy,
fn swish_d<P>(self, beta: Self) -> (Self, Self)where
P: Policy,
fn algebraic_sigmoid_d_n<P, const N: usize>(self) -> (Self, Self)where
P: Policy,
Source§fn algebraic_sigmoid_d<P>(self, n: u32) -> (Self, Self)where
P: Policy,
fn algebraic_sigmoid_d<P>(self, n: u32) -> (Self, Self)where
P: Policy,
algebraic_sigmoid_d_n.fn algebraic_swish_d<P>(self) -> (Self, Self)where
P: Policy,
Source§impl<V> SpecializedRealSpecialMath<Compensated<<V as GenericVector>::Element>> for Compensated<V>
Available on crate feature special only.
impl<V> SpecializedRealSpecialMath<Compensated<<V as GenericVector>::Element>> for Compensated<V>
special only.fn erfinv<P: Policy>(self) -> Self
fn probit<P: Policy>(self) -> Self
fn lgamma_r<P: Policy>(self) -> (Self, Self)
fn langevin<P: Policy>(self) -> Self
fn langevin_1m<P: Policy>(self) -> Self
fn inv_langevin<P: Policy>(self) -> Self
fn inv_langevin_1m<P: Policy>(self) -> Self
Source§fn log_ndtr<P>(self) -> Selfwhere
P: Policy,
fn log_ndtr<P>(self) -> Selfwhere
P: Policy,
ln(ndtr(x)), finite wherever x is: ln(erfc) in the moderate region, erfcx
with -x^2/2 kept in the log domain in the tail, ln_1p of the complement on the
right. See generic::ndtr. Element types without a Weideman table
(Compensated) inherit their direct erfcx’s range, about |x| < 37.Source§fn logerfc<P>(self) -> Selfwhere
P: Policy,
fn logerfc<P>(self) -> Selfwhere
P: Policy,
ln(erfc(x)) on the same construction as log_ndtr, with the
tail on the right and ln_1p(+-erf(|x|)) on the bounded side.Source§fn log_ndtr_with_deriv<P>(self) -> (Self, Self)where
P: Policy,
fn log_ndtr_with_deriv<P>(self) -> (Self, Self)where
P: Policy,
(ln ndtr(x), phi(x)/ndtr(x)), the value with the inverse Mills ratio, which is its
derivative. What inv_log_ndtr’s Newton and Dual both need.Source§fn inv_log_ndtr<P>(self) -> Selfwhere
P: Policy,
fn inv_log_ndtr<P>(self) -> Selfwhere
P: Policy,
Source§fn inv_digamma<P>(self) -> Selfwhere
P: Policy,
fn inv_digamma<P>(self) -> Selfwhere
P: Policy,
x > 0 with digamma(x) = y. Newton on digamma with trigamma, and the
Stirling fixed point above y = 6.Source§fn wright_omega<P>(self) -> Selfwhere
P: Policy,
fn wright_omega<P>(self) -> Selfwhere
P: Policy,
w > 0 with w + ln w = x. Newton, and the Lagrange series below x = -7.Source§fn fresnel_c<P>(self) -> Selfwhere
P: Policy,
fn fresnel_c<P>(self) -> Selfwhere
P: Policy,
C(x) alone. Unlike the Airy singles this is genuinely the pair with one half
dead: the two share the argument reduction, the phase and both auxiliaries, so
only one Chebyshev series and one reconstruction fall out. They are pure, so
they do fall out.Source§fn sinint<P>(self) -> Selfwhere
P: Policy,
fn sinint<P>(self) -> Selfwhere
P: Policy,
Si(x) alone. See fresnel_c for what is and is not saved.Source§fn bessel_i_ratio<P>(self, _nu: Self) -> Selfwhere
P: Policy,
fn bessel_i_ratio<P>(self, _nu: Self) -> Selfwhere
P: Policy,
Source§fn inv_bessel_i_ratio<P>(self, _nu: Self) -> Selfwhere
P: Policy,
fn inv_bessel_i_ratio<P>(self, _nu: Self) -> Selfwhere
P: Policy,
kappa with I_nu(kappa) / I_{nu-1}(kappa) = r. Newton on the ratio. Same
arrangement as bessel_i_ratio.Source§fn bessel_i_ratio_1m<P>(self, _nu: Self) -> Selfwhere
P: Policy,
fn bessel_i_ratio_1m<P>(self, _nu: Self) -> Selfwhere
P: Policy,
1 - I_nu(x) / I_{nu-1}(x), accurate where the ratio is within an ulp of 1.Source§fn inv_bessel_i_ratio_1m<P>(self, _nu: Self) -> Selfwhere
P: Policy,
fn inv_bessel_i_ratio_1m<P>(self, _nu: Self) -> Selfwhere
P: Policy,
kappa with 1 - I_nu(kappa) / I_{nu-1}(kappa) = t, the complement form.Source§fn bessel_ratio<P, F>(self, nu: Self) -> Selfwhere
P: Policy,
F: BesselRatioFamily,
fn bessel_ratio<P, F>(self, nu: Self) -> Selfwhere
P: Policy,
F: BesselRatioFamily,
bessel::ratio::<F>(nu): see BesselRatioFamily.Source§fn inv_bessel_ratio<P, F>(self, nu: Self) -> Selfwhere
P: Policy,
F: BesselRatioFamily,
fn inv_bessel_ratio<P, F>(self, nu: Self) -> Selfwhere
P: Policy,
F: BesselRatioFamily,
inv_bessel::ratio::<F>(r).Source§fn bessel_ratio_1m<P, F>(self, nu: Self) -> Selfwhere
P: Policy,
F: BesselRatioFamily,
fn bessel_ratio_1m<P, F>(self, nu: Self) -> Selfwhere
P: Policy,
F: BesselRatioFamily,
bessel_ratio_1m::<F>(nu).Source§fn inv_bessel_ratio_1m<P, F>(self, nu: Self) -> Selfwhere
P: Policy,
F: BesselRatioFamily,
fn inv_bessel_ratio_1m<P, F>(self, nu: Self) -> Selfwhere
P: Policy,
F: BesselRatioFamily,
inv_bessel_ratio_1m::<F>(t).Source§fn gauss_legendre<P>(self, n: u32) -> (Self, Self)where
P: Policy,
fn gauss_legendre<P>(self, n: u32) -> (Self, Self)where
P: Policy,
(x_k, w_k) of the n-point Gauss-Legendre rule, the index k per lane. See
generic::quadrature.Source§fn gauss_hermite<P>(self, n: u32) -> (Self, Self)where
P: Policy,
fn gauss_hermite<P>(self, n: u32) -> (Self, Self)where
P: Policy,
(x_k, w_k) of the n-point Gauss-Hermite rule, the index k per lane.Source§fn gauss_laguerre<P>(self, alpha: Self, n: u32) -> (Self, Self)where
P: Policy,
fn gauss_laguerre<P>(self, alpha: Self, n: u32) -> (Self, Self)where
P: Policy,
(x_k, w_k) of the n-point Gauss-Laguerre rule with weight x^alpha e^{-x}, the
index k and alpha per lane.Source§fn agm<P>(a: Self, b: Self) -> Selfwhere
P: Policy,
fn agm<P>(a: Self, b: Self) -> Selfwhere
P: Policy,
AGM(a, b), sharing its recurrence with the complete elliptic integrals.Source§fn pochhammer<P>(z: Self, m: Self) -> Selfwhere
P: Policy,
fn pochhammer<P>(z: Self, m: Self) -> Selfwhere
P: Policy,
zeta(s) - 1, the primitive of the pair: the Euler-Maclaurin sum’s leading term is
the 1, so omitting it is exact where subtracting it afterwards is not. Read moreSource§fn jacobi_elliptic<P>(u: Self, k: Self) -> (Self, Self, Self)where
P: Policy,
fn jacobi_elliptic<P>(u: Self, k: Self) -> (Self, Self, Self)where
P: Policy,
(sn, cn, dn) by the arithmetic-only descending Landen transformation. Read moreSource§fn inv_boxcox<P>(self, lambda: Self) -> Selfwhere
P: Policy,
fn inv_boxcox<P>(self, lambda: Self) -> Selfwhere
P: Policy,
Source§fn inv_boxcox_1p<P>(self, lambda: Self) -> Selfwhere
P: Policy,
fn inv_boxcox_1p<P>(self, lambda: Self) -> Selfwhere
P: Policy,
Source§fn yeo_johnson<P>(self, lambda: Self) -> Selfwhere
P: Policy,
fn yeo_johnson<P>(self, lambda: Self) -> Selfwhere
P: Policy,
Source§fn inv_yeo_johnson<P>(self, lambda: Self) -> Selfwhere
P: Policy,
fn inv_yeo_johnson<P>(self, lambda: Self) -> Selfwhere
P: Policy,
fn gelu<P>(self, alpha: Self) -> Selfwhere
P: Policy,
fn swish<P>(self, beta: Self) -> Selfwhere
P: Policy,
fn algebraic_sigmoid_n<P, const N: usize>(self) -> Selfwhere
P: Policy,
Source§fn algebraic_sigmoid<P>(self, n: u32) -> Selfwhere
P: Policy,
fn algebraic_sigmoid<P>(self, n: u32) -> Selfwhere
P: Policy,
algebraic_sigmoid_n, same arithmetic.fn algebraic_swish<P>(self) -> Selfwhere
P: Policy,
fn gaussian_integral<P>(x0: Self, x1: Self, a: Self, c: Self) -> Selfwhere
P: Policy,
Source§fn spherical_harmonics_table<P, const L: usize, const N: usize, const CS: bool>(
table: &mut ShTable<Self::Primal, N>,
)where
P: Policy,
fn spherical_harmonics_table<P, const L: usize, const N: usize, const CS: bool>(
table: &mut ShTable<Self::Primal, N>,
)where
P: Policy,
Source§impl<V> SpecializedSpatialMath<Compensated<<V as GenericVector>::Element>> for Compensated<V>where
V: RealMathWithPolicy + CompensatedFloatVector,
impl<V> SpecializedSpatialMath<Compensated<<V as GenericVector>::Element>> for Compensated<V>where
V: RealMathWithPolicy + CompensatedFloatVector,
fn l2_norm_squared<P: Policy>(self) -> Self
fn l2_norm<P: Policy>(self) -> Self
fn l1_norm<P: Policy>(self) -> Self
fn hypot_n<P, const N: usize>(values: [Self; N]) -> Selfwhere
P: Policy,
Source§fn hypot<P>(self, y: Self) -> Selfwhere
P: Policy,
fn hypot<P>(self, y: Self) -> Selfwhere
P: Policy,
fn inv_hypot_n<P, const N: usize>(values: [Self; N]) -> Selfwhere
P: Policy,
fn hypot_s<P>(values: &[Self]) -> Selfwhere
P: Policy,
fn inv_hypot<P>(values: &[Self]) -> Selfwhere
P: Policy,
Source§impl<V> SpecializedSpecialMath<Compensated<<V as GenericVector>::Element>> for Compensated<V>
Available on crate feature special only.
impl<V> SpecializedSpecialMath<Compensated<<V as GenericVector>::Element>> for Compensated<V>
special only.Source§fn polygamma<P: Policy>(self, n: u32) -> Self
fn polygamma<P: Policy>(self, n: u32) -> Self
n = 0 and n = 1 reach the tuned double-double digamma/trigamma. n >= 2
returns NaN: no double-double algorithm exists for the higher orders yet, and
silently routing through an f64-precision path would put 53 good bits in a
106-bit container, the same reason Compensated refuses the shared Lanczos
tables. NaN over quiet precision loss, like the real kernel’s unimplemented
regions.
Source§type ExpIntDetails = Compensated<V>
type ExpIntDetails = Compensated<V>
expint kernel. Almost always
Self, with an empty ExpIntDetails impl taking every default.fn erf<P: Policy>(self) -> Self
fn erfc<P: Policy>(self) -> Self
fn tgamma<P: Policy>(self) -> Self
fn beta<P: Policy>(a: Self, b: Self) -> Self
fn lambert_w<P: Policy>(self) -> (Self, Self)
fn lgamma<P: Policy>(self) -> Self
fn digamma<P: Policy>(self) -> Self
Source§fn exp_two_sum(a: Self, b: Self) -> (Self, Self)
fn exp_two_sum(a: Self, b: Self) -> (Self, Self)
(a + b, the rounding it discarded). Internal to
this trait; it is a lowering detail of the Poisson exponent. Read moreSource§const LAGUERRE_PRODUCT_SEED_CAP: i32 = 0
const LAGUERRE_PRODUCT_SEED_CAP: i32 = 0
laguerre_function_i seeds by the direct product
x^{alpha/2} / sqrt(alpha!) (a scalar factorial, powi, at most one sqrt) instead
of the general exp(alpha/2 ln x - lgamma(alpha+1)/2). 0 disables it. Read moreSource§fn expint_n<P, const N: usize>(self) -> Selfwhere
P: Policy,
fn expint_n<P, const N: usize>(self) -> Selfwhere
P: Policy,
E_N(x) for integer order N.Source§fn expint_primal_n<P, const N: usize>(self) -> (Self, Self)where
P: Policy,
fn expint_primal_n<P, const N: usize>(self) -> (Self, Self)where
P: Policy,
Source§fn expint_primal<P>(self, n: u32) -> (Self, Self)where
P: Policy,
fn expint_primal<P>(self, n: u32) -> (Self, Self)where
P: Policy,
expint_primal_n: the same E_1
core, the same recurrence with the order as a value.fn logistic_sigmoid<P>(self) -> Selfwhere
P: Policy,
fn softplus<P>(self, k: Self, rcp_k: Self) -> Selfwhere
P: Policy,
Source§fn zetac<P>(self) -> Selfwhere
P: Policy,
fn zetac<P>(self) -> Selfwhere
P: Policy,
Compensated keeps the default: the Euler-Maclaurin coefficients are tabulated to
f64, so a double-double built from them would carry 53 real bits and noise, the same
reason it has no GammaPrimalTables impl. Dual overrides it through
zeta_with_deriv.Source§fn polylog<P>(
self,
order: PolylogOrder<E, <Self::Signed as GenericVector>::Element>,
) -> Selfwhere
P: Policy,
fn polylog<P>(
self,
order: PolylogOrder<E, <Self::Signed as GenericVector>::Element>,
) -> Selfwhere
P: Policy,
Li_s(z) at a scalar order. Defaulted for the same reason as zetac:
the coefficient precompute is f64, so a double-double has nothing to reach for.
Dual overrides it through the order-lowering identity Li_s' = Li_{s-1}/z.Source§fn zeta_with_deriv<P, const ZETAC: bool>(self) -> (Self, Self)where
P: Policy,
fn zeta_with_deriv<P, const ZETAC: bool>(self) -> (Self, Self)where
P: Policy,
(zeta(s), zeta'(s)), or (zeta(s) - 1, zeta'(s)) when ZETAC is set: the two
functions differ by a constant, so one derivative serves both. Read moreSource§fn bessel_i<P, const N: i32>(self) -> Selfwhere
P: Policy,
fn bessel_i<P, const N: i32>(self) -> Selfwhere
P: Policy,
I_N(x), or e^{-|x|} I_N(x) when SCALED: the modified Bessel function of the
first kind at compile-time integer order. Read moreSource§fn bessel_i_scaled<P, const N: i32>(self) -> Selfwhere
P: Policy,
fn bessel_i_scaled<P, const N: i32>(self) -> Selfwhere
P: Policy,
e^{-|x|} I_N(x). Not a wrapper over bessel_i: above the series
threshold the coefficient tables are the scaled value, so this form skips the
exponential the unscaled one pays for, and stays finite where I_N overflows.Source§fn bessel_k<P, const N: i32>(self) -> Selfwhere
P: Policy,
fn bessel_k<P, const N: i32>(self) -> Selfwhere
P: Policy,
K_N(x), the modified Bessel function of the second kind at compile-time integer
order. Defaulted for the same reason as bessel_i.Source§fn bessel_k_scaled<P, const N: i32>(self) -> Selfwhere
P: Policy,
fn bessel_k_scaled<P, const N: i32>(self) -> Selfwhere
P: Policy,
e^{x} K_N(x). Not a wrapper: above the series threshold the tables are natively the
scaled quantity, so this form skips the exponential the unscaled one pays for, and
stays in range where K_N has decayed to zero.Source§fn bessel_j<P, const N: i32>(self) -> Selfwhere
P: Policy,
fn bessel_j<P, const N: i32>(self) -> Selfwhere
P: Policy,
J_N(x), the oscillatory Bessel function of the first kind. Orders 0 and 1 only for
now. Higher orders want a recurrence that is not written yet.Source§fn bessel_y<P, const N: i32>(self) -> Selfwhere
P: Policy,
fn bessel_y<P, const N: i32>(self) -> Selfwhere
P: Policy,
Y_N(x), the oscillatory Bessel function of the second kind.Source§fn bessel_i_with_deriv<P, const N: i32, const SCALED: bool>(
self,
) -> (Self, Self)where
P: Policy,
fn bessel_i_with_deriv<P, const N: i32, const SCALED: bool>(
self,
) -> (Self, Self)where
P: Policy,
Source§fn bessel_k_with_deriv<P, const N: i32, const SCALED: bool>(
self,
) -> (Self, Self)where
P: Policy,
fn bessel_k_with_deriv<P, const N: i32, const SCALED: bool>(
self,
) -> (Self, Self)where
P: Policy,
(K_N(x), d/dx K_N(x)). See bessel_i_with_deriv.Source§fn bessel_j_with_deriv<P, const N: i32>(self) -> (Self, Self)where
P: Policy,
fn bessel_j_with_deriv<P, const N: i32>(self) -> (Self, Self)where
P: Policy,
(J_N(x), d/dx J_N(x)). See bessel_i_with_deriv.Source§fn bessel_y_with_deriv<P, const N: i32>(self) -> (Self, Self)where
P: Policy,
fn bessel_y_with_deriv<P, const N: i32>(self) -> (Self, Self)where
P: Policy,
(Y_N(x), d/dx Y_N(x)). See bessel_i_with_deriv.Source§fn bessel_iv<P, const SCALED: bool>(
self,
_order: BesselOrder<Self, Self::Signed>,
) -> Selfwhere
P: Policy,
fn bessel_iv<P, const SCALED: bool>(
self,
_order: BesselOrder<Self, Self::Signed>,
) -> Selfwhere
P: Policy,
I_n(x) with a per-lane order. See bessel_i.Source§fn bessel_kv<P, const SCALED: bool>(
self,
_order: BesselOrder<Self, Self::Signed>,
) -> Selfwhere
P: Policy,
fn bessel_kv<P, const SCALED: bool>(
self,
_order: BesselOrder<Self, Self::Signed>,
) -> Selfwhere
P: Policy,
K_n(x) with a per-lane order. See bessel_k.Source§fn bessel_jv<P>(self, _order: BesselOrder<Self, Self::Signed>) -> Selfwhere
P: Policy,
fn bessel_jv<P>(self, _order: BesselOrder<Self, Self::Signed>) -> Selfwhere
P: Policy,
J_n(x) with a per-lane order. See bessel_j.Source§fn bessel_yv<P>(self, _order: BesselOrder<Self, Self::Signed>) -> Selfwhere
P: Policy,
fn bessel_yv<P>(self, _order: BesselOrder<Self, Self::Signed>) -> Selfwhere
P: Policy,
Y_n(x) with a per-lane order. See bessel_y.Source§fn sph_bessel_j_n<P, const N: usize>(self) -> Selfwhere
P: Policy,
fn sph_bessel_j_n<P, const N: usize>(self) -> Selfwhere
P: Policy,
j_n(x), the spherical Bessel function of the first kind. See
sph_bessel_j.Source§fn sph_bessel_y_n<P, const N: usize>(self) -> Selfwhere
P: Policy,
fn sph_bessel_y_n<P, const N: usize>(self) -> Selfwhere
P: Policy,
y_n(x). See sph_bessel_y.Source§fn sph_bessel_i_n<P, const N: usize>(self) -> Selfwhere
P: Policy,
fn sph_bessel_i_n<P, const N: usize>(self) -> Selfwhere
P: Policy,
i_n(x). See sph_bessel_i.Source§fn sph_bessel_i_scaled_n<P, const N: usize>(self) -> Selfwhere
P: Policy,
fn sph_bessel_i_scaled_n<P, const N: usize>(self) -> Selfwhere
P: Policy,
e^{-x} i_n(x). See sph_bessel_i_scaled.Source§fn sph_bessel_k_n<P, const N: usize>(self) -> Selfwhere
P: Policy,
fn sph_bessel_k_n<P, const N: usize>(self) -> Selfwhere
P: Policy,
k_n(x). See sph_bessel_k.Source§fn sph_bessel_k_scaled_n<P, const N: usize>(self) -> Selfwhere
P: Policy,
fn sph_bessel_k_scaled_n<P, const N: usize>(self) -> Selfwhere
P: Policy,
e^{x} k_n(x). See sph_bessel_k_scaled.Source§fn sph_bessel_j_with_deriv_n<P, const N: usize>(self) -> (Self, Self)where
P: Policy,
fn sph_bessel_j_with_deriv_n<P, const N: usize>(self) -> (Self, Self)where
P: Policy,
(j_n(x), j_n'(x)), both from one walk. Read moreSource§fn sph_bessel_y_with_deriv_n<P, const N: usize>(self) -> (Self, Self)where
P: Policy,
fn sph_bessel_y_with_deriv_n<P, const N: usize>(self) -> (Self, Self)where
P: Policy,
(y_n(x), y_n'(x)). See sph_bessel_j_with_deriv.Source§fn sph_bessel_i_with_deriv_n<P, const N: usize, const SCALED: bool>(
self,
) -> (Self, Self)where
P: Policy,
fn sph_bessel_i_with_deriv_n<P, const N: usize, const SCALED: bool>(
self,
) -> (Self, Self)where
P: Policy,
(i_n(x), i_n'(x)), scaled by e^{-x} when SCALED, in which case the derivative is
the scaled function’s own, d/dx(e^{-x} i_n) = e^{-x}(i_n' - i_n).Source§fn sph_bessel_k_with_deriv_n<P, const N: usize, const SCALED: bool>(
self,
) -> (Self, Self)where
P: Policy,
fn sph_bessel_k_with_deriv_n<P, const N: usize, const SCALED: bool>(
self,
) -> (Self, Self)where
P: Policy,
(k_n(x), k_n'(x)), scaled by e^{x} when SCALED.Source§fn sph_bessel_j<P>(self, n: u32) -> Selfwhere
P: Policy,
fn sph_bessel_j<P>(self, n: u32) -> Selfwhere
P: Policy,
j_n(x) for a runtime order. See sph_bessel_j.Source§fn sph_bessel_y<P>(self, n: u32) -> Selfwhere
P: Policy,
fn sph_bessel_y<P>(self, n: u32) -> Selfwhere
P: Policy,
y_n(x) for a runtime order.Source§fn sph_bessel_i<P>(self, n: u32) -> Selfwhere
P: Policy,
fn sph_bessel_i<P>(self, n: u32) -> Selfwhere
P: Policy,
i_n(x) for a runtime order.Source§fn sph_bessel_i_scaled<P>(self, n: u32) -> Selfwhere
P: Policy,
fn sph_bessel_i_scaled<P>(self, n: u32) -> Selfwhere
P: Policy,
e^{-x} i_n(x) for a runtime order.Source§fn sph_bessel_k<P>(self, n: u32) -> Selfwhere
P: Policy,
fn sph_bessel_k<P>(self, n: u32) -> Selfwhere
P: Policy,
k_n(x) for a runtime order.Source§fn sph_bessel_k_scaled<P>(self, n: u32) -> Selfwhere
P: Policy,
fn sph_bessel_k_scaled<P>(self, n: u32) -> Selfwhere
P: Policy,
e^{x} k_n(x) for a runtime order.Source§fn sph_bessel_j_with_deriv<P>(self, n: u32) -> (Self, Self)where
P: Policy,
fn sph_bessel_j_with_deriv<P>(self, n: u32) -> (Self, Self)where
P: Policy,
(j_n(x), j_n'(x)) for a runtime order. See
sph_bessel_j_with_deriv_n.Source§fn sph_bessel_y_with_deriv<P>(self, n: u32) -> (Self, Self)where
P: Policy,
fn sph_bessel_y_with_deriv<P>(self, n: u32) -> (Self, Self)where
P: Policy,
(y_n(x), y_n'(x)) for a runtime order.Source§fn sph_bessel_i_with_deriv<P, const SCALED: bool>(self, n: u32) -> (Self, Self)where
P: Policy,
fn sph_bessel_i_with_deriv<P, const SCALED: bool>(self, n: u32) -> (Self, Self)where
P: Policy,
(i_n(x), i_n'(x)) for a runtime order, scaled by e^{-x} when SCALED.Source§fn sph_bessel_k_with_deriv<P, const SCALED: bool>(self, n: u32) -> (Self, Self)where
P: Policy,
fn sph_bessel_k_with_deriv<P, const SCALED: bool>(self, n: u32) -> (Self, Self)where
P: Policy,
(k_n(x), k_n'(x)) for a runtime order, scaled by e^{x} when SCALED.Source§fn bessel_n<P, F, const N: i32>(self) -> Selfwhere
P: Policy,
F: BesselFamily,
fn bessel_n<P, F, const N: i32>(self) -> Selfwhere
P: Policy,
F: BesselFamily,
bessel_n::<F, N>(): see BesselFamily.Source§fn bessel<P, F>(self, order: BesselOrder<Self, Self::Signed>) -> Selfwhere
P: Policy,
F: BesselFamily,
fn bessel<P, F>(self, order: BesselOrder<Self, Self::Signed>) -> Selfwhere
P: Policy,
F: BesselFamily,
bessel::<F>(order): see BesselFamily.Source§fn sph_bessel_n<P, F, const N: usize>(self) -> Selfwhere
P: Policy,
F: BesselFamily,
fn sph_bessel_n<P, F, const N: usize>(self) -> Selfwhere
P: Policy,
F: BesselFamily,
sph_bessel_n::<F, N>().Source§fn sph_bessel<P, F>(self, n: u32) -> Selfwhere
P: Policy,
F: BesselFamily,
fn sph_bessel<P, F>(self, n: u32) -> Selfwhere
P: Policy,
F: BesselFamily,
sph_bessel::<F>(n).Source§fn airy_all<P, const SCALED: bool>(self) -> (Self, Self, Self, Self)where
P: Policy,
fn airy_all<P, const SCALED: bool>(self) -> (Self, Self, Self, Self)where
P: Policy,
SCALED.Source§fn bessel_jv_scaled<P>(self, order: BesselOrder<Self, Self::Signed>) -> Selfwhere
P: Policy,
fn bessel_jv_scaled<P>(self, order: BesselOrder<Self, Self::Signed>) -> Selfwhere
P: Policy,
Scaled(J) at runtime order: e^{-|Im z|} J_nu(z), SciPy’s jve. The scale factor
is 1 on the real axis, so the default is the unscaled value. Complex overrides.Source§fn bessel_yv_scaled<P>(self, order: BesselOrder<Self, Self::Signed>) -> Selfwhere
P: Policy,
fn bessel_yv_scaled<P>(self, order: BesselOrder<Self, Self::Signed>) -> Selfwhere
P: Policy,
Source§fn airy_tuple<P>(self) -> (Self, Self, Self, Self)where
P: Policy,
fn airy_tuple<P>(self) -> (Self, Self, Self, Self)where
P: Policy,
Source§fn airy_tuple_scaled<P>(self) -> (Self, Self, Self, Self)where
P: Policy,
fn airy_tuple_scaled<P>(self) -> (Self, Self, Self, Self)where
P: Policy,
Source§fn airy_ai_scaled<P>(self) -> Selfwhere
P: Policy,
fn airy_ai_scaled<P>(self) -> Selfwhere
P: Policy,
e^zeta Ai(x) on the positive axis. See airy_ai_scaled.Source§fn airy_bi_scaled<P>(self) -> Selfwhere
P: Policy,
fn airy_bi_scaled<P>(self) -> Selfwhere
P: Policy,
e^-zeta Bi(x) on the positive axis. See airy_bi_scaled.Source§fn airy_ai_prime<P>(self) -> Selfwhere
P: Policy,
fn airy_ai_prime<P>(self) -> Selfwhere
P: Policy,
Ai'(x) alone. See airy_ai_prime.Source§fn airy_ai_prime_scaled<P>(self) -> Selfwhere
P: Policy,
fn airy_ai_prime_scaled<P>(self) -> Selfwhere
P: Policy,
e^zeta Ai'(x) on the positive axis. See
airy_ai_prime_scaled.Source§fn airy_bi_prime<P>(self) -> Selfwhere
P: Policy,
fn airy_bi_prime<P>(self) -> Selfwhere
P: Policy,
Bi'(x) alone. See airy_bi_prime.Source§fn airy_bi_prime_scaled<P>(self) -> Selfwhere
P: Policy,
fn airy_bi_prime_scaled<P>(self) -> Selfwhere
P: Policy,
e^-zeta Bi'(x) on the positive axis. See
airy_bi_prime_scaled.fn hermite_n<P, const N: usize>(x: Self) -> Selfwhere
P: Policy,
fn hermitev<P>(x: Self, n: Self::Unsigned) -> Selfwhere
P: Policy,
Source§fn hermite<P>(self, n: u32) -> Selfwhere
P: Policy,
fn hermite<P>(self, n: u32) -> Selfwhere
P: Policy,
hermitev with the degree splatted.
Nothing cheaper is correct.fn hermite_function_n<P, const N: usize>(x: Self) -> Selfwhere
P: Policy,
fn hermite_function<P>(x: Self, n: u32) -> Selfwhere
P: Policy,
fn hermite_function_series_n<P, const N: usize>(
self,
coeffs: &[Self::Element; N],
) -> Selfwhere
P: Policy,
fn hermite_function_series<P>(self, coeffs: &[Self::Element]) -> Selfwhere
P: Policy,
fn laguerre_n<P, const N: usize>(x: Self, alpha: Self) -> Selfwhere
P: Policy,
fn laguerrev<P>(x: Self, alpha: Self, n: Self::Unsigned) -> Selfwhere
P: Policy,
Source§fn laguerre<P>(self, alpha: Self, n: u32) -> Selfwhere
P: Policy,
fn laguerre<P>(self, alpha: Self, n: u32) -> Selfwhere
P: Policy,
laguerrev with the degree splatted.fn laguerre_function_n<P, const N: usize>(x: Self, alpha: Self) -> Selfwhere
P: Policy,
fn laguerre_function<P>(x: Self, alpha: Self, n: u32) -> Selfwhere
P: Policy,
fn laguerre_function_i_n<P, const N: usize>(x: Self, alpha: i32) -> Selfwhere
P: Policy,
fn laguerre_function_i<P>(x: Self, alpha: i32, n: u32) -> Selfwhere
P: Policy,
fn poisson_pmf<P>(self, lambda: Self) -> Selfwhere
P: Policy,
fn poisson_log_pmf<P>(self, lambda: Self) -> Selfwhere
P: Policy,
fn laguerre_function_series_n<P, const N: usize>(
self,
alpha: Self,
coeffs: &[Self::Element; N],
) -> Selfwhere
P: Policy,
fn laguerre_function_series_i_n<P, const N: usize>(
self,
alpha: i32,
coeffs: &[Self::Element; N],
) -> Selfwhere
P: Policy,
fn laguerre_function_series<P>(
self,
alpha: Self,
coeffs: &[Self::Element],
) -> Selfwhere
P: Policy,
fn laguerre_function_series_i<P>(
self,
alpha: i32,
coeffs: &[Self::Element],
) -> Selfwhere
P: Policy,
fn chebyshev<P, const K: usize>(self, coeffs: &[Self::Element]) -> Selfwhere
P: Policy,
fn chebyshev_n<P, const K: usize, const N: usize>(
self,
coeffs: &[Self::Element; N],
) -> Selfwhere
P: Policy,
fn jacobi<P>(x: Self, alpha: Self, beta: Self, n: u32, m: u32) -> Selfwhere
P: Policy,
fn gaussian<P>(x: Self, a: Self, c: Self) -> Selfwhere
P: Policy,
fn lbeta<P>(a: Self, b: Self) -> Selfwhere
P: Policy,
fn logit<P>(self) -> Selfwhere
P: Policy,
fn logit_1m<P>(self) -> Selfwhere
P: Policy,
fn planck<P>(self) -> Selfwhere
P: Policy,
fn legendre0<P, const N: u32>(x: Self, n: u32) -> Selfwhere
P: Policy,
fn legendre<P>(x: Self, n: u32, m: u32) -> Selfwhere
P: Policy,
fn legendre_series_n<P, const N: usize>(
self,
coeffs: &[Self::Element; N],
) -> Selfwhere
P: Policy,
fn legendre_series<P>(self, coeffs: &[Self::Element]) -> Selfwhere
P: Policy,
fn zernike_r<P>(rho: Self, n: u32, m: u32) -> Selfwhere
P: Policy,
fn zernike<P, const NORM: u8>(rho: Self, theta: Self, n: u32, m: i32) -> Selfwhere
P: Policy,
fn zernike_basis<P, const L: usize, const NORM: u8, const N: usize>(
x: Self,
y: Self,
out: &mut [Self; N],
)where
P: Policy,
fn phi_n<P, const N: usize>(self) -> Selfwhere
P: Policy,
Source§impl<V> SpecializedTranscendentalMath<Compensated<<V as GenericVector>::Element>> for Compensated<V>where
V: RealMathWithPolicy + CompensatedFloatVector,
impl<V> SpecializedTranscendentalMath<Compensated<<V as GenericVector>::Element>> for Compensated<V>where
V: RealMathWithPolicy + CompensatedFloatVector,
Source§fn sincos_pi<P: Policy>(self) -> (Self, Self)
fn sincos_pi<P: Policy>(self) -> (Self, Self)
$(\sin \pi x, \cos \pi x)$, reducing before multiplying by pi.
The inherited default is sin_cos(self * PI), which throws away most of what
this type exists for. Forming x * PI rounds the product, so the argument handed
to sin_cos already carries an absolute error of about |x| * 2^-106; at
x = -1000.5 (an ordinary argument for the gamma reflection) that is three or
four digits gone before any trigonometry happens.
Reducing first avoids it entirely. sin(pi(n + r)) = (-1)^n sin(pi r) for integer
n, and x - round(x) is exact, so the only rounded product is r * PI with
|r| <= 1/2. Same for cosine, with the same sign flip.
Source§fn atanhc<P: Policy>(self) -> Self
fn atanhc<P: Policy>(self) -> Self
atanh(x)/x, the cardinal form of atanh: same structure as sinc below, with the
even series 1 + x^2/3 + x^4/5. Domain [-1, 1], where both ends are +inf.
Source§fn sinhc<P: Policy>(self) -> Self
fn sinhc<P: Policy>(self) -> Self
sinh(x)/x, the hyperbolic twin of sinc above and structurally identical to it:
the series 1 + x^2/6 + x^4/120 adds where sinc subtracts, and the limit at
infinity is +inf rather than zero.
Source§fn ln1m_expnx_ext<P: Policy>(self, _lnx: Self) -> Self
fn ln1m_expnx_ext<P: Policy>(self, _lnx: Self) -> Self
The _ext form exists so a caller who already has ln(x) can hand it to the
low-precision approximation instead of paying for it twice. The compensated
path never takes that approximation (it evaluates ln(1 - e^-x) exactly), so
there is nothing to reuse and the hint is dropped, the same way the f64 kernel
(math/specialized/pd.rs) and Complex do.
fn sin_cos<P: Policy>(self) -> (Self, Self)
fn sinc<P: Policy>(self) -> Self
fn sinh_cosh<P: Policy>(self) -> (Self, Self)
fn sinh<P: Policy>(self) -> Self
fn cosh<P: Policy>(self) -> Self
fn tanh<P: Policy>(self) -> Self
fn asin<P: Policy>(self) -> Self
fn acos<P: Policy>(self) -> Self
fn atan<P: Policy>(self) -> Self
fn asinh<P: Policy>(self) -> Self
fn acosh<P: Policy>(self) -> Self
fn atanh<P: Policy>(self) -> Self
fn exp<P: Policy>(self) -> Self
fn exph<P: Policy>(self) -> Self
fn exp2<P: Policy>(self) -> Self
fn exp10<P: Policy>(self) -> Self
fn exp_m1<P: Policy>(self) -> Self
fn exp2_m1<P: Policy>(self) -> Self
fn exp10_m1<P: Policy>(self) -> Self
fn powf<P: Policy>(self, e: Self) -> Self
fn cbrt<P: Policy>(self) -> Self
fn ln<P: Policy>(self) -> Self
fn ln_1p<P: Policy>(self) -> Self
fn log2<P: Policy>(self) -> Self
fn log10<P: Policy>(self) -> Self
fn log_n_n<P: Policy, const N: usize>(self) -> Self
fn sin<P>(self) -> Selfwhere
P: Policy,
fn cos<P>(self) -> Selfwhere
P: Policy,
fn tan<P>(self) -> Selfwhere
P: Policy,
fn sin_pi<P>(self) -> Selfwhere
P: Policy,
fn cos_pi<P>(self) -> Selfwhere
P: Policy,
fn tan_pi<P>(self) -> Selfwhere
P: Policy,
fn sinc_pi<P>(self) -> Selfwhere
P: Policy,
Source§fn xlog_guarded(x: Self, y: Self, ln_y: Self) -> Self
fn xlog_guarded(x: Self, y: Self, ln_y: Self) -> Self
fn xlogy<P>(self, y: Self) -> Selfwhere
P: Policy,
fn xlog1py<P>(self, y: Self) -> Selfwhere
P: Policy,
Source§fn cosh_m1<P>(self) -> Selfwhere
P: Policy,
fn cosh_m1<P>(self) -> Selfwhere
P: Policy,
cosh(x) - 1 = 2 sinh^2(x/2), an exact identity, so no type needs to override this:
the composition inherits whatever accuracy that type’s sinh has, and near zero
sinh(x/2) is already x/2 to full relative precision, giving x^2/2 with no
cancellation anywhere. Same treatment as versin above.fn sqrt1pm1<P>(self) -> Selfwhere
P: Policy,
fn sqrt1mexp<P>(self) -> Selfwhere
P: Policy,
fn compound<P>(self, n: Self) -> Selfwhere
P: Policy,
fn powf_m1<P>(self, e: Self) -> Selfwhere
P: Policy,
fn compound_m1<P>(self, n: Self) -> Selfwhere
P: Policy,
fn haversin<P>(self) -> Selfwhere
P: Policy,
fn versin<P>(self) -> Selfwhere
P: Policy,
fn versinc<P>(self) -> Selfwhere
P: Policy,
fn cos_m1<P>(self) -> Selfwhere
P: Policy,
fn nth_root_n<P, const N: usize>(self) -> Selfwhere
P: Policy,
Source§fn nth_root<P>(self, n: u32) -> Selfwhere
P: Policy,
fn nth_root<P>(self, n: u32) -> Selfwhere
P: Policy,
nth_root_n: the same arithmetic with the degree
as a value, so the two agree to the bit at every n. The special cases are one uniform
branch on n rather than a compile-time fold.fn log2_p1<P>(self) -> Selfwhere
P: Policy,
fn log10_p1<P>(self) -> Selfwhere
P: Policy,
Source§fn log1pmx<P>(self) -> Selfwhere
P: Policy,
fn log1pmx<P>(self) -> Selfwhere
P: Policy,
generic::log1pmx_internal. The default exists so that
ordered-comparison-free types (Complex above all, which cannot select a window at
all) still get a correct answer rather than blocking the whole method.fn log<P>(self, base: Self) -> Selfwhere
P: Policy,
Source§fn ln1m_expnx<P>(self) -> Selfwhere
P: Policy,
fn ln1m_expnx<P>(self) -> Selfwhere
P: Policy,
Source§impl<V: CompensatedFloatVector> SplatVector<Compensated<<V as GenericVector>::Element>> for Compensated<V>
impl<V: CompensatedFloatVector> SplatVector<Compensated<<V as GenericVector>::Element>> for Compensated<V>
Source§type Splat<T: SplatConst<Compensated<V::Element>>> = Compensated<V>
type Splat<T: SplatConst<Compensated<V::Element>>> = Compensated<V>
T, the type exposing the splatted vector
constant via VectorValue.Source§impl<V: ScalarValue> Square for Compensated<V>
impl<V: ScalarValue> Square for Compensated<V>
Source§impl<V: CompensatedFloatVector> SquareMasked<<V as GenericVector>::Mask> for Compensated<V>
impl<V: CompensatedFloatVector> SquareMasked<<V as GenericVector>::Mask> for Compensated<V>
impl<V: PartialEq> StructuralPartialEq for Compensated<V>
Source§impl<V: ScalarValue> Sub for Compensated<V>
impl<V: ScalarValue> Sub for Compensated<V>
Source§impl<V: ScalarValue> Sub<V> for Compensated<V>
impl<V: ScalarValue> Sub<V> for Compensated<V>
Source§impl<V: Copy, T> SubAssign<T> for Compensated<V>where
Self: Sub<T, Output = Self>,
impl<V: Copy, T> SubAssign<T> for Compensated<V>where
Self: Sub<T, Output = Self>,
Source§fn sub_assign(&mut self, rhs: T)
fn sub_assign(&mut self, rhs: T)
-= operation. Read moreSource§impl<V: CompensatedFloatVector, Rhs> SubAssignMasked<<V as GenericVector>::Mask, Rhs> for Compensated<V>where
Compensated<V>: Sub<Rhs, Output = Self>,
impl<V: CompensatedFloatVector, Rhs> SubAssignMasked<<V as GenericVector>::Mask, Rhs> for Compensated<V>where
Compensated<V>: Sub<Rhs, Output = Self>,
Source§fn sub_assign_c(&mut self, mask: V::Mask, rhs: Rhs)
fn sub_assign_c(&mut self, mask: V::Mask, rhs: Rhs)
Source§fn sub_assign_m(&mut self, src: Self, mask: V::Mask, rhs: Rhs)
fn sub_assign_m(&mut self, src: Self, mask: V::Mask, rhs: Rhs)
Source§fn sub_assign_z(&mut self, mask: V::Mask, rhs: Rhs)
fn sub_assign_z(&mut self, mask: V::Mask, rhs: Rhs)
SubAssign masked (zeroed where mask is false).Source§impl<V: CompensatedFloatVector, Rhs> SubMasked<<V as GenericVector>::Mask, Rhs> for Compensated<V>where
Compensated<V>: Sub<Rhs, Output = Self>,
impl<V: CompensatedFloatVector, Rhs> SubMasked<<V as GenericVector>::Mask, Rhs> for Compensated<V>where
Compensated<V>: Sub<Rhs, Output = Self>,
Source§impl<V: ScalarValue> Sum for Compensated<V>
impl<V: ScalarValue> Sum for Compensated<V>
Source§impl<V: CompensatedFloatVector> Swizzle<<V as GenericVector>::Lanes> for Compensated<V>
impl<V: CompensatedFloatVector> Swizzle<<V as GenericVector>::Lanes> for Compensated<V>
Source§fn swizzle_const<I: SwizzleIndices<V::Lanes>>(self, other: Self) -> Self
fn swizzle_const<I: SwizzleIndices<V::Lanes>>(self, other: Self) -> Self
Source§fn permutev_const<I: SwizzleIndices<V::Lanes>>(self) -> Self
fn permutev_const<I: SwizzleIndices<V::Lanes>>(self) -> Self
Source§impl<V: CompensatedFloatVector, E: SplatConst<Compensated<V::Element>>> VectorValue<E, Compensated<V>> for Compensated<V>
impl<V: CompensatedFloatVector, E: SplatConst<Compensated<V::Element>>> VectorValue<E, Compensated<V>> for Compensated<V>
Source§const VALUE: Compensated<V>
const VALUE: Compensated<V>
Auto Trait Implementations§
impl<V> Freeze for Compensated<V>where
V: Freeze,
impl<V> RefUnwindSafe for Compensated<V>where
V: RefUnwindSafe,
impl<V> Send for Compensated<V>where
V: Send,
impl<V> Sync for Compensated<V>where
V: Sync,
impl<V> Unpin for Compensated<V>where
V: Unpin,
impl<V> UnsafeUnpin for Compensated<V>where
V: UnsafeUnpin,
impl<V> UnwindSafe for Compensated<V>where
V: UnwindSafe,
Blanket Implementations§
impl<V, Mask, Rhs> AssignMaskedNumOps<Mask, Rhs> for Vwhere
V: AddAssignMasked<Mask, Rhs> + SubAssignMasked<Mask, Rhs> + MulAssignMasked<Mask, Rhs> + DivAssignMasked<Mask, Rhs> + RemAssignMasked<Mask, Rhs>,
Source§impl<V> BernoulliMath for V
impl<V> BernoulliMath for V
Source§fn bernoulli_numbers(b1: Self::Element) -> BernoulliSequence<Self> ⓘ
fn bernoulli_numbers(b1: Self::Element) -> BernoulliSequence<Self> ⓘ
$B_0, B_1, B_2, \ldots$ as splatted vectors, zeros included. Read moreSource§impl<E, V> BesselDetails<V> for V
impl<E, V> BesselDetails<V> for V
Source§fn near(z: V) -> <V as GenericVector>::Mask
fn near(z: V) -> <V as GenericVector>::Mask
|z| <= 2, where K comes from Temme’s series.Source§fn beyond(
z: V,
threshold: <V as PrimalProjection>::Primal,
) -> <V as GenericVector>::Mask
fn beyond( z: V, threshold: <V as PrimalProjection>::Primal, ) -> <V as GenericVector>::Mask
|z| >= threshold, per lane: where I takes the asymptotic series.Source§fn valid(z: V) -> <V as GenericVector>::Mask
fn valid(z: V) -> <V as GenericVector>::Mask
Source§fn exp_far(
z: V,
threshold: <V as PrimalProjection>::Primal,
) -> <V as GenericVector>::Mask
fn exp_far( z: V, threshold: <V as PrimalProjection>::Primal, ) -> <V as GenericVector>::Mask
e^z overflows before e^z * a does, so the exponential is
halved and applied twice: Re z >= threshold.Source§const ASYM_TWO_TERMS: bool = false
const ASYM_TWO_TERMS: bool = false
I carries its second exponential. Read moreSource§fn asym_second_exponent(z: V, _nu: <V as PrimalProjection>::Primal) -> V
fn asym_second_exponent(z: V, _nu: <V as PrimalProjection>::Primal) -> V
$-2z \pm (\nu+1/2)\pi i$
with the sign of Im z. Only read when ASYM_TWO_TERMS.Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CompensatedGammaOps for Twhere
T: FloatVector + TranscendentalMathWithPolicy,
§impl<T> Conv for T
impl<T> Conv for T
Source§impl<M> CoreMath for Mwhere
M: CoreMathWithPolicy,
impl<M> CoreMath for Mwhere
M: CoreMathWithPolicy,
Source§fn poly_n_primal<N>(self, coeffs: &GenericArray<Self::Primal, N>) -> Selfwhere
N: ArrayLength,
fn poly_n_primal<N>(self, coeffs: &GenericArray<Self::Primal, N>) -> Selfwhere
N: ArrayLength,
Source§fn poly_rev_n_primal<N>(self, coeffs: &GenericArray<Self::Primal, N>) -> Selfwhere
N: ArrayLength,
fn poly_rev_n_primal<N>(self, coeffs: &GenericArray<Self::Primal, N>) -> Selfwhere
N: ArrayLength,
Source§fn poly_primal(self, coeffs: &[Self::Primal]) -> Self
fn poly_primal(self, coeffs: &[Self::Primal]) -> Self
poly_n_primal over a runtime-length slice. Read moreSource§fn poly_rev_primal(self, coeffs: &[Self::Primal]) -> Self
fn poly_rev_primal(self, coeffs: &[Self::Primal]) -> Self
poly_rev_n_primal over a runtime-length slice. Read moreSource§fn poly(self, coeffs: &[Self::Element]) -> Self
fn poly(self, coeffs: &[Self::Element]) -> Self
self. Read moreSource§fn poly_rev(self, coeffs: &[Self::Element]) -> Self
fn poly_rev(self, coeffs: &[Self::Element]) -> Self
self, but with the coefficients in reverse order. Read moreSource§fn poly_n<const N: usize>(self, coeffs: &[Self::Element; N]) -> Self
fn poly_n<const N: usize>(self, coeffs: &[Self::Element; N]) -> Self
self. Read moreSource§fn poly_rev_n<const N: usize>(self, coeffs: &[Self::Element; N]) -> Self
fn poly_rev_n<const N: usize>(self, coeffs: &[Self::Element; N]) -> Self
self, but with the coefficients in reverse order. Read moreSource§fn poly_rational_n<const N: usize, const D: usize>(
self,
numerator: &[Self::Element; N],
denominator: &[Self::Element; D],
) -> Self
fn poly_rational_n<const N: usize, const D: usize>( self, numerator: &[Self::Element; N], denominator: &[Self::Element; D], ) -> Self
self, given the numerator and denominator coefficients. Read moreSource§fn approx_reciprocal(self) -> Self
fn approx_reciprocal(self) -> Self
Source§fn approx_div(self, divisor: Self) -> Self
fn approx_div(self, divisor: Self) -> Self
Source§fn approx_div_sqrt(self, denom: Self) -> Self
fn approx_div_sqrt(self, denom: Self) -> Self
$a/\sqrt{b}$, spelled a.approx_div_sqrt(b), as one kernel rather
than a divide bolted onto a square root. Read moreSource§fn difference_of_products(self, b: Self, c: Self, d: Self) -> Self
fn difference_of_products(self, b: Self, c: Self, d: Self) -> Self
$ab - cd$, spelled a.difference_of_products(b, c, d), evaluated so the
two products cannot cancel catastrophically. Read moreSource§fn sum_of_products(self, b: Self, c: Self, d: Self) -> Self
fn sum_of_products(self, b: Self, c: Self, d: Self) -> Self
Source§fn harmonic_mean_n<const N: usize>(values: [Self; N]) -> Self
fn harmonic_mean_n<const N: usize>(values: [Self; N]) -> Self
Source§fn harmonic_mean(values: &[Self]) -> Self
fn harmonic_mean(values: &[Self]) -> Self
harmonic_mean_n over a runtime-length slice. Read moreSource§fn inv_sum_inv_n<const N: usize>(values: [Self; N]) -> Self
fn inv_sum_inv_n<const N: usize>(values: [Self; N]) -> Self
Source§fn inv_sum_inv(values: &[Self]) -> Self
fn inv_sum_inv(values: &[Self]) -> Self
inv_sum_inv_n over a runtime-length slice. Read moreSource§fn inverse_sqrt(self) -> Self
fn inverse_sqrt(self) -> Self
Source§impl<E, V> CoreMathWithPolicy for Vwhere
E: FloatElement,
V: FloatVector<Element = E> + FloatVector + PrimalProjection + SpecializedCoreMath<E>,
impl<E, V> CoreMathWithPolicy for Vwhere
E: FloatElement,
V: FloatVector<Element = E> + FloatVector + PrimalProjection + SpecializedCoreMath<E>,
Source§fn poly_n_primal_p<P, N>(
self,
coeffs: &GenericArray<<V as PrimalProjection>::Primal, N>,
) -> Vwhere
P: Policy,
N: ArrayLength,
fn poly_n_primal_p<P, N>(
self,
coeffs: &GenericArray<<V as PrimalProjection>::Primal, N>,
) -> Vwhere
P: Policy,
N: ArrayLength,
poly with the coefficients held in Primal form.
The augmented fields of a constant (a Dual’s derivatives, a Complex’s
imaginary part) are identically zero, so carrying coefficients in Self
stores those zeros and then adds them at every Horner step. Neither the
storage nor the addition can be optimized away: x + 0.0 is not x when
x is -0.0, so the adds survive to run time.
Taking them as Self::Primal removes both. The coefficient array shrinks by
the augmentation factor (4x for Dual<V, 3>, 2x for Complex), and each
Horner step adds to the primal component alone.
For a type that is its own primal this is exactly poly with
pre-splatted coefficients, and the default impl reduces to it.
Coefficients are vectors, not elements: a caller with a constant table has usually splatted it once already, and the composites that benefit most are the ones for which splatting per call would be the expensive part.
The length is a typenum length rather than a
const N: usize so that a coefficient table can be supplied by a type that
knows its own length only as an associated type. Literal call sites spell it
[GenericArray::from_array].
Source§fn poly_rev_n_primal_p<P, N>(
self,
coeffs: &GenericArray<<V as PrimalProjection>::Primal, N>,
) -> Vwhere
P: Policy,
N: ArrayLength,
fn poly_rev_n_primal_p<P, N>(
self,
coeffs: &GenericArray<<V as PrimalProjection>::Primal, N>,
) -> Vwhere
P: Policy,
N: ArrayLength,
poly_rev with the coefficients held in Primal form.
Same trade as poly_n_primal (the constants carry no
augmented fields to store or add), with the coefficients in reverse order.
Source§fn poly_primal_p<P>(self, coeffs: &[<V as PrimalProjection>::Primal]) -> Vwhere
P: Policy,
fn poly_primal_p<P>(self, coeffs: &[<V as PrimalProjection>::Primal]) -> Vwhere
P: Policy,
poly_n_primal over a runtime-length slice.
A plain slice rather than a GenericArray, since the length is no longer
carried in the type, which is also why this one has no reason to reach for
typenum. Same primal-Horner step and the same saving on the addend. What
the runtime length costs is the unrolling and, for real vectors, the ILP
lowering, exactly as in poly.
The empty polynomial is 0.
Source§fn poly_rev_primal_p<P>(self, coeffs: &[<V as PrimalProjection>::Primal]) -> Vwhere
P: Policy,
fn poly_rev_primal_p<P>(self, coeffs: &[<V as PrimalProjection>::Primal]) -> Vwhere
P: Policy,
poly_rev_n_primal over a runtime-length slice.
Same trade as poly_primal, coefficients descending.
Source§fn poly_p<P>(self, coeffs: &[<V as GenericVector>::Element]) -> Vwhere
P: Policy,
fn poly_p<P>(self, coeffs: &[<V as GenericVector>::Element]) -> Vwhere
P: Policy,
Computes the polynomial with the given coefficients at self.
This will use fused multiply-add instructions where available for improved performance and accuracy, but falls back to standard operations if not.
If you know the length of your coefficient array at compile time,
strongly consider using poly_n instead,
which can be optimized more aggressively.
Source§fn poly_rev_p<P>(self, coeffs: &[<V as GenericVector>::Element]) -> Vwhere
P: Policy,
fn poly_rev_p<P>(self, coeffs: &[<V as GenericVector>::Element]) -> Vwhere
P: Policy,
Computes the polynomial with the given coefficients at self, but with the coefficients in reverse order.
This will use fused multiply-add instructions where available for improved performance and accuracy, but falls back to standard operations if not.
If you know the length of your coefficient array at compile time,
strongly consider using poly_rev_n instead,
which can be optimized more aggressively.
Source§fn poly_n_p<P, const N: usize>(
self,
coeffs: &[<V as GenericVector>::Element; N],
) -> Vwhere
P: Policy,
fn poly_n_p<P, const N: usize>(
self,
coeffs: &[<V as GenericVector>::Element; N],
) -> Vwhere
P: Policy,
Computes the polynomial with the given coefficients at self.
This will use fused multiply-add instructions where available for improved performance and accuracy, but falls back to standard operations if not.
Source§fn poly_rev_n_p<P, const N: usize>(
self,
coeffs: &[<V as GenericVector>::Element; N],
) -> Vwhere
P: Policy,
fn poly_rev_n_p<P, const N: usize>(
self,
coeffs: &[<V as GenericVector>::Element; N],
) -> Vwhere
P: Policy,
Computes the polynomial with the given coefficients at self, but with the coefficients in reverse order.
This will use fused multiply-add instructions where available for improved performance and accuracy, but falls back to standard operations if not.
Source§fn poly_rational_n_p<P, const N: usize, const D: usize>(
self,
numerator: &[<V as GenericVector>::Element; N],
denominator: &[<V as GenericVector>::Element; D],
) -> Vwhere
P: Policy,
fn poly_rational_n_p<P, const N: usize, const D: usize>(
self,
numerator: &[<V as GenericVector>::Element; N],
denominator: &[<V as GenericVector>::Element; D],
) -> Vwhere
P: Policy,
Computes the ratio of two polynomials at self, given the numerator and denominator coefficients.
Equivalent to poly_n(numerator) / poly_n(denominator), but with improved numerical stability in some cases.
This will use fused multiply-add instructions where available for improved performance and accuracy, but falls back to standard operations if not.
Source§fn approx_reciprocal_p<P>(self) -> Vwhere
P: Policy,
fn approx_reciprocal_p<P>(self) -> Vwhere
P: Policy,
Returns the multiplicative inverse of self, which is 1 / self.
approx_ because the fast precision tiers take the hardware reciprocal
estimate (refined by one Newton step above Worst) where one exists. Best
and above, Preserve denormal policies, and backends without an estimate all
get the exact 1 / self.
If using the policy version, you may select lower precision policies for extra performance, at the cost of accuracy.
Source§fn approx_div_p<P>(self, divisor: V) -> Vwhere
P: Policy,
fn approx_div_p<P>(self, divisor: V) -> Vwhere
P: Policy,
Returns the result of dividing self by divisor, i.e., self / divisor.
Depending on the precision policy and available features, this may be optimized to use approximate reciprocal and multiplication for better performance, at the cost of accuracy.
Source§fn approx_div_sqrt_p<P>(self, denom: V) -> Vwhere
P: Policy,
fn approx_div_sqrt_p<P>(self, denom: V) -> Vwhere
P: Policy,
Returns $a/\sqrt{b}$, spelled a.approx_div_sqrt(b), as one kernel rather
than a divide bolted onto a square root.
Reach for this wherever a quantity is normalized by a root: a direction
divided by its length, a value divided by a standard deviation, a weight
divided by $\sqrt{n}$. Writing a / b.sqrt() gets the same answer at the
top precision tier and a strictly worse one everywhere else, because the
separate spelling cannot use the hardware reciprocal-square-root estimate.
Depending on the precision policy and available features this is either a
multiply by the hardware reciprocal-square-root estimate (refined by one Newton
step above the lowest tier), or an exact square root and divide. At Best and
above it is exactly a / b.sqrt(), since a hardware divide already returns that
correctly rounded and there is nothing left for the kernel to add.
$b = 0$ gives infinity and $b < 0$ gives NaN, inherited from the root.
Source§fn difference_of_products_p<P>(self, b: V, c: V, d: V) -> Vwhere
P: Policy,
fn difference_of_products_p<P>(self, b: V, c: V, d: V) -> Vwhere
P: Policy,
$ab - cd$, spelled a.difference_of_products(b, c, d), evaluated so the
two products cannot cancel catastrophically.
Reach for this wherever an expression’s correctness argument is “the two
errors cancel by symmetry”: cross products and perp-dots, 2x2
determinants and matrix adjugates, discriminants $b^2 - 4ac$, the
$ac - bd$ of a complex multiply, and every sign test built on one of
those.
Longhand gets this wrong in two ways. Naive a * b - c * d loses the
whole difference when the products are close. A one-sided fused
spelling (a.mul_sube(b, c * d)) looks like the fix and is worse for the
case that usually matters, because it leaves one product exact and rounds
the other, so $ab - ba$ comes back as a small non-zero rather than
exactly zero. It does that only on hardware with FMA, which makes it a
portability bug as much as an accuracy one: the same source gives a
self-cross-product of zero on baseline x86 and a denormal on AArch64.
Three lowerings, chosen at compile time from the FMA capability and the precision policy:
| condition | evaluated as | cost |
|---|---|---|
| no true FMA | a * b - c * d | 3 ops |
true FMA, precision below Average | fma(a, b, -cd) | 2 ops |
true FMA, precision Average or above | Kahan’s compensated form | 4 ops |
Kahan’s form recovers the discarded rounding of $cd$ with a second FMA
and adds it back, for ~1.5 ulp, correctly signed, and exact whenever the
two products are equal. Average is the default policy’s tier, so that is
what an unsuffixed call gets. Without FMA the naive form is both the
cheapest available and the only one with the exactness property, so it
runs at every precision rather than emulating a fused multiply.
Source§fn sum_of_products_p<P>(self, b: V, c: V, d: V) -> Vwhere
P: Policy,
fn sum_of_products_p<P>(self, b: V, c: V, d: V) -> Vwhere
P: Policy,
$ab + cd$, spelled a.sum_of_products(b, c, d).
The companion to difference_of_products,
with the same three lowerings and the same reasoning. A sum of products
cancels exactly as badly as a difference when the two terms have opposite
signs, which is the ordinary case for the imaginary half of a complex
multiply.
Source§fn harmonic_mean_n_p<P, const N: usize>(values: [V; N]) -> Vwhere
P: Policy,
fn harmonic_mean_n_p<P, const N: usize>(values: [V; N]) -> Vwhere
P: Policy,
The harmonic mean of N values, $N / \sum_i 1/x_i$.
The mean that averages rates: harmonic over speeds gives the average speed of a journey, over resistances the value each resistor could be replaced by, over precision and recall the F1 score. Dominated by the smallest element, which is the property that makes it the right average for anything that behaves like a bottleneck.
Evaluated by scaling every reciprocal by the smallest element before summing, so
the sum lands in [1, N] and no term can overflow whatever the spread of the
inputs. Written directly as $N/\sum 1/x_i$ a single denormal input sends its
reciprocal to infinity and collapses the answer to zero. Scaled, an input range of
5e-324 to 1e300 is still exact. Below Average precision the direct form runs,
with approximate reciprocals, and that failure comes back.
A zero anywhere in the input gives 0, which is the limit rather than a special
case. An infinite element simply contributes nothing.
Source§fn harmonic_mean_p<P>(values: &[V]) -> Vwhere
P: Policy,
fn harmonic_mean_p<P>(values: &[V]) -> Vwhere
P: Policy,
harmonic_mean_n over a runtime-length slice.
Same evaluation and same edge cases. The length simply is not a constant, so the
loops cannot unroll and the reduction is serial rather than log-depth. If you know
the count at compile time, prefer
harmonic_mean_n.
The mean of no values is NaN (0/0), matching the empty-average convention
rather than inventing a value.
Source§fn inv_sum_inv_n_p<P, const N: usize>(values: [V; N]) -> Vwhere
P: Policy,
fn inv_sum_inv_n_p<P, const N: usize>(values: [V; N]) -> Vwhere
P: Policy,
$1 / \sum_i 1/x_i$, the reciprocal of the sum of reciprocals of N values.
harmonic_mean_n without the N, and the quantity most
physical “combine these” laws actually want: resistors in parallel, capacitors in
series, spring compliances, the reduced mass $m_1 m_2/(m_1+m_2)$ of a two-body
problem, thermal contact conductances, and the effective conductivity of a layered
medium. Each of those is this function, not the harmonic mean, which is N
times larger, a factor that silently multiplies through an entire model if the
two are confused.
The distinguishing identity: inv_sum_inv of N copies of x is $x/N$, while
the harmonic mean of them is $x$.
Same scaled evaluation and same edge cases as
harmonic_mean_n.
Source§fn inv_sum_inv_p<P>(values: &[V]) -> Vwhere
P: Policy,
fn inv_sum_inv_p<P>(values: &[V]) -> Vwhere
P: Policy,
inv_sum_inv_n over a runtime-length slice.
Same evaluation and same edge cases as
harmonic_mean, with the same loss of unrolling. The
empty input gives $1/0 =$ infinity, the identity of the parallel-combination
law this implements.
Source§fn inverse_sqrt_p<P>(self) -> Vwhere
P: Policy,
fn inverse_sqrt_p<P>(self) -> Vwhere
P: Policy,
Returns the inverse square root of self, which is 1 / sqrt(self).
If using the policy version, you may select lower precision policies for extra performance, at the cost of accuracy.
§impl<T> FmtForward for T
impl<T> FmtForward for T
§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.§fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
Source§impl<V> GenericVector2 for V
impl<V> GenericVector2 for V
Source§impl<V> GenericVector3 for V
impl<V> GenericVector3 for V
Source§impl<V> GenericVector4 for V
impl<V> GenericVector4 for V
Source§impl<T> LowerBounded for Twhere
T: Bounded,
impl<T> LowerBounded for Twhere
T: Bounded,
impl<T, A, B> MaskInteroperable<A, B> for Twhere
T: GenericVector,
<T as GenericVector>::Mask: CastMask<<A as GenericVector>::Mask> + CastMask<<B as GenericVector>::Mask>,
A: GenericVector<Lanes = <T as GenericVector>::Lanes>,
<A as GenericVector>::Mask: CastMask<<T as GenericVector>::Mask> + CastMask<<B as GenericVector>::Mask>,
B: GenericVector<Lanes = <T as GenericVector>::Lanes>,
<B as GenericVector>::Mask: CastMask<<T as GenericVector>::Mask> + CastMask<<A as GenericVector>::Mask>,
impl<V, Mask, Rhs> MaskedNumOps<Mask, Rhs> for V
impl<T, Rhs> NumAssignOps<Rhs> for T
impl<T, Rhs, Output> NumOps<Rhs, Output> for T
impl<V, A, B> PartiallyInteroperable<A, B> for Vwhere
V: GenericVector + CastVector<V> + CastVector<A> + CastVector<B>,
<V as GenericVector>::Mask: CastMask<<A as GenericVector>::Mask> + CastMask<<B as GenericVector>::Mask>,
A: CastVector<V> + GenericVector<Lanes = <V as GenericVector>::Lanes>,
<A as GenericVector>::Mask: CastMask<<V as GenericVector>::Mask> + CastMask<<B as GenericVector>::Mask>,
B: CastVector<V> + GenericVector<Lanes = <V as GenericVector>::Lanes>,
<B as GenericVector>::Mask: CastMask<<V as GenericVector>::Mask> + CastMask<<A as GenericVector>::Mask>,
§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read more§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read more§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.impl<M> PrimalMath for Mwhere
M: PrimalMathWithPolicy,
impl<E, V> PrimalMathWithPolicy for Vwhere
E: FloatElement,
V: FloatVector<Element = E> + RealMathWithPolicy<Primal = V> + PrimalProjection + SpecializedPrimalMath<E>,
Source§impl<E, V> PrimalProjection for V
impl<E, V> PrimalProjection for V
Source§fn from_primal(p: <V as PrimalProjection>::Primal) -> V
fn from_primal(p: <V as PrimalProjection>::Primal) -> V
Self: every non-primal field
(derivative parts, imaginary part) is initialized to zero. The identity
for primal types. Read moreSource§impl<M> RealMath for Mwhere
M: RealMathWithPolicy,
impl<M> RealMath for Mwhere
M: RealMathWithPolicy,
Source§fn tolerance() -> Self
fn tolerance() -> Self
Source§fn to_degrees(self) -> Self
fn to_degrees(self) -> Self
Source§fn to_radians(self) -> Self
fn to_radians(self) -> Self
Source§fn wrap_angle(self) -> Self
fn wrap_angle(self) -> Self
Source§fn angle_diff(self, other: Self) -> Self
fn angle_diff(self, other: Self) -> Self
Source§fn logaddexp(self, other: Self) -> Self
fn logaddexp(self, other: Self) -> Self
$\ln(e^{a} + e^{b})$ computed in a numerically stable way that avoids overflow,
where a = self and b = other. Read moreSource§fn logsumexp_n<const N: usize>(values: [Self; N]) -> Self
fn logsumexp_n<const N: usize>(values: [Self; N]) -> Self
$\ln\left(\sum_{i} e^{x_i}\right)$ over N values, computed in a
numerically stable way that avoids overflow. Read moreSource§fn logsumexp(values: &[Self]) -> Self
fn logsumexp(values: &[Self]) -> Self
logsumexp_n over a runtime-length slice. Read moreSource§fn logsubexp(self, other: Self) -> Self
fn logsubexp(self, other: Self) -> Self
$\ln(e^{a} - e^{b})$ where a = self and b = other, computed in a
numerically stable way that avoids overflow. Read moreSource§fn kl_div(self, y: Self) -> Self
fn kl_div(self, y: Self) -> Self
x = self. Read moreSource§fn smoothstep<const N: usize>(self, edges: Option<(Self, Self)>) -> Self
fn smoothstep<const N: usize>(self, edges: Option<(Self, Self)>) -> Self
2N-1. Note: The “smoothness”
for higher order is in terms of the number of continuous derivatives,
not in terms of visual smoothness, though they are related in some ways. Read moreSource§fn inverse_smoothstep<const N: usize>(self, edges: Option<(Self, Self)>) -> Self
fn inverse_smoothstep<const N: usize>(self, edges: Option<(Self, Self)>) -> Self
self, which is the value that would produce self when passed to smoothstep. Read moreSource§fn smoothstep_derivative<const N: usize>(
self,
edges: Option<(Self, Self)>,
) -> Self
fn smoothstep_derivative<const N: usize>( self, edges: Option<(Self, Self)>, ) -> Self
smoothstep function of order 2N-1, at the given point.Source§fn smooth_interpolator(self, edges: Option<(Self, Self)>, k: Self) -> Self
fn smooth_interpolator(self, edges: Option<(Self, Self)>, k: Self) -> Self
Source§fn smooth_interpolator_inverse(
self,
edges: Option<(Self, Self)>,
k: Self,
) -> Self
fn smooth_interpolator_inverse( self, edges: Option<(Self, Self)>, k: Self, ) -> Self
smooth_interpolator. Read moreSource§impl<E, V> RealMathWithPolicy for Vwhere
E: FloatElement,
V: FloatVector<Element = E> + TranscendentalMathWithPolicy + SpatialMathWithPolicy + SpecializedRealMath<E>,
impl<E, V> RealMathWithPolicy for Vwhere
E: FloatElement,
V: FloatVector<Element = E> + TranscendentalMathWithPolicy + SpatialMathWithPolicy + SpecializedRealMath<E>,
Source§fn tolerance_p<P>() -> Vwhere
P: Policy,
fn tolerance_p<P>() -> Vwhere
P: Policy,
Returns the precision tolerance based on the selected policy. This is a good default tolerance to use for numerical methods.
Source§fn to_degrees_p<P>(self) -> Vwhere
P: Policy,
fn to_degrees_p<P>(self) -> Vwhere
P: Policy,
Converts angles from radians to degrees.
Source§fn to_radians_p<P>(self) -> Vwhere
P: Policy,
fn to_radians_p<P>(self) -> Vwhere
P: Policy,
Converts angles from degrees to radians.
Source§fn wrap_angle_p<P>(self) -> Vwhere
P: Policy,
fn wrap_angle_p<P>(self) -> Vwhere
P: Policy,
Wraps the angle (radians) in self to the range [-π, π).
The formula for this is self - floor((self + π) / 2π) * 2π
Source§fn angle_diff_p<P>(self, other: V) -> Vwhere
P: Policy,
fn angle_diff_p<P>(self, other: V) -> Vwhere
P: Policy,
Computes the smallest difference between two angles (in radians), taking into account angle wrapping.
To get the “distance” between two angles, use the absolute value of the result.
Source§fn atan2_p<P>(self, x: V) -> Vwhere
P: Policy,
fn atan2_p<P>(self, x: V) -> Vwhere
P: Policy,
Returns the four-quadrant arctangent of self and x.
This method is only defined for real-valued types.
Source§fn lerp_p<P>(self, a: V, b: V) -> Vwhere
P: Policy,
fn lerp_p<P>(self, a: V, b: V) -> Vwhere
P: Policy,
Linearly interpolates between a and b based on the value of self.
This operation is not clamped.
Source§fn rescale_p<P>(self, in_min: V, in_max: V, out_min: V, out_max: V) -> Vwhere
P: Policy,
fn rescale_p<P>(self, in_min: V, in_max: V, out_min: V, out_max: V) -> Vwhere
P: Policy,
Scales self from the input range [in_min, in_max] to the output range [out_min, out_max].
This operation is not clamped.
Source§fn logaddexp_p<P>(self, other: V) -> Vwhere
P: Policy,
fn logaddexp_p<P>(self, other: V) -> Vwhere
P: Policy,
Returns $\ln(e^{a} + e^{b})$ computed in a numerically stable way that avoids overflow,
where a = self and b = other.
Evaluated as $\max(a, b) + \ln(1 + e^{-|a - b|})$, so the result is accurate even when a
and b are large. This is the workhorse of stable log-domain probability arithmetic
(e.g. the two-argument log-sum-exp).
Source§fn logmean_p<P>(self, other: V) -> Vwhere
P: Policy,
fn logmean_p<P>(self, other: V) -> Vwhere
P: Policy,
The logarithmic mean $L(x, y) = \frac{x - y}{\ln x - \ln y}$, for positive x and y.
Sits between the geometric and arithmetic means, and is the mean that arises whenever a quantity varies exponentially across an interval, the log-mean temperature difference of a heat exchanger being the standard example.
The defining form cancels in both the numerator and the denominator as x approaches y,
which is the common case rather than a corner. Evaluated here as
$\frac{x - y}{2\,\mathrm{atanh}\!\left(\frac{x-y}{x+y}\right)}$, which is stable
throughout. For nearby arguments the subtraction is exact by Sterbenz’s lemma and atanh
is accurate near zero. Equal arguments return x, the limiting value.
Source§fn logsumexp_n_p<P, const N: usize>(values: [V; N]) -> Vwhere
P: Policy,
fn logsumexp_n_p<P, const N: usize>(values: [V; N]) -> Vwhere
P: Policy,
Returns $\ln\left(\sum_{i} e^{x_i}\right)$ over N values, computed in a
numerically stable way that avoids overflow.
The N-ary logaddexp: normalizing a set of log-weights,
the denominator of a log-softmax, the forward pass of an HMM. The largest term is
factored out first, so no intermediate exponential can overflow whatever the
inputs are.
N = 0 gives -inf, the empty sum and the identity of logaddexp, so folding
this over any partition of the inputs agrees with running it over all of them at
once. Above the Worst precision policy the non-dominant terms go through
ln_1p, which keeps the answer accurate when one weight dominates.
§Examples
use thermite::prelude::*;
type V = Vector<f64>;
// Overflows outright if evaluated as `ln(e^1000 + e^1001 + e^999)`.
let y = V::logsumexp_n([V::splat(1000.0), V::splat(1001.0), V::splat(999.0)]);
assert!((y.extract::<0>() - 1001.4076059644443).abs() < 1e-12);Source§fn logsumexp_p<P>(values: &[V]) -> Vwhere
P: Policy,
fn logsumexp_p<P>(values: &[V]) -> Vwhere
P: Policy,
logsumexp_n over a runtime-length slice.
Same shifted evaluation and the same overflow safety. The length simply is not a
constant, so the max and the sum are serial folds rather than tree reductions and
the N = 1/N = 2 shortcuts are runtime branches. If you know the count at
compile time, prefer logsumexp_n.
The empty input gives -inf, the identity of logaddexp, so folding this over
any partition of the inputs agrees.
Source§fn logsubexp_p<P>(self, other: V) -> Vwhere
P: Policy,
fn logsubexp_p<P>(self, other: V) -> Vwhere
P: Policy,
Returns $\ln(e^{a} - e^{b})$ where a = self and b = other, computed in a
numerically stable way that avoids overflow.
The subtractive counterpart of logaddexp, for removing a
term from a log-domain sum (a leave-one-out normalizer, a difference of
cumulative distribution functions in log space). Evaluated as
$a + \ln(1 - e^{-(a - b)})$ via ln1m_expnx,
so no intermediate exponential overflows and the precision ladder is that
kernel’s.
At Average precision and above, $\ln(1 - e^{-x})$ is split into two regimes
at $\ln 2$, keeping the subtraction inside exp_m1 below the split and inside
ln_1p above it, which is accurate at both ends of the gap. A single
$(1 - e^{-x})$ followed by a log loses the small gaps to cancellation and the
large ones to $1 - e^{-x}$ rounding to exactly 1. Below Average,
ln1m_expnx’s cheaper forms apply, with the accuracy losses those tiers accept.
The result exists only for a >= b, and is -inf at a == b. An a < b input
is out of domain and gives NaN at Average precision and above.
Source§fn entr_p<P>(self) -> Vwhere
P: Policy,
fn entr_p<P>(self) -> Vwhere
P: Policy,
The entropy term $-x \ln x$ of self, extended to the closed half-line.
\mathrm{entr}(x) = \begin{cases} -x \ln x & x > 0 \\ 0 & x = 0 \\ -\infty & x < 0\end{cases}The $x = 0$ value is the limit. The $-\infty$ below zero is not a limit but a
convention, the extended-value form that keeps entr concave over all of $\mathbb{R}$
so a convex solver can use it as a barrier. SciPy, CVXPY and Convex.jl all define it
this way. Summing entr over a distribution gives its Shannon entropy in nats.
Source§fn rel_entr_p<P>(self, y: V) -> Vwhere
P: Policy,
fn rel_entr_p<P>(self, y: V) -> Vwhere
P: Policy,
The relative-entropy term $x \ln(x/y)$, with x = self.
\mathrm{rel\_entr}(x, y) = \begin{cases} x \ln(x/y) & x > 0,\; y > 0 \\ 0 & x = 0,\; y \ge 0 \\ +\infty & \text{otherwise}\end{cases}This is the Kullback-Leibler summand: $D_{KL}(P \Vert Q)$ is the sum of
rel_entr over the two distributions, and kl_div is the one
that carries extra terms, not this. The naming is
SciPy’s and catches people out in both directions.
The $+\infty$ covers $y = 0$ at positive x (an event the model assigns zero
probability but the data observed, which is genuinely infinite surprise) as well as
negative inputs, which are out of domain.
Source§fn kl_div_p<P>(self, y: V) -> Vwhere
P: Policy,
fn kl_div_p<P>(self, y: V) -> Vwhere
P: Policy,
The convex-programming Kullback-Leibler divergence term, with x = self.
\mathrm{kl\_div}(x, y) = \begin{cases} x \ln(x/y) - x + y & x > 0,\; y > 0 \\ y & x = 0,\; y \ge 0 \\ +\infty & \text{otherwise}\end{cases}The $-x + y$ tail is not part of the Kullback-Leibler divergence. It is what
makes this the Bregman divergence generated by $x \ln x$, which is non-negative and
zero only at $x = y$ even when the arguments are unnormalized, the property a
solver needs and that the bare summand lacks. For the divergence itself use
rel_entr, whose sum over a normalized pair equals this
one’s because the tails cancel.
That tail is also why the written form cannot be evaluated as written. With
$y = x(1+u)$ the log term is $-xu + xu^2/2$ and the tail is $+xu$: two
first-order quantities cancelling to a second-order answer, so near $x = y$ the
direct spelling is not imprecise but entirely wrong. Evaluated here as the
identity $-x \cdot \mathrm{log1pmx}((y-x)/x)$, which moves the cancellation inside
log1pmx, where it belongs. Same identity as the
Poisson deviance; bd0 in thermite-special is this function under another name.
Source§fn smoothstep_p<P, const N: usize>(self, edges: Option<(V, V)>) -> Vwhere
P: Policy,
fn smoothstep_p<P, const N: usize>(self, edges: Option<(V, V)>) -> Vwhere
P: Policy,
Generalized smoothstep function of Order 2N-1. Note: The “smoothness”
for higher order is in terms of the number of continuous derivatives,
not in terms of visual smoothness, though they are related in some ways.
For N=0, this is equivalent to the step function.
For N=1, this is a linear line between 0 and 1.
For N=2, this is equivalent to the standard 3rd-order smoothstep function.
For N=3, this is equivalent to the 5th-order “smootherstep” function.
For single precision, N can go up to 10, whereas for double precision, N can go up to 20.
See smooth_interpolator for a more advanced interpolator with
infinite differentiability.
§Examples
use thermite::prelude::*;
type V = Vector<f64>;
// Standard 3rd-order smoothstep (N = 2) over the default [0, 1] edges:
// 3t^2 - 2t^3
let y = V::splat(0.25).smoothstep::<2>(None);
assert!((y.extract::<0>() - 0.15625).abs() < 1e-15);Source§fn inverse_smoothstep_p<P, const N: usize>(self, edges: Option<(V, V)>) -> Vwhere
P: Policy,
fn inverse_smoothstep_p<P, const N: usize>(self, edges: Option<(V, V)>) -> Vwhere
P: Policy,
Returns the inverse smoothstep of self, which is the value that would produce self when passed to smoothstep.
N from 0..=2 have fast closed-form solutions, while higher N use numerical root-finding methods, which will inherently be much slower.
§Examples
Round-trips smoothstep, even at high orders where
the inverse must be found numerically:
use thermite::prelude::*;
type V = Vector<f64>;
let x = V::splat(1.0 / 16.0);
let y = x.smoothstep::<12>(None);
let x_back = y.inverse_smoothstep::<12>(None);
assert!((x_back.extract::<0>() - x.extract::<0>()).abs() < 1e-9);Source§fn smoothstep_derivative_p<P, const N: usize>(self, edges: Option<(V, V)>) -> Vwhere
P: Policy,
fn smoothstep_derivative_p<P, const N: usize>(self, edges: Option<(V, V)>) -> Vwhere
P: Policy,
Derivative of the smoothstep function of order 2N-1, at the given point.
Source§fn smooth_interpolator_p<P>(self, edges: Option<(V, V)>, k: V) -> Vwhere
P: Policy,
fn smooth_interpolator_p<P>(self, edges: Option<(V, V)>, k: V) -> Vwhere
P: Policy,
C∞-smooth interpolation factor between the given edges (defaulting to 0 and 1).
Constructs a smooth transition function using:
f(x) = e^(-1 / (k * x))
g(x) = f(x) / (f(x) + f(1 - x))The result is C∞-differentiable (infinitely smooth), with all derivatives vanishing at both endpoints, so it beats polynomial smoothstep wherever flatness at the edges is what matters.
The k parameter controls the shape of the transition:
k < 1: sharpens the curve, concentrating the transition near the midpoint.k = 1: the standard balanced sigmoid-like transition.k > 1: stretches the transition region, so the curve is more gradual.$k \approx 2/\sqrt{3}$(~1.1547): the function becomes bimodal. Use with caution above this value.
Source§fn smooth_interpolator_inverse_p<P>(self, edges: Option<(V, V)>, k: V) -> Vwhere
P: Policy,
fn smooth_interpolator_inverse_p<P>(self, edges: Option<(V, V)>, k: V) -> Vwhere
P: Policy,
Inverse of smooth_interpolator.
Given an output value y in [0, 1], recovers the input x such that
smooth_interpolator(x, edges, k) ≈ y.
Source§impl<M> RealPrimalMath for Mwhere
M: RealPrimalMathWithPolicy,
impl<M> RealPrimalMath for Mwhere
M: RealPrimalMathWithPolicy,
Source§fn spherical_harmonics_d<const L: usize, const N: usize, const CS: bool>(
x: Self,
y: Self,
z: Self,
out: &mut [Self; N],
ddx: &mut [Self; N],
ddy: &mut [Self; N],
ddz: &mut [Self; N],
)
fn spherical_harmonics_d<const L: usize, const N: usize, const CS: bool>( x: Self, y: Self, z: Self, out: &mut [Self; N], ddx: &mut [Self; N], ddy: &mut [Self; N], ddz: &mut [Self; N], )
spherical_harmonics plus the
ambient Cartesian gradient of every harmonic, into ddx/ddy/ddz. Read moreSource§fn zernike_basis_d<const L: usize, const NORM: u8, const N: usize>(
x: Self,
y: Self,
out: &mut [Self; N],
ddx: &mut [Self; N],
ddy: &mut [Self; N],
)
fn zernike_basis_d<const L: usize, const NORM: u8, const N: usize>( x: Self, y: Self, out: &mut [Self; N], ddx: &mut [Self; N], ddy: &mut [Self; N], )
zernike_basis plus $\partial Z_n^m/\partial x$
and $\partial Z_n^m/\partial y$ for every mode, in the same ANSI layout. Read moreSource§fn spherical_harmonics_d_with<const L: usize, const N: usize>(
table: &ShTable<Self, N>,
x: Self,
y: Self,
z: Self,
out: &mut [Self; N],
ddx: &mut [Self; N],
ddy: &mut [Self; N],
ddz: &mut [Self; N],
)
fn spherical_harmonics_d_with<const L: usize, const N: usize>( table: &ShTable<Self, N>, x: Self, y: Self, z: Self, out: &mut [Self; N], ddx: &mut [Self; N], ddy: &mut [Self; N], ddz: &mut [Self; N], )
spherical_harmonics_with plus
the ambient Cartesian gradients, from a prebuilt table.Source§fn softplus_d(self, k: Self, rcp_k: Self) -> (Self, Self)
fn softplus_d(self, k: Self, rcp_k: Self) -> (Self, Self)
Source§fn algebraic_sigmoid_d_n<const N: usize>(self) -> (Self, Self)
fn algebraic_sigmoid_d_n<const N: usize>(self) -> (Self, Self)
algebraic_sigmoid together with its derivative w.r.t. x.Source§fn algebraic_sigmoid_d(self, n: u32) -> (Self, Self)
fn algebraic_sigmoid_d(self, n: u32) -> (Self, Self)
algebraic_sigmoid_d_n for a degree known
only at runtime.Source§fn algebraic_swish_d(self) -> (Self, Self)
fn algebraic_swish_d(self) -> (Self, Self)
algebraic_swish together with its derivative w.r.t. x.Source§fn langevin_d(self) -> (Self, Self)
fn langevin_d(self) -> (Self, Self)
Source§impl<E, V> RealPrimalMathWithPolicy for Vwhere
V: FloatVector<Element = E> + RealSpecialMathWithPolicy + PrimalMathWithPolicy + SpecializedRealPrimalMath<E>,
impl<E, V> RealPrimalMathWithPolicy for Vwhere
V: FloatVector<Element = E> + RealSpecialMathWithPolicy + PrimalMathWithPolicy + SpecializedRealPrimalMath<E>,
Source§fn spherical_harmonics_d_p<P, const L: usize, const N: usize, const CS: bool>(
x: V,
y: V,
z: V,
out: &mut [V; N],
ddx: &mut [V; N],
ddy: &mut [V; N],
ddz: &mut [V; N],
)where
P: Policy,
fn spherical_harmonics_d_p<P, const L: usize, const N: usize, const CS: bool>(
x: V,
y: V,
z: V,
out: &mut [V; N],
ddx: &mut [V; N],
ddy: &mut [V; N],
ddz: &mut [V; N],
)where
P: Policy,
spherical_harmonics plus the
ambient Cartesian gradient of every harmonic, into ddx/ddy/ddz.
Lives on RealPrimalMath rather than RealSpecialMath, so Dual does
not get it, and should not want it. If you need $\partial/\partial(x,y,z)$,
call this directly rather than evaluating
spherical_harmonics on a
Dual<V, 3> seeded with an identity Jacobian: this shares the recurrence
between the value and all three gradients, whereas dual arithmetic carries a
derivative through every operation and costs roughly twice as much.
Dual earns its keep on the value form instead, where (x, y, z) are
themselves functions of upstream parameters and the chain rule has real work
to do. Even there, going the other way (contracting these three gradients
against an upstream Jacobian) loses: spherical harmonics cost about two
operations per harmonic to evaluate but three per harmonic per parameter to
contract, because one recurrence produces the whole basis.
The derivatives are those of the polynomial form at the given (unit)
input. Project out the radial component (g - (g . n) n) for the
tangential gradient. Shares all recurrence work with the value pass, since
the gradients come from tabulated norm ratios, not new recurrences.
Source§fn zernike_basis_d_p<P, const L: usize, const NORM: u8, const N: usize>(
x: V,
y: V,
out: &mut [V; N],
ddx: &mut [V; N],
ddy: &mut [V; N],
)where
P: Policy,
fn zernike_basis_d_p<P, const L: usize, const NORM: u8, const N: usize>(
x: V,
y: V,
out: &mut [V; N],
ddx: &mut [V; N],
ddy: &mut [V; N],
)where
P: Policy,
zernike_basis plus $\partial Z_n^m/\partial x$
and $\partial Z_n^m/\partial y$ for every mode, in the same ANSI layout.
This is what a Shack-Hartmann wavefront reconstruction integrates against. The sensor measures local wavefront slopes, not the wavefront itself, so the fit matrix is built from the gradient basis and the value basis never appears in it.
Lives on RealPrimalMath rather than SpecialMath for the same reason
spherical_harmonics_d does: Dual
should not get it and should not want it. Seeding a Dual<V, 2> and calling the
value form carries two derivative components through every operation of the whole
ladder, where this differentiates only the two factors that depend on the point
and shares the radial recurrence between the value and both gradients.
The gradient is finite everywhere, including the pupil centre. That is the
practical dividend of the Cartesian formulation: the polar
$\partial_\theta Z/\rho$ is singular there, and hand-rolled polar
implementations guard the origin with a special case.
N must equal (L+1)(L+2)/2, and NORM is as on
zernike_basis. All three output buffers are
written in full.
Source§fn spherical_harmonics_d_with_p<P, const L: usize, const N: usize>(
table: &ShTable<V, N>,
x: V,
y: V,
z: V,
out: &mut [V; N],
ddx: &mut [V; N],
ddy: &mut [V; N],
ddz: &mut [V; N],
)where
P: Policy,
fn spherical_harmonics_d_with_p<P, const L: usize, const N: usize>(
table: &ShTable<V, N>,
x: V,
y: V,
z: V,
out: &mut [V; N],
ddx: &mut [V; N],
ddy: &mut [V; N],
ddz: &mut [V; N],
)where
P: Policy,
spherical_harmonics_with plus
the ambient Cartesian gradients, from a prebuilt table.
Source§fn softplus_d_p<P>(self, k: V, rcp_k: V) -> (V, V)where
P: Policy,
fn softplus_d_p<P>(self, k: V, rcp_k: V) -> (V, V)where
P: Policy,
softplus together with its derivative w.r.t. x
(the logistic sigmoid $\sigma(kx)$).
Source§fn gelu_d_p<P>(self, alpha: V) -> (V, V)where
P: Policy,
fn gelu_d_p<P>(self, alpha: V) -> (V, V)where
P: Policy,
gelu together with its derivative w.r.t. x.
Source§fn swish_d_p<P>(self, beta: V) -> (V, V)where
P: Policy,
fn swish_d_p<P>(self, beta: V) -> (V, V)where
P: Policy,
swish together with its derivative w.r.t. x.
Source§fn algebraic_sigmoid_d_n_p<P, const N: usize>(self) -> (V, V)where
P: Policy,
fn algebraic_sigmoid_d_n_p<P, const N: usize>(self) -> (V, V)where
P: Policy,
algebraic_sigmoid together with its derivative w.r.t. x.
Source§fn algebraic_sigmoid_d_p<P>(self, n: u32) -> (V, V)where
P: Policy,
fn algebraic_sigmoid_d_p<P>(self, n: u32) -> (V, V)where
P: Policy,
algebraic_sigmoid_d_n for a degree known
only at runtime.
Source§fn algebraic_swish_d_p<P>(self) -> (V, V)where
P: Policy,
fn algebraic_swish_d_p<P>(self) -> (V, V)where
P: Policy,
algebraic_swish together with its derivative w.r.t. x.
Source§impl<M> RealSpecialMath for Mwhere
M: RealSpecialMathWithPolicy,
impl<M> RealSpecialMath for Mwhere
M: RealSpecialMathWithPolicy,
Source§fn probit(self) -> Self
fn probit(self) -> Self
Source§fn log_ndtr(self) -> Self
fn log_ndtr(self) -> Self
$\ln \Phi(x)$, the logarithm of the standard normal CDF, finite
for every finite x. Read moreSource§fn fresnel(self) -> (Self, Self)
fn fresnel(self) -> (Self, Self)
$S(x) = \int_0^x \sin(\pi t^2/2)\,dt$ and
$C(x) = \int_0^x \cos(\pi t^2/2)\,dt$, together. Read moreSource§fn sici(self) -> (Self, Self)
fn sici(self) -> (Self, Self)
$\mathrm{Si}(x) = \int_0^x \frac{\sin t}{t}\,dt$
and $\mathrm{Ci}(x) = \gamma + \ln x + \int_0^x \frac{\cos t - 1}{t}\,dt$,
together. Returns (Si, Ci). Read moreSource§fn inv_log_ndtr(self) -> Self
fn inv_log_ndtr(self) -> Self
Source§fn inv_digamma(self) -> Self
fn inv_digamma(self) -> Self
Source§fn wright_omega(self) -> Self
fn wright_omega(self) -> Self
Source§fn bessel_ratio<F>(self, nu: Self) -> Selfwhere
F: BesselRatioFamily,
fn bessel_ratio<F>(self, nu: Self) -> Selfwhere
F: BesselRatioFamily,
$A_\nu(x) = I_\nu(x) / I_{\nu-1}(x)$ for
nu >= 1, odd in x. Read moreSource§fn inv_bessel_ratio<F>(self, nu: Self) -> Selfwhere
F: BesselRatioFamily,
fn inv_bessel_ratio<F>(self, nu: Self) -> Selfwhere
F: BesselRatioFamily,
bessel_ratio: the
concentration $\kappa$ with $I_\nu(\kappa)/I_{\nu-1}(\kappa) = r$, for
0 <= r < 1, odd in r. Read moreSource§fn bessel_ratio_1m<F>(self, nu: Self) -> Selfwhere
F: BesselRatioFamily,
fn bessel_ratio_1m<F>(self, nu: Self) -> Selfwhere
F: BesselRatioFamily,
$1 - A_\nu(x)$, the complement of
bessel_ratio, to full relative accuracy
where the ratio itself is within an ulp of 1. Read moreSource§fn inv_bessel_ratio_1m<F>(self, nu: Self) -> Selfwhere
F: BesselRatioFamily,
fn inv_bessel_ratio_1m<F>(self, nu: Self) -> Selfwhere
F: BesselRatioFamily,
bessel_ratio_1m:
the concentration $\kappa$ with $1 - I_\nu(\kappa)/I_{\nu-1}(\kappa) = t$, for
0 < t <= 2 (t = 1 - r). Read moreSource§fn gauss_legendre(self, n: u32) -> (Self, Self)
fn gauss_legendre(self, n: u32) -> (Self, Self)
k-th node and weight of the n-point Gauss-Legendre quadrature
rule on $[-1, 1]$, with the root index k taken per lane. Read moreSource§fn gauss_hermite(self, n: u32) -> (Self, Self)
fn gauss_hermite(self, n: u32) -> (Self, Self)
k-th node and weight of the n-point Gauss-Hermite rule, for
$\int_{-\infty}^{\infty} f(x) e^{-x^2}\,dx \approx \sum_k w_k f(x_k)$, the root
index k per lane (k = 0 the largest root, $x_{n-1-k} = -x_k$). Read moreSource§fn gauss_laguerre(self, alpha: Self, n: u32) -> (Self, Self)
fn gauss_laguerre(self, alpha: Self, n: u32) -> (Self, Self)
k-th node and weight of the n-point Gauss-Laguerre rule, for
$\int_0^{\infty} f(x)\, x^\alpha e^{-x}\,dx \approx \sum_k w_k f(x_k)$, the root
index k and alpha > -1 per lane (k = 0 the largest root). Read moreSource§fn pochhammer(self, m: Self) -> Self
fn pochhammer(self, m: Self) -> Self
$(z)_m = \dfrac{\Gamma(z+m)}{\Gamma(z)}$. Read moreSource§fn jacobi_elliptic(self, k: Self) -> (Self, Self, Self)
fn jacobi_elliptic(self, k: Self) -> (Self, Self, Self)
$(\mathrm{sn}, \mathrm{cn}, \mathrm{dn})$
at argument self and modulus k, all three from one evaluation. Read moreSource§fn agm(self, other: Self) -> Self
fn agm(self, other: Self) -> Self
$\mathrm{AGM}(a, b)$ of two non-negative
arguments. Read moreSource§fn langevin(self) -> Self
fn langevin(self) -> Self
$L(x) = \coth x - \frac{1}{x}$. Read moreSource§fn inv_langevin(self) -> Self
fn inv_langevin(self) -> Self
Source§fn langevin_1m(self) -> Self
fn langevin_1m(self) -> Self
1 - L(x), the complement of the Langevin function,
accurately where L(x) is within rounding of 1. Read moreSource§fn inv_langevin_1m(self) -> Self
fn inv_langevin_1m(self) -> Self
Source§fn gelu(self, alpha: Self) -> Self
fn gelu(self, alpha: Self) -> Self
$\tfrac{1}{2} x \left(1 + \operatorname{erf}\!\left(\frac{\alpha x}{\sqrt{2}}\right)\right)$,
where alpha helps control the shape of the curve. The standard GELU function
is recovered when alpha is 1. Read moreSource§fn swish(self, beta: Self) -> Self
fn swish(self, beta: Self) -> Self
$x\,\sigma(\beta x) = \frac{x}{1 + e^{-\beta x}}$,
where beta controls the sharpness of the gate. The standard Swish/SiLU function
is recovered when beta is 1. As beta -> 0, the output approaches x/2 (half-identity);
as beta -> inf, Swish approaches ReLU. Read moreSource§fn algebraic_sigmoid_n<const N: usize>(self) -> Self
fn algebraic_sigmoid_n<const N: usize>(self) -> Self
$\frac{x}{(1 + |x|^N)^{1/N}}$, where
N is a positive integer parameter that controls the steepness of the curve. Read moreSource§fn algebraic_sigmoid(self, n: u32) -> Self
fn algebraic_sigmoid(self, n: u32) -> Self
algebraic_sigmoid_n, same arithmetic.Source§fn algebraic_swish(self) -> Self
fn algebraic_swish(self) -> Self
Source§fn lgamma_r(self) -> (Self, Self)
fn lgamma_r(self) -> (Self, Self)
$\ln|\Gamma(x)|$) for any real input, for each value in a vector,
and returns the sign of the Gamma function from before the absolute value was taken.Source§fn gaussian_integral(x0: Self, x1: Self, a: Self, c: Self) -> Self
fn gaussian_integral(x0: Self, x1: Self, a: Self, c: Self) -> Self
x0 to x1, with amplitude a and standard deviation c.
This is more efficient than evaluating the indefinite integral at both limits and subtracting. Read moreSource§fn inv_boxcox(self, lambda: Self) -> Self
fn inv_boxcox(self, lambda: Self) -> Self
Source§fn inv_boxcox_1p(self, lambda: Self) -> Self
fn inv_boxcox_1p(self, lambda: Self) -> Self
Source§fn yeo_johnson(self, lambda: Self) -> Self
fn yeo_johnson(self, lambda: Self) -> Self
Source§fn inv_yeo_johnson(self, lambda: Self) -> Self
fn inv_yeo_johnson(self, lambda: Self) -> Self
Source§fn spherical_harmonics<const L: usize, const N: usize, const CS: bool>(
x: Self,
y: Self,
z: Self,
out: &mut [Self; N],
)
fn spherical_harmonics<const L: usize, const N: usize, const CS: bool>( x: Self, y: Self, z: Self, out: &mut [Self; N], )
L at the unit
direction (x, y, z), into out[l * (l + 1) + m] for m in -l..=l. Read moreSource§fn spherical_harmonics_table<const L: usize, const N: usize, const CS: bool>(
table: &mut ShTable<Self::Primal, N>,
)
fn spherical_harmonics_table<const L: usize, const N: usize, const CS: bool>( table: &mut ShTable<Self::Primal, N>, )
spherical_harmonics_with
and spherical_harmonics_d_with evaluate. Read moreSource§impl<E, V> RealSpecialMathWithPolicy for V
impl<E, V> RealSpecialMathWithPolicy for V
Source§fn probit_p<P>(self) -> Vwhere
P: Policy,
fn probit_p<P>(self) -> Vwhere
P: Policy,
Computes the Probit function, the inverse of the cumulative distribution function of the standard normal distribution.
Source§fn ndtr_p<P>(self) -> Vwhere
P: Policy,
fn ndtr_p<P>(self) -> Vwhere
P: Policy,
Computes the cumulative distribution function of the standard normal
distribution, the inverse of probit:
\Phi(x) = \frac{1}{\sqrt{2\pi}} \int_{-\infty}^{x} e^{-t^2/2}\,dt
= \tfrac12 \operatorname{erfc}\!\left(-\frac{x}{\sqrt 2}\right)The probability that a standard normal variable falls below x: z-scores to
p-values, the N(d_1)/N(d_2) terms of Black-Scholes, the probit link, and
x * ndtr(x) is GELU. The name is Cephes/SciPy’s.
Underflows to zero below about x = -38.6 (f64) and -14.4 (f32). When
the tail probability itself is the quantity of interest, use
log_ndtr, which is finite there.
Source§fn log_ndtr_p<P>(self) -> Vwhere
P: Policy,
fn log_ndtr_p<P>(self) -> Vwhere
P: Policy,
Computes $\ln \Phi(x)$, the logarithm of the standard normal CDF, finite
for every finite x.
ln(ndtr(x)) is -inf below x ~ -38.6 in f64 (-14.4 in f32), exactly
where a probit or censored-regression likelihood, a truncated-normal density,
or an expected-improvement acquisition needs the tail: log_ndtr(-100) is an
ordinary -5004.6. The kernel keeps $-x^2/2$ in the log domain and takes
the rest from erfcx, which has no underflow, so the
left tail carries full relative accuracy to the largest x whose square is
representable. On the right it is ln_1p of the complement, so
log_ndtr(10) = -7.6e-24 rather than a rounded zero.
Costs one erfcx, one ln_1p, and an exp for the lanes with x > 0.
Source§fn logerfc_p<P>(self) -> Vwhere
P: Policy,
fn logerfc_p<P>(self) -> Vwhere
P: Policy,
Computes $\ln \operatorname{erfc}(x)$, finite for every finite x.
erfc underflows at x ~ 27 (f64) / 9.3 (f32) and its logarithm does
not: logerfc(100) = -10004.8. This is the log-domain form of a Gaussian
tail wherever erfc rather than the normal CDF is the natural quantity
(Ewald sums, Gaussian-smeared edges, the Mills ratio in the log domain), and
it is log_ndtr with x = -\sqrt 2 x'. Built on
erfcx with $-x^2$ kept in the log domain. On the
left, where erfc(x) is between 1 and 2, it is ln_1p(erf(|x|)), so the
result stays accurate down to logerfc(-1e-20) = 1.13e-20.
Source§fn fresnel_p<P>(self) -> (V, V)where
P: Policy,
fn fresnel_p<P>(self) -> (V, V)where
P: Policy,
The Fresnel integrals $S(x) = \int_0^x \sin(\pi t^2/2)\,dt$ and
$C(x) = \int_0^x \cos(\pi t^2/2)\,dt$, together.
Returns (S, C), the same order as SciPy’s fresnel and this crate’s own
sici.
Both are odd, both tend to 1/2, and both stay in [0.32, 0.72] past the
first oscillation. Measured against a 45-digit oracle over x from 1e-4 to
1e15: 2.80 ulp (C) and 2.64 (S) in f64, 2.14 and 3.40 in f32 out to
1e7.
The phase $\pi x^2/2$ is carried in two words and reduced exactly, which is
not a refinement but the whole of the large-argument accuracy: computed the
obvious way as x*x*0.5, the phase is already 5.3e-6 wrong at x = 98765 and
returns the wrong sign by $x \approx 10^9$, and since C and S are
1/2 plus a term of size $1/(\pi x)$ that error lands straight on the
result. Below Average the residual is dropped and that behaviour returns.
Above x = 1.147e16 (f64) / 2.136e7 (f32) the oscillating correction is
under half an ulp of 1/2, and both are exactly 1/2.
Source§fn fresnel_c_p<P>(self) -> Vwhere
P: Policy,
fn fresnel_c_p<P>(self) -> Vwhere
P: Policy,
$C(x)$ alone. See fresnel.
Unlike airy::<Ai> this is not a cheaper evaluation by
much: C and S share the argument reduction, the phase and both
auxiliaries, so asking for one drops a single Chebyshev series and one
reconstruction: roughly a third, not three quarters.
Source§fn fresnel_s_p<P>(self) -> Vwhere
P: Policy,
fn fresnel_s_p<P>(self) -> Vwhere
P: Policy,
$S(x)$ alone. See fresnel_c.
Source§fn sici_p<P>(self) -> (V, V)where
P: Policy,
fn sici_p<P>(self) -> (V, V)where
P: Policy,
The trigonometric integrals $\mathrm{Si}(x) = \int_0^x \frac{\sin t}{t}\,dt$
and $\mathrm{Ci}(x) = \gamma + \ln x + \int_0^x \frac{\cos t - 1}{t}\,dt$,
together. Returns (Si, Ci).
Si is odd. Ci is real only on the positive axis ($\mathrm{Ci}(-x) = \mathrm{Ci}(x) + i\pi$), so this returns Ci(|x|), dropping the imaginary
part, which is what SciPy’s sici does. Ci(0) is $-\infty$.
Measured 2.03 ulp (Si) and 1.42 (Ci, against its envelope) in f64 over
x from 1e-4 to 1e15. In f32, 1.34 and 1.99.
Two things worth knowing before relying on Ci:
- It has zeros, the first near
x = 0.6165, and no algorithm is relatively accurate at one. The accuracy above is relative to$\lvert\gamma + \ln x\rvert + \lvert\mathrm{Cin}\rvert$below the crossover and to the$1/x$envelope above it. - Its large-argument accuracy is
sin_cos’s: forCithe oscillation is the value, so a phase error is a relative error, and full argument reduction is aBest-tier property.Siis insulated, tending to$\pi/2$with the oscillation only a$1/x$correction, and is$\pi/2$exactly abovex = 1.147e16(f64) /2.136e7(f32).Cihas no such cutoff: it decays like$1/x$and stays representable for every finitex.
Source§fn inv_log_ndtr_p<P>(self) -> Vwhere
P: Policy,
fn inv_log_ndtr_p<P>(self) -> Vwhere
P: Policy,
Computes the inverse of log_ndtr: the x with
$\ln \Phi(x) = y$, for y <= 0. The quantile of a log-probability.
probit of $e^y$ stops working once $e^y$
underflows (y < -745 in f64), which is exactly where a log-likelihood, a
truncated-normal EM step or an extreme-value fit needs the quantile. This
inverts log_ndtr directly, by Newton with the inverse Mills ratio as the
derivative, from a probit(e^y) seed one precision tier down where that
exists and from the tail asymptotic below. Within a few ulp of the true inverse
of the given y over the whole domain. y = 0 gives +inf, y = -inf gives
-inf, and y > 0 is NaN.
Source§fn inv_digamma_p<P>(self) -> Vwhere
P: Policy,
fn inv_digamma_p<P>(self) -> Vwhere
P: Policy,
Computes the inverse of the digamma function on $(0, \infty)$: the x with
$\psi(x) = y$.
The maximum-likelihood estimate of a gamma shape or a Dirichlet concentration is
this function of a mean log. Newton on digamma with trigamma from Minka’s
seed ($e^y + 1/2$ above y = -2.22, $-1/(y + \gamma)$ below). Above y = 6
the Stirling series is solved for x directly, since there Newton on digamma
cannot see past digamma’s own rounding. +inf maps to +inf and -inf to 0.
Source§fn wright_omega_p<P>(self) -> Vwhere
P: Policy,
fn wright_omega_p<P>(self) -> Vwhere
P: Policy,
Computes the Wright omega function, the $\omega > 0$ with
$\omega + \ln \omega = x$.
This is $W_0(e^x)$, the principal Lambert W of an exponential, evaluated without
forming $e^x$: $W_0(e^x)$ overflows past x = 709 where $\omega(x) \approx x - \ln x$
is ordinary. Newton on $\omega + \ln \omega - x$ from a cheap seed per region.
Below x = -7 the Lagrange series in $e^x$ is the answer outright.
Source§fn bessel_ratio_p<P, F>(self, nu: V) -> Vwhere
P: Policy,
F: BesselRatioFamily,
fn bessel_ratio_p<P, F>(self, nu: V) -> Vwhere
P: Policy,
F: BesselRatioFamily,
Computes the modified Bessel ratio $A_\nu(x) = I_\nu(x) / I_{\nu-1}(x)$ for
nu >= 1, odd in x.
With $p = 2\nu$ this is the mean resultant length of a von Mises-Fisher
distribution on $S^{p-1}$ at concentration x. nu = 1 is the von Mises circle
$I_1/I_0$, and nu = 3/2 is the langevin function.
Never forms the two Bessel functions where they would underflow: a series pair for
small x, the continued fraction for the ratio in the middle, and the scaled
quotient only where x dominates the order. The order is a plain vector, but whole
and half-integer orders reach their fast Bessel kernels through the order simplifier.
Source§fn inv_bessel_ratio_p<P, F>(self, nu: V) -> Vwhere
P: Policy,
F: BesselRatioFamily,
fn inv_bessel_ratio_p<P, F>(self, nu: V) -> Vwhere
P: Policy,
F: BesselRatioFamily,
Computes the inverse of bessel_ratio: the
concentration $\kappa$ with $I_\nu(\kappa)/I_{\nu-1}(\kappa) = r$, for
0 <= r < 1, odd in r.
The maximum-likelihood concentration of a von Mises-Fisher distribution from its
observed mean resultant length, in any dimension $p = 2\nu$. Banerjee’s
$r(p - r^2)/(1 - r^2)$ seeds a Newton whose derivative is the closed form
$1 - A^2 - (2\nu - 1)A/\kappa$, so each step is one ratio evaluation. r = 1
gives +inf, r > 1 NaN.
As r -> 1 the problem itself is ill-conditioned: $\kappa \sim (p-1)/(2(1-r))$,
and an ulp of r is a relative $2\kappa\epsilon/(p-1)$ of $\kappa$. The result is
the exact inverse of the given r to that extent.
Source§fn bessel_ratio_1m_p<P, F>(self, nu: V) -> Vwhere
P: Policy,
F: BesselRatioFamily,
fn bessel_ratio_1m_p<P, F>(self, nu: V) -> Vwhere
P: Policy,
F: BesselRatioFamily,
Computes $1 - A_\nu(x)$, the complement of
bessel_ratio, to full relative accuracy
where the ratio itself is within an ulp of 1.
1 - bessel::ratio::<I>(x) is gone once $A$ rounds to 1 (x past 1e16 (p-1)/2),
and is only accurate to $\epsilon/(1 - A)$ before that. This evaluates the
complement directly for x >= 8 nu, from the Hankel expansions at a reduced order
and the ratio recurrence walked upward in complement form. $A$ is odd, so
$1 - A(-x) = 2 - (1 - A(x))$.
Source§fn inv_bessel_ratio_1m_p<P, F>(self, nu: V) -> Vwhere
P: Policy,
F: BesselRatioFamily,
fn inv_bessel_ratio_1m_p<P, F>(self, nu: V) -> Vwhere
P: Policy,
F: BesselRatioFamily,
Computes the inverse of bessel_ratio_1m:
the concentration $\kappa$ with $1 - I_\nu(\kappa)/I_{\nu-1}(\kappa) = t$, for
0 < t <= 2 (t = 1 - r).
The complement form of inv_bessel_ratio
for nearly concentrated data: $\kappa \sim (p-1)/(2t)$ as t -> 0. This form
keeps full relative accuracy there instead of losing $2\kappa\epsilon/(p-1)$
to the rounding of r. It is the inv_langevin_1m
move in every dimension. t = 0 gives +inf. t in (1, 2] is a negative r
and returns the mirrored $\kappa$.
Source§fn gauss_legendre_p<P>(self, n: u32) -> (V, V)where
P: Policy,
fn gauss_legendre_p<P>(self, n: u32) -> (V, V)where
P: Policy,
Computes the k-th node and weight of the n-point Gauss-Legendre quadrature
rule on $[-1, 1]$, with the root index k taken per lane.
The rule integrates every polynomial through degree $2n - 1$ exactly:
$\int_{-1}^{1} f \approx \sum_k w_k f(x_k)$, $x_k$ the roots of $P_n$ in
descending order (k = 0 is the largest, $x_{n-1-k} = -x_k$) and
$w_k = 2 / ((1 - x_k^2) P_n'(x_k)^2)$. The packet is the rule: sweep k over
0..n in packets of consecutive indices and store the two vectors. Every lane
runs the same O(n) recurrence, so a packet of roots costs one root.
Tricomi’s $\cos(\pi(k + 3/4)/(n + 1/2))$ seeds a Newton on $P_n$ from the
recurrence, and nodes land within a few $\epsilon$ absolute. A non-integer or
out-of-range k gives NaN in both.
let n = 16;
for base in (0..n).step_by(V::LANES) {
let k = V::from_array(core::array::from_fn(|i| (base + i) as f64));
let (x, w) = k.gauss_legendre(n as u32); // lanes past n - 1 are NaN
}Source§fn gauss_hermite_p<P>(self, n: u32) -> (V, V)where
P: Policy,
fn gauss_hermite_p<P>(self, n: u32) -> (V, V)where
P: Policy,
Computes the k-th node and weight of the n-point Gauss-Hermite rule, for
$\int_{-\infty}^{\infty} f(x) e^{-x^2}\,dx \approx \sum_k w_k f(x_k)$, the root
index k per lane (k = 0 the largest root, $x_{n-1-k} = -x_k$).
Same shape as gauss_legendre: a packet of
consecutive indices is the rule. Seeded from the WKB phase of the Hermite equation
and finished by Newton on $H_n/n!$, whose recurrence stays in range where the raw
$H_n$ overflows at degree 48. The weights are the unscaled ones, which reach
$e^{-x_k^2}$ at the outer nodes. The scalar factor in them underflows past
n = 170 in f64 and n = 40 in f32, which bounds the rule.
Source§fn gauss_laguerre_p<P>(self, alpha: V, n: u32) -> (V, V)where
P: Policy,
fn gauss_laguerre_p<P>(self, alpha: V, n: u32) -> (V, V)where
P: Policy,
Computes the k-th node and weight of the n-point Gauss-Laguerre rule, for
$\int_0^{\infty} f(x)\, x^\alpha e^{-x}\,dx \approx \sum_k w_k f(x_k)$, the root
index k and alpha > -1 per lane (k = 0 the largest root).
Same shape as gauss_legendre. Seeded from the
WKB phase of the Laguerre equation, whose phase count between the turning points
carries the Bessel-zero offset on the left and the Airy offset on the right, and
finished by Newton on the raw $L_n^\alpha$ with Hildebrand’s weight
$\Gamma(n+\alpha+1)/(n!\,x_k\,L_n^{\alpha\prime}(x_k)^2)$. Unscaled weights, which
reach $e^{-x_k}$ at the outer nodes. $L_{n-1}$ at the largest root grows like
$e^{x/2}$, which bounds the rule near n = 170 in f64 and n = 20 in f32.
Source§fn pochhammer_p<P>(self, m: V) -> Vwhere
P: Policy,
fn pochhammer_p<P>(self, m: V) -> Vwhere
P: Policy,
Computes the Pochhammer symbol $(z)_m = \dfrac{\Gamma(z+m)}{\Gamma(z)}$.
Combinatorics calls this the rising factorial, and for a non-negative integer
m it is exactly the ascending product $z(z+1)\cdots(z+m-1)$. The name here is
the special-function one because the function is not restricted to integers: m
is any real, which is what the hypergeometric series need and what “factorial”
would misdescribe.
Note that the notation $(z)_m$ is ambiguous in the literature: it means the
rising factorial in special functions and the falling factorial through much of
combinatorics and statistics. This function is the rising one. The falling
factorial is pochhammer(z - n + 1, n), and the two are related by
$z^{(\bar n)} = (-1)^n (-z)^{(\underline n)}$. Neither is shipped separately,
being an argument transform away.
§Accuracy
The obvious spelling exp(lgamma(z+m) - lgamma(z)) cancels catastrophically
whenever m is small beside z: at z = 1e8, m = 1e-4 it has no correct
digits. This does not use it.
At Average precision and above (which includes the default policy), integer m
up to 20 in absolute value takes an exact product, 0.00 ulp median and 4.2 worst.
That path also covers negative z and returns exact zeros at the poles: $(-2)_3$
is 0.
Below Average it is compiled out and integer m goes through the Stirling
difference like anything else, which measures 4.2 ulp median and 172 worst. The
difference that shows is the exactness rather than the ulp count: $(3)_1$ comes
back as 3.0000000000000018 there, and $(200)_2$ as 40200.00000000002.
Any other m with z and z+m both positive takes a Stirling difference
arranged so nothing large is ever subtracted from anything large. Its error is the
floor for anything exponentiating a logarithm, tracking
$|\ln (z)_m|\cdot\epsilon$. Over 6924 measured points with z in [0.1, 8.9]
that is a median of 2.6 ulp and a 99th percentile of 25. Individual points scale
with the result’s own logarithm, reaching 259 ulp where the value is near 1e163,
and falling to nothing as the result approaches 1.
A non-integer m with z or z+m non-positive (a ratio taken across Gamma’s
poles) has no cheap rearrangement and does fall back to the logarithmic form,
inheriting its cancellation.
Source§fn jacobi_elliptic_p<P>(self, k: V) -> (V, V, V)where
P: Policy,
fn jacobi_elliptic_p<P>(self, k: V) -> (V, V, V)where
P: Policy,
Computes the Jacobi elliptic functions $(\mathrm{sn}, \mathrm{cn}, \mathrm{dn})$
at argument self and modulus k, all three from one evaluation.
All three are made from a single angle, the amplitude
$\varphi = \mathrm{am}(u, k)$, defined by $F(\varphi, k) = u$, so this function
inverts the incomplete integral of the first kind that
ellint evaluates:
\mathrm{sn}(u, k) = \sin\varphi, \qquad
\mathrm{cn}(u, k) = \cos\varphi, \qquad
\mathrm{dn}(u, k) = \sqrt{1 - k^2 \sin^2\varphi}Hence their names: sine amplitude, cosine amplitude and delta amplitude. At
k = 0 the amplitude is u and they collapse to $(\sin u, \cos u, 1)$. At
k = 1 they stop being periodic and become
$(\tanh u, \operatorname{sech} u, \operatorname{sech} u)$.
§Why one function and not three
The triple is closed under differentiation in u, each derivative a product
of the other two:
\frac{d\,\mathrm{sn}}{du} = \mathrm{cn}\,\mathrm{dn}, \qquad
\frac{d\,\mathrm{cn}}{du} = -\mathrm{sn}\,\mathrm{dn}, \qquad
\frac{d\,\mathrm{dn}}{du} = -k^2\,\mathrm{sn}\,\mathrm{cn}so they are one object the way $(\sin, \cos)$ are, and
Dual differentiates them without touching the
iteration underneath. It also costs nothing to return all three: they share the
entire computation, and only the last few operations differ.
The other nine Jacobi functions in Glaisher’s notation (ns, nc, nd, sc,
sd, cs, cd, ds, dc) are reciprocals and ratios of these three, so this
gives all twelve.
§Domain and accuracy
Only $k^2$ enters, so the sign of k does not matter. |k| > 1 is out of
domain and gives NaN. Worst absolute error measured against mpmath at 40 digits
over |u| <= 8 and k in [0, 1) is 8.3 eps for sn, 4.1 for cn and 3.8 for
dn. Absolute is the meaningful metric: all three are bounded by 1 and all three
have zeros, so relative accuracy at a zero depends on how well that zero’s
location is known, exactly as for sin. For the same reason accuracy falls off
slowly with |u|, that being the argument of the single trigonometric call
inside.
Source§fn agm_p<P>(self, other: V) -> Vwhere
P: Policy,
fn agm_p<P>(self, other: V) -> Vwhere
P: Policy,
Computes the arithmetic-geometric mean $\mathrm{AGM}(a, b)$ of two non-negative
arguments.
Iterating $a \mapsto (a + b)/2$ against $b \mapsto \sqrt{ab}$ drives the two
sequences to a common limit, quadratically: the pair closes to within a factor of
a few in a handful of passes from any starting ratio, and the correct digits then
double per pass. The loop is branchless and costs one sqrt per iteration, with no
transcendentals anywhere, which is why it is also the engine behind the complete
elliptic integrals, $K(k) = \pi / (2\,\mathrm{AGM}(1, k'))$, reached through
ellint rather than by calling this directly.
Symmetric in its arguments and homogeneous, $\mathrm{AGM}(ca, cb) = c\,\mathrm{AGM}(a, b)$. AGM(a, 0) is 0 and AGM(inf, b) is inf. A negative
argument is outside the domain (the geometric mean’s sign becomes ambiguous after
the first pass) and returns NaN under overflow checking, as does a zero paired with
an infinity.
The geometric mean is formed as one product, so two arguments both above
$\sqrt{\text{MAX}}$ (about 1.3e154 in f64, 1.8e19 in f32) overflow to infinity
even where the mean is representable. Scale both by a common power of two first if
that range matters. Homogeneity makes it exact.
Source§fn langevin_p<P>(self) -> Vwhere
P: Policy,
fn langevin_p<P>(self) -> Vwhere
P: Policy,
Computes the Langevin function $L(x) = \coth x - \frac{1}{x}$.
Odd, strictly increasing, L(0) = 0, L'(0) = 1/3, L(x) -> 1 as x -> ∞.
This is the mean resultant length $A_3(\kappa)$ of a von Mises-Fisher
distribution on the sphere, and the freely-jointed-chain force-extension law
in polymer physics.
Evaluated as an odd minimax polynomial for |x| <= 2 (the direct form
coth x - 1/x cancels catastrophically there, losing 3u/x^2), and as
1 - 1/x + 2/(e^{2x} - 1) beyond. Both branches are accurate to a few ulp
at every precision policy. The policy mainly selects the exp.
To also obtain the derivative L'(x), use
langevin_d.
Source§fn inv_langevin_p<P>(self) -> Vwhere
P: Policy,
fn inv_langevin_p<P>(self) -> Vwhere
P: Policy,
Computes the inverse Langevin function $L^{-1}(y)$ for |y| < 1.
Odd, with a simple pole at y = 1: L^-1(y) ~ 1/(1-y). |y| = 1 returns
±∞, and |y| > 1 returns NaN under overflow checking (an unspecified
value otherwise). Its condition number is 1/(1-y), so near the pole the
result cannot be more accurate than that, however exact the arithmetic. A
consumer that knows 1 - y should form it before rounding.
A rational seed (the same family as Cohen’s Pade approximant, which the vMF literature knows as the Banerjee et al. concentration estimator) is refined by Newton (f32) or Halley (f64) steps whose count follows the precision policy:
| precision | steps | relative error |
|---|---|---|
Worst | 0 | ~2e-5 |
Medium, Average, Best | 1 | full (a few ulp) |
Reference | 2 | full |
Source§fn langevin_1m_p<P>(self) -> Vwhere
P: Policy,
fn langevin_1m_p<P>(self) -> Vwhere
P: Policy,
Computes 1 - L(x), the complement of the Langevin function,
accurately where L(x) is within rounding of 1.
1 - L(x) ~ 1/x, so once x > 1/u (sharpness ~1e7 in f32, ~1e16 in f64)
langevin(x) rounds to exactly 1 and its complement is gone. This returns it
to full relative precision at any x, from the same intermediates. Same cost
as langevin. Negative x gives 1 + L(|x|).
Pairs with inv_langevin_1m: the vMF
convolution kappa' = L^-1(L(k1) L(k2)) should be formed as
inv_langevin_1m(a + b - a*b) with a = langevin_1m(k1), b = langevin_1m(k2),
which is cancellation-free at every sharpness.
Source§fn inv_langevin_1m_p<P>(self) -> Vwhere
P: Policy,
fn inv_langevin_1m_p<P>(self) -> Vwhere
P: Policy,
Computes L^-1(1 - t) from the complement t directly.
The inverse Langevin function has a pole at
y = 1 and a condition number of 1/(1-y), so a caller that knows 1 - y
(see langevin_1m) should pass it here rather
than form y and lose its low digits: this entry point works in t throughout
and is accurate to a few ulp at any sharpness. t = 0 returns +∞, t > 1
gives the negative branch, and t < 0 is out of the domain (NaN under
overflow checking). Same cost as inv_langevin.
Source§fn gelu_p<P>(self, alpha: V) -> Vwhere
P: Policy,
fn gelu_p<P>(self, alpha: V) -> Vwhere
P: Policy,
GELU activation function, defined as $\tfrac{1}{2} x \left(1 + \operatorname{erf}\!\left(\frac{\alpha x}{\sqrt{2}}\right)\right)$,
where alpha helps control the shape of the curve. The standard GELU function
is recovered when alpha is 1.
For f32 vectors, this remains decently accurate even with the Medium and Worst precision policies,
thanks to good erf implementations at the various precision levels. See erf for more details.
To also obtain the derivative with respect to x (which shares most of the computation), use
gelu_d.
Source§fn swish_p<P>(self, beta: V) -> Vwhere
P: Policy,
fn swish_p<P>(self, beta: V) -> Vwhere
P: Policy,
Swish activation function, defined as $x\,\sigma(\beta x) = \frac{x}{1 + e^{-\beta x}}$,
where beta controls the sharpness of the gate. The standard Swish/SiLU function
is recovered when beta is 1. As beta -> 0, the output approaches x/2 (half-identity);
as beta -> inf, Swish approaches ReLU.
To also obtain the derivative with respect to x, use
swish_d.
Source§fn algebraic_sigmoid_n_p<P, const N: usize>(self) -> Vwhere
P: Policy,
fn algebraic_sigmoid_n_p<P, const N: usize>(self) -> Vwhere
P: Policy,
Computes the algebraic sigmoid function, defined as $\frac{x}{(1 + |x|^N)^{1/N}}$, where
N is a positive integer parameter that controls the steepness of the curve.
This also has the unique behavior where for N=0, the function is just the identity function,
and for N=1 it is the softsign function.
Note: This function uses $|x|^N$ (the real absolute value), so it is non-holomorphic
and only meaningful for real-valued inputs.
To also obtain the derivative with respect to x, use
algebraic_sigmoid_d.
Source§fn algebraic_sigmoid_p<P>(self, n: u32) -> Vwhere
P: Policy,
fn algebraic_sigmoid_p<P>(self, n: u32) -> Vwhere
P: Policy,
The algebraic sigmoid for a degree known only at runtime. The runtime twin of
algebraic_sigmoid_n, same arithmetic.
Source§fn algebraic_swish_p<P>(self) -> Vwhere
P: Policy,
fn algebraic_swish_p<P>(self) -> Vwhere
P: Policy,
Algebraic analogue of the Swish activation,
defined as $x\left(\frac{1}{2} + \frac{x}{2\sqrt{1 + x^2}}\right)$. Equivalent to gating x by
(1 + algebraic_sigmoid_n::<2>(x)) / 2, the [0, 1]-rescaled N=2 algebraic sigmoid.
Like standard Swish/SiLU, this is smooth and non-monotonic (it dips slightly below zero
for moderately negative x before rising) and shares the same asymptotes (f(x) -> x as
x -> ∞, f(x) -> 0 as x -> -∞). Unlike Swish, it requires no exp or log, which
is substantially cheaper on hardware without fast transcendentals.
To also obtain the derivative with respect to x (which shares most of the underlying
computation, notably $1/\sqrt{1 + x^2}$), use
algebraic_swish_d.
§Historical note
Algebraic gating functions of this form are effectively unknown in modern deep learning,
which standardized on exp-based activations (sigmoid, Swish/SiLU, GELU) once GPUs made
exp essentially free, a single-cycle special-function-unit op on most modern hardware.
On CPUs the calculus is different: a vectorized exp still costs ~20+ cycles even with
good polynomial approximations, while sqrt/rsqrt are cheap hardware ops (often
approximated in 4-7 cycles). For CPU-side inference, training on CPU, or embedded targets
without a transcendental SFU, this remains a competitive Swish-shaped activation at a
fraction of the cost.
Source§fn lgamma_r_p<P>(self) -> (V, V)where
P: Policy,
fn lgamma_r_p<P>(self) -> (V, V)where
P: Policy,
Computes the natural log of the Gamma function ($\ln|\Gamma(x)|$) for any real input, for each value in a vector,
and returns the sign of the Gamma function from before the absolute value was taken.
Source§fn gaussian_integral_p<P>(x0: V, x1: V, a: V, c: V) -> Vwhere
P: Policy,
fn gaussian_integral_p<P>(x0: V, x1: V, a: V, c: V) -> Vwhere
P: Policy,
Computes the definite integral of the Gaussian function from x0 to x1, with amplitude a and standard deviation c.
This is more efficient than evaluating the indefinite integral at both limits and subtracting.
The position b is assumed to be zero, so offset the limits accordingly for a non-zero position.
Source§fn boxcox_p<P>(self, lambda: V) -> Vwhere
P: Policy,
fn boxcox_p<P>(self, lambda: V) -> Vwhere
P: Policy,
The Box-Cox transform of x = self
with parameter lambda.
\mathrm{boxcox}(x, \lambda) = \begin{cases} \dfrac{x^\lambda - 1}{\lambda} & \lambda \ne 0 \\[6pt] \ln x & \lambda = 0\end{cases}The variance-stabilizing power transform of applied statistics: $\lambda$ is fitted
to make skewed data as close to normal as possible before a model sees it, and the
family interpolates the transforms people otherwise pick by hand: $\lambda = 1$
leaves the data alone up to a shift, $1/2$ is a square root, $0$ a logarithm,
$-1$ a reciprocal. A fixture of statistical software since Box and Cox introduced
it in 1964.
The two cases are one function: $\ln x$ is the limit as $\lambda \to 0$, not a
separate rule. Written out, $(x^\lambda - 1)/\lambda$ is $0/0$ there, and the
trouble is not confined to the point. Computing $x^\lambda$ and subtracting one
cancels, so the naive form is already wrong in the fifth digit at
$\lambda = 10^{-12}$ and returns a flat zero by $10^{-300}$. That matters because
a fitting routine searches $\lambda$ near zero, which is the usual answer for
right-skewed data.
Evaluated as powf_m1(x, lambda)/lambda,
which forms $x^\lambda - 1$ without ever forming $x^\lambda$, so there is nothing to
cancel and no series or crossover is needed. Measured against a 60-digit oracle,
it holds a few ulp from $\lambda = 10^{-300}$ to $\lambda = \pm 8$. Only the exact
$\lambda = 0$ is selected apart.
Domain is $x > 0$, and a negative x gives NaN. At $x = 0$ the limits are taken:
$-1/\lambda$ for $\lambda > 0$ and $-\infty$ otherwise, which is the
conventional choice. That needs no special case: powf_m1(0, lambda) is $-1$
above zero and $+\infty$ below, and the division does the rest.
Source§fn boxcox_1p_p<P>(self, lambda: V) -> Vwhere
P: Policy,
fn boxcox_1p_p<P>(self, lambda: V) -> Vwhere
P: Policy,
The Box-Cox transform of $1 + x$, where x = self.
\mathrm{boxcox1p}(x, \lambda) = \begin{cases} \dfrac{(1 + x)^\lambda - 1}{\lambda} & \lambda \ne 0 \\[6pt] \ln (1 + x) & \lambda = 0\end{cases}The shifted form exists for the same reason ln_1p
does: when x is small, $1 + x$ rounds it away, and every digit of the answer
with it. Calling boxcox(1 + x, lambda) loses x entirely once
$|x| < \varepsilon$, where this returns $\lambda x$ to full precision. Built on
compound_m1, which forms
$(1 + x)^\lambda - 1$ without forming either $1 + x$ or $(1+x)^\lambda$.
This is also the kernel underneath yeo_johnson, whose
argument is data centered near zero by construction.
Domain is $x > -1$; below that the result is NaN. At $x = -1$ the limits are
$-1/\lambda$ for $\lambda > 0$ and $-\infty$ otherwise.
Source§fn inv_boxcox_p<P>(self, lambda: V) -> Vwhere
P: Policy,
fn inv_boxcox_p<P>(self, lambda: V) -> Vwhere
P: Policy,
The inverse Box-Cox transform of
y = self with parameter lambda, undoing boxcox.
\mathrm{boxcox}^{-1}(y, \lambda) = \begin{cases} (\lambda y + 1)^{1/\lambda} & \lambda \ne 0 \\[6pt] e^y & \lambda = 0\end{cases}Wanted by anyone who uses the forward transform: a model fitted on transformed data predicts in transformed units, and the prediction has to come back.
Evaluated as $\exp\!\left(\ln(1 + \lambda y)/\lambda\right)$ rather than as a
literal power, which is not merely a rearrangement. The whole
point of boxcox is that it stays accurate as $\lambda \to 0$,
and $\lambda$ fitted near zero is the common case. There $\lambda y$ is tiny,
so forming $\lambda y + 1$ and raising it to the power $1/\lambda$ throws away
exactly the digits the forward transform took care to keep. Through ln_1p the
exponent tends smoothly to y, so the $\lambda = 0$ case is the limit rather
than a discontinuity, and only the exact zero is selected apart.
The range of the forward transform is $\lambda y + 1 > 0$. Outside it the result
is NaN, and on the boundary it is $0$ for $\lambda > 0$ and $+\infty$ below.
Source§fn inv_boxcox_1p_p<P>(self, lambda: V) -> Vwhere
P: Policy,
fn inv_boxcox_1p_p<P>(self, lambda: V) -> Vwhere
P: Policy,
The inverse of boxcox_1p.
\mathrm{boxcox1p}^{-1}(y, \lambda) = \begin{cases} (\lambda y + 1)^{1/\lambda} - 1 & \lambda \ne 0 \\[6pt] e^y - 1 & \lambda = 0\end{cases}The same exponent as inv_boxcox with expm1 outside it
instead of exp, so a result near zero keeps its relative accuracy, which, this
being the inverse of a transform applied to data centered near zero, is the
ordinary case rather than an edge one. Also the kernel underneath
inv_yeo_johnson.
Source§fn yeo_johnson_p<P>(self, lambda: V) -> Vwhere
P: Policy,
fn yeo_johnson_p<P>(self, lambda: V) -> Vwhere
P: Policy,
The Yeo-Johnson transform of
y = self with parameter lambda.
\psi(y, \lambda) = \begin{cases}
\dfrac{(y + 1)^\lambda - 1}{\lambda} & y \ge 0,\ \lambda \ne 0 \\[6pt]
\ln(y + 1) & y \ge 0,\ \lambda = 0 \\[6pt]
-\dfrac{(1 - y)^{2 - \lambda} - 1}{2 - \lambda} & y < 0,\ \lambda \ne 2 \\[6pt]
-\ln(1 - y) & y < 0,\ \lambda = 2
\end{cases}Box-Cox’s sibling, and the one that gets used more, since it is defined on the whole
real line rather than on $x > 0$. Same job (fit $\lambda$ by maximum likelihood
to make skewed data as close to normal as a power transform can) without the “add a
constant to make everything positive first” step, which is an arbitrary choice that
changes the fitted $\lambda$. Introduced by Yeo and Johnson in 2000.
§One kernel, not four
The four cases are one function seen twice. The $y < 0$ branch is the $y \ge 0$
branch applied to $|y|$ with $\lambda$ reflected to $2 - \lambda$ and the
result negated, which is what makes $\psi$ smooth in $\lambda$ across $y = 0$
in the first place. Folding the sign out first therefore collapses the two
logarithmic special cases ($\lambda = 0$ above zero, $\lambda = 2$ below) into
the single seam that boxcox_1p already handles, and the whole
transform is $\pm\,\mathrm{boxcox1p}(|y|, \lambda\ \mathrm{or}\ 2 - \lambda)$.
That the kernel is the 1p form and not boxcox applied to
$1 + |y|$ matters here more than anywhere else. $\psi(y, \lambda) \approx y$
near the origin for every $\lambda$, and the origin is where the data is: the
transform’s reason for existing is samples that straddle zero. Forming $1 + |y|$
would round away everything below $\varepsilon$ and return a flat zero there.
The value is finite for every finite y, so there is nothing to guard: the two
domain edges of the kernel are at $|y| = -1$, which the fold never reaches.
Source§fn inv_yeo_johnson_p<P>(self, lambda: V) -> Vwhere
P: Policy,
fn inv_yeo_johnson_p<P>(self, lambda: V) -> Vwhere
P: Policy,
The inverse Yeo-Johnson transform,
undoing yeo_johnson.
\psi^{-1}(z, \lambda) = \begin{cases}
(\lambda z + 1)^{1/\lambda} - 1 & z \ge 0,\ \lambda \ne 0 \\[6pt]
e^z - 1 & z \ge 0,\ \lambda = 0 \\[6pt]
1 - \left((\lambda - 2) z + 1\right)^{1/(2 - \lambda)} & z < 0,\ \lambda \ne 2 \\[6pt]
1 - e^{-z} & z < 0,\ \lambda = 2
\end{cases}The same sign fold as the forward transform, over
inv_boxcox_1p. $\psi$ is increasing and fixes the origin,
so the branch on the way back is the sign of the transformed value, which is the
sign of y.
Unlike the forward direction this one has a range to respect: for $\lambda > 0$
the transform’s image is bounded below by $-1/\lambda$, and a z past that came
from no y. Such an input gives NaN rather than a plausible-looking number.
Source§fn spherical_harmonics_p<P, const L: usize, const N: usize, const CS: bool>(
x: V,
y: V,
z: V,
out: &mut [V; N],
)where
P: Policy,
fn spherical_harmonics_p<P, const L: usize, const N: usize, const CS: bool>(
x: V,
y: V,
z: V,
out: &mut [V; N],
)where
P: Policy,
Evaluates all real spherical harmonics through degree L at the unit
direction (x, y, z), into out[l * (l + 1) + m] for m in -l..=l.
Orthonormal real harmonics. Evaluation is pure polynomial arithmetic:
no trigonometry, no division, O(L^2) FMAs total, exact zeros for every
m != 0 harmonic at the poles, fully unrolled at compile time for each
L up to MAX_SH_DEGREE (above that it takes the rolled general path,
which is correct at any degree but roughly 10x slower).
CS picks the phase convention. false gives the standard real-SH
tables ($Y_{11} = \sqrt{3/4\pi}\,x$); true applies the Condon-Shortley
$(-1)^{|m|}$ phase, negating every odd-|m| harmonic to match Sloan’s
SHEval and the physics convention ($Y_{11} = -\sqrt{3/4\pi}\,x$). The
choice is baked into a constant table, so neither costs an instruction,
but mixing the two silently corrupts any projection/reconstruction
round-trip, which is why it must be named.
N must equal (L + 1)^2 (compile-time checked). The direction is
assumed unit-length, and nothing renormalizes. See
sh_impl for the full convention, algorithm,
and domain notes.
use thermite::prelude::*;
use thermite_special::RealSpecialMath;
type V = Vector<f64>;
let (x, y, z) = (V::splat(0.6), V::splat(0.0), V::splat(0.8));
let mut sh = [V::ZERO; 9];
V::spherical_harmonics::<2, 9, false>(x, y, z, &mut sh);
// Y(1,1) = sqrt(3/4pi) * x
assert!((sh[3].extract::<0>() - 0.48860251190292 * 0.6).abs() < 1e-14);
// Condon-Shortley negates odd |m|, and agrees on even |m|.
let mut cs = [V::ZERO; 9];
V::spherical_harmonics::<2, 9, true>(x, y, z, &mut cs);
assert_eq!(cs[3].extract::<0>(), -sh[3].extract::<0>());
assert_eq!(cs[8].extract::<0>(), sh[8].extract::<0>());Source§fn spherical_harmonics_table_p<P, const L: usize, const N: usize, const CS: bool>(
table: &mut ShTable<<V as PrimalProjection>::Primal, N>,
)where
P: Policy,
fn spherical_harmonics_table_p<P, const L: usize, const N: usize, const CS: bool>(
table: &mut ShTable<<V as PrimalProjection>::Primal, N>,
)where
P: Policy,
Builds the runtime coefficient table that spherical_harmonics_with
and spherical_harmonics_d_with evaluate.
The table depends only on L and CS, never on the direction, so a caller
sweeping many directions should build it once rather than calling the
one-shot spherical_harmonics
per direction. The phase is baked in here, which is why the evaluators take
no CS.
The table is typed by Self::Primal, the unaugmented value type: the
recurrence coefficients are constants, so a Dual’s derivative parts and a
Complex’s imaginary part would only store zeros. For plain vectors and
Compensated the primal is Self and nothing changes. For Dual the table
is a fraction of the size and its entries multiply as reals.
use thermite::prelude::*;
use thermite_special::{RealSpecialMath, ShTable};
type V = Vector<f64>;
const L: usize = 3;
const N: usize = (L + 1) * (L + 1);
let mut table = ShTable::<V, N>::zeroed();
V::spherical_harmonics_table::<L, N, false>(&mut table);
let mut sh = [V::ZERO; N];
for &(x, y, z) in &[(1.0, 0.0, 0.0), (0.0, 1.0, 0.0)] {
V::spherical_harmonics_with::<L, N>(
&table, V::splat(x), V::splat(y), V::splat(z), &mut sh,
);
}
assert!((sh[1].extract::<0>() - 0.48860251190292).abs() < 1e-14);Source§fn spherical_harmonics_with_p<P, const L: usize, const N: usize>(
table: &ShTable<<V as PrimalProjection>::Primal, N>,
x: V,
y: V,
z: V,
out: &mut [V; N],
)where
P: Policy,
fn spherical_harmonics_with_p<P, const L: usize, const N: usize>(
table: &ShTable<<V as PrimalProjection>::Primal, N>,
x: V,
y: V,
z: V,
out: &mut [V; N],
)where
P: Policy,
Evaluates all harmonics through degree L from a prebuilt table.
The table holds Self::Primal coefficients. See
spherical_harmonics_table
for how to build it and why, and
spherical_harmonics for the
conventions and layout.
Source§impl<M> SpatialMath for Mwhere
M: SpatialMathWithPolicy,
impl<M> SpatialMath for Mwhere
M: SpatialMathWithPolicy,
Source§fn hypot_n<const N: usize>(values: [Self; N]) -> Self
fn hypot_n<const N: usize>(values: [Self; N]) -> Self
$\sqrt{x_1^2 + x_2^2 + \dots + x_N^2}$. Read moreSource§fn inv_hypot_n<const N: usize>(values: [Self; N]) -> Self
fn inv_hypot_n<const N: usize>(values: [Self; N]) -> Self
$1/\sqrt{x_1^2 + x_2^2 + \dots + x_N^2}$. Read moreSource§fn inv_hypot(values: &[Self]) -> Self
fn inv_hypot(values: &[Self]) -> Self
inv_hypot_n over a runtime-length slice. Read moreSource§fn l2_norm_squared(self) -> Self
fn l2_norm_squared(self) -> Self
Source§impl<E, V> SpatialMathWithPolicy for Vwhere
E: FloatElement,
V: FloatVector<Element = E> + CoreMathWithPolicy + SpecializedSpatialMath<E>,
impl<E, V> SpatialMathWithPolicy for Vwhere
E: FloatElement,
V: FloatVector<Element = E> + CoreMathWithPolicy + SpecializedSpatialMath<E>,
Source§fn hypot_p<P>(self, other: V) -> Vwhere
P: Policy,
fn hypot_p<P>(self, other: V) -> Vwhere
P: Policy,
Computes the Euclidean norm (hypotenuse) of self and other, i.e., sqrt(self^2 + other^2).
This is not higher performance than the naive implementation, but is more resistant to overflow and underflow. If using the worst precision policy, it becomes equivalent to the naive implementation.
Check out hypot_n for N values known at compile time,
and hypot_s for a runtime-length slice.
Source§fn hypot_n_p<P, const N: usize>(values: [V; N]) -> Vwhere
P: Policy,
fn hypot_n_p<P, const N: usize>(values: [V; N]) -> Vwhere
P: Policy,
Computes the Euclidean norm (hypotenuse) of N values, i.e., $\sqrt{x_1^2 + x_2^2 + \dots + x_N^2}$.
This is typically higher performance than naively computing the sum of squares and then taking the square root, especially for larger N, and is more resistant to overflow and underflow when using average or higher precision policies.
N = 2 is written out as its own arm in every lowering, so
hypot is this function at that length rather than a
separate kernel.
Source§fn inv_hypot_n_p<P, const N: usize>(values: [V; N]) -> Vwhere
P: Policy,
fn inv_hypot_n_p<P, const N: usize>(values: [V; N]) -> Vwhere
P: Policy,
Computes the inverse Euclidean norm (inverse hypotenuse) of N values, i.e., $1/\sqrt{x_1^2 + x_2^2 + \dots + x_N^2}$.
This is typically higher performance than naively computing the sum of squares, taking the square root, and then inverting, especially for larger N, and is more resistant to overflow and underflow when using average or higher precision policies.
At lower precision policies, we can take advantage of fast approximate inverse square root implementations for better performance.
Source§fn inv_hypot_p<P>(values: &[V]) -> Vwhere
P: Policy,
fn inv_hypot_p<P>(values: &[V]) -> Vwhere
P: Policy,
inv_hypot_n over a runtime-length slice.
Same trade as hypot_s. The inverse norm of no
values is infinity, matching 1/0.
Source§fn l1_norm_p<P>(self) -> Vwhere
P: Policy,
fn l1_norm_p<P>(self) -> Vwhere
P: Policy,
L1 Norm, or the “Manhattan” distance from the origin.
For 1D vectors, this is equivalent to the absolute value.
Source§fn l2_norm_p<P>(self) -> Vwhere
P: Policy,
fn l2_norm_p<P>(self) -> Vwhere
P: Policy,
L2 Norm, or the “Euclidean” distance from the origin.
For 1D vectors, this is equivalent to the absolute value.
Source§fn l2_norm_squared_p<P>(self) -> Vwhere
P: Policy,
fn l2_norm_squared_p<P>(self) -> Vwhere
P: Policy,
Squared L2 Norm, or the squared “Euclidean” distance from the origin.
For 1D vectors, this is equivalent to squaring the value.
Source§impl<M> SpecialMath for Mwhere
M: SpecialMathWithPolicy,
impl<M> SpecialMath for Mwhere
M: SpecialMathWithPolicy,
Source§fn erfcx(self) -> Self
fn erfcx(self) -> Self
$\operatorname{erfcx}(x) = e^{x^2}\operatorname{erfc}(x)$. Read moreSource§fn logistic_sigmoid(self) -> Self
fn logistic_sigmoid(self) -> Self
$\sigma(x) = \frac{1}{1 + e^{-x}}$. Read moreSource§fn softplus(self, k: Self, rcp_k: Self) -> Self
fn softplus(self, k: Self, rcp_k: Self) -> Self
$\frac{1}{k}\ln(1 + e^{kx})$. Read moreSource§fn tgamma(self) -> Self
fn tgamma(self) -> Self
$\Gamma(z)$) for any real input, for each value in a vector. Read moreSource§fn lgamma(self) -> Self
fn lgamma(self) -> Self
$\ln|\Gamma(x)|$) for any real input, for each value in a vector.Source§fn poisson_pmf(self, lambda: Self) -> Self
fn poisson_pmf(self, lambda: Self) -> Self
$P(k; \lambda) = e^{-\lambda}\lambda^k / k!$ at k = self,
for real $k \ge 0$ and mean $\lambda \ge 0$. Read moreSource§fn poisson_log_pmf(self, lambda: Self) -> Self
fn poisson_log_pmf(self, lambda: Self) -> Self
$\ln P(k; \lambda)$, the log of poisson_pmf, formed
directly (no exp then ln) so it stays finite far in the tails where the mass
itself underflows.Source§fn digamma(self) -> Self
fn digamma(self) -> Self
$\psi(x) = \frac{\mathrm{d}}{\mathrm{d}x}\ln\Gamma(x) = \frac{\Gamma'(x)}{\Gamma(x)}$
for any real input, for each value in a vector. Read moreSource§fn trigamma(self) -> Self
fn trigamma(self) -> Self
$\psi_1(x) = \frac{\mathrm{d}}{\mathrm{d}x}\psi(x)$,
the second derivative of $\ln\Gamma$. Read moreSource§fn zeta(self) -> Self
fn zeta(self) -> Self
$\zeta(s) = \sum_{n\ge1} n^{-s}$. Read moreSource§fn polylog(
self,
order: PolylogOrder<Self::Element, <Self::Signed as GenericVector>::Element>,
) -> Self
fn polylog( self, order: PolylogOrder<Self::Element, <Self::Signed as GenericVector>::Element>, ) -> Self
$\mathrm{Li}_s(z) = \sum_{k \ge 1} z^k / k^s$, continued
to the whole plane, at a scalar real order given as a PolylogOrder. Read moreSource§fn bessel_n<F, const N: i32>(self) -> Selfwhere
F: BesselFamily,
fn bessel_n<F, const N: i32>(self) -> Selfwhere
F: BesselFamily,
Source§fn bessel<F>(self, order: BesselOrder<Self, Self::Signed>) -> Selfwhere
F: BesselFamily,
fn bessel<F>(self, order: BesselOrder<Self, Self::Signed>) -> Selfwhere
F: BesselFamily,
Source§fn sph_bessel_n<F, const N: usize>(self) -> Selfwhere
F: BesselFamily,
fn sph_bessel_n<F, const N: usize>(self) -> Selfwhere
F: BesselFamily,
Source§fn sph_bessel<F>(self, n: u32) -> Selfwhere
F: BesselFamily,
fn sph_bessel<F>(self, n: u32) -> Selfwhere
F: BesselFamily,
sph_bessel_n for an order known only at runtime.Source§fn airy_all<const SCALED: bool>(self) -> (Self, Self, Self, Self)
fn airy_all<const SCALED: bool>(self) -> (Self, Self, Self, Self)
$(\mathrm{Ai}, \mathrm{Ai}', \mathrm{Bi}, \mathrm{Bi}')$, all four, with the
exponential factored out on the positive axis when SCALED (SciPy airy / airye). Read moreSource§fn lbeta(self, y: Self) -> Self
fn lbeta(self, y: Self) -> Self
$\ln\left|\mathrm{B}(x, y)\right|$, the log of the absolute Beta function. Read moreSource§fn jacobi(self, alpha: Self, beta: Self, n: u32, m: u32) -> Self
fn jacobi(self, alpha: Self, beta: Self, n: u32, m: u32) -> Self
Source§fn hermite_n<const N: usize>(self) -> Self
fn hermite_n<const N: usize>(self) -> Self
$H_N(x)$ where x is self and N is the polynomial degree. Read moreSource§fn hermitev(self, n: Self::Unsigned) -> Self
fn hermitev(self, n: Self::Unsigned) -> Self
H_n(x) where x is self and n is a vector of unsigned integers representing the polynomial degree. Read moreSource§fn hermite_function_n<const N: usize>(self) -> Self
fn hermite_function_n<const N: usize>(self) -> Self
Source§fn hermite_function(self, n: u32) -> Self
fn hermite_function(self, n: u32) -> Self
$\psi_n(x)$ for a degree known only at runtime. The runtime twin of
hermite_function_n: the same seed and recurrence,
with the per-step constants computed rather than folded.Source§fn hermite_function_series_n<const N: usize>(
self,
coeffs: &[Self::Element; N],
) -> Self
fn hermite_function_series_n<const N: usize>( self, coeffs: &[Self::Element; N], ) -> Self
x = self: Read moreSource§fn hermite_function_series(self, coeffs: &[Self::Element]) -> Self
fn hermite_function_series(self, coeffs: &[Self::Element]) -> Self
hermite_function_series_n over a
runtime-length coefficient slice. Read moreSource§fn laguerre_n<const N: usize>(self, alpha: Self) -> Self
fn laguerre_n<const N: usize>(self, alpha: Self) -> Self
$L_N^{(\alpha)}(x)$, where x is self and N is the polynomial degree. Read moreSource§fn laguerrev(self, alpha: Self, n: Self::Unsigned) -> Self
fn laguerrev(self, alpha: Self, n: Self::Unsigned) -> Self
$L_n^{(\alpha)}(x)$ where n is a vector of unsigned integers giving the degree per lane. Read moreSource§fn laguerre(self, alpha: Self, n: u32) -> Self
fn laguerre(self, alpha: Self, n: u32) -> Self
$L_n^{(\alpha)}(x)$ for a degree known only at runtime:
laguerrev with the degree splatted. The runtime twin of
laguerre_n.Source§fn laguerre_function_n<const N: usize>(self, alpha: Self) -> Self
fn laguerre_function_n<const N: usize>(self, alpha: Self) -> Self
Source§fn laguerre_function(self, alpha: Self, n: u32) -> Self
fn laguerre_function(self, alpha: Self, n: u32) -> Self
$\ell_n^{(\alpha)}(x)$ for a degree known only at runtime. The runtime twin of
laguerre_function_n: the same seed and
recurrence, with the per-step scales computed rather than folded.Source§fn laguerre_function_i_n<const N: usize>(self, alpha: i32) -> Self
fn laguerre_function_i_n<const N: usize>(self, alpha: i32) -> Self
Source§fn laguerre_function_i(self, alpha: i32, n: u32) -> Self
fn laguerre_function_i(self, alpha: i32, n: u32) -> Self
laguerre_function_i_n for a degree known only
at runtime.Source§fn laguerre_function_series_n<const N: usize>(
self,
alpha: Self,
coeffs: &[Self::Element; N],
) -> Self
fn laguerre_function_series_n<const N: usize>( self, alpha: Self, coeffs: &[Self::Element; N], ) -> Self
x = self: Read moreSource§fn laguerre_function_series(self, alpha: Self, coeffs: &[Self::Element]) -> Self
fn laguerre_function_series(self, alpha: Self, coeffs: &[Self::Element]) -> Self
laguerre_function_series_n over a
runtime-length coefficient slice. Read moreSource§fn laguerre_function_series_i_n<const N: usize>(
self,
alpha: i32,
coeffs: &[Self::Element; N],
) -> Self
fn laguerre_function_series_i_n<const N: usize>( self, alpha: i32, coeffs: &[Self::Element; N], ) -> Self
laguerre_function_series at a scalar integer
weight, in the same relation to it as
laguerre_function_i is to
laguerre_function. See there for what the integer
form buys.Source§fn laguerre_function_series_i(
self,
alpha: i32,
coeffs: &[Self::Element],
) -> Self
fn laguerre_function_series_i( self, alpha: i32, coeffs: &[Self::Element], ) -> Self
laguerre_function_series_i_n over a
runtime-length coefficient slice. Read moreSource§fn chebyshev<const K: usize>(self, coeffs: &[Self::Element]) -> Self
fn chebyshev<const K: usize>(self, coeffs: &[Self::Element]) -> Self
chebyshev_n over a runtime-length coefficient slice. Read moreSource§fn gaussian(self, a: Self, c: Self) -> Self
fn gaussian(self, a: Self, c: Self) -> Self
a and standard deviation c, defined as $a\, e^{-\frac{1}{2}(x/c)^2}$. Read moreSource§fn planck(self) -> Self
fn planck(self) -> Self
$\frac{x^3}{e^x - 1}$, finite at x = 0 where it
vanishes like $x^2$. Read moreSource§fn legendre(self, n: u32, m: u32) -> Self
fn legendre(self, n: u32, m: u32) -> Self
Source§fn legendre_series(self, coeffs: &[Self::Element]) -> Self
fn legendre_series(self, coeffs: &[Self::Element]) -> Self
legendre_series_n over a runtime-length
coefficient slice. Read moreSource§fn zernike_basis<const L: usize, const NORM: u8, const N: usize>(
x: Self,
y: Self,
out: &mut [Self; N],
)
fn zernike_basis<const L: usize, const NORM: u8, const N: usize>( x: Self, y: Self, out: &mut [Self; N], )
L at the Cartesian pupil point
(x, y), into out[j] for the ANSI Z80.28 / OSA index $j = (n(n+2) + m)/2$. Read moreSource§fn expint_n<const N: usize>(self) -> Self
fn expint_n<const N: usize>(self) -> Self
E_n(x) for integer order n.Source§fn expint(self, n: u32) -> Self
fn expint(self, n: u32) -> Self
E_n(x) for an order known only at runtime. The runtime twin of
expint_n: the same E_1 kernel, the same recurrence and the
same continued-fraction handover, so the two agree to the bit.Source§fn phi(self, n: u32) -> Self
fn phi(self, n: u32) -> Self
$\varphi_n(x)$ for an order known only at runtime. The runtime twin of
phi_n: the same series and recurrence arms, with the series
length worked out from n per call rather than at compile time.Source§fn carlson<K>(kind: K) -> Selfwhere
K: CarlsonKind<Output = Self>,
fn carlson<K>(kind: K) -> Selfwhere
K: CarlsonKind<Output = Self>,
CarlsonKind request struct
with named fields. The arity (and which argument is the parameter / repeated one)
is fixed per kind, so the wrong shape is a compile error. Read moreSource§fn ellint<K>(kind: K) -> Selfwhere
K: EllipticKind<Output = Self>,
fn ellint<K>(kind: K) -> Selfwhere
K: EllipticKind<Output = Self>,
EllipticKind request struct. Each
form (EllintK/EllintF/EllintE/
EllintEInc/EllintD/EllintDInc/
EllintPi/EllintPiInc) carries exactly
its own arguments, and completeness is encoded by whether the struct has a phi field. Read moreSource§impl<E, V> SpecialMathWithPolicy for V
impl<E, V> SpecialMathWithPolicy for V
Source§fn erf_p<P>(self) -> Vwhere
P: Policy,
fn erf_p<P>(self) -> Vwhere
P: Policy,
Computes the error function.
For f32 vectors, this is still decently accurate even with the Medium and Worst precision policies,
thanks to good approximations that don’t rely on the precision of exp. Subsequently, performance
of the lower precision policies is excellent. Furthermore, if using on a GPU with native exp support,
all precision policies will have good performance and accuracy.
Below Best, the f64 kernel forms erf as $1 - m\,e^{-x^2}$, whose error is a fixed
absolute ulp of 1: erf(0) comes out 2.2e-16 and erf(1e-8) is only 2e-8 relative.
From Best up, |x| < 0.84375 takes a direct $x + x\,R(x^2)/S(x^2)$ arm that is
exact at zero and relatively accurate down to the denormals. The f32 kernel carries
that arm from Average.
Source§fn erfc_p<P>(self) -> Vwhere
P: Policy,
fn erfc_p<P>(self) -> Vwhere
P: Policy,
Computes the complementary error function.
The f64 kernel is one product of six rationals times $e^{-x^2}$ over the whole
line, within about 3 ulp everywhere on hardware with a fused multiply-add: the one
error that grows, the rounding of $x^2$ under the exponential amplified by $x^2$,
is removed with the exact residual of the product at every tier. Without a native
FMA that residual is unavailable, so Best removes the growth with a bit-split of x
instead, and the lower tiers keep it (47 ulp at x = 14, 237 at x = 24).
Source§fn erfcx_p<P>(self) -> Vwhere
P: Policy,
fn erfcx_p<P>(self) -> Vwhere
P: Policy,
Computes the scaled complementary error function,
$\operatorname{erfcx}(x) = e^{x^2}\operatorname{erfc}(x)$.
erfc underflows to zero at x ~ 27 in f64 and x ~ 9 in f32,
where the true value is $e^{-x^2}/(x\sqrt{\pi})$, nonzero and merely too small to
represent. Anything reading a Gaussian tail past that point silently gets zero:
importance weights, log-likelihoods, censored-data models, the Voigt profile.
erfcx removes the exponential and decays only as $1/(x\sqrt{\pi})$, so it is
representable for every finite argument and keeps full relative accuracy.
Computed on the real backends as the Faddeeva function restricted to the imaginary
axis, $w(ix) = \operatorname{erfcx}(x)$, where Weideman’s rational approximation
degenerates to real arithmetic: one reciprocal and one Horner, no transcendental at
all for x >= 0. That makes it cheaper than the erfc it complements, and
measures 1.22 ulp worst over $x \in [0, 10^{15}]$ at the Best tier and above.
Negative arguments use $\operatorname{erfcx}(-x) = 2e^{x^2} - \operatorname{erfcx}(x)$
and legitimately overflow below about -26.6 (f64), the function itself growing
like $e^{x^2}$ in that direction.
The two are related by $\operatorname{erfc}(x) = e^{-x^2}\operatorname{erfcx}(x)$,
which is the numerically sound way to recover a tail value that erfc alone cannot
hold. Keep the $-x^2$ in the log domain rather than exponentiating it.
Source§fn logistic_sigmoid_p<P>(self) -> Vwhere
P: Policy,
fn logistic_sigmoid_p<P>(self) -> Vwhere
P: Policy,
Computes the Logistic sigmoid function, defined as $\sigma(x) = \frac{1}{1 + e^{-x}}$.
It’s worth mentioning that the derivative of the logistic sigmoid can be computed very cheaply from the output of the logistic sigmoid itself, in the form of:
let s = x.logistic_sigmoid();
let derivative = s * (1.0 - s); // or s.nmul_adde(s, s), which may be slightly fasterNotably, for f32 and f64 this implementation still has good precision for the Worst
precision policy, and for the Best precision policies handles very large positive and negative
inputs without overflow or underflow issues.
Source§fn logit_p<P>(self) -> Vwhere
P: Policy,
fn logit_p<P>(self) -> Vwhere
P: Policy,
Computes the logit $\ln\!\frac{p}{1-p}$, the inverse of
logistic_sigmoid.
Evaluated as $\ln(p) - \ln_{1p}(-p)$, which is accurate for small p where the direct
quotient is not. For p approaching 1 no evaluation order helps. $1 - p$ has already
lost its low digits inside the input itself, and the information is not recoverable from
p. A caller who knows $q = 1 - p$ should pass it to
logit_1m instead, which is exact at the far end of the range.
p = 0 gives -∞, p = 1 gives +∞, and p outside [0, 1] is out of domain.
Source§fn logit_1m_p<P>(self) -> Vwhere
P: Policy,
fn logit_1m_p<P>(self) -> Vwhere
P: Policy,
Computes $\mathrm{logit}(1 - q) = \ln\!\frac{1-q}{q}$ from the complement q directly.
The companion entry point to logit, in the same relationship as
langevin_1m has to
langevin. The logit diverges as its argument approaches 1,
and near that end $1 - p$ cannot be formed from p without losing every digit that
matters. Working in q throughout sidesteps that: evaluated as
$\ln_{1p}(-q) - \ln(q)$, accurate to a few ulp however small q is.
Note the sign convention follows the substitution, so logit_1m(q) == -logit(q) as
functions of the same number. The two differ in which probability the argument names.
Source§fn softplus_p<P>(self, k: V, rcp_k: V) -> Vwhere
P: Policy,
fn softplus_p<P>(self, k: V, rcp_k: V) -> Vwhere
P: Policy,
Computes the softplus function, defined as $\frac{1}{k}\ln(1 + e^{kx})$.
This is a smooth approximation to the ReLU function that is more numerically stable for large inputs.
The parameter k controls the steepness of the curve, with larger values approaching ReLU more closely.
Pass k = 1 and rcp_k = 1 for the standard softplus with no steepness scaling.
rcp_k must equal 1/k. It is passed explicitly so callers that invoke softplus repeatedly
with the same k can pre-compute the reciprocal once rather than recomputing it per call.
To also obtain the derivative with respect to x, use
softplus_d.
Source§fn tgamma_p<P>(self) -> Vwhere
P: Policy,
fn tgamma_p<P>(self) -> Vwhere
P: Policy,
Computes the Gamma function ($\Gamma(z)$) for any real input, for each value in a vector.
This implementation uses a few different behaviors to ensure the greatest precision where possible.
- For non-integer positive inputs, it uses the Lanczos approximation.
- For small non-integer negative inputs, it uses the recursive identity
$\Gamma(z) = \Gamma(z+1)/z$untilzis positive. - For large non-integer negative inputs, it uses the reflection formula
$-\pi / (\Gamma(z)\sin(\pi z)\,z)$. - For positive integers, it simply computes the factorial in a tight loop to ensure precision. Lookup tables could not be used with SIMD.
- At zero, the result will be positive or negative infinity based on the input sign (signed zero is a thing).
NOTE: The Gamma function is not defined for negative integers.
Source§fn lgamma_p<P>(self) -> Vwhere
P: Policy,
fn lgamma_p<P>(self) -> Vwhere
P: Policy,
Computes the natural log of the Gamma function ($\ln|\Gamma(x)|$) for any real input, for each value in a vector.
Source§fn poisson_pmf_p<P>(self, lambda: V) -> Vwhere
P: Policy,
fn poisson_pmf_p<P>(self, lambda: V) -> Vwhere
P: Policy,
The Poisson probability mass $P(k; \lambda) = e^{-\lambda}\lambda^k / k!$ at k = self,
for real $k \ge 0$ and mean $\lambda \ge 0$.
Not exp(k ln lambda - lambda - lgamma(k+1)): that forms an $O(1)$ answer as the
exponential of a difference of large numbers, and half an ulp of
$\ln\Gamma(k+1) = O(k \ln k)$ becomes that many ulp of the mass. For $k \ge 9$ this
uses Loader’s saddle-point form (the one R’s dpois uses),
P(k; \lambda) = \frac{e^{-\mathrm{stirlerr}(k) - \mathrm{bd0}(k, \lambda)}}{\sqrt{2\pi k}}with stirlerr the Stirling remainder (a short $1/k^2$ series) and bd0 the
deviance $k \ln(k/\lambda) + \lambda - k$ (a series in $(k-\lambda)/(k+\lambda)$ near
the peak, where the direct form cancels): both are small where the mass is not
negligible, so the exponential amplifies nothing, and there is no lgamma and no
ln at all near the peak. Below $k = 9$ the same machinery is used after shifting
k up by an integer, with the exact product $(k+1)\cdots(k+m)$ taken back out, so
there is no lgamma anywhere, and mixed vectors share one ln, one stirlerr and
one exp. Real k is allowed because
the Gamma density is the same function: $f(x; a) = P(a-1; x)$ for shape $a \ge 1$
(unit scale).
Edges: $\lambda = 0$ gives 1 at $k = 0$ and 0 above; $k = 0$ is $e^{-\lambda}$.
Source§fn poisson_log_pmf_p<P>(self, lambda: V) -> Vwhere
P: Policy,
fn poisson_log_pmf_p<P>(self, lambda: V) -> Vwhere
P: Policy,
$\ln P(k; \lambda)$, the log of poisson_pmf, formed
directly (no exp then ln) so it stays finite far in the tails where the mass
itself underflows.
Source§fn digamma_p<P>(self) -> Vwhere
P: Policy,
fn digamma_p<P>(self) -> Vwhere
P: Policy,
Computes the digamma function $\psi(x) = \frac{\mathrm{d}}{\mathrm{d}x}\ln\Gamma(x) = \frac{\Gamma'(x)}{\Gamma(x)}$
for any real input, for each value in a vector.
The argument is handled in three regimes:
- For
x >= 10, an asymptotic expansion in$1/x^2$is used. - For smaller
x, the recurrence$\psi(x) = \psi(x+1) - 1/x$shifts the argument into[1, 2], where a rational minimax approximation$\psi(x) = (x - x_0)(Y + R(x-1))$is used ($x_0$is the positive root of$\psi$). - For
x <= -1, the reflection formula$\psi(1-x) = \psi(x) + \pi\cot(\pi x)$is applied.
NOTE: The digamma function is not defined at zero or the negative integers. Those inputs yield NaN when overflow checking is enabled.
Source§fn trigamma_p<P>(self) -> Vwhere
P: Policy,
fn trigamma_p<P>(self) -> Vwhere
P: Policy,
Computes the trigamma function $\psi_1(x) = \frac{\mathrm{d}}{\mathrm{d}x}\psi(x)$,
the second derivative of $\ln\Gamma$.
Real vectors run a dedicated kernel (three minimax rational regions with a single
recurrence step and the $\pi^2/\sin^2(\pi x)$ reflection) that is a little tighter
than the general polygamma machinery at
order 1. polygamma(1) routes here, so the two spellings agree exactly. Complex
vectors have their own implementation, which is the reason this lives on
SpecialMath while polygamma is real-only.
The poles at zero and the negative integers evaluate to +inf: $\psi_1$ has
double poles, so unlike digamma the two one-sided limits
agree.
Source§fn polygamma_p<P>(self, n: u32) -> Vwhere
P: Policy,
fn polygamma_p<P>(self, n: u32) -> Vwhere
P: Policy,
Computes the polygamma function $\psi_n(x) = \frac{\mathrm{d}^n}{\mathrm{d}x^n}\psi(x)$,
the n-th derivative of digamma (n = 0 is digamma,
n = 1 is trigamma).
The order n is a runtime scalar shared by every lane. That is a deliberate design
choice: it closes the Gamma family under differentiation, since
$\psi_n'(x) = \psi_{n+1}(x)$ is reachable by passing n + 1, which is what lets
forward-mode AD (Dual) differentiate through any member of the family to any depth.
All order-dependent coefficients are scalar work splatted once, so uniform n
costs a vector nothing.
For n >= 2, real vectors run a masked recurrence up to the transition point
$N = 0.4\,d_{10} + 4n$ and then the Bernoulli asymptotic series on the positive
axis. Negative arguments reflect through the n-th derivative of $\cot(\pi x)$
(tabulated to n = 20, above which negative arguments return NaN). At zero
and the negative integers, odd n returns +inf (the correct two-sided limit)
and even n has one-sided limits of opposite sign, so it returns NaN when
overflow checking is enabled.
Complex vectors run the same recurrence-plus-series in complex arithmetic, gated
on $\operatorname{Re} z$, reflecting the half-plane $\operatorname{Re} z < 1/2$
through the same tabulated $\cot$ derivative (so the n <= 20 reflection reach
applies there too). Only psi_n of a real variable is real, so this is the
family member that makes polygamma complex-capable at all orders.
Orders where $n!$ overflows the element type (n >= 171 for f64, n >= 35 for
f32) return the signed infinity carried by the leading term on the real positive
axis, and NaN over C.
Source§fn zeta_p<P>(self) -> Vwhere
P: Policy,
fn zeta_p<P>(self) -> Vwhere
P: Policy,
Computes the Riemann zeta function $\zeta(s) = \sum_{n\ge1} n^{-s}$.
Evaluated as 1 + zetac, which is where the accuracy
argument lives (see there). Worst relative error measured against mpmath at 40
digits: 4.4e-16 for s in [1.5, 5], 4.3e-16 for [5, 40], 2.3e-15 through the
critical strip [0.1, 0.9], and 4.6e-16 approaching the pole at s = 1, which
returns infinity.
Negative s goes through the functional equation
$\zeta(s) = 2^s\pi^{s-1}\sin(\pi s/2)\,\Gamma(1-s)\,\zeta(1-s)$, landing back at
$1-s > 1$ where the series is at its most accurate. That arm costs a tgamma and
a sin_pi beyond the main path, so it is gated on a lane needing it.
This is the Riemann zeta of one real argument. The two-argument Hurwitz form
$\zeta(s, q)$ is not provided: it generalizes the same expansion but loses the
prime factorization that makes this one cheap, so it is a separate and materially
more expensive function rather than a special case of this one.
Source§fn zetac_p<P>(self) -> Vwhere
P: Policy,
fn zetac_p<P>(self) -> Vwhere
P: Policy,
Computes $\zeta(s) - 1$, accurately where $\zeta(s)$ is within rounding of 1.
$\zeta$ approaches 1 quickly: $\zeta(40) - 1$ is about 9.1e-13, already below
the mantissa of $\zeta$ itself, and $\zeta(80) - 1$ is 8.3e-25. Forming
zeta and subtracting 1 therefore destroys the answer: at
s = 40 it is off by 9e-8 relative, at s = 80 by 100%, and past s = 200 it
returns a flat zero.
This is not a wrapper around that subtraction. The Euler-Maclaurin sum underneath
opens with the $n = 1$ term, which is the 1, so the complement is obtained by
omitting it, with no cancellation anywhere and still full relative accuracy
at s = 700, where the value is around 1e-211. $\zeta$ is the derived form here,
the same way exp relates to exp_m1.
Same accuracy and the same negative-s handling as zeta.
Source§fn polylog_p<P>(
self,
order: PolylogOrder<<V as GenericVector>::Element, <<V as GenericVector>::Signed as GenericVector>::Element>,
) -> Vwhere
P: Policy,
fn polylog_p<P>(
self,
order: PolylogOrder<<V as GenericVector>::Element, <<V as GenericVector>::Signed as GenericVector>::Element>,
) -> Vwhere
P: Policy,
Computes the polylogarithm $\mathrm{Li}_s(z) = \sum_{k \ge 1} z^k / k^s$, continued
to the whole plane, at a scalar real order given as a PolylogOrder.
The order is uniform across the packet and tagged by class, because whole-number
order is a different, far cheaper algorithm than arbitrary real order and every
order-dependent coefficient is a per-call scalar precompute. See the
order module for why it is not a vector. Integer
covers both signs: $n \le 0$ is the closed rational form (a polynomial in
$z/(1-z)$), $n = 1$ is $-\ln(1-z)$, and $n \ge 2$ runs entirely on tabulated
$\zeta$ values. Real is the general algorithm (Wood 1992,
Roughan 2026): the defining series, the unity series about $z = 1$ with its two
cancelling poles fused algebraically so orders arbitrarily close to an integer cost
nothing extra, and Wood’s m-th-root identity in the far field.
On a real vector the argument is real and the result is the real part of the
principal value, which for $z > 1$ (the cut) is the same from either side. Complex
vectors return the full value. On the cut it follows the sign of $\mathrm{Im}\,z$’s
zero, C99 style, with -0 giving mpmath’s and Wood’s convention for a bare real.
let li2 = z.polylog(PolylogOrder::Integer(2)); // the dilogarithm
let fd = (-x.exp()).polylog(PolylogOrder::Real(1.5)); // -F_{1/2}(x)/Gamma(3/2)The order is spelled in the vector’s own element types: Real carries
Self::Element (a complex element on a complex vector, of which only a real value
is implemented and anything else answers NaN, or a dual element on a dual vector, whose
derivative part must be zero) and Integer carries the signed lane element
(i64 on an f64 vector, i32 on an f32 one). Every order-dependent coefficient
is computed once per call in that element type through the scalar math surface.
Special values: $\mathrm{Li}_s(1) = \zeta(s)$ for $s > 1$ and $+\infty$ below,
$\mathrm{Li}_s(-1) = -\eta(s)$, $\mathrm{Li}_s(0) = 0$. Every arm is a fixed-length
series whose length follows the policy’s precision tier. Whole-number orders past
$n = 79$ (binary64) or $n = 34$ (binary32, where $n!$ overflows) return NaN in
the far field ($|\ln z| > 3.2$). The series and unity arms have no such limit. Cost
grows with $\ln|z|$ in the far field at real order (one unity series per root,
$m \approx \ln|z| / 2.08$ roots).
Measured against mpmath on 4952 points (real and complex $z$, orders from -6 to
30 and a dozen real ones including $2 + 10^{-9}$), binary64 at Precision:
whole-number orders $n \ge 0$ within 1.3e-14 relative on the real line. Negative
whole orders within 1.5e-13 (the alternating defining series on the negative axis
peaks at ~2500x its sum). Real orders within 3.1e-13, with the far field’s m-th-root
sum cancelling by $m^{s-1}$, which is what makes binary32 real order 1.1e-4 there
and 2e-5 elsewhere. On the cut the real part is accurate normwise (the imaginary part
can be a millionth of it near $z = 1$ at $s = 1 + 10^{-6}$).
Autodiff closes by $\mathrm{Li}_s'(z) = \mathrm{Li}_{s-1}(z)/z$ with the order
lowered by one, which is why the runtime order is what the trait carries.
Source§fn bessel_n_p<P, F, const N: i32>(self) -> Vwhere
P: Policy,
F: BesselFamily,
fn bessel_n_p<P, F, const N: i32>(self) -> Vwhere
P: Policy,
F: BesselFamily,
A cylindrical Bessel function at compile-time order, selected by family marker:
J, Y, I, K, or any of
them under Scaled. N is signed and the families reflect at
negative order ($J_{-n} = (-1)^n J_n$, $I_{-n} = I_n$).
let j2 = x.bessel_n::<J, 2>(); // J_2(x)
let ke = x.bessel_n::<Scaled<K>, 0>(); // e^x K_0(x)The marker only selects: each spelling is a one-line route into the kernel for that
family, scaling and order form, with nothing evaluated that was not asked for. Scaled<J> and
Scaled<Y> are the SciPy jve/yve scalings by $e^{-|\mathrm{Im}\,z|}$, which
is 1 on the real axis, so on a real vector they are J and Y unchanged. On a
complex vector they are the scaled values.
Source§fn bessel_p<P, F>(
self,
order: BesselOrder<V, <V as GenericVector>::Signed>,
) -> Vwhere
P: Policy,
F: BesselFamily,
fn bessel_p<P, F>(
self,
order: BesselOrder<V, <V as GenericVector>::Signed>,
) -> Vwhere
P: Policy,
F: BesselFamily,
bessel_n with the order taken per lane, at runtime,
as a BesselOrder of any class.
let iv = x.bessel::<Scaled<I>>(BesselOrder::Real(nu)); // e^{-|x|} I_nu(x)
let jh = x.bessel::<J>(BesselOrder::HalfInteger(k)); // J_{k/2}(x), elementarySource§fn sph_bessel_n_p<P, F, const N: usize>(self) -> Vwhere
P: Policy,
F: BesselFamily,
fn sph_bessel_n_p<P, F, const N: usize>(self) -> Vwhere
P: Policy,
F: BesselFamily,
Source§fn sph_bessel_p<P, F>(self, n: u32) -> Vwhere
P: Policy,
F: BesselFamily,
fn sph_bessel_p<P, F>(self, n: u32) -> Vwhere
P: Policy,
F: BesselFamily,
sph_bessel_n for an order known only at runtime.
Source§fn airy_p<P, W>(self) -> V
fn airy_p<P, W>(self) -> V
One Airy function selected by marker: Ai, AiPrime,
Bi, BiPrime, or any of them under
Scaled.
Not a slice of airy_all: the four outputs come from two
Bessel passes (order 1/3 for the values, 2/3 for the derivatives), and asking for
one runs one pass (Ai skips the I half of it too, so it is roughly a quarter
of the tuple). Take the tuple when you want more than one of them.
let ai = x.airy::<Ai>();
let bp = x.airy::<Scaled<BiPrime>>(); // e^{-zeta} Bi'(x) on the positive axisSource§fn airy_all_p<P, const SCALED: bool>(self) -> (V, V, V, V)where
P: Policy,
fn airy_all_p<P, const SCALED: bool>(self) -> (V, V, V, V)where
P: Policy,
$(\mathrm{Ai}, \mathrm{Ai}', \mathrm{Bi}, \mathrm{Bi}')$, all four, with the
exponential factored out on the positive axis when SCALED (SciPy airy / airye).
Prefer the scaled form on accuracy grounds, not only range: on the positive
axis the kernel produces $e^{\zeta}K$ natively, so it evaluates no exponential
anywhere and holds 1-3 eps where the unscaled one reaches 684 at x = 100
($\zeta = \tfrac{2}{3}x^{3/2}$). Unscaled, Ai underflows past x ~ 104 and
Bi overflows past x ~ 104.5. For x < 0 the functions oscillate, nothing is
factored out, and the phase error grows like $|x|^{3/2}$ in every library.
Source§fn lbeta_p<P>(self, y: V) -> Vwhere
P: Policy,
fn lbeta_p<P>(self, y: V) -> Vwhere
P: Policy,
Computes $\ln\left|\mathrm{B}(x, y)\right|$, the log of the absolute Beta function.
beta itself underflows to zero for quite ordinary arguments
($\mathrm{B}(200, 200)$ is about 1e-121, already gone in f32) and overflows for
arguments straddling the poles. The log form has range to spare in both directions and is
what the surrounding computation usually wants anyway, since Beta almost always appears
inside a product of Gammas that is about to be logged.
Evaluated as $\ln\Gamma(x) + \ln\Gamma(y) - \ln\Gamma(x+y)$. The absolute value follows
lgamma, so recover the sign from
lgamma_r if the arguments can be negative.
This buys range at some cost in relative accuracy. The three lgamma terms cancel
against each other, shedding roughly $\log_{10}\frac{\ln\Gamma(x+y)}{|\ln \mathrm{B}|}$
digits. That is under one digit at $x = y = 200$, and a little over two at
$x = 200,\ y = 1$ where the terms are near 860 and the answer is near -5.3. It remains
far better conditioned than beta, which simply has no value to
return across most of that domain.
Source§fn jacobi_p<P>(self, alpha: V, beta: V, n: u32, m: u32) -> Vwhere
P: Policy,
fn jacobi_p<P>(self, alpha: V, beta: V, n: u32, m: u32) -> Vwhere
P: Policy,
Computes the m-th derivative of the n-th degree Jacobi polynomial
A the special case where α and β are both zero, the Jacobi polynomial reduces to a Legendre polynomial.
NOTE: Given constant α, β or n, LLVM will happily optimize those away and unroll loops.
Source§fn hermite_n_p<P, const N: usize>(self) -> Vwhere
P: Policy,
fn hermite_n_p<P, const N: usize>(self) -> Vwhere
P: Policy,
Computes the N-th degree physicists’ Hermite polynomial
$H_N(x)$ where x is self and N is the polynomial degree.
Evaluated by the three-term recurrence
H_{n+1}(x) = 2x\,H_n(x) - 2n\,H_{n-1}(x)seeded with $H_0 = 1$ and $H_1(x) = 2x$. The trip count is N, with no data
dependence, so LLVM unrolls the whole thing into straight-line FMA.
The derivative is another member of the same family, $H_n'(x) = 2n\,H_{n-1}(x)$, so a
value-and-slope pair costs one extra call rather than a separate kernel. The
probabilists’ polynomials are a rescaling, $He_n(x) = 2^{-n/2} H_n(x/\sqrt{2})$.
NOTE: this is the raw polynomial, which grows fast: $H_n(0) = (-2)^{n/2} (n-1)!!$ for
even n, and $H_n(x) \sim (2x)^n$ in the tails. It leaves binary32 range at the origin
around degree 48 and binary64 around 300, and much earlier for |x| of a few units. If
what you actually want is the normalized Hermite function (the quantum harmonic
oscillator eigenstate, a Hermite-Gauss beam mode, or the basis of a Hermite spectral
method), use hermite_function, which folds the
Gaussian weight and the normalization into the recurrence and stays $O(1)$ at every
degree. The raw polynomial is the right primitive for Gauss-Hermite quadrature
node-finding at modest n and for anything that genuinely wants $H_n$ itself.
Source§fn hermitev_p<P>(self, n: <V as GenericVector>::Unsigned) -> Vwhere
P: Policy,
fn hermitev_p<P>(self, n: <V as GenericVector>::Unsigned) -> Vwhere
P: Policy,
Computes the n-th degree physicists’ Hermite polynomial
H_n(x) where x is self and n is a vector of unsigned integers representing the polynomial degree.
The polynomial is calculated independently per-lane with the given degree in n.
This uses the recurrence relation to compute the polynomial iteratively.
Source§fn hermite_function_n_p<P, const N: usize>(self) -> Vwhere
P: Policy,
fn hermite_function_n_p<P, const N: usize>(self) -> Vwhere
P: Policy,
Computes the orthonormal Hermite function
\psi_N(x) = \frac{1}{\sqrt{2^N N! \sqrt{\pi}}}\, e^{-x^2/2}\, H_N(x)where x is self. These are the eigenfunctions of the quantum harmonic oscillator
and of the Fourier transform, the Hermite-Gauss modes of a paraxial beam, and the
basis of Hermite spectral methods. They are orthonormal on the whole line,
$\int \psi_m \psi_n\, dx = \delta_{mn}$.
Evaluated by the recurrence on the functions themselves,
\psi_{n+1}(x) = \sqrt{\tfrac{2}{n+1}}\, x\, \psi_n(x) - \sqrt{\tfrac{n}{n+1}}\, \psi_{n-1}(x)which keeps every intermediate $O(1)$ (the polynomial’s growth and the Gaussian’s
decay cancel inside each step), so unlike hermite it does not
overflow at high degree. Both square roots are literals under the unrolled loop. The
per-step cost is one FMA on the critical path.
§Range
The only quantity that can leave the exponent range is the Gaussian seed, which is
carried as $e^{-x^2/4}$ in two halves to double the reach. Full accuracy at every
degree holds for $|x|$ under about 18.7 (binary32) or 53 (binary64), which covers
every degree up to about 175 / 1400 everywhere on the line, since past the turning
point $\sqrt{2n+1}$ the true value decays faster than the seed. Beyond that the result
is still correct wherever $e^{-x^2/4}$ is representable, and zero past it.
Under a Best-or-better precision policy on true-FMA hardware, the rounding of $x^2$
(which is the entire error budget of a Gaussian at large x) is recovered exactly and
corrected to first order.
Source§fn hermite_function_p<P>(self, n: u32) -> Vwhere
P: Policy,
fn hermite_function_p<P>(self, n: u32) -> Vwhere
P: Policy,
$\psi_n(x)$ for a degree known only at runtime. The runtime twin of
hermite_function_n: the same seed and recurrence,
with the per-step constants computed rather than folded.
Source§fn hermite_function_series_n_p<P, const N: usize>(
self,
coeffs: &[<V as GenericVector>::Element; N],
) -> Vwhere
P: Policy,
fn hermite_function_series_n_p<P, const N: usize>(
self,
coeffs: &[<V as GenericVector>::Element; N],
) -> Vwhere
P: Policy,
Evaluates a finite series of Hermite functions at x = self:
\sum_{k=0}^{N-1} \mathrm{coeffs}[k] \cdot \psi_k(x)with $\psi_k$ as in hermite_function. Evaluated by
Clenshaw’s backward recurrence, which is more stable than summing the functions one at
a time and never forms them individually. N is the length of the coefficient array,
so the highest function is $\psi_{N-1}$; N = 0 is rejected.
Same range as hermite_function: the coefficients are
pre-scaled by half of the Gaussian and the outer factor carries the other half, so the
running Clenshaw values grow no faster than $e^{x^2/4}$.
Source§fn hermite_function_series_p<P>(
self,
coeffs: &[<V as GenericVector>::Element],
) -> Vwhere
P: Policy,
fn hermite_function_series_p<P>(
self,
coeffs: &[<V as GenericVector>::Element],
) -> Vwhere
P: Policy,
hermite_function_series_n over a
runtime-length coefficient slice.
Same recurrence, same pre-scaling, same range. The length is the only difference,
and it costs real work rather than only unrolling: the recurrence coefficients
$\sqrt{2/(k+1)}$ and $\sqrt{k/(k+1)}$ fold to literals when N is a constant
and become per-step square roots when it is not. Prefer the const form when the
degree is known.
An empty coefficient slice is 0, where the const form rejects N = 0 at compile
time.
Source§fn laguerre_n_p<P, const N: usize>(self, alpha: V) -> Vwhere
P: Policy,
fn laguerre_n_p<P, const N: usize>(self, alpha: V) -> Vwhere
P: Policy,
Computes the generalized (associated) Laguerre polynomial
$L_N^{(\alpha)}(x)$, where x is self and N is the polynomial degree.
Passing alpha = Self::ZERO gives the ordinary Laguerre polynomial $L_N(x)$; because
alpha is an ordinary argument rather than a const generic, that case folds away
completely when the zero is visible at the call site.
Evaluated by the three-term recurrence
(n+1)\,L_{n+1}^{(\alpha)}(x) = (2n + \alpha + 1 - x)\,L_n^{(\alpha)}(x) - (n + \alpha)\,L_{n-1}^{(\alpha)}(x)seeded with $L_0^{(\alpha)} = 1$ and $L_1^{(\alpha)}(x) = 1 + \alpha - x$. The trip count
is N, with no data dependence, so LLVM unrolls the whole thing into straight-line FMA.
The derivative is another member of the same family,
$\frac{\mathrm{d}}{\mathrm{d}x} L_n^{(\alpha)}(x) = -L_{n-1}^{(\alpha+1)}(x)$, so a
value-and-slope pair costs one extra call rather than a separate kernel.
NOTE: the forward recurrence is the standard evaluation route (Boost and GSL both use
it) and is well behaved across the oscillatory region $0 \le x \lesssim 4n$. Past that
$L_n^{(\alpha)}$ itself grows like $(-x)^n/n!$ and will overflow for large N and x
on its own account.
Laguerre-Gaussian beam modes, the radial part of the hydrogen wavefunction, the quantum harmonic oscillator and coherent-state expansions, and Gauss-Laguerre quadrature.
Source§fn laguerrev_p<P>(self, alpha: V, n: <V as GenericVector>::Unsigned) -> Vwhere
P: Policy,
fn laguerrev_p<P>(self, alpha: V, n: <V as GenericVector>::Unsigned) -> Vwhere
P: Policy,
Computes the generalized (associated) Laguerre polynomial
$L_n^{(\alpha)}(x)$ where n is a vector of unsigned integers giving the degree per lane.
The per-lane counterpart of laguerre, in the same relation to it
as hermitev is to hermite. The
recurrence runs to the largest n in the vector and lanes freeze at their own degree, so
the cost is set by max(n) rather than by any one lane.
Source§fn laguerre_p<P>(self, alpha: V, n: u32) -> Vwhere
P: Policy,
fn laguerre_p<P>(self, alpha: V, n: u32) -> Vwhere
P: Policy,
$L_n^{(\alpha)}(x)$ for a degree known only at runtime:
laguerrev with the degree splatted. The runtime twin of
laguerre_n.
Source§fn laguerre_function_n_p<P, const N: usize>(self, alpha: V) -> Vwhere
P: Policy,
fn laguerre_function_n_p<P, const N: usize>(self, alpha: V) -> Vwhere
P: Policy,
Computes the orthonormal generalized Laguerre function
l_N^{(\alpha)}(x) = \sqrt{\frac{N!}{\Gamma(N+\alpha+1)}}\; x^{\alpha/2} e^{-x/2}\, L_N^{(\alpha)}(x)where x is self. Orthonormal on the half-line, $\int_0^\infty l_m l_n\, dx = \delta_{mn}$.
This is the radial factor of Laguerre-Gauss beam modes and (up to a power of x from the
spherical measure) of the hydrogen wavefunctions. Defined for $x \ge 0$ and
$\alpha > -1$, and nothing is checked outside that.
Evaluated by the recurrence on the functions themselves, with
$s_k = \sqrt{(k+1)(k+\alpha+1)}$:
l_{k+1} = \frac{(2k + \alpha + 1 - x)\, l_k - s_{k-1}\, l_{k-1}}{s_k}which keeps every intermediate $O(1)$, so unlike laguerre
it does not overflow at high degree or large x. alpha is a runtime vector, so each
step also carries a sqrt and a reciprocal, beside the recurrence rather than on its
critical path, and folded to literals when alpha is a visible constant. The seed
is skipped outright by a uniform branch when every lane has alpha = 0, which is the
ordinary Laguerre function and by far the common case.
§Range
The Gaussian-like seed $x^{\alpha/2} e^{-x/2}$ is carried as $e^{-x/4}$ in two
halves, as in hermite_function. Full accuracy at
every degree for x under about 350 (binary32) or 2800 (binary64), covering every
degree up to roughly 87 / 700 everywhere on the half-line (the turning point of
$l_n^{(\alpha)}$ is near 4n).
alpha is unrestricted over the same x range. The seed’s whole parameter
dependence, $x^{\alpha/2}/\sqrt{\Gamma(\alpha+1)}$, is the square root of the Poisson
mass $P(\alpha; x)$ and is evaluated as poisson_pmf
is (Loader’s saddle-point form, one exponential of a small exponent), so neither
factor materializes (separately $x^{\alpha/2}$ overflows binary64 near
$\alpha = 250$ and $1/\sqrt{\Gamma(\alpha+1)}$ underflows near $\alpha = 320$,
and their overlap would be inf * 0) and nothing large is exponentiated: 0-3 ulp
at the peak x ~ alpha out to $\alpha = 1400$, against a 50-digit oracle.
Source§fn laguerre_function_p<P>(self, alpha: V, n: u32) -> Vwhere
P: Policy,
fn laguerre_function_p<P>(self, alpha: V, n: u32) -> Vwhere
P: Policy,
$\ell_n^{(\alpha)}(x)$ for a degree known only at runtime. The runtime twin of
laguerre_function_n: the same seed and
recurrence, with the per-step scales computed rather than folded.
Source§fn laguerre_function_i_n_p<P, const N: usize>(self, alpha: i32) -> Vwhere
P: Policy,
fn laguerre_function_i_n_p<P, const N: usize>(self, alpha: i32) -> Vwhere
P: Policy,
laguerre_function at an integer weight, taken as a
scalar i32 rather than a vector.
Same function and same range. What changes is what the compiler can see. Every
quantity the recurrence derives from the weight (the $s_k = \sqrt{(k+1)(k+\alpha+1)}$
and their reciprocals, and the $2k+\alpha+1$ offsets) becomes a scalar constant
instead of a vector sqrt and reciprocal per step, and folds to a literal outright
when alpha is compile-time known.
The seed changes too. Up to $\alpha = 170$ (binary64) / 29 (binary32) the
normalization $x^{\alpha/2}/\sqrt{\alpha!}$ is a scalar factorial, a powi and at
most one sqrt, with no ln, lgamma or second exp at all, and a few ulp more
accurate than the log form, whose lgamma error is amplified by the exponential.
$\alpha = 0$ is a scalar test that skips even that. Beyond the cap it takes
the vector form’s saddle-point seed. Measured on AVX2 f64x4 at degree 4:
about 5x faster than the vector form at a literal small weight, 2x at a runtime one.
Prefer this whenever the weight is a non-negative integer, which every classical
application has: the hydrogen radial functions use $\alpha = 2\ell+1$ and the
Laguerre-Gauss beam modes use $\alpha = |\ell|$. Negative values are out of domain,
as $\alpha \le -1$ is for the general form.
Like the series forms this is inlined into the caller rather than given its own
dispatch trampoline: the weight is a plain i32 argument, and a shared
out-of-line copy would take it at runtime, which both defeats the folding above
and (measured) stops LLVM overlapping consecutive evaluations, at 7x the cost.
Call it from inside a #[thermite::dispatch] body.
Source§fn laguerre_function_i_p<P>(self, alpha: i32, n: u32) -> Vwhere
P: Policy,
fn laguerre_function_i_p<P>(self, alpha: i32, n: u32) -> Vwhere
P: Policy,
laguerre_function_i_n for a degree known only
at runtime.
Source§fn laguerre_function_series_n_p<P, const N: usize>(
self,
alpha: V,
coeffs: &[<V as GenericVector>::Element; N],
) -> Vwhere
P: Policy,
fn laguerre_function_series_n_p<P, const N: usize>(
self,
alpha: V,
coeffs: &[<V as GenericVector>::Element; N],
) -> Vwhere
P: Policy,
Evaluates a finite series of generalized Laguerre functions at x = self:
\sum_{k=0}^{N-1} \mathrm{coeffs}[k] \cdot l_k^{(\alpha)}(x)with $l_k^{(\alpha)}$ as in laguerre_function.
Clenshaw’s backward recurrence, same range as the single function; N is the
coefficient count and N = 0 is rejected.
Source§fn laguerre_function_series_p<P>(
self,
alpha: V,
coeffs: &[<V as GenericVector>::Element],
) -> Vwhere
P: Policy,
fn laguerre_function_series_p<P>(
self,
alpha: V,
coeffs: &[<V as GenericVector>::Element],
) -> Vwhere
P: Policy,
laguerre_function_series_n over a
runtime-length coefficient slice.
Same recurrence, same pre-scaling, same range. The per-step weights are computed
rather than folded, as in
hermite_function_series. An empty
coefficient slice is 0.
Source§fn laguerre_function_series_i_n_p<P, const N: usize>(
self,
alpha: i32,
coeffs: &[<V as GenericVector>::Element; N],
) -> Vwhere
P: Policy,
fn laguerre_function_series_i_n_p<P, const N: usize>(
self,
alpha: i32,
coeffs: &[<V as GenericVector>::Element; N],
) -> Vwhere
P: Policy,
laguerre_function_series at a scalar integer
weight, in the same relation to it as
laguerre_function_i is to
laguerre_function. See there for what the integer
form buys.
Source§fn laguerre_function_series_i_p<P>(
self,
alpha: i32,
coeffs: &[<V as GenericVector>::Element],
) -> Vwhere
P: Policy,
fn laguerre_function_series_i_p<P>(
self,
alpha: i32,
coeffs: &[<V as GenericVector>::Element],
) -> Vwhere
P: Policy,
laguerre_function_series_i_n over a
runtime-length coefficient slice.
The _n is the coefficient count and the _i is the integer weight, in that
order because the length is the newer axis, and both mean what they do everywhere else.
An empty coefficient slice is 0.
Source§fn chebyshev_n_p<P, const K: usize, const N: usize>(
self,
coeffs: &[<V as GenericVector>::Element; N],
) -> Vwhere
P: Policy,
fn chebyshev_n_p<P, const K: usize, const N: usize>(
self,
coeffs: &[<V as GenericVector>::Element; N],
) -> Vwhere
P: Policy,
Evaluates a finite series of Chebyshev polynomials
of the K-th kind at x = self:
\sum_{k=0}^{N-1} \mathrm{coeffs}[k] \cdot P_k(x)where P_k is T_k, U_k, V_k, or W_k depending on K. All four kinds share the
recurrence $P_{k+1}(x) = 2x \cdot P_k(x) - P_{k-1}(x)$ with P_0(x) = 1, and differ only in
P_1(x):
K | Kind | P_1(x) | Notes |
|---|---|---|---|
1 | First (T_k) | x | Most common, the minimax/approximation basis on [-1, 1]. |
2 | Second (U_k) | 2x | Related to $\sin((k+1)\theta)/\sin(\theta)$ under $x = \cos\theta$. |
3 | Third (V_k) | 2x - 1 | “Airfoil” polynomials; $\cos((k+\tfrac12)\theta)/\cos(\theta/2)$. |
4 | Fourth (W_k) | 2x + 1 | $\sin((k+\tfrac12)\theta)/\sin(\theta/2)$. |
Any other value of K is a compile-time error.
There is deliberately no single-polynomial T_n(x) entry point beside this, unlike
legendre or hermite. Chebyshev
polynomials are used almost exclusively as an approximation basis, i.e. as a series;
their quadrature nodes and weights are closed-form, so nothing needs to iterate on a
lone $T_n$; and the one genuine single-$T_n$ application (Chebyshev filter response,
Dolph-Chebyshev windows) needs $|x| > 1$, where the right evaluation is
$\cosh(n \cosh^{-1} x)$ and not this recurrence at all. A unit coefficient array
recovers $T_n$ if it is ever wanted.
Evaluation is done via Clenshaw’s backward recurrence with FMA, which is
more numerically stable than a forward sum when the partial sums of
$\sum c_k P_k$ are much smaller than $\max_k |c_k P_k|$ (e.g. fitted minimax series
with alternating-sign coefficients). N is the length of the coefficient
slice, so the highest polynomial term is P_{N-1}; N = 0 is rejected,
N = 1 evaluates to coeffs[0].
coeffs[0] multiplies P_0 = 1, coeffs[1] multiplies P_1(x) (which depends on K),
and so on. Because LLVM sees both K and N as constants, the recurrence loop and the
P_1 selection are fully unrolled and specialized at monomorphization time.
§Accuracy near $x = \pm 1$
The plain recurrence forms $2x b_{k+1} - b_{k+2}$ with consecutive $b_k$ of nearly
equal magnitude as x approaches either endpoint, and cancels. This is a property of
the recurrence, not of the series: measured against a 60-digit oracle at N = 24,
it costs up to 37 ulp on sums whose own condition number is about 1, and up to 230 ulp
on unstructured coefficients.
Under a Best-or-better precision policy, real vectors instead take Reinsch’s
modification, which recurs on the differences (near +1) or sums (near -1) so the
small quantity is never formed by subtraction. On the same grid that bounds the error
envelope 2.5x to 17x tighter across all four kinds. It is an envelope improvement
rather than a pointwise one (individual arguments can land worse), and costs
roughly 2x on the recurrence’s dependency chain, which is why it is gated.
binary32 gains the same way, 2.6x to 13.5x on its own grid. Measuring it needs an
f32-native one: 1 - 2^-j rounds to exactly 1.0 for every j >= 24, so an f64
grid piles two thirds of its points onto the endpoint itself, where the endpoint
form degenerates into a plain running sum and the two policies agree, and never
samples the f32 neighbourhood where the cancellation actually bites.
Coefficients from a minimax or least-squares fit decay geometrically and barely notice either way (about 3 ulp to 1). The gap opens on slowly-decaying or non-decaying spectra: truncated expansions, near-singular functions, or coefficients that came from somewhere other than a fit.
Complex and the composite arithmetics keep the plain recurrence at every policy,
since Reinsch needs a real copysign and a meaningful nearest endpoint.
Source§fn chebyshev_p<P, const K: usize>(
self,
coeffs: &[<V as GenericVector>::Element],
) -> Vwhere
P: Policy,
fn chebyshev_p<P, const K: usize>(
self,
coeffs: &[<V as GenericVector>::Element],
) -> Vwhere
P: Policy,
chebyshev_n over a runtime-length coefficient slice.
K stays a const generic, since it selects which Chebyshev kind, not how many
coefficients, and there are exactly four. Only the length becomes dynamic.
Same recurrence and the same Best-precision Reinsch form near $x = \pm 1$; what
the runtime length costs is the unrolling and the folded coeffs indices. An empty
coefficient slice is 0.
Source§fn gaussian_p<P>(self, a: V, c: V) -> Vwhere
P: Policy,
fn gaussian_p<P>(self, a: V, c: V) -> Vwhere
P: Policy,
Computes the Gaussian function with amplitude a and standard deviation c, defined as $a\, e^{-\frac{1}{2}(x/c)^2}$.
The position b is assumed to be zero. For a non-zero position, use self - b as the input.
Source§fn planck_p<P>(self) -> Vwhere
P: Policy,
fn planck_p<P>(self) -> Vwhere
P: Policy,
Computes the Planck shape factor $\frac{x^3}{e^x - 1}$, finite at x = 0 where it
vanishes like $x^2$.
The dimensionless kernel of Planck’s law: substituting $x = h\nu/kT$ recovers the
spectral radiance up to a scale factor, so this is the part worth computing carefully and
the constants are left to the caller. Radiative transfer, climate radiation budgets, and
stellar atmospheres.
The denominator cancels for small x and the quotient is $0/0$ at the origin.
Evaluated here as $x^2/\varphi_1(x)$ using phi_n::<1>, which is finite and equal
to 1 there, so the singularity never forms rather than being patched after the fact.
Source§fn legendre_p<P>(self, n: u32, m: u32) -> Vwhere
P: Policy,
fn legendre_p<P>(self, n: u32, m: u32) -> Vwhere
P: Policy,
Computes the m-th associated n-th degree Legendre polynomial, where m=0 signifies the regular n-th degree Legendre polynomial.
If m is odd, the input is only valid between -1 and 1
NOTE: Given constant n and/or m, LLVM will happily unroll and optimize inner loops.
Internally, this is computed with jacobi when m > 0.
Source§fn legendre_series_n_p<P, const N: usize>(
self,
coeffs: &[<V as GenericVector>::Element; N],
) -> Vwhere
P: Policy,
fn legendre_series_n_p<P, const N: usize>(
self,
coeffs: &[<V as GenericVector>::Element; N],
) -> Vwhere
P: Policy,
Evaluates a finite Legendre series
at x = self:
\sum_{k=0}^{N-1} \mathrm{coeffs}[k] \cdot P_k(x)The form a Legendre-moment expansion takes: Mie and Henyey-Greenstein scattering
phase functions tabulated by their moments, multipole expansions in $\cos\theta$, and
the polar factor of a spherical-harmonic expansion at fixed order.
Evaluated by Clenshaw’s backward recurrence on the Legendre three-term relation, which
is more stable than building each $P_k$ with legendre and
summing, and does $O(N)$ work rather than $O(N^2)$. The recurrence ratios
$(2k+1)/(k+1)$ and $k/(k+1)$ are literals under the unrolled loop, so the per-step
cost matches chebyshev: one FMA on the critical path. N
is the coefficient count; N = 0 is rejected, N = 1 evaluates to coeffs[0].
Plain Clenshaw at every policy: the endpoint cancellation that chebyshev treats
under Best precision exists here too ($P_n(1) = 1$ for every n), but its
Reinsch-style rewrite for the Legendre ratios has not been derived or measured.
Source§fn legendre_series_p<P>(self, coeffs: &[<V as GenericVector>::Element]) -> Vwhere
P: Policy,
fn legendre_series_p<P>(self, coeffs: &[<V as GenericVector>::Element]) -> Vwhere
P: Policy,
legendre_series_n over a runtime-length
coefficient slice.
Plain Clenshaw here too. The recurrence ratios $(2k+1)/(k+1)$ and $k/(k+1)$ are
literals only when N is a constant, so this pays a division per step where the
const form pays none, the widest const-versus-slice gap of the series family.
An empty coefficient slice is 0.
Source§fn zernike_r_p<P>(self, n: u32, m: u32) -> Vwhere
P: Policy,
fn zernike_r_p<P>(self, n: u32, m: u32) -> Vwhere
P: Policy,
Computes the Zernike radial
polynomial $R_n^m(\rho)$, where rho is self.
Returns zero unless $m \le n$ with $n - m$ even, the condition for the mode to
exist. m is the absolute azimuthal frequency here. The sign only affects the
angular factor, which lives in zernike.
Evaluated through the shifted Jacobi identity
R_n^m(\rho) = \rho^m\, P_{(n-m)/2}^{(0,\,m)}\!\left(2\rho^2 - 1\right)rather than the textbook sum
$\sum_k (-1)^k \frac{(n-k)!}{k!\,((n+m)/2 - k)!\,((n-m)/2 - k)!} \rho^{n-2k}$, which
alternates factorials of size $(n-k)!$ against an answer bounded by 1 and loses all
precision somewhere around n = 10-15. That is well inside the range adaptive optics,
ophthalmology and surface metrology actually use.
The $(-1)^{(n-m)/2}$ prefactor usually seen with this identity is absent because the
argument is written $2\rho^2 - 1$ rather than $1 - 2\rho^2$: reflecting a Jacobi
polynomial swaps its two parameters and absorbs exactly that sign.
The polynomial is only orthogonal on $\rho \in [0, 1]$ and grows quickly outside it.
Nothing clamps the argument, so an unnormalized pupil coordinate stays the caller’s
problem.
Source§fn zernike_p<P, const NORM: u8>(self, theta: V, n: u32, m: i32) -> Vwhere
P: Policy,
fn zernike_p<P, const NORM: u8>(self, theta: V, n: u32, m: i32) -> Vwhere
P: Policy,
Computes the Zernike polynomial $Z_n^m(\rho, \theta)$ on the unit disc, with rho
as self:
Z_n^m(\rho, \theta) = N_n^m\, R_n^{|m|}(\rho) \times
\begin{cases} \cos(m\theta) & m \ge 0 \\ \sin(|m|\theta) & m < 0 \end{cases}Returns zero unless $|m| \le n$ with $n - |m|$ even.
NORM selects the normalization $N_n^m$ and must be either
ZERNIKE_UNIT_PEAK ($N = 1$, so $R_n^m(1) = 1$ and coefficients read as peak
amplitude) or ZERNIKE_ORTHONORMAL
($N_n^m = \sqrt{2(n+1)/(1 + \delta_{m,0})}$, the ANSI Z80.28 and Noll convention,
under which coefficients read as RMS contributions). Any other value is a compile-time
error. There is deliberately no default: the two differ by a factor of up to
$\sqrt{2(n+1)}$ per mode, and picking one silently is how coefficient sets get
misinterpreted.
(n, m) is a runtime pair rather than a const generic on purpose. The workload is a
basis, not a function. A wavefront fit evaluates tens to hundreds of modes over
thousands of pupil samples, with the mode list coming from a config or a sensor
geometry, so the degree is loop-invariant across the vector axis and const-generic
specialization would buy a jump table rather than an unrolled loop.
The single-index conventions (ANSI Z80.28 / OSA, Noll, Fringe) and the conversions
between them are in crate::zernike. They disagree from the second term
onward, so convert at the boundary rather than assuming.
Source§fn zernike_basis_p<P, const L: usize, const NORM: u8, const N: usize>(
x: V,
y: V,
out: &mut [V; N],
)where
P: Policy,
fn zernike_basis_p<P, const L: usize, const NORM: u8, const N: usize>(
x: V,
y: V,
out: &mut [V; N],
)where
P: Policy,
Evaluates all Zernike modes through degree L at the Cartesian pupil point
(x, y), into out[j] for the ANSI Z80.28 / OSA index $j = (n(n+2) + m)/2$.
N must equal (L+1)(L+2)/2 (compile-time checked), and NORM is
ZERNIKE_UNIT_PEAK or ZERNIKE_ORTHONORMAL as on
zernike.
This is the entry point a wavefront fit or reconstruction wants. It is not merely
a loop over zernike. Substituting $s = x^2+y^2$
splits every mode into a polynomial in s times $\operatorname{Re}$ or
$\operatorname{Im}$ of $(x+iy)^{|m|}$, which is where the $\rho^{|m|}$ and the
$\cos m\theta$ both come from at once. Evaluation is then pure polynomial
arithmetic: no atan2, no sqrt, no trigonometry, no division, $O(L^2)$ FMAs
for the entire basis, and no singularity at the pupil centre. Calling the
single-mode form per mode instead costs a sin_cos and a powi each and restarts
the radial recurrence every time, for $O(L^3)$ work.
Cartesian input is part of that, not a convenience: pupil samples arrive as
(x, y), and a polar entry point would charge an atan2 per sample for an angle
this kernel immediately dissolves.
Fully unrolled at compile time for each L up to
MAX_ZERNIKE_DEGREE; above that it takes a
rolled path that is correct at any degree and substantially slower.
Nothing normalizes (x, y) onto the unit disc. Outside it the polynomials still
evaluate correctly and simply are not orthogonal.
The layout is ANSI because it is the scheme whose index has a closed form and
whose degree truncation is contiguous. Noll and Fringe callers gather through
noll_to_ansi /
fringe_to_ansi.
use thermite::prelude::*;
use thermite_special::{SpecialMath, ZERNIKE_ORTHONORMAL};
use thermite_special::zernike::noll_to_ansi;
type V = Vector<f64>;
const L: usize = 4;
const N: usize = 15; // (L+1)(L+2)/2
let mut basis = [V::ZERO; N];
V::zernike_basis::<L, ZERNIKE_ORTHONORMAL, N>(V::splat(0.3), V::splat(0.4), &mut basis);
// Noll 4 is defocus, Z_2^0 = sqrt(3) (2 rho^2 - 1) orthonormal.
let defocus = basis[noll_to_ansi(4) as usize].extract::<0>();
assert!((defocus - 3f64.sqrt() * (2.0 * 0.25 - 1.0)).abs() < 1e-14);Source§fn lambert_w_p<P>(self) -> (V, V)where
P: Policy,
fn lambert_w_p<P>(self) -> (V, V)where
P: Policy,
Computes both branches of the Lambert W function simultaneously: ($W_0(x)$, $W_{-1}(x)$).
The $W_0$ result is valid for x >= -1/e; the $W_{-1}$ result is valid for -1/e <= x < 0.
Outside these domains, the respective result is NaN (when overflow checking is enabled).
Source§fn expint_n_p<P, const N: usize>(self) -> Vwhere
P: Policy,
fn expint_n_p<P, const N: usize>(self) -> Vwhere
P: Policy,
Computes the generalized exponential integral E_n(x) for integer order n.
Source§fn expint_p<P>(self, n: u32) -> Vwhere
P: Policy,
fn expint_p<P>(self, n: u32) -> Vwhere
P: Policy,
E_n(x) for an order known only at runtime. The runtime twin of
expint_n: the same E_1 kernel, the same recurrence and the
same continued-fraction handover, so the two agree to the bit.
Source§fn phi_n_p<P, const N: usize>(self) -> Vwhere
P: Policy,
fn phi_n_p<P, const N: usize>(self) -> Vwhere
P: Policy,
Returns $\varphi_N(x)$, the N-th phi-function of exponential integrators.
\varphi_0(x) = e^x, \qquad
\varphi_{k+1}(x) = \frac{\varphi_k(x) - 1/k!}{x}, \qquad
\varphi_k(x) = \sum_{n \ge 0} \frac{x^n}{(n + k)!}, \qquad
\varphi_k(0) = \frac{1}{k!}phi_n::<0> is exp. phi_n::<1> is $(e^x - 1)/x$, which written out
directly is $0/0$ at the origin and loses most of the mantissa near it, so it is
evaluated as $\mathrm{expm1}(x)/x$ with the removable singularity filled in (the
value is 1), which is accurate across the whole line. Outside the
exponential-integrator literature phi_n::<1> goes by exprel, which is the name
SciPy, Boost and the statistics literature use for it. There is no separate
exprel here because this is it. Beyond that the recurrence is
the wrong way to compute them: each step subtracts 1/k! from a value that is barely
larger while |x| is small, so $\varphi_2 = (\mathrm{expm1}(x) - x)/x^2$ loses twice the bits
phi_n::<1> would have, and gets worse with N. Below |x| = N this sums the series
instead (its terms are monotone there, so nothing cancels), and above it runs the
recurrence upward from expm1, where the amplification per step is bounded. Measured
against mpmath, both arms sit within a few ulp for N <= 8.
The series arm’s length is bounded by the policy’s max_iterations. The primitive
float types know their precision statically and use a fixed count instead. Nothing
caps N, though nothing needs it large: ETDRK4 wants phi_1..phi_3, and exponential
Rosenbrock methods rarely go past phi_4.
phi_n::<1> alone is the coefficient that keeps appearing wherever an exponential is
integrated over a finite step:
- The in-scattering integral through a homogeneous medium,
$\int_0^t e^{-\sigma s}\,ds = t\,\varphi_1(-\sigma t)$. The singular case is the empty medium, which is not an edge case in practice. - Exact stepping of an Ornstein-Uhlenbeck process, and the Langevin thermostat’s mean-reversion factor.
- Frame-rate-independent exponential smoothing, usually written
1 - exp(-k * dt)and then divided byk.
The higher orders are the coefficients of exponential time differencing: integrating
y' = Ly + N(y) exactly over a step gives $y(h) = e^{hL} y_0 + h\,\varphi_1(hL)\,N$, and
expanding N in time along the step brings in $\varphi_2, \varphi_3, \ldots$ as the
weights of the higher-order terms.
Source§fn phi_p<P>(self, n: u32) -> Vwhere
P: Policy,
fn phi_p<P>(self, n: u32) -> Vwhere
P: Policy,
$\varphi_n(x)$ for an order known only at runtime. The runtime twin of
phi_n: the same series and recurrence arms, with the series
length worked out from n per call rather than at compile time.
Source§fn carlson_p<P, K>(kind: K) -> Vwhere
P: Policy,
K: CarlsonKind<Output = V>,
fn carlson_p<P, K>(kind: K) -> Vwhere
P: Policy,
K: CarlsonKind<Output = V>,
Carlson symmetric elliptic integral, selected by a CarlsonKind request struct
with named fields. The arity (and which argument is the parameter / repeated one)
is fixed per kind, so the wrong shape is a compile error.
let rf = V::carlson(CarlsonRf { x, y, z });
let rj = V::carlson_p::<Precision, _>(CarlsonRj { x, y, z, p });Source§fn ellint_p<P, K>(kind: K) -> Vwhere
P: Policy,
K: EllipticKind<Output = V>,
fn ellint_p<P, K>(kind: K) -> Vwhere
P: Policy,
K: EllipticKind<Output = V>,
Legendre elliptic integral, selected by an EllipticKind request struct. Each
form (EllintK/EllintF/EllintE/
EllintEInc/EllintD/EllintDInc/
EllintPi/EllintPiInc) carries exactly
its own arguments, and completeness is encoded by whether the struct has a phi field.
Two family members that are not Legendre integrals dispatch through here as well,
because they are built from the same Carlson forms and belong beside their siblings:
JacobiZeta, the oscillating part of $E(\varphi, k)$, and
HeumanLambda, its complementary-modulus companion.
let k_int = V::ellint(EllintK { k }); // K(k)
let e_inc = V::ellint_p::<Precision, _>(EllintEInc { phi, k }); // E(phi, k)
let z = V::ellint(JacobiZeta { phi, k }); // Z(phi, k)Source§impl<V> Swizzle3 for V
impl<V> Swizzle3 for V
fn xyz(self) -> V
fn xxx(self) -> V
fn xxy(self) -> V
fn xxz(self) -> V
fn xyx(self) -> V
fn xyy(self) -> V
fn xzx(self) -> V
fn xzy(self) -> V
fn xzz(self) -> V
fn yxx(self) -> V
fn yxy(self) -> V
fn yxz(self) -> V
fn yyx(self) -> V
fn yyy(self) -> V
fn yyz(self) -> V
fn yzx(self) -> V
fn yzy(self) -> V
fn yzz(self) -> V
fn zxx(self) -> V
fn zxy(self) -> V
fn zxz(self) -> V
fn zyx(self) -> V
fn zyy(self) -> V
fn zyz(self) -> V
fn zzx(self) -> V
fn zzy(self) -> V
fn zzz(self) -> V
Source§impl<V> Swizzle4 for V
impl<V> Swizzle4 for V
fn xyzw(self) -> V
fn xxxx(self) -> V
fn xxxy(self) -> V
fn xxxz(self) -> V
fn xxxw(self) -> V
fn xxyx(self) -> V
fn xxyy(self) -> V
fn xxyz(self) -> V
fn xxyw(self) -> V
fn xxzx(self) -> V
fn xxzy(self) -> V
fn xxzz(self) -> V
fn xxzw(self) -> V
fn xxwx(self) -> V
fn xxwy(self) -> V
fn xxwz(self) -> V
fn xxww(self) -> V
fn xyxx(self) -> V
fn xyxy(self) -> V
fn xyxz(self) -> V
fn xyxw(self) -> V
fn xyyx(self) -> V
fn xyyy(self) -> V
fn xyyz(self) -> V
fn xyyw(self) -> V
fn xyzx(self) -> V
fn xyzy(self) -> V
fn xyzz(self) -> V
fn xywx(self) -> V
fn xywy(self) -> V
fn xywz(self) -> V
fn xyww(self) -> V
fn xzxx(self) -> V
fn xzxy(self) -> V
fn xzxz(self) -> V
fn xzxw(self) -> V
fn xzyx(self) -> V
fn xzyy(self) -> V
fn xzyz(self) -> V
fn xzyw(self) -> V
fn xzzx(self) -> V
fn xzzy(self) -> V
fn xzzz(self) -> V
fn xzzw(self) -> V
fn xzwx(self) -> V
fn xzwy(self) -> V
fn xzwz(self) -> V
fn xzww(self) -> V
fn xwxx(self) -> V
fn xwxy(self) -> V
fn xwxz(self) -> V
fn xwxw(self) -> V
fn xwyx(self) -> V
fn xwyy(self) -> V
fn xwyz(self) -> V
fn xwyw(self) -> V
fn xwzx(self) -> V
fn xwzy(self) -> V
fn xwzz(self) -> V
fn xwzw(self) -> V
fn xwwx(self) -> V
fn xwwy(self) -> V
fn xwwz(self) -> V
fn xwww(self) -> V
fn yxxx(self) -> V
fn yxxy(self) -> V
fn yxxz(self) -> V
fn yxxw(self) -> V
fn yxyx(self) -> V
fn yxyy(self) -> V
fn yxyz(self) -> V
fn yxyw(self) -> V
fn yxzx(self) -> V
fn yxzy(self) -> V
fn yxzz(self) -> V
fn yxzw(self) -> V
fn yxwx(self) -> V
fn yxwy(self) -> V
fn yxwz(self) -> V
fn yxww(self) -> V
fn yyxx(self) -> V
fn yyxy(self) -> V
fn yyxz(self) -> V
fn yyxw(self) -> V
fn yyyx(self) -> V
fn yyyy(self) -> V
fn yyyz(self) -> V
fn yyyw(self) -> V
fn yyzx(self) -> V
fn yyzy(self) -> V
fn yyzz(self) -> V
fn yyzw(self) -> V
fn yywx(self) -> V
fn yywy(self) -> V
fn yywz(self) -> V
fn yyww(self) -> V
fn yzxx(self) -> V
fn yzxy(self) -> V
fn yzxz(self) -> V
fn yzxw(self) -> V
fn yzyx(self) -> V
fn yzyy(self) -> V
fn yzyz(self) -> V
fn yzyw(self) -> V
fn yzzx(self) -> V
fn yzzy(self) -> V
fn yzzz(self) -> V
fn yzzw(self) -> V
fn yzwx(self) -> V
fn yzwy(self) -> V
fn yzwz(self) -> V
fn yzww(self) -> V
fn ywxx(self) -> V
fn ywxy(self) -> V
fn ywxz(self) -> V
fn ywxw(self) -> V
fn ywyx(self) -> V
fn ywyy(self) -> V
fn ywyz(self) -> V
fn ywyw(self) -> V
fn ywzx(self) -> V
fn ywzy(self) -> V
fn ywzz(self) -> V
fn ywzw(self) -> V
fn ywwx(self) -> V
fn ywwy(self) -> V
fn ywwz(self) -> V
fn ywww(self) -> V
fn zxxx(self) -> V
fn zxxy(self) -> V
fn zxxz(self) -> V
fn zxxw(self) -> V
fn zxyx(self) -> V
fn zxyy(self) -> V
fn zxyz(self) -> V
fn zxyw(self) -> V
fn zxzx(self) -> V
fn zxzy(self) -> V
fn zxzz(self) -> V
fn zxzw(self) -> V
fn zxwx(self) -> V
fn zxwy(self) -> V
fn zxwz(self) -> V
fn zxww(self) -> V
fn zyxx(self) -> V
fn zyxy(self) -> V
fn zyxz(self) -> V
fn zyxw(self) -> V
fn zyyx(self) -> V
fn zyyy(self) -> V
fn zyyz(self) -> V
fn zyyw(self) -> V
fn zyzx(self) -> V
fn zyzy(self) -> V
fn zyzz(self) -> V
fn zyzw(self) -> V
fn zywx(self) -> V
fn zywy(self) -> V
fn zywz(self) -> V
fn zyww(self) -> V
fn zzxx(self) -> V
fn zzxy(self) -> V
fn zzxz(self) -> V
fn zzxw(self) -> V
fn zzyx(self) -> V
fn zzyy(self) -> V
fn zzyz(self) -> V
fn zzyw(self) -> V
fn zzzx(self) -> V
fn zzzy(self) -> V
fn zzzz(self) -> V
fn zzzw(self) -> V
fn zzwx(self) -> V
fn zzwy(self) -> V
fn zzwz(self) -> V
fn zzww(self) -> V
fn zwxx(self) -> V
fn zwxy(self) -> V
fn zwxz(self) -> V
fn zwxw(self) -> V
fn zwyx(self) -> V
fn zwyy(self) -> V
fn zwyz(self) -> V
fn zwyw(self) -> V
fn zwzx(self) -> V
fn zwzy(self) -> V
fn zwzz(self) -> V
fn zwzw(self) -> V
fn zwwx(self) -> V
fn zwwy(self) -> V
fn zwwz(self) -> V
fn zwww(self) -> V
fn wxxx(self) -> V
fn wxxy(self) -> V
fn wxxz(self) -> V
fn wxxw(self) -> V
fn wxyx(self) -> V
fn wxyy(self) -> V
fn wxyz(self) -> V
fn wxyw(self) -> V
fn wxzx(self) -> V
fn wxzy(self) -> V
fn wxzz(self) -> V
fn wxzw(self) -> V
fn wxwx(self) -> V
fn wxwy(self) -> V
fn wxwz(self) -> V
fn wxww(self) -> V
fn wyxx(self) -> V
fn wyxy(self) -> V
fn wyxz(self) -> V
fn wyxw(self) -> V
fn wyyx(self) -> V
fn wyyy(self) -> V
fn wyyz(self) -> V
fn wyyw(self) -> V
fn wyzx(self) -> V
fn wyzy(self) -> V
fn wyzz(self) -> V
fn wyzw(self) -> V
fn wywx(self) -> V
fn wywy(self) -> V
fn wywz(self) -> V
fn wyww(self) -> V
fn wzxx(self) -> V
fn wzxy(self) -> V
fn wzxz(self) -> V
fn wzxw(self) -> V
fn wzyx(self) -> V
fn wzyy(self) -> V
fn wzyz(self) -> V
fn wzyw(self) -> V
fn wzzx(self) -> V
fn wzzy(self) -> V
fn wzzz(self) -> V
fn wzzw(self) -> V
fn wzwx(self) -> V
fn wzwy(self) -> V
fn wzwz(self) -> V
fn wzww(self) -> V
fn wwxx(self) -> V
fn wwxy(self) -> V
fn wwxz(self) -> V
fn wwxw(self) -> V
fn wwyx(self) -> V
fn wwyy(self) -> V
fn wwyz(self) -> V
fn wwyw(self) -> V
fn wwzx(self) -> V
fn wwzy(self) -> V
fn wwzz(self) -> V
fn wwzw(self) -> V
fn wwwx(self) -> V
fn wwwy(self) -> V
fn wwwz(self) -> V
fn wwww(self) -> V
impl<V> SwizzleVector for V
§impl<T> Tap for T
impl<T> Tap for T
§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read more§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read more§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read more§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read more§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read more§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read more§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.Source§impl<M> TranscendentalMath for Mwhere
M: TranscendentalMathWithPolicy,
impl<M> TranscendentalMath for Mwhere
M: TranscendentalMathWithPolicy,
Source§fn sin_cos(self) -> (Self, Self)
fn sin_cos(self) -> (Self, Self)
sin and cos separately.Source§fn versin(self) -> Self
fn versin(self) -> Self
$1 - \cos(x)$ of self, evaluated as $2\sin^2(x/2)$ (accurate near zero).Source§fn haversin(self) -> Self
fn haversin(self) -> Self
$\tfrac{1 - \cos(x)}{2}$ of self, evaluated as $\sin^2(x/2)$ (accurate near zero). Read moreSource§fn sincos_pi(self) -> (Self, Self)
fn sincos_pi(self) -> (Self, Self)
pi * x, together. This will be more efficient than calling sin_pi and cos_pi separately,
and more precise than computing them manually with sin(pi * x) and cos(pi * x).Source§fn sin_pi(self) -> Self
fn sin_pi(self) -> Self
pi * x, with improved precision when the policy allows.Source§fn cos_pi(self) -> Self
fn cos_pi(self) -> Self
pi * x, with improved precision when the policy allows.Source§fn tan_pi(self) -> Self
fn tan_pi(self) -> Self
pi * x, with improved precision when the policy allows.Source§fn sinc_pi(self) -> Self
fn sinc_pi(self) -> Self
$\frac{\sin(\pi x)}{\pi x}$ with improved precision when the policy allows.Source§fn sinh_cosh(self) -> (Self, Self)
fn sinh_cosh(self) -> (Self, Self)
sinh and cosh separately.Source§fn exp_m1(self) -> Self
fn exp_m1(self) -> Self
exp(self) - 1 of self, which is more precise than calculating exp(self) - 1 directly.Source§fn exp2_m1(self) -> Self
fn exp2_m1(self) -> Self
2^(self) - 1, which is more precise than calculating exp2(self) - 1 directly.Source§fn exp10_m1(self) -> Self
fn exp10_m1(self) -> Self
10^(self) - 1, which is more precise than calculating exp10(self) - 1 directly.Source§fn sqrt1pm1(self) -> Self
fn sqrt1pm1(self) -> Self
$\sqrt{1 + x} - 1$ of self, which is more precise than sqrt(1 + x) - 1 directly near zero. Read moreSource§fn compound_m1(self, n: Self) -> Self
fn compound_m1(self, n: Self) -> Self
Source§fn nth_root_n<const N: usize>(self) -> Self
fn nth_root_n<const N: usize>(self) -> Self
self. Read moreSource§fn atanhc(self) -> Self
fn atanhc(self) -> Self
$\operatorname{atanh}(x)/x$ of self, with its removable singularity
$\mathrm{atanhc}(0) = 1$ filled in. Read moreSource§fn log2_p1(self) -> Self
fn log2_p1(self) -> Self
$\log_2(1 + x)$ of self, which is more precise than log2(1 + x) directly near zero.Source§fn log10_p1(self) -> Self
fn log10_p1(self) -> Self
$\log_{10}(1 + x)$ of self, which is more precise than log10(1 + x) directly near zero.Source§fn log1pmx(self) -> Self
fn log1pmx(self) -> Self
$\ln(1 + x) - x$ of self, which is accurate near zero where the
subtraction otherwise cancels away every significant digit. Read moreSource§fn log_n(self, n: u32) -> Self
fn log_n(self, n: u32) -> Self
self with respect to an integer base known only at runtime. Read moreSource§fn ln1m_expnx(self) -> Self
fn ln1m_expnx(self) -> Self
$\ln(1 - e^{-x})$, which depending on the policy may be
an approximation more performant than the exact calculation. If you’re using a policy with below
average precision, and happen to have ln(x) available, you can use ln1m_expnx_ext instead
to provide that.Source§fn ln1m_expnx_ext(self, lnx: Self) -> Self
fn ln1m_expnx_ext(self, lnx: Self) -> Self
ln(1 - exp(lnx)), which depending on the policy may be
an approximation more performant than the exact calculation. If you’re using a policy with below
average precision, it’s recommended to use this function instead of ln1m_expnx to provide ln(x) directly. Read moreSource§impl<E, V> TranscendentalMathWithPolicy for Vwhere
E: FloatElement,
V: FloatVector<Element = E> + CoreMathWithPolicy + SpecializedTranscendentalMath<E>,
impl<E, V> TranscendentalMathWithPolicy for Vwhere
E: FloatElement,
V: FloatVector<Element = E> + CoreMathWithPolicy + SpecializedTranscendentalMath<E>,
Source§fn sin_cos_p<P>(self) -> (V, V)where
P: Policy,
fn sin_cos_p<P>(self) -> (V, V)where
P: Policy,
Trigonometric sine and cosine, together. This will be more efficient than calling sin and cos separately.
Source§fn cos_m1_p<P>(self) -> Vwhere
P: Policy,
fn cos_m1_p<P>(self) -> Vwhere
P: Policy,
Returns cos(x) - 1 of self, which is more precise than cos(x) - 1 directly near zero.
Evaluated as $-2\sin^2(x/2)$, which has no cancellation near x = 0.
Source§fn versin_p<P>(self) -> Vwhere
P: Policy,
fn versin_p<P>(self) -> Vwhere
P: Policy,
Returns the versine $1 - \cos(x)$ of self, evaluated as $2\sin^2(x/2)$ (accurate near zero).
Source§fn haversin_p<P>(self) -> Vwhere
P: Policy,
fn haversin_p<P>(self) -> Vwhere
P: Policy,
Returns the haversine $\tfrac{1 - \cos(x)}{2}$ of self, evaluated as $\sin^2(x/2)$ (accurate near zero).
This is the kernel of the haversine great-circle-distance formula.
Source§fn sincos_pi_p<P>(self) -> (V, V)where
P: Policy,
fn sincos_pi_p<P>(self) -> (V, V)where
P: Policy,
Sine and cosine of pi * x, together. This will be more efficient than calling sin_pi and cos_pi separately,
and more precise than computing them manually with sin(pi * x) and cos(pi * x).
Source§fn sin_pi_p<P>(self) -> Vwhere
P: Policy,
fn sin_pi_p<P>(self) -> Vwhere
P: Policy,
Trigonometric sine of pi * x, with improved precision when the policy allows.
Source§fn cos_pi_p<P>(self) -> Vwhere
P: Policy,
fn cos_pi_p<P>(self) -> Vwhere
P: Policy,
Trigonometric cosine of pi * x, with improved precision when the policy allows.
Source§fn tan_pi_p<P>(self) -> Vwhere
P: Policy,
fn tan_pi_p<P>(self) -> Vwhere
P: Policy,
Trigonometric tangent of pi * x, with improved precision when the policy allows.
Source§fn sinc_p<P>(self) -> Vwhere
P: Policy,
fn sinc_p<P>(self) -> Vwhere
P: Policy,
Computes sin(x) / x with improved precision when the policy allows.
Source§fn versinc_p<P>(self) -> Vwhere
P: Policy,
fn versinc_p<P>(self) -> Vwhere
P: Policy,
Returns $\frac{1 - \cos(x)}{x^2}$, finite at x = 0 where it takes the value 1/2.
The $x^2$ denominator is the one worth naming: $\frac{1-\cos x}{x}$ is simply zero at the
origin and carries no removable singularity, while this ratio tends to 1/2 and is what
actually appears in practice.
Written directly, $1 - \cos x$ has already lost half the mantissa by x of order 1e-4.
Evaluated here as $\tfrac{1}{2}\,\mathrm{sinc}^2(x/2)$, an exact identity that needs no series
and no cutoff, and inherits sinc’s behaviour at the origin.
This is the second Rodrigues coefficient of the SO(3) exponential map, alongside
sinc as the first. Rigid-body and Lie-group integrators, IMU
preintegration, and skinning all evaluate it once per timestep. The prevailing practice is a
hand-rolled Taylor cutoff with an arbitrary epsilon.
Source§fn sinc_pi_p<P>(self) -> Vwhere
P: Policy,
fn sinc_pi_p<P>(self) -> Vwhere
P: Policy,
Computes $\frac{\sin(\pi x)}{\pi x}$ with improved precision when the policy allows.
Source§fn sinh_cosh_p<P>(self) -> (V, V)where
P: Policy,
fn sinh_cosh_p<P>(self) -> (V, V)where
P: Policy,
Hyperbolic sine and cosine, together. This will be more efficient than calling sinh and cosh separately.
Source§fn exph_p<P>(self) -> Vwhere
P: Policy,
fn exph_p<P>(self) -> Vwhere
P: Policy,
The Half exponential function, returns 0.5 * e^(self).
Source§fn exp_m1_p<P>(self) -> Vwhere
P: Policy,
fn exp_m1_p<P>(self) -> Vwhere
P: Policy,
Returns exp(self) - 1 of self, which is more precise than calculating exp(self) - 1 directly.
Source§fn exp2_m1_p<P>(self) -> Vwhere
P: Policy,
fn exp2_m1_p<P>(self) -> Vwhere
P: Policy,
Returns 2^(self) - 1, which is more precise than calculating exp2(self) - 1 directly.
Source§fn exp10_m1_p<P>(self) -> Vwhere
P: Policy,
fn exp10_m1_p<P>(self) -> Vwhere
P: Policy,
Returns 10^(self) - 1, which is more precise than calculating exp10(self) - 1 directly.
Source§fn sqrt1pm1_p<P>(self) -> Vwhere
P: Policy,
fn sqrt1pm1_p<P>(self) -> Vwhere
P: Policy,
Returns $\sqrt{1 + x} - 1$ of self, which is more precise than sqrt(1 + x) - 1 directly near zero.
Evaluated as $\frac{x}{\sqrt{1 + x} + 1}$, which has no cancellation near x = 0.
Source§fn sqrt1mexp_p<P>(self) -> Vwhere
P: Policy,
fn sqrt1mexp_p<P>(self) -> Vwhere
P: Policy,
Returns $\sqrt{1 - e^{-x}}$ for x >= 0, without the cancellation of the direct form.
$1 - e^{-x}$ annihilates for small x, so this is evaluated as
$\sqrt{-\mathrm{expm1}(-x)}$, which is accurate all the way down. Negative x is
outside the domain and gives NaN.
This is the noise scaling of an exactly-integrated Ornstein-Uhlenbeck step: Langevin and Bussi-Parrinello thermostats, and the variance-preserving schedules used by diffusion models.
Source§fn powf_m1_p<P>(self, e: V) -> Vwhere
P: Policy,
fn powf_m1_p<P>(self, e: V) -> Vwhere
P: Policy,
Returns $x^e - 1$ where x = self, computed accurately as $e^{e \ln(x)}$-style expm1.
More precise than powf(x, e) - 1 when the result is near zero (i.e. x near 1 or e near 0),
e.g. compound returns/growth rates.
Source§fn compound_p<P>(self, n: V) -> Vwhere
P: Policy,
fn compound_p<P>(self, n: V) -> Vwhere
P: Policy,
Returns $(1 + x)^n$ where x = self, computed accurately near x = 0 as $e^{n \ln(1 + x)}$.
This is the IEEE 754 compound operation, and is more precise than powf(1 + x, n) for small x
(e.g. compound-growth/interest over n periods at rate x).
Source§fn compound_m1_p<P>(self, n: V) -> Vwhere
P: Policy,
fn compound_m1_p<P>(self, n: V) -> Vwhere
P: Policy,
Returns $(1 + x)^n - 1$ where x = self, the $-1$ sibling of
compound.
Accurate at both ends where the two obvious spellings are not: compound(x, n) - 1
cancels when the result is near zero (small x or small n), and
powf_m1(1 + x, n) has already lost x entirely by $|x| < \varepsilon$ because
forming $1 + x$ rounds it away. Evaluated as $\mathrm{expm1}(n \ln(1 + x))$,
which does neither.
This is the numerator of the shifted Box-Cox transform, and the kernel underneath
thermite-special’s boxcox_1p and Yeo-Johnson transform, whose whole reason for
existing is data that straddles zero.
Source§fn nth_root_n_p<P, const N: usize>(self) -> Vwhere
P: Policy,
fn nth_root_n_p<P, const N: usize>(self) -> Vwhere
P: Policy,
Returns the Nth root of self.
This is often faster and more accurate than using powf(1.0 / N as float). Supports
negative numbers for odd N.
Source§fn nth_root_p<P>(self, n: u32) -> Vwhere
P: Policy,
fn nth_root_p<P>(self, n: u32) -> Vwhere
P: Policy,
Returns the n-th root of self for a degree known only at runtime.
The same arithmetic as nth_root_n, so the two agree
to the bit at every n. The difference is that the special cases (n of 1 to 4) are one
uniform branch on the value rather than a compile-time fold. Prefer the const form when
the degree is a literal.
Source§fn ln_p<P>(self) -> Vwhere
P: Policy,
fn ln_p<P>(self) -> Vwhere
P: Policy,
Returns the natural logarithm of self.
§Examples
Every math function takes a precision policy via its _p variant. A quick
sweep against a scalar reference is the cheapest way to validate that a
policy choice is accurate enough for your domain:
use thermite::prelude::*;
use thermite::math::policy::policies::Precision;
type V = Vector<f64>;
let mut max_err = 0.0f64;
for i in 1..=1000 {
let x = i as f64 * 0.05;
let y = V::splat(x).ln_p::<Precision>().extract::<0>();
max_err = max_err.max((y - x.ln()).abs() / x.ln().abs().max(1.0));
}
assert!(max_err < 1e-14, "max relative error {max_err}");Source§fn xlogy_p<P>(self, y: V) -> Vwhere
P: Policy,
fn xlogy_p<P>(self, y: V) -> Vwhere
P: Policy,
Returns $x \ln y$ with x = self, defined as 0 wherever x is zero.
$0 \cdot \ln 0$ is $0 \cdot -\infty =$ NaN written directly, and one NaN poisons
every reduction downstream of it: a cross-entropy over a batch with a single
zero-probability term returns NaN for the whole batch. The convention $x = 0 \Rightarrow 0$ is the limit $\lim_{x \to 0^+} x \ln y$ and is what information
theory assumes everywhere.
A NaN y still propagates, taking priority over the zero guard, matching SciPy
and PyTorch. A negative y does not: it is not NaN, so x = 0 gives 0 there and
only a non-zero x yields NaN from the log.
This is the case where a vector implementation is strictly ahead of a scalar one: the guard is a masked select costing one instruction, where scalar code needs a branch per element.
Source§fn sinhc_p<P>(self) -> Vwhere
P: Policy,
fn sinhc_p<P>(self) -> Vwhere
P: Policy,
Returns $\sinh(x)/x$ of self, the hyperbolic counterpart of sinc,
with its removable singularity $\mathrm{sinhc}(0) = 1$ filled in.
Written directly, $\sinh(x)/x$ is $0/0$ at the origin. Small arguments take the
even series $1 + x^2/6 + x^4/120$ instead, which also skips the sinh.
Unlike sinc, this one grows: $\mathrm{sinhc}(\pm\infty) = +\infty$, and the naive
spelling gets $\infty/\infty = $ NaN there rather than the limit.
Turns up in the hyperbolic exponential map, catenary curves, beam and rod stiffness
matrices in FEM, exact solutions of linear ODE blocks, and the Einstein heat-capacity
function $x^2 e^x/(e^x-1)^2$, which is exactly $1/\mathrm{sinhc}(x/2)^2$.
Source§fn atanhc_p<P>(self) -> Vwhere
P: Policy,
fn atanhc_p<P>(self) -> Vwhere
P: Policy,
Returns $\operatorname{atanh}(x)/x$ of self, with its removable singularity
$\mathrm{atanhc}(0) = 1$ filled in.
The cardinal form of atanh, in the same relation to it as
sinc is to sin. Small arguments take the even series
$1 + x^2/3 + x^4/5$, which also skips the atanh.
Defined on $[-1, 1]$, even, with $\mathrm{atanhc}(\pm 1) = +\infty$ and NaN outside.
This is the shape the logarithmic mean actually reduces to, and
logmean is written on it: with
$f = \frac{a-b}{a+b}$,
\mathrm{logmean}(a, b) = \frac{a+b}{2\,\mathrm{atanhc}(f)}so the $a \to b$ limit is carried by this function rather than special-cased there.
It is also the inner object of the Ismail-Roe entropy-stable flux, where production
codes write it as an if on $f^2 < 10^{-4}$ that mispredicts across shocks. It also
turns up in relativistic velocity addition, optical-depth ratios in radiative
transfer, and the Legendre function of the second kind $Q_0$.
Source§fn cosh_m1_p<P>(self) -> Vwhere
P: Policy,
fn cosh_m1_p<P>(self) -> Vwhere
P: Policy,
Returns $\cosh(x) - 1$ of self, the hyperbolic counterpart of
cos_m1.
Evaluated as $2\sinh^2(x/2)$, an exact identity rather than an approximation, so
there is no series and no cutoff, the same treatment shipped versin gets from
$1 - \cos x = 2\sin^2(x/2)$. The direct spelling instead cancels: $\cosh x - 1$ is
$O(x^2)$ against a $\cosh$ of $1$, so it has lost half the mantissa by
$x \approx 10^{-8}$ and all of it by $x \approx 10^{-16}$.
Source§fn log2_p1_p<P>(self) -> Vwhere
P: Policy,
fn log2_p1_p<P>(self) -> Vwhere
P: Policy,
Returns $\log_2(1 + x)$ of self, which is more precise than log2(1 + x) directly near zero.
Source§fn log10_p1_p<P>(self) -> Vwhere
P: Policy,
fn log10_p1_p<P>(self) -> Vwhere
P: Policy,
Returns $\log_{10}(1 + x)$ of self, which is more precise than log10(1 + x) directly near zero.
Source§fn log1pmx_p<P>(self) -> Vwhere
P: Policy,
fn log1pmx_p<P>(self) -> Vwhere
P: Policy,
Returns $\ln(1 + x) - x$ of self, which is accurate near zero where the
subtraction otherwise cancels away every significant digit.
$\ln(1+x) \approx x - x^2/2$ for small x, so the difference is $O(x^2)$ while
both terms are $O(x)$: computing it as written costs about $2\varepsilon/|x|$ of
relative error, which is total loss by $|x| \approx \varepsilon$. On
$-1/2 \le x \le 1$ this instead sums the odd series in $r = x/(2+x)$,
\ln(1+x) - x = r\left(2r^2 \sum_{k \ge 0} \frac{r^{2k}}{2k+3} - x\right)which has no cancellation and is exact at $x = 0$. Outside that window the
direct form is already accurate to a few ulp and is what runs.
The Medium and Worst policies return the direct form everywhere, dropping the
series and its window test. That is a change of behavior near zero, not a few ulp:
once $1 + x$ rounds to 1 the direct form yields $-x$, which differs from
$-x^2/2$ by every digit and by an unbounded factor. Those tiers are the right
choice for arguments that stay clear of zero (where the direct form is a few ulp
anyway, so the series is pure cost) and the wrong one for the near-zero case this
function exists to serve. Ask for Average or better there.
The natural home of $\ln(1+x)-x$ is a density or deviance: the Poisson/binomial
deviance is $-k \cdot \mathrm{log1pmx}((\lambda-k)/k)$, and the same shape turns up
in entropies, Kullback-Leibler divergences and saddle-point approximations.
Source§fn log_p<P>(self, base: V) -> Vwhere
P: Policy,
fn log_p<P>(self, base: V) -> Vwhere
P: Policy,
Returns the logarithm of self with respect to the given base.
This is simply a convenience method for self.log2() / base.log2().
Source§fn log_n_n_p<P, const N: usize>(self) -> Vwhere
P: Policy,
fn log_n_n_p<P, const N: usize>(self) -> Vwhere
P: Policy,
Returns the logarithm of self with respect to the given integer base N.
This is efficient for bases <=32 using a lookup table, and falls back to the general log(x)/libm::log(N)
implementation for larger bases.
For bases 0 and 1, the result is 0 and Infinity respectively.
Source§fn log_n_p<P>(self, n: u32) -> Vwhere
P: Policy,
fn log_n_p<P>(self, n: u32) -> Vwhere
P: Policy,
Returns the logarithm of self with respect to an integer base known only at runtime.
The runtime twin of log_n_n: the same table lookup for
bases up to 32 and the same fallback above, so the two agree to the bit. Bases 0 and 1
give 0 and infinity as there.
Source§fn ln1m_expnx_p<P>(self) -> Vwhere
P: Policy,
fn ln1m_expnx_p<P>(self) -> Vwhere
P: Policy,
Returns $\ln(1 - e^{-x})$, which depending on the policy may be
an approximation more performant than the exact calculation. If you’re using a policy with below
average precision, and happen to have ln(x) available, you can use ln1m_expnx_ext instead
to provide that.
Source§fn ln1m_expnx_ext_p<P>(self, lnx: V) -> Vwhere
P: Policy,
fn ln1m_expnx_ext_p<P>(self, lnx: V) -> Vwhere
P: Policy,
Returns ln(1 - exp(lnx)), which depending on the policy may be
an approximation more performant than the exact calculation. If you’re using a policy with below
average precision, it’s recommended to use this function instead of ln1m_expnx to provide ln(x) directly.
Although not obvious, ln(x) is used internally for the approximation, and if it’s already available,
you may as well use this function to avoid recomputing it.