use thermite::{
math::{
TranscendentalMathWithPolicy,
policy::{
DenormalBehavior, PrecisionPolicy,
policies::{CheckOverflow, ExtraPrecision, WorstPrecision},
},
specialized::SpecializedTranscendentalMath,
},
prelude::*,
};
use crate::RealSpecialMathWithPolicy as _;
use super::*;
impl<V: FloatVectorWithBits<Element = f64>> SpecializedSpecialMath<f64> for V
where
V: TranscendentalMathWithPolicy<Element = f64>,
V: SpecializedTranscendentalMath<f64>,
{
type ExpIntDetails = Self;
#[inline(always)]
fn lambert_w<P: Policy>(self) -> (Self, Self) {
type Approx<P> = WorstPrecision<CheckOverflow<P, false>>;
let x = self;
let p0 = x.mul_adde(Self::E, Self::ONE); let p = (p0 + p0).sqrt();
let puiseux_numer = p * p.mul_adde(
p.mul_adde(
thermite::const_splat!(f64: 11.0 / 72.0),
thermite::const_splat!(f64: -1.0 / 3.0),
),
Self::ONE,
);
let puiseux_denom = p0.mul_adde(p * thermite::const_splat!(f64: 0.12991546098765432), Self::ONE);
let puiseux = puiseux_numer / puiseux_denom;
let w0_branch = puiseux + Self::NEG_ONE;
let wm1_branch = Self::NEG_ONE - puiseux;
let ex = x * Self::E;
let w0_mid = ex / (Self::TWO + ex);
let lnx = x.abs().ln_p::<Approx<P>>();
let l2 = lnx.ln_p::<Approx<P>>();
let w0_asymptotic = (lnx - l2) + (l2 / lnx);
let wm1_asymptotic = lnx - (-lnx).ln_p::<Approx<P>>();
let near_branch = x.cmp_lt(Self::splat(-0.1));
let large = x.cmp_gt(Self::E);
let mut w0 = near_branch.select(w0_branch, large.select(w0_asymptotic, w0_mid));
let near_branch_m1 = x.cmp_lt(Self::splat(-0.25));
let mut wm1 = near_branch_m1.select(wm1_branch, wm1_asymptotic);
#[inline(always)]
fn halley_step<P: Policy, W>(w: W, x: W) -> W
where
W: FloatVectorWithBits<Element = f64> + SpecializedTranscendentalMath<f64>,
{
let enw = (-w).exp_p::<P>();
let wp1 = w + W::ONE;
let q = wp1.mul_adde(wp1, W::ONE); let wp2h_x = wp1.mul_adde(x, x); let g = x.nmul_adde(enw, w); let d = wp2h_x.mul_adde(enw, q); (wp1 + wp1).nmul_adde(g / d, w)
}
#[rustfmt::skip]
let num_iters = if const { P::POLICY.precision.ge(PrecisionPolicy::Best) } { 3 } else { 2 };
w0 = halley_step::<Approx<P>, Self>(w0, x);
wm1 = halley_step::<Approx<P>, Self>(wm1, x);
let mut _iter = 0usize;
while _iter < num_iters {
_iter += 1;
w0 = halley_step::<CheckOverflow<P, false>, Self>(w0, x);
wm1 = halley_step::<CheckOverflow<P, false>, Self>(wm1, x);
}
if const { P::POLICY.precision.ge(PrecisionPolicy::Average) } {
let x_is_zero = x.is_zero();
w0 = x.cmp_eq(Self::FRAC_NEG_1_E).select(Self::NEG_ONE, w0);
w0 = w0.nz(x_is_zero);
wm1 = x.cmp_eq(Self::FRAC_NEG_1_E).select(Self::NEG_ONE, wm1);
wm1 = x_is_zero.select(Self::NEG_INFINITY, wm1); }
if const { matches!(P::POLICY.denormal_behavior, DenormalBehavior::Preserve) } {
w0 = x.is_subnormal().select(x, w0);
}
if const { P::POLICY.check_overflow } {
let in_domain = x.cmp_ge(Self::FRAC_NEG_1_E);
w0 = in_domain.select(w0, Self::NAN);
w0 = x.cmp_eq(Self::INFINITY).select(Self::INFINITY, w0);
wm1 = in_domain.select(wm1, Self::NAN);
wm1 = x.cmp_gt(Self::ZERO).select(Self::NAN, wm1);
}
(w0, wm1)
}
#[inline(always)]
#[allow(const_item_mutation)]
fn erf<P: Policy>(self) -> Self {
erf_d_internal::<Self, P, false, false>(self, &mut V::EMPTY)
}
#[inline(always)]
#[allow(const_item_mutation)]
fn erfc<P: Policy>(self) -> Self {
erf_d_internal::<Self, P, true, false>(self, &mut V::EMPTY)
}
#[inline(always)]
fn lgamma<P: Policy>(self) -> Self {
Self::lgamma_r::<P>(self).0
}
#[inline(always)]
fn tgamma<P: Policy>(self) -> Self {
let z = self;
if const { P::POLICY.precision.lt(PrecisionPolicy::Average) } {
let (lgamma, sign) = z.lgamma_r_p::<P>();
return lgamma.exp_p::<ExtraPrecision<P>>() * sign;
}
generic::gamma::tgamma_impl::<P, _, _, _>(z, &crate::tables::LANCZOS_F64, 172.0, crate::tables::LN_MAX_F64)
}
#[inline(always)]
fn trigamma<P: Policy>(self) -> Self {
generic::trigamma::trigamma_impl::<P, _, _>(self, &crate::tables::TRIGAMMA_F64)
}
#[inline(always)]
fn digamma<P: Policy>(self) -> Self {
generic::digamma::digamma_impl::<P, _, _, _, _, _, _>(self, &crate::tables::DIGAMMA_F64)
}
#[inline(always)]
fn beta<P: Policy>(a: Self, b: Self) -> Self {
generic::gamma::beta_impl::<P, _, _, _>(a, b, &crate::tables::LANCZOS_F64)
}
#[inline(always)]
fn expint<P: Policy, const N: usize>(self) -> Self {
generic::expint::expint_double::<P, f64, Self, N>(self)
}
#[inline(always)]
fn expint_primal<P: Policy, const N: usize>(self) -> (Self, Self) {
generic::expint::expint_double_primal::<P, f64, Self, N>(self)
}
}
impl<V: FloatVectorWithBits<Element = f64>> SpecializedRealSpecialMath<f64> for V
where
V: TranscendentalMathWithPolicy<Element = f64>,
V: SpecializedTranscendentalMath<f64>,
{
#[inline(always)]
fn erfinv<P: Policy>(self) -> Self {
const ALPHA: f64 = 0.147;
const RCP_PI_ALPHA_2: f64 = 4.330746750799873; const RCP_ALPHA: f64 = 1.0 / ALPHA;
const SQRT_PI_2: f64 = 0.8862269254527580136490837416706;
let y = self.flush_denormals_p::<P>();
let a = y.abs();
let q = Self::ONE - a; let omsq = q * (Self::ONE + a);
let lnv = omsq.ln_p::<P>(); let t1 = lnv.mul_adde(Self::HALF, Self::splat(RCP_PI_ALPHA_2));
let mut x = (t1.mul_adde(t1, lnv * Self::splat(-RCP_ALPHA)).sqrt() - t1).sqrt();
let steps = if const { P::POLICY.precision.ge(PrecisionPolicy::Best) } {
2
} else {
1
};
let mut i = 0;
while i < steps {
let mut exp_neg = Self::EMPTY;
let erfc = erf_d_internal::<Self, P, true, true>(x, &mut exp_neg);
let u = (q - erfc) * Self::splat(SQRT_PI_2) / exp_neg;
x -= u / x.mul_adde(u, Self::ONE);
i += 1;
}
let mut res = x.copysign(y);
if const { P::POLICY.check_overflow } {
res = a.cmp_eq(Self::ONE).select(Self::INFINITY.copysign(y), res); res = a.cmp_gt(Self::ONE).select(Self::NAN, res); }
res
}
#[inline(always)]
fn lgamma_r<P: Policy>(self) -> (Self, Self) {
generic::gamma::lgamma_r_impl::<P, _, _, _>(self, &crate::tables::LANCZOS_F64)
}
#[inline(always)]
fn probit<P: Policy>(self) -> Self {
const A: [f64; 6] = [
2.506628277459239e+00,
-3.066479806614716e+01,
1.383577518672690e+02,
-2.759285104469687e+02,
2.209460984245205e+02,
-3.969683028665376e+01,
];
const B: [f64; 6] = [
1.0,
-1.328068155288572e+01,
6.680131188771972e+01,
-1.556989798598866e+02,
1.615858368580409e+02,
-5.447609879822406e+01,
];
const C: [f64; 6] = [
2.938163982698783e+00,
4.374664141464968e+00,
-2.549732539343734e+00,
-2.400758277161838e+00,
-3.223964580411365e-01,
-7.784894002430293e-03,
];
const D: [f64; 5] = [
1.0,
3.754408661907416e+00,
2.445134137142996e+00,
3.224671290700398e-01,
7.784695709041462e-03,
];
generic::probit::probit_acklam::<P, _, _, true>(self, &A, &B, &C, &D)
}
#[inline(always)]
fn gelu<P: Policy>(self, alpha: Self) -> Self {
let x = self;
let alpha_x = alpha * x;
let mut unused = Self::EMPTY;
let erf = erf_d_internal::<V, P, false, false>(alpha_x * Self::FRAC_1_SQRT_2, &mut unused);
if V::HAS_TRUE_FMA {
let half_x = x * Self::HALF;
half_x.mul_add(erf, half_x) } else {
erf.mul_adde(Self::HALF, Self::HALF) * x
}
}
}
impl<V: FloatVectorWithBits<Element = f64>> SpecializedRealPrimalMath<f64> for V
where
V: TranscendentalMathWithPolicy<Element = f64>,
V: SpecializedTranscendentalMath<f64>,
{
#[inline(always)]
fn gelu_d<P: Policy>(self, alpha: Self) -> (Self, Self) {
let x = self;
let alpha_x = alpha * x;
let mut exp_neg_ax2 = Self::EMPTY;
let erf = erf_d_internal::<V, P, false, true>(alpha_x * Self::FRAC_1_SQRT_2, &mut exp_neg_ax2);
let y;
let dy;
let half_erf = erf.mul_adde(Self::HALF, Self::HALF);
if V::HAS_TRUE_FMA {
let half_x = x * Self::HALF;
y = half_x.mul_add(erf, half_x); dy = (alpha_x * Self::FRAC_1_SQRT_TAU).mul_add(exp_neg_ax2, half_erf);
} else {
y = half_erf * x;
dy = half_erf + alpha_x * Self::FRAC_1_SQRT_TAU * exp_neg_ax2;
}
(y, dy)
}
}
#[rustfmt::skip]
#[inline(always)]
fn erf_d_internal<V: FloatVectorWithBits<Element = f64>, P: Policy, const C: bool, const O: bool>(x0: V, out_exp_neg_x2: &mut V) -> V {
let sign = x0.signed_zero();
let x = (x0 ^ sign).flush_denormals_p::<P>();
let x2 = if const { matches!(P::POLICY.denormal_behavior, DenormalBehavior::Ignore) } {
x0 * x0
} else {
x * x
};
let e = (-x2).exp_p::<P>();
let a0: V = thermite::const_splat!(f64: 0.56418958354775629);
let a1 = x + thermite::const_splat!(f64: 2.06955023132914151);
let b0 = x2 + x.mul_adde(thermite::const_splat!(f64: 2.71078540045147805), thermite::const_splat!(f64: 5.80755613130301624));
let b1 = x2 + x.mul_adde(thermite::const_splat!(f64: 3.47954057099518960), thermite::const_splat!(f64: 12.06166887286239555));
let c0 = x2 + x.mul_adde(thermite::const_splat!(f64: 3.47469513777439592), thermite::const_splat!(f64: 12.07402036406381411));
let c1 = x2 + x.mul_adde(thermite::const_splat!(f64: 3.72068443960225092), thermite::const_splat!(f64: 8.44319781003968454));
let d0 = x2 + x.mul_adde(thermite::const_splat!(f64: 4.00561509202259545), thermite::const_splat!(f64: 9.30596659485887898));
let d1 = x2 + x.mul_adde(thermite::const_splat!(f64: 3.90225704029924078), thermite::const_splat!(f64: 6.36161630953880464));
let e0 = x2 + x.mul_adde(thermite::const_splat!(f64: 5.16722705817812584), thermite::const_splat!(f64: 9.12661617673673262));
let e1 = x2 + x.mul_adde(thermite::const_splat!(f64: 4.03296893109262491), thermite::const_splat!(f64: 5.13578530585681539));
let f0 = x2 + x.mul_adde(thermite::const_splat!(f64: 5.95908795446633271), thermite::const_splat!(f64: 9.19435612886969243));
let f1 = x2 + x.mul_adde(thermite::const_splat!(f64: 4.11240942957450885), thermite::const_splat!(f64: 4.48640329523408675));
let m = if const { P::POLICY.precision.ge(PrecisionPolicy::Best) } {
(a0 / a1) * (b0 / b1) * (c0 / c1) * (d0 / d1) * (e0 / e1) * (f0 / f1)
} else {
let n = (a0 * b0) * (c0 * d0) * (e0 * f0);
let d = (a1 * b1) * (c1 * d1) * (e1 * f1);
n / d
};
if O {
*out_exp_neg_x2 = e;
}
if !C {
e.nmul_adde(m, V::ONE) ^ sign
} else if const { V::HAS_TRUE_FMA } {
x0.select_negative(m.nmul_add(e, V::TWO), m * e)
} else {
let y = m * e;
x0.select_negative(V::TWO - y, y)
}
}
impl<V: FloatVectorWithBits<Element = f64>> super::ExpIntDetails<f64, V> for V {}