#![allow(clippy::excessive_precision)]
use thermite::{
mask::GenericMask,
math::{
CoreMathWithPolicy as _, FloatConsts, TranscendentalMathWithPolicy as _,
policy::{
Policy, PrecisionPolicy,
policies::{CheckOverflow, ExtraPrecision, LessPrecision},
},
specialized::FlushDenormals,
},
register::{Element, FloatElement},
vector::{NumericVector, PartialOrdVector, SplatConst},
};
use super::SpecialMathWithPolicy as _;
pub(crate) mod generic;
mod pd;
mod ps;
pub trait ExpIntDetails<E, V: thermite::vector::FloatVector<Element = E>> {
#[inline(always)]
fn use_series(z: V) -> V::Mask {
z.cmp_lt(V::ONE)
}
#[inline(always)]
fn invalid(z: V) -> V::Mask {
z.cmp_lt(V::ZERO) | z.is_nan()
}
#[inline(always)]
fn cf_tiny() -> V {
V::MIN_POSITIVE
}
}
pub trait SpecializedSpecialMath<E>: thermite::math::specialized::SpecializedTranscendentalMath<E> {
type ExpIntDetails: ExpIntDetails<E, Self>;
fn erf<P: Policy>(self) -> Self;
#[inline(always)]
fn erfc<P: Policy>(self) -> Self {
Self::ONE - self.erf_p::<P>()
}
#[inline(always)]
fn expint<P: Policy, const N: usize>(self) -> Self {
self.expint_primal::<P, N>().0
}
#[inline(always)]
fn expint_primal<P: Policy, const N: usize>(self) -> (Self, Self) {
let x = self;
let exp_neg_x = (-x).exp_p::<P>();
let inv_x = x.reciprocal_p::<P>();
let e0 = exp_neg_x * inv_x;
if const { N == 0 } {
let mut value = e0;
let mut prev = e0 * (Self::ONE + inv_x);
if const { P::POLICY.check_overflow } {
let x_is_zero = x.is_zero();
value = x_is_zero.select(Self::INFINITY, value);
prev = x_is_zero.select(Self::INFINITY, prev);
let bad = <Self::ExpIntDetails as ExpIntDetails<E, Self>>::invalid(x);
value = bad.select(Self::NAN, value);
prev = bad.select(Self::NAN, prev);
}
return (value, prev);
}
let use_series = <Self::ExpIntDetails as ExpIntDetails<E, Self>>::use_series(x);
let neg_x = -x;
let mut s_term = neg_x; let mut s_sum = s_term;
let tiny = <Self::ExpIntDetails as ExpIntDetails<E, Self>>::cf_tiny();
let mut cf_f = tiny;
let mut cf_c = tiny;
let mut cf_d = {
let b1 = x + Self::ONE;
let d1 = b1.reciprocal_p::<P>();
cf_c = b1 + cf_c.reciprocal_p::<P>();
let delta = cf_c * d1;
cf_f *= delta; d1
};
let eps = Self::splat(E::EPSILON);
let mut series_done = !use_series; let mut cf_done = use_series;
let mut k = 1usize;
while k < const { P::POLICY.max_iterations } {
let kf = Self::splat(E::from_int(k as thermite::LargeInt));
let kp1 = Self::splat(E::from_int(k as thermite::LargeInt + 1));
if !series_done.all() {
s_term *= (neg_x * kf) / (kp1 * kp1);
s_sum = series_done.select(s_sum, s_sum + s_term);
let term_small = s_term.abs().cmp_lt(s_sum.abs() * eps);
series_done = GenericMask::ternlog::<{ thermite::ternlog_imm!(A | (B & C)) }>(
series_done,
use_series,
term_small,
);
}
if !cf_done.all() {
let neg_a_k = kf * kf; let b_k = (x + kf) + (kf + Self::ONE);
let d_denom = neg_a_k.nmul_adde(cf_d, b_k); let new_d = d_denom.cmp_eq(Self::ZERO).select(tiny, d_denom).reciprocal_p::<P>();
let new_c = b_k - neg_a_k / cf_c;
let new_c = new_c.cmp_eq(Self::ZERO).select(tiny, new_c);
let delta = new_c * new_d;
cf_d = new_d;
cf_c = new_c;
cf_f = cf_done.select(cf_f, cf_f * delta);
let cf_converged = (delta - Self::ONE).abs().cmp_lt(eps);
cf_done =
GenericMask::ternlog::<{ thermite::ternlog_imm!(A | (!B & C)) }>(cf_done, use_series, cf_converged);
}
if (series_done & cf_done).all() {
break;
}
k += 1;
}
let mut series_result = Self::EMPTY;
let mut cf_result = Self::EMPTY;
if use_series.any() {
series_result = (-Self::EULER_GAMMA - s_sum) - x.ln_p::<P>();
}
if !use_series.all() {
cf_result = cf_f * exp_neg_x;
}
let mut e_n = use_series.select(series_result, cf_result);
let mut e_prev = e0;
if const { N > 1 } {
let mut n = 1u32;
while n < N as u32 {
let nf = Self::splat(E::from_int(n as thermite::LargeInt));
e_prev = e_n;
e_n = x.nmul_adde(e_n, exp_neg_x) / nf;
n += 1;
}
}
if const { P::POLICY.check_overflow } {
let x_is_zero = x.is_zero();
if const { N == 1 } {
e_n = x_is_zero.select(Self::INFINITY, e_n);
} else if const { N > 1 } {
e_n = x_is_zero.select(Self::splat(E::ONE / E::from_int(N as thermite::LargeInt - 1)), e_n);
}
if const { N <= 2 } {
e_prev = x_is_zero.select(Self::INFINITY, e_prev);
} else {
e_prev = x_is_zero.select(Self::splat(E::ONE / E::from_int(N as thermite::LargeInt - 2)), e_prev);
}
let bad = <Self::ExpIntDetails as ExpIntDetails<E, Self>>::invalid(x);
e_n = bad.select(Self::NAN, e_n);
e_prev = bad.select(Self::NAN, e_prev);
}
(e_n, e_prev)
}
#[inline(always)]
fn logistic_sigmoid<P: Policy>(self) -> Self {
if const { P::POLICY.precision.gt(PrecisionPolicy::Average) } {
let is_pos = self.is_positive();
let x = self.neg_c(is_pos); let e = x.exp_p::<P>();
let n = is_pos.select(Self::ONE, e);
let d = Self::ONE + e;
return n / d;
}
(Self::ONE + (-self).exp_p::<P>()).reciprocal_p::<ExtraPrecision<P>>()
}
#[inline(always)]
fn softplus<P: Policy>(self, k: Self, rcp_k: Self) -> Self {
if const { P::POLICY.precision.lt(PrecisionPolicy::Average) } {
let k = k.scale(FloatConsts::LOG2_E);
let rcp_k = rcp_k.scale(FloatConsts::LN_2);
let kx = self * k;
let e = kx.abs().neg().exp2_p::<CheckOverflow<P, true>>();
return (Self::ONE + e).log2_p::<P>().mul_adde(rcp_k, self.max(Self::ZERO));
}
let kx = self * k;
let e = kx.abs().neg().exp_p::<P>();
e.ln_1p_p::<P>().mul_adde(rcp_k, self.max(Self::ZERO))
}
fn tgamma<P: Policy>(self) -> Self;
fn lgamma<P: Policy>(self) -> Self;
fn digamma<P: Policy>(self) -> Self;
fn trigamma<P: Policy>(self) -> Self;
#[inline(always)]
fn hermite<P: Policy, const N: usize>(mut x: Self) -> Self {
#[cfg(not(target_arch = "spirv"))]
if let Some(new_x) = FlushDenormals::<P>::flush_denormals([x]) {
x = new_x[0];
}
let mut p0 = Self::ONE;
if const { N == 0 } {
return p0;
}
let mut p1 = x + x;
cfg_if::cfg_if! {
if #[cfg(all(feature = "spirv", target_arch = "spirv"))] {
use crunchy::unroll;
macro_rules! unroll_poly {
($($len:tt),*) => {
$( if const { N == $len } {
unroll! { for n in 0..$len {
(p0, p1) = (p1, p0);
const cf: thermite::LargeInt = (1 + n) as thermite::LargeInt;
let next0 = x.mul_sube(p0, p1.scale(E::ConstInt::<{cf}>::VALUE));
p1 = next0 + next0; }}
} else )* {
let mut c = 1;
let mut cf = E::ONE;
while c < N {
(p0, p1) = (p1, p0);
let next0 = x.mul_sube(p0, p1.scale(cf));
p1 = next0 + next0;
c += 1;
cf = cf + E::ONE;
}
}
};
}
unroll_poly!(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); } else {
let mut c = 1;
let mut cf = Self::ONE;
while c < N {
(p0, p1) = (p1, p0);
let next0 = x.mul_sube(p0, cf * p1);
p1 = next0 + next0;
c += 1;
cf += Self::ONE;
}
}
}
p1
}
#[inline(always)]
fn hermitev<P: Policy>(mut x: Self, n: Self::Unsigned) -> Self {
#[cfg(not(target_arch = "spirv"))]
if let Some(new_x) = FlushDenormals::<P>::flush_denormals([x]) {
x = new_x[0];
}
let i1 = Self::Unsigned::ONE;
let n_is_zero = n.cmp_eq(Self::Unsigned::ZERO);
let mut c = i1;
let mut cf = Self::ONE;
let mut p0 = Self::ONE;
let mut p1 = x + x;
loop {
let cont = c.cmp_lt(n);
if cont.none() {
break;
}
(p0, p1) = (p1, p0);
let next0 = x.mul_sube(p0, cf * p1);
let next = next0 + next0;
p1 = cont.select(next, p1);
c += i1;
cf += Self::ONE;
}
n_is_zero.select(Self::ONE, p1)
}
#[inline(always)]
fn chebyshev<P: Policy, const K: usize, const N: usize>(self, coeffs: &[Self::Element; N]) -> Self {
const {
assert!(K >= 1 && K <= 4, "chebyshev: K must be 1, 2, 3, or 4");
assert!(N >= 1, "chebyshev: N must be at least 1");
}
if const { N == 1 } {
return Self::splat(coeffs[0]);
}
let x = self;
let x2 = x + x;
let p1 = if const { K == 1 } {
x
} else if const { K == 2 } {
x2
} else if const { K == 3 } {
x2 - Self::ONE
} else if const { K == 4 } {
x2 + Self::ONE
} else {
unsafe { core::hint::unreachable_unchecked() }
};
let cn1 = Self::splat(coeffs[N - 1]);
let cn2 = Self::splat(coeffs[N - 2]);
if const { N == 2 } {
return p1.mul_adde(cn1, cn2);
}
let mut b1 = x2.mul_adde(cn1, cn2); let mut b2 = cn1;
let mut k = N - 2;
while k > 1 {
k -= 1;
let bk = x2.mul_adde(b1, Self::splat(coeffs[k]) - b2);
b2 = b1;
b1 = bk;
}
b1.mul_adde(p1, Self::splat(coeffs[0]) - b2)
}
#[inline(always)]
fn jacobi<P: Policy>(mut x: Self, mut alpha: Self, mut beta: Self, mut n: u32, m: u32) -> Self {
if thermite::unlikely(m > n) {
return Self::ZERO;
}
#[cfg(not(target_arch = "spirv"))]
if let Some(new) = FlushDenormals::<P>::flush_denormals([x, alpha, beta]) {
x = new[0];
alpha = new[1];
beta = new[2];
}
let mut scale = Self::ONE;
if m > 0 {
let mut jf = Self::ONE;
let nf = Self::splat(E::from_int(n as thermite::LargeInt));
let t0 = Self::HALF * (nf + alpha + beta);
let mut _iter = 0;
while _iter < m {
_iter += 1;
scale *= Self::HALF.mul_adde(jf, t0);
jf += Self::ONE;
}
let mf = Self::splat(E::from_int(m as thermite::LargeInt));
alpha += mf;
beta += mf;
n -= m;
}
if thermite::unlikely(n == 0) {
return scale; }
let mut y0 = Self::ONE;
let alpha_p_beta = alpha + beta;
let alpha_sqr = alpha * alpha;
let beta_sqr = beta * beta;
let alpha1 = alpha - Self::ONE;
let beta1 = beta - Self::ONE;
let alpha2beta2 = alpha_sqr - beta_sqr;
let mut y1 = Self::HALF * (x.mul_adde(alpha, alpha) + x.mul_sube(beta, beta) + x + x);
let mut yk = y1;
let mut k = E::ConstInt::<2>::VALUE;
let k_max = E::from_int(n as thermite::LargeInt) * (<E as Element>::ONE + E::EPSILON);
while k < k_max {
let kf = Self::splat(k);
let kf2 = Self::TWO * kf;
let k_alpha_p_beta = kf + alpha_p_beta;
let k2_alpha_p_beta = kf2 + alpha_p_beta;
let k2_alpha_p_beta_m2 = k2_alpha_p_beta - Self::TWO;
let denom = kf2 * k_alpha_p_beta * k2_alpha_p_beta_m2;
let t0 = x.mul_adde(k2_alpha_p_beta * k2_alpha_p_beta_m2, alpha2beta2);
let gamma1 = k2_alpha_p_beta.mul_sube(t0, t0);
let gamma0 = Self::TWO * (kf + alpha1) * (kf + beta1) * k2_alpha_p_beta;
yk = gamma1.mul_sube(y1, gamma0 * y0) / denom;
y0 = y1;
y1 = yk;
k = k + <E as Element>::ONE;
}
scale * yk
}
#[inline(always)]
fn gaussian<P: Policy>(mut x: Self, a: Self, c: Self) -> Self {
#[cfg(not(target_arch = "spirv"))]
if let Some(new_x) = FlushDenormals::<P>::flush_denormals([x]) {
x = new_x[0];
}
let xc = if const { P::POLICY.precision.le(PrecisionPolicy::Worst) } {
x * c.reciprocal_p::<P>()
} else {
x / c
};
a * (-Self::HALF * xc * xc).exp_p::<P>()
}
fn beta<P: Policy>(a: Self, b: Self) -> Self;
#[rustfmt::skip]
#[inline(always)]
fn legendre0<P: Policy, const N: u32>(x: Self, n: u32) -> Self {
macro_rules! c { ($n:literal / $d:literal) => { Self::splat(E::from_int($n) / E::from_int($d)) }; }
let x2 = x.square();
let x4 = x2.square();
let x8 = x4.square();
if const { N != 0 } {
unsafe { core::hint::assert_unchecked(N == n); }
}
match n {
1 => x,
2 => x2.mul_adde(c!(3 / 2), c!(-1 / 2)),
3 => x * x2.mul_adde(c!(5 / 2), c!(-3 / 2)),
4 => x4.mul_adde(c!(35 / 8), x2.mul_adde(c!(-15 / 4), c!(3 / 8))),
5 => x * x4.mul_adde(c!(63 / 8), x2.mul_adde(c!(-35 / 4), c!(15 / 8))),
6 => x4.mul_adde(
x2.mul_adde(c!(231 / 16), c!(-315 / 16)),
x2.mul_adde(c!(105 / 16), c!(-5 / 16)),
),
7 => x * x4.mul_adde(
x2.mul_adde(c!(429 / 16), c!(-693 / 16)),
x2.mul_adde(c!(315 / 16), c!(-35 / 16)),
),
8 => x8.mul_adde(c!(6435 / 128), x4.mul_adde(
x2.mul_adde(c!(-3003 / 32), c!(3465 / 64)),
x2.mul_adde(c!(-315 / 32), c!(35 / 128)),
)),
9 => x * x8.mul_adde(c!(12155 / 128), x4.mul_adde(
x2.mul_adde(c!(-6435 / 32), c!(9009 / 64)),
x2.mul_adde(c!(-1155 / 32), c!(315 / 128)),
)),
10 => x8.mul_adde(
x2.mul_adde(c!(46189 / 256), c!(-109395 / 256)),
x4.mul_adde(
x2.mul_adde(c!(45045 / 128), c!(-15015 / 128)),
x2.mul_adde(c!(3465 / 256), c!(-63 / 256)),
),
),
11 => x * x8.mul_adde(
x2.mul_adde(c!(88179 / 256), c!(-230945 / 256)),
x4.mul_adde(
x2.mul_adde(c!(109395 / 128), c!(-45045 / 128)),
x2.mul_adde(c!(15015 / 256), c!(-693 / 256)),
),
),
12 => x8.mul_adde(
x4.mul_adde(c!(676039 / 1024), x2.mul_adde(c!(-969969 / 512), c!(2078505 / 1024))),
x4.mul_adde(
x2.mul_adde(c!(-255255 / 256), c!(225225 / 1024)),
x2.mul_adde(c!(-9009 / 512), c!(231 / 1024)),
),
),
13 => x * x8.mul_adde(
x4.mul_adde(c!(1300075 / 1024), x2.mul_adde(c!(-2028117 / 512), c!(4849845 / 1024))),
x4.mul_adde(
x2.mul_adde(c!(-692835 / 256), c!(765765 / 1024)),
x2.mul_adde(c!(-45045 / 512), c!(3003 / 1024)),
),
),
_ => unsafe { core::hint::unreachable_unchecked() },
}
}
#[inline(always)]
fn legendre<P: Policy>(mut x: Self, n: u32, m: u32) -> Self {
#[cfg(not(target_arch = "spirv"))]
if let Some(new_x) = FlushDenormals::<P>::flush_denormals([x]) {
x = new_x[0];
}
match (n, m) {
(0, 0) => return Self::ONE,
(n, 0) if n < 14 => return Self::legendre0::<P, 0>(x, n),
(n, 0) => {
let mut k = 14;
let mut p0 = Self::legendre0::<P, 12>(x, 12); let mut p1 = Self::legendre0::<P, 13>(x, 13);
while k <= n {
let nf = Self::splat(E::from_int(k as thermite::LargeInt));
let tmp = p1;
p1 = x.mul_sube((nf + nf).mul_sube(p1, p1), nf.mul_sube(p0, p0)) / nf;
p0 = tmp;
k += 1;
}
return p1;
}
_ => {}
}
let jacobi = Self::jacobi::<P>(x, Self::ZERO, Self::ZERO, n, m);
let x12 = x.nmul_adde(x, Self::ONE);
if m & 1 == 0 {
jacobi * Self::powi::<P>(x12, (m >> 1) as i32)
} else {
-jacobi * Self::powi::<P>(x12, m as i32).sqrt()
}
}
fn lambert_w<P: Policy>(self) -> (Self, Self);
}
pub use generic::elliptic::{
CarlsonKind, CarlsonRc, CarlsonRd, CarlsonRf, CarlsonRg, CarlsonRj, EllintD, EllintDInc, EllintE, EllintEInc,
EllintF, EllintK, EllintPi, EllintPiInc, EllipticConsts, EllipticKind, WrapTo,
};
pub trait SpecializedRealSpecialMath<E>: SpecializedSpecialMath<E> {
fn erfinv<P: Policy>(self) -> Self;
fn probit<P: Policy>(self) -> Self;
#[inline(always)]
fn gelu<P: Policy>(self, alpha: Self) -> Self {
let alpha_x = alpha * self;
let erf = alpha_x.scale(FloatConsts::FRAC_1_SQRT_2).erf_p::<P>();
if Self::HAS_TRUE_FMA {
let half_x = self.scale(E::ConstRatio::<1, 2>::VALUE);
half_x.mul_add(erf, half_x) } else {
self.scale(E::ConstRatio::<1, 2>::VALUE) * (Self::ONE + erf)
}
}
#[inline(always)]
fn swish<P: Policy>(self, beta: Self) -> Self {
let x = self;
let beta_x = beta * x;
let e = (-beta_x).exp_p::<P>();
let s = (Self::ONE + e).reciprocal_p::<P>();
x * s
}
fn lgamma_r<P: Policy>(self) -> (Self, Self);
#[inline(always)]
fn algebraic_sigmoid<P: Policy, const N: usize>(self) -> Self {
if const { N == 0 } {
return self; }
let pre_root = Self::ONE + self.abs().powi_p::<P>(N as i32);
let denom = match N {
1 => pre_root,
2 => pre_root.sqrt(),
3 => pre_root.cbrt_p::<P>(),
4 if const { P::POLICY.precision.le(PrecisionPolicy::Average) } => pre_root.sqrt().sqrt(),
_ => {
let x = pre_root;
let mut y = x.powf_p::<CheckOverflow<LessPrecision<P>, false>>(Self::splat(
E::ONE / E::from_int(N as thermite::LargeInt),
));
let y_n = y.powi_p::<P>(N as i32);
let np1 = Self::splat(E::from_int((N + 1) as thermite::LargeInt));
let nm1 = Self::splat(E::from_int((N - 1) as thermite::LargeInt));
let n = y * (x - y_n); let d = y_n.mul_adde(np1, x * nm1);
y += (n + n) / d;
y
}
};
let mut y = if const { P::POLICY.precision.lt(PrecisionPolicy::Average) } {
self * denom.reciprocal_p::<P>()
} else {
self / denom
};
if const { P::POLICY.check_overflow } {
y = pre_root.is_infinite().select(self.signum(), y);
}
y
}
#[inline(always)]
fn algebraic_swish<P: Policy>(self) -> Self {
let x = self;
if const { Self::HAS_TRUE_FMA } {
if const { Self::HAS_APPROX_RSQRT } {
let a = x.mul_add(x, Self::ONE);
let y0 = a.rsqrt();
let ay2 = a * y0 * y0;
let ch = ay2.nmul_add(Self::HALF, Self::splat(<E as FloatElement>::ConstRatio::<3, 2>::VALUE));
let r_inv = y0 * ch; let q = x * r_inv;
let xh = Self::HALF * x;
q.mul_add(xh, xh)
} else {
let a = x.mul_add(x, Self::ONE);
let q = x / a.sqrt();
let xh = x * Self::HALF;
q.mul_add(xh, xh)
}
} else if const { Self::HAS_APPROX_RCP } {
let a = x * x + Self::ONE;
let y0 = a.rsqrt();
let ay2 = a * y0 * y0;
let c = Self::splat(<E as FloatElement>::ConstInt::<3>::VALUE) - ay2;
let r_inv_2 = y0 * c; let hxy1 = Self::splat(<E as FloatElement>::ConstRatio::<1, 4>::VALUE) * (x * r_inv_2); let w = Self::HALF + hxy1; x * w
} else {
let a = x * x + Self::ONE;
let q = x / a.sqrt();
let q1 = q + Self::ONE;
x * Self::HALF * q1
}
}
#[inline(always)]
fn gaussian_integral<P: Policy>(x0: Self, x1: Self, a: Self, c: Self) -> Self {
let common = Self::SQRT_FRAC_PI_2 * a * c;
let denom = Self::SQRT_2 * c;
let (a1, a0) = if const { P::POLICY.precision.le(PrecisionPolicy::Medium) } {
let d = denom.reciprocal_p::<P>();
(x1 * d, x0 * d)
} else {
(x1 / denom, x0 / denom)
};
common * (a1.erf_p::<P>() - a0.erf_p::<P>())
}
}
pub trait SpecializedRealPrimalMath<E>: SpecializedRealSpecialMath<E> {
#[inline(always)]
fn softplus_d<P: Policy>(self, k: Self, rcp_k: Self) -> (Self, Self) {
if const { P::POLICY.precision.lt(PrecisionPolicy::Average) } {
let k = k.scale(FloatConsts::LOG2_E);
let rcp_k = rcp_k.scale(FloatConsts::LN_2);
let kx = self * k;
let e = kx.abs().neg().exp2_p::<CheckOverflow<P, true>>();
let y = (Self::ONE + e).log2_p::<P>().mul_adde(rcp_k, self.max(Self::ZERO));
let rcp = (Self::ONE + e).reciprocal_p::<P>();
let dy = kx.select_negative(e * rcp, rcp);
return (y, dy);
}
let kx = self * k;
let e = kx.abs().neg().exp_p::<P>();
let y = e.ln_1p_p::<P>().mul_adde(rcp_k, self.max(Self::ZERO));
let rcp = (e + Self::ONE).reciprocal_p::<P>();
let dy = kx.select_negative(e * rcp, rcp);
(y, dy)
}
#[inline(always)]
fn gelu_d<P: Policy>(self, alpha: Self) -> (Self, Self) {
let alpha_x = alpha * self;
let erf = alpha_x.scale(FloatConsts::FRAC_1_SQRT_2).erf_p::<P>();
let y = if Self::HAS_TRUE_FMA {
let half_x = self.scale(E::ConstRatio::<1, 2>::VALUE);
half_x.mul_add(erf, half_x) } else {
self.scale(E::ConstRatio::<1, 2>::VALUE) * (Self::ONE + erf)
};
let dy = (alpha_x * alpha_x)
.scale(E::ConstRatio::<{ -1 }, 2>::VALUE)
.exp_p::<P>()
.scale(FloatConsts::FRAC_1_SQRT_TAU);
(y, dy.mul_adde(alpha_x, y))
}
#[inline(always)]
fn swish_d<P: Policy>(self, beta: Self) -> (Self, Self) {
let x = self;
let beta_x = beta * x;
let e = (-beta_x).exp_p::<P>();
let s = (Self::ONE + e).reciprocal_p::<P>();
let y = x * s;
let dy = (beta * y).mul_adde(e * s, s);
(y, dy)
}
#[inline(always)]
fn algebraic_sigmoid_d<P: Policy, const N: usize>(self) -> (Self, Self) {
if const { N == 0 } {
return (self, Self::ONE); }
let pre_root = Self::ONE + self.abs().powi_p::<P>(N as i32);
let denom = match N {
1 => pre_root,
2 => pre_root.sqrt(),
3 => pre_root.cbrt_p::<P>(),
4 if const { P::POLICY.precision.le(PrecisionPolicy::Average) } => pre_root.sqrt().sqrt(),
_ => {
let x = pre_root;
let mut y = x.powf_p::<CheckOverflow<LessPrecision<P>, false>>(Self::splat(
E::ONE / E::from_int(N as thermite::LargeInt),
));
let y_n = y.powi_p::<P>(N as i32);
let np1 = Self::splat(E::from_int((N + 1) as thermite::LargeInt));
let nm1 = Self::splat(E::from_int((N - 1) as thermite::LargeInt));
let n = y * (x - y_n); let d = y_n.mul_adde(np1, x * nm1);
y += (n + n) / d;
y
}
};
let mut y;
let mut dy;
if const { P::POLICY.precision.lt(PrecisionPolicy::Average) } {
let inv_denom = denom.reciprocal_p::<P>();
y = self * inv_denom;
dy = inv_denom / pre_root;
} else {
y = self / denom;
dy = (pre_root * denom).reciprocal_p::<P>();
}
if const { P::POLICY.check_overflow } {
let is_infinite = pre_root.is_infinite();
y = is_infinite.select(self.signum(), y);
dy = dy.nz(is_infinite); }
(y, dy)
}
#[inline(always)]
fn algebraic_swish_d<P: Policy>(self) -> (Self, Self) {
let x = self;
if const { Self::HAS_TRUE_FMA } {
if const { Self::HAS_APPROX_RSQRT } {
let a = x.mul_add(x, Self::ONE);
let y0 = a.rsqrt();
let ay2 = a * y0 * y0;
let ch = ay2.nmul_add(Self::HALF, Self::splat(<E as FloatElement>::ConstRatio::<3, 2>::VALUE));
let r_inv = y0 * ch; let q = x * r_inv;
let xh = Self::HALF * x;
let y = q.mul_add(xh, xh);
let inv_a = r_inv * r_inv;
let qa = q.mul_add(inv_a, q); let dy = qa.mul_add(Self::HALF, Self::HALF);
(y, dy)
} else {
let a = x.mul_add(x, Self::ONE);
let q = x / a.sqrt();
let xh = x * Self::HALF;
let y = q.mul_add(xh, xh);
let inv_a = a.reciprocal_p::<P>();
let qa = q.mul_add(inv_a, q);
let dy = qa.mul_add(Self::HALF, Self::HALF);
(y, dy)
}
} else if const { Self::HAS_APPROX_RCP } {
let a = x * x + Self::ONE;
let y0 = a.rsqrt();
let ay2 = a * y0 * y0;
let c = Self::splat(<E as FloatElement>::ConstInt::<3>::VALUE) - ay2;
let r_inv_2 = y0 * c; let hxy1 = Self::splat(<E as FloatElement>::ConstRatio::<1, 4>::VALUE) * (x * r_inv_2); let w = Self::HALF + hxy1; let y = x * w;
let inv_a = Self::splat(<E as FloatElement>::ConstRatio::<1, 4>::VALUE) * (r_inv_2 * r_inv_2);
let dy = w + hxy1 * inv_a;
(y, dy)
} else {
let a = x * x + Self::ONE;
let q = x / a.sqrt();
let q1 = q + Self::ONE;
let y = x * Self::HALF * q1;
let dy = Self::HALF * (q1 + q / a);
(y, dy)
}
}
}