1#![allow(clippy::excessive_precision)]
2
3use thermite::{
4 mask::GenericMask,
5 math::{
6 CoreMathWithPolicy as _, FloatConsts, TranscendentalMathWithPolicy as _,
7 policy::{
8 Policy, PrecisionPolicy,
9 policies::{CheckOverflow, ExtraPrecision, LessPrecision},
10 },
11 specialized::FlushDenormals,
12 },
13 register::{Element, FloatElement},
14 vector::{NumericVector, PartialOrdVector, SplatConst},
15};
16
17use super::SpecialMathWithPolicy as _;
18
19pub(crate) mod generic;
20mod pd;
21mod ps;
22
23pub trait ExpIntDetails<E, V: thermite::vector::FloatVector<Element = E>> {
35 #[inline(always)]
41 fn use_series(z: V) -> V::Mask {
42 z.cmp_lt(V::ONE)
43 }
44
45 #[inline(always)]
51 fn invalid(z: V) -> V::Mask {
52 z.cmp_lt(V::ZERO) | z.is_nan()
53 }
54
55 #[inline(always)]
64 fn cf_tiny() -> V {
65 V::MIN_POSITIVE
66 }
67}
68
69pub trait SpecializedSpecialMath<E>: thermite::math::specialized::SpecializedTranscendentalMath<E> {
70 type ExpIntDetails: ExpIntDetails<E, Self>;
73
74 fn erf<P: Policy>(self) -> Self;
75
76 #[inline(always)]
77 fn erfc<P: Policy>(self) -> Self {
78 Self::ONE - self.erf_p::<P>()
79 }
80
81 #[inline(always)]
83 fn expint<P: Policy, const N: usize>(self) -> Self {
84 self.expint_primal::<P, N>().0
85 }
86
87 #[inline(always)]
100 fn expint_primal<P: Policy, const N: usize>(self) -> (Self, Self) {
101 let x = self;
102
103 let exp_neg_x = (-x).exp_p::<P>();
108 let inv_x = x.reciprocal_p::<P>();
109 let e0 = exp_neg_x * inv_x;
110
111 if const { N == 0 } {
112 let mut value = e0;
113 let mut prev = e0 * (Self::ONE + inv_x);
114
115 if const { P::POLICY.check_overflow } {
116 let x_is_zero = x.is_zero();
118 value = x_is_zero.select(Self::INFINITY, value);
119 prev = x_is_zero.select(Self::INFINITY, prev);
120
121 let bad = <Self::ExpIntDetails as ExpIntDetails<E, Self>>::invalid(x);
122 value = bad.select(Self::NAN, value);
123 prev = bad.select(Self::NAN, prev);
124 }
125
126 return (value, prev);
127 }
128
129 let use_series = <Self::ExpIntDetails as ExpIntDetails<E, Self>>::use_series(x);
144
145 let neg_x = -x;
147 let mut s_term = neg_x; let mut s_sum = s_term; let tiny = <Self::ExpIntDetails as ExpIntDetails<E, Self>>::cf_tiny();
160
161 let mut cf_f = tiny;
163 let mut cf_c = tiny;
164
165 let mut cf_d = {
168 let b1 = x + Self::ONE;
169 let d1 = b1.reciprocal_p::<P>();
171 cf_c = b1 + cf_c.reciprocal_p::<P>();
173 let delta = cf_c * d1;
174 cf_f *= delta; d1
176 };
177
178 let eps = Self::splat(E::EPSILON);
180
181 let mut series_done = !use_series; let mut cf_done = use_series; let mut k = 1usize;
185 while k < const { P::POLICY.max_iterations } {
186 let kf = Self::splat(E::from_int(k as thermite::LargeInt));
187 let kp1 = Self::splat(E::from_int(k as thermite::LargeInt + 1));
188
189 if !series_done.all() {
192 s_term *= (neg_x * kf) / (kp1 * kp1);
193 s_sum = series_done.select(s_sum, s_sum + s_term);
194
195 let term_small = s_term.abs().cmp_lt(s_sum.abs() * eps);
196
197 series_done = GenericMask::ternlog::<{ thermite::ternlog_imm!(A | (B & C)) }>(
199 series_done,
200 use_series,
201 term_small,
202 );
203 }
204
205 if !cf_done.all() {
208 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>();
214
215 let new_c = b_k - neg_a_k / cf_c;
217 let new_c = new_c.cmp_eq(Self::ZERO).select(tiny, new_c);
218
219 let delta = new_c * new_d;
220
221 cf_d = new_d;
222 cf_c = new_c;
223 cf_f = cf_done.select(cf_f, cf_f * delta);
224
225 let cf_converged = (delta - Self::ONE).abs().cmp_lt(eps);
226
227 cf_done =
228 GenericMask::ternlog::<{ thermite::ternlog_imm!(A | (!B & C)) }>(cf_done, use_series, cf_converged);
229 }
230
231 if (series_done & cf_done).all() {
232 break;
233 }
234
235 k += 1;
236 }
237
238 let mut series_result = Self::EMPTY;
242
243 let mut cf_result = Self::EMPTY;
245
246 if use_series.any() {
247 series_result = (-Self::EULER_GAMMA - s_sum) - x.ln_p::<P>();
248 }
249
250 if !use_series.all() {
251 cf_result = cf_f * exp_neg_x;
252 }
253
254 let mut e_n = use_series.select(series_result, cf_result);
255
256 let mut e_prev = e0;
259
260 if const { N > 1 } {
263 let mut n = 1u32;
264 while n < N as u32 {
265 let nf = Self::splat(E::from_int(n as thermite::LargeInt));
266 e_prev = e_n;
267 e_n = x.nmul_adde(e_n, exp_neg_x) / nf;
268 n += 1;
269 }
270 }
271
272 if const { P::POLICY.check_overflow } {
274 let x_is_zero = x.is_zero();
276 if const { N == 1 } {
277 e_n = x_is_zero.select(Self::INFINITY, e_n);
278 } else if const { N > 1 } {
279 e_n = x_is_zero.select(Self::splat(E::ONE / E::from_int(N as thermite::LargeInt - 1)), e_n);
280 }
281
282 if const { N <= 2 } {
284 e_prev = x_is_zero.select(Self::INFINITY, e_prev);
285 } else {
286 e_prev = x_is_zero.select(Self::splat(E::ONE / E::from_int(N as thermite::LargeInt - 2)), e_prev);
287 }
288
289 let bad = <Self::ExpIntDetails as ExpIntDetails<E, Self>>::invalid(x);
291 e_n = bad.select(Self::NAN, e_n);
292 e_prev = bad.select(Self::NAN, e_prev);
293 }
294
295 (e_n, e_prev)
296 }
297
298 #[inline(always)]
299 fn logistic_sigmoid<P: Policy>(self) -> Self {
300 if const { P::POLICY.precision.gt(PrecisionPolicy::Average) } {
301 let is_pos = self.is_positive();
302 let x = self.neg_c(is_pos); let e = x.exp_p::<P>();
304
305 let n = is_pos.select(Self::ONE, e);
306 let d = Self::ONE + e;
307
308 return n / d;
309 }
310
311 (Self::ONE + (-self).exp_p::<P>()).reciprocal_p::<ExtraPrecision<P>>()
312 }
313
314 #[inline(always)]
315 fn softplus<P: Policy>(self, k: Self, rcp_k: Self) -> Self {
316 if const { P::POLICY.precision.lt(PrecisionPolicy::Average) } {
319 let k = k.scale(FloatConsts::LOG2_E);
321 let rcp_k = rcp_k.scale(FloatConsts::LN_2);
322
323 let kx = self * k;
324
325 let e = kx.abs().neg().exp2_p::<CheckOverflow<P, true>>();
327 return (Self::ONE + e).log2_p::<P>().mul_adde(rcp_k, self.max(Self::ZERO));
328 }
329
330 let kx = self * k;
331
332 let e = kx.abs().neg().exp_p::<P>();
333
334 e.ln_1p_p::<P>().mul_adde(rcp_k, self.max(Self::ZERO))
336 }
337
338 fn tgamma<P: Policy>(self) -> Self;
339 fn lgamma<P: Policy>(self) -> Self;
340 fn digamma<P: Policy>(self) -> Self;
341
342 fn trigamma<P: Policy>(self) -> Self;
355
356 #[inline(always)]
357 fn hermite<P: Policy, const N: usize>(mut x: Self) -> Self {
358 #[cfg(not(target_arch = "spirv"))]
359 if let Some(new_x) = FlushDenormals::<P>::flush_denormals([x]) {
360 x = new_x[0];
361 }
362
363 let mut p0 = Self::ONE;
364
365 if const { N == 0 } {
366 return p0;
367 }
368
369 let mut p1 = x + x; cfg_if::cfg_if! {
372 if #[cfg(all(feature = "spirv", target_arch = "spirv"))] {
373 use crunchy::unroll;
374
375 macro_rules! unroll_poly {
376 ($($len:tt),*) => {
377 $( if const { N == $len } {
378 unroll! { for n in 0..$len {
379 (p0, p1) = (p1, p0); const cf: thermite::LargeInt = (1 + n) as thermite::LargeInt;
382 let next0 = x.mul_sube(p0, p1.scale(E::ConstInt::<{cf}>::VALUE));
383 p1 = next0 + next0; }}
385 } else )* {
386 let mut c = 1;
387 let mut cf = E::ONE;
388
389 while c < N {
390 (p0, p1) = (p1, p0); let next0 = x.mul_sube(p0, p1.scale(cf));
393 p1 = next0 + next0; c += 1;
396 cf = cf + E::ONE;
397 }
398 }
399 };
400 }
401
402 unroll_poly!(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); } else {
404 let mut c = 1;
405 let mut cf = Self::ONE;
406
407 while c < N {
408 (p0, p1) = (p1, p0); let next0 = x.mul_sube(p0, cf * p1);
411 p1 = next0 + next0; c += 1;
414 cf += Self::ONE;
415 }
416 }
417 }
418
419 p1
420 }
421
422 #[inline(always)]
423 fn hermitev<P: Policy>(mut x: Self, n: Self::Unsigned) -> Self {
424 #[cfg(not(target_arch = "spirv"))]
425 if let Some(new_x) = FlushDenormals::<P>::flush_denormals([x]) {
426 x = new_x[0];
427 }
428
429 let i1 = Self::Unsigned::ONE;
430 let n_is_zero = n.cmp_eq(Self::Unsigned::ZERO);
431
432 let mut c = i1;
433
434 let mut cf = Self::ONE;
436
437 let mut p0 = Self::ONE;
438 let mut p1 = x + x; loop {
441 let cont = c.cmp_lt(n);
442
443 if cont.none() {
444 break;
445 }
446
447 (p0, p1) = (p1, p0); let next0 = x.mul_sube(p0, cf * p1);
450 let next = next0 + next0; p1 = cont.select(next, p1);
453
454 c += i1;
455 cf += Self::ONE;
456 }
457
458 n_is_zero.select(Self::ONE, p1)
459 }
460
461 #[inline(always)]
462 fn chebyshev<P: Policy, const K: usize, const N: usize>(self, coeffs: &[Self::Element; N]) -> Self {
463 const {
464 assert!(K >= 1 && K <= 4, "chebyshev: K must be 1, 2, 3, or 4");
465 assert!(N >= 1, "chebyshev: N must be at least 1");
466 }
467
468 if const { N == 1 } {
470 return Self::splat(coeffs[0]);
471 }
472
473 let x = self;
474 let x2 = x + x;
475
476 let p1 = if const { K == 1 } {
478 x
479 } else if const { K == 2 } {
480 x2
481 } else if const { K == 3 } {
482 x2 - Self::ONE
483 } else if const { K == 4 } {
484 x2 + Self::ONE
485 } else {
486 unsafe { core::hint::unreachable_unchecked() }
487 };
488
489 let cn1 = Self::splat(coeffs[N - 1]);
490 let cn2 = Self::splat(coeffs[N - 2]);
491
492 if const { N == 2 } {
494 return p1.mul_adde(cn1, cn2);
495 }
496
497 let mut b1 = x2.mul_adde(cn1, cn2); let mut b2 = cn1; let mut k = N - 2;
517 while k > 1 {
518 k -= 1;
519 let bk = x2.mul_adde(b1, Self::splat(coeffs[k]) - b2);
521 b2 = b1;
522 b1 = bk;
523 }
524
525 b1.mul_adde(p1, Self::splat(coeffs[0]) - b2)
527 }
528
529 #[inline(always)]
530 fn jacobi<P: Policy>(mut x: Self, mut alpha: Self, mut beta: Self, mut n: u32, m: u32) -> Self {
531 if thermite::unlikely(m > n) {
532 return Self::ZERO;
533 }
534
535 #[cfg(not(target_arch = "spirv"))]
536 if let Some(new) = FlushDenormals::<P>::flush_denormals([x, alpha, beta]) {
537 x = new[0];
538 alpha = new[1];
539 beta = new[2];
540 }
541
542 let mut scale = Self::ONE;
543
544 if m > 0 {
545 let mut jf = Self::ONE;
546 let nf = Self::splat(E::from_int(n as thermite::LargeInt));
547
548 let t0 = Self::HALF * (nf + alpha + beta);
549
550 let mut _iter = 0;
551 while _iter < m {
552 _iter += 1;
553 scale *= Self::HALF.mul_adde(jf, t0);
554 jf += Self::ONE;
555 }
556
557 let mf = Self::splat(E::from_int(m as thermite::LargeInt));
558
559 alpha += mf;
560 beta += mf;
561 n -= m;
562 }
563
564 if thermite::unlikely(n == 0) {
565 return scale; }
567
568 let mut y0 = Self::ONE;
569
570 let alpha_p_beta = alpha + beta;
571 let alpha_sqr = alpha * alpha;
572 let beta_sqr = beta * beta;
573 let alpha1 = alpha - Self::ONE;
574 let beta1 = beta - Self::ONE;
575 let alpha2beta2 = alpha_sqr - beta_sqr;
576
577 let mut y1 = Self::HALF * (x.mul_adde(alpha, alpha) + x.mul_sube(beta, beta) + x + x);
579
580 let mut yk = y1;
581 let mut k = E::ConstInt::<2>::VALUE;
582
583 let k_max = E::from_int(n as thermite::LargeInt) * (<E as Element>::ONE + E::EPSILON);
584
585 while k < k_max {
586 let kf = Self::splat(k);
587 let kf2 = Self::TWO * kf;
588
589 let k_alpha_p_beta = kf + alpha_p_beta;
590 let k2_alpha_p_beta = kf2 + alpha_p_beta;
591
592 let k2_alpha_p_beta_m2 = k2_alpha_p_beta - Self::TWO;
593
594 let denom = kf2 * k_alpha_p_beta * k2_alpha_p_beta_m2;
595 let t0 = x.mul_adde(k2_alpha_p_beta * k2_alpha_p_beta_m2, alpha2beta2);
596 let gamma1 = k2_alpha_p_beta.mul_sube(t0, t0);
597 let gamma0 = Self::TWO * (kf + alpha1) * (kf + beta1) * k2_alpha_p_beta;
598
599 yk = gamma1.mul_sube(y1, gamma0 * y0) / denom;
600
601 y0 = y1;
602 y1 = yk;
603
604 k = k + <E as Element>::ONE;
605 }
606
607 scale * yk
608 }
609
610 #[inline(always)]
611 fn gaussian<P: Policy>(mut x: Self, a: Self, c: Self) -> Self {
612 #[cfg(not(target_arch = "spirv"))]
613 if let Some(new_x) = FlushDenormals::<P>::flush_denormals([x]) {
614 x = new_x[0];
615 }
616
617 let xc = if const { P::POLICY.precision.le(PrecisionPolicy::Worst) } {
618 x * c.reciprocal_p::<P>()
619 } else {
620 x / c
621 };
622
623 a * (-Self::HALF * xc * xc).exp_p::<P>()
624 }
625
626 fn beta<P: Policy>(a: Self, b: Self) -> Self;
627
628 #[rustfmt::skip]
629 #[inline(always)]
630 fn legendre0<P: Policy, const N: u32>(x: Self, n: u32) -> Self {
631 macro_rules! c { ($n:literal / $d:literal) => { Self::splat(E::from_int($n) / E::from_int($d)) }; }
632
633 let x2 = x.square();
634 let x4 = x2.square();
635 let x8 = x4.square();
636
637 if const { N != 0 } {
638 unsafe { core::hint::assert_unchecked(N == n); }
639 }
640
641 match n {
643 1 => x,
644 2 => x2.mul_adde(c!(3 / 2), c!(-1 / 2)),
645 3 => x * x2.mul_adde(c!(5 / 2), c!(-3 / 2)),
646 4 => x4.mul_adde(c!(35 / 8), x2.mul_adde(c!(-15 / 4), c!(3 / 8))),
647 5 => x * x4.mul_adde(c!(63 / 8), x2.mul_adde(c!(-35 / 4), c!(15 / 8))),
648 6 => x4.mul_adde(
649 x2.mul_adde(c!(231 / 16), c!(-315 / 16)),
650 x2.mul_adde(c!(105 / 16), c!(-5 / 16)),
651 ),
652 7 => x * x4.mul_adde(
653 x2.mul_adde(c!(429 / 16), c!(-693 / 16)),
654 x2.mul_adde(c!(315 / 16), c!(-35 / 16)),
655 ),
656 8 => x8.mul_adde(c!(6435 / 128), x4.mul_adde(
657 x2.mul_adde(c!(-3003 / 32), c!(3465 / 64)),
658 x2.mul_adde(c!(-315 / 32), c!(35 / 128)),
659 )),
660 9 => x * x8.mul_adde(c!(12155 / 128), x4.mul_adde(
661 x2.mul_adde(c!(-6435 / 32), c!(9009 / 64)),
662 x2.mul_adde(c!(-1155 / 32), c!(315 / 128)),
663 )),
664 10 => x8.mul_adde(
665 x2.mul_adde(c!(46189 / 256), c!(-109395 / 256)),
666 x4.mul_adde(
667 x2.mul_adde(c!(45045 / 128), c!(-15015 / 128)),
668 x2.mul_adde(c!(3465 / 256), c!(-63 / 256)),
669 ),
670 ),
671 11 => x * x8.mul_adde(
672 x2.mul_adde(c!(88179 / 256), c!(-230945 / 256)),
673 x4.mul_adde(
674 x2.mul_adde(c!(109395 / 128), c!(-45045 / 128)),
675 x2.mul_adde(c!(15015 / 256), c!(-693 / 256)),
676 ),
677 ),
678 12 => x8.mul_adde(
679 x4.mul_adde(c!(676039 / 1024), x2.mul_adde(c!(-969969 / 512), c!(2078505 / 1024))),
680 x4.mul_adde(
681 x2.mul_adde(c!(-255255 / 256), c!(225225 / 1024)),
682 x2.mul_adde(c!(-9009 / 512), c!(231 / 1024)),
683 ),
684 ),
685 13 => x * x8.mul_adde(
686 x4.mul_adde(c!(1300075 / 1024), x2.mul_adde(c!(-2028117 / 512), c!(4849845 / 1024))),
687 x4.mul_adde(
688 x2.mul_adde(c!(-692835 / 256), c!(765765 / 1024)),
689 x2.mul_adde(c!(-45045 / 512), c!(3003 / 1024)),
690 ),
691 ),
692 _ => unsafe { core::hint::unreachable_unchecked() },
693 }
694 }
695
696 #[inline(always)]
697 fn legendre<P: Policy>(mut x: Self, n: u32, m: u32) -> Self {
698 #[cfg(not(target_arch = "spirv"))]
699 if let Some(new_x) = FlushDenormals::<P>::flush_denormals([x]) {
700 x = new_x[0];
701 }
702
703 match (n, m) {
704 (0, 0) => return Self::ONE,
705 (n, 0) if n < 14 => return Self::legendre0::<P, 0>(x, n),
706 (n, 0) => {
707 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 {
714 let nf = Self::splat(E::from_int(k as thermite::LargeInt));
715
716 let tmp = p1;
717 p1 = x.mul_sube((nf + nf).mul_sube(p1, p1), nf.mul_sube(p0, p0)) / nf;
718 p0 = tmp;
719
720 k += 1;
721 }
722
723 return p1;
724 }
725 _ => {}
726 }
727
728 let jacobi = Self::jacobi::<P>(x, Self::ZERO, Self::ZERO, n, m);
729
730 let x12 = x.nmul_adde(x, Self::ONE); if m & 1 == 0 {
733 jacobi * Self::powi::<P>(x12, (m >> 1) as i32)
734 } else {
735 -jacobi * Self::powi::<P>(x12, m as i32).sqrt()
737 }
738 }
739
740 fn lambert_w<P: Policy>(self) -> (Self, Self);
741
742 }
745
746pub use generic::elliptic::{
753 CarlsonKind, CarlsonRc, CarlsonRd, CarlsonRf, CarlsonRg, CarlsonRj, EllintD, EllintDInc, EllintE, EllintEInc,
754 EllintF, EllintK, EllintPi, EllintPiInc, EllipticConsts, EllipticKind, WrapTo,
755};
756
757pub trait SpecializedRealSpecialMath<E>: SpecializedSpecialMath<E> {
763 fn erfinv<P: Policy>(self) -> Self;
764 fn probit<P: Policy>(self) -> Self;
765
766 #[inline(always)]
767 fn gelu<P: Policy>(self, alpha: Self) -> Self {
768 let alpha_x = alpha * self;
769
770 let erf = alpha_x.scale(FloatConsts::FRAC_1_SQRT_2).erf_p::<P>();
772
773 if Self::HAS_TRUE_FMA {
774 let half_x = self.scale(E::ConstRatio::<1, 2>::VALUE);
776 half_x.mul_add(erf, half_x) } else {
778 self.scale(E::ConstRatio::<1, 2>::VALUE) * (Self::ONE + erf)
779 }
780 }
781
782 #[inline(always)]
783 fn swish<P: Policy>(self, beta: Self) -> Self {
784 let x = self;
785 let beta_x = beta * x;
786
787 let e = (-beta_x).exp_p::<P>();
789 let s = (Self::ONE + e).reciprocal_p::<P>();
790
791 x * s
792 }
793
794 fn lgamma_r<P: Policy>(self) -> (Self, Self);
795
796 #[inline(always)]
797 fn algebraic_sigmoid<P: Policy, const N: usize>(self) -> Self {
798 if const { N == 0 } {
799 return self; }
801
802 let pre_root = Self::ONE + self.abs().powi_p::<P>(N as i32); let denom = match N {
805 1 => pre_root,
806 2 => pre_root.sqrt(),
807 3 => pre_root.cbrt_p::<P>(),
808 4 if const { P::POLICY.precision.le(PrecisionPolicy::Average) } => pre_root.sqrt().sqrt(),
809 _ => {
810 let x = pre_root;
812
813 let mut y = x.powf_p::<CheckOverflow<LessPrecision<P>, false>>(Self::splat(
815 E::ONE / E::from_int(N as thermite::LargeInt),
816 ));
817
818 let y_n = y.powi_p::<P>(N as i32);
820
821 let np1 = Self::splat(E::from_int((N + 1) as thermite::LargeInt));
822 let nm1 = Self::splat(E::from_int((N - 1) as thermite::LargeInt));
823
824 let n = y * (x - y_n); let d = y_n.mul_adde(np1, x * nm1);
826
827 y += (n + n) / d;
828
829 y
830 }
831 };
832
833 let mut y = if const { P::POLICY.precision.lt(PrecisionPolicy::Average) } {
836 self * denom.reciprocal_p::<P>()
839 } else {
840 self / denom
841 };
842
843 if const { P::POLICY.check_overflow } {
844 y = pre_root.is_infinite().select(self.signum(), y);
845 }
846
847 y
848 }
849
850 #[inline(always)]
857 fn algebraic_swish<P: Policy>(self) -> Self {
858 let x = self;
859
860 if const { Self::HAS_TRUE_FMA } {
861 if const { Self::HAS_APPROX_RSQRT } {
864 let a = x.mul_add(x, Self::ONE);
865 let y0 = a.rsqrt();
866 let ay2 = a * y0 * y0;
867 let ch = ay2.nmul_add(Self::HALF, Self::splat(<E as FloatElement>::ConstRatio::<3, 2>::VALUE));
868 let r_inv = y0 * ch; let q = x * r_inv;
870 let xh = Self::HALF * x;
871 q.mul_add(xh, xh)
872 } else {
873 let a = x.mul_add(x, Self::ONE);
874 let q = x / a.sqrt();
875 let xh = x * Self::HALF;
876 q.mul_add(xh, xh)
877 }
878 } else if const { Self::HAS_APPROX_RCP } {
879 let a = x * x + Self::ONE;
880 let y0 = a.rsqrt();
881 let ay2 = a * y0 * y0;
882 let c = Self::splat(<E as FloatElement>::ConstInt::<3>::VALUE) - ay2;
883 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
887 } else {
888 let a = x * x + Self::ONE;
889 let q = x / a.sqrt();
890 let q1 = q + Self::ONE;
891 x * Self::HALF * q1
892 }
893 }
894
895 #[inline(always)]
896 fn gaussian_integral<P: Policy>(x0: Self, x1: Self, a: Self, c: Self) -> Self {
897 let common = Self::SQRT_FRAC_PI_2 * a * c;
899 let denom = Self::SQRT_2 * c;
900
901 let (a1, a0) = if const { P::POLICY.precision.le(PrecisionPolicy::Medium) } {
902 let d = denom.reciprocal_p::<P>();
903 (x1 * d, x0 * d)
904 } else {
905 (x1 / denom, x0 / denom)
906 };
907
908 common * (a1.erf_p::<P>() - a0.erf_p::<P>())
909 }
910}
911
912pub trait SpecializedRealPrimalMath<E>: SpecializedRealSpecialMath<E> {
919 #[inline(always)]
920 fn softplus_d<P: Policy>(self, k: Self, rcp_k: Self) -> (Self, Self) {
921 if const { P::POLICY.precision.lt(PrecisionPolicy::Average) } {
922 let k = k.scale(FloatConsts::LOG2_E);
923 let rcp_k = rcp_k.scale(FloatConsts::LN_2);
924
925 let kx = self * k;
926
927 let e = kx.abs().neg().exp2_p::<CheckOverflow<P, true>>();
928 let y = (Self::ONE + e).log2_p::<P>().mul_adde(rcp_k, self.max(Self::ZERO));
929
930 let rcp = (Self::ONE + e).reciprocal_p::<P>();
931 let dy = kx.select_negative(e * rcp, rcp);
932
933 return (y, dy);
934 }
935
936 let kx = self * k;
937
938 let e = kx.abs().neg().exp_p::<P>();
939
940 let y = e.ln_1p_p::<P>().mul_adde(rcp_k, self.max(Self::ZERO));
942
943 let rcp = (e + Self::ONE).reciprocal_p::<P>();
945 let dy = kx.select_negative(e * rcp, rcp);
946
947 (y, dy)
948 }
949
950 #[inline(always)]
951 fn gelu_d<P: Policy>(self, alpha: Self) -> (Self, Self) {
952 let alpha_x = alpha * self;
953
954 let erf = alpha_x.scale(FloatConsts::FRAC_1_SQRT_2).erf_p::<P>();
956
957 let y = if Self::HAS_TRUE_FMA {
958 let half_x = self.scale(E::ConstRatio::<1, 2>::VALUE);
959 half_x.mul_add(erf, half_x) } else {
961 self.scale(E::ConstRatio::<1, 2>::VALUE) * (Self::ONE + erf)
962 };
963
964 let dy = (alpha_x * alpha_x)
965 .scale(E::ConstRatio::<{ -1 }, 2>::VALUE)
966 .exp_p::<P>()
967 .scale(FloatConsts::FRAC_1_SQRT_TAU);
968
969 (y, dy.mul_adde(alpha_x, y))
970 }
971
972 #[inline(always)]
973 fn swish_d<P: Policy>(self, beta: Self) -> (Self, Self) {
974 let x = self;
975 let beta_x = beta * x;
976
977 let e = (-beta_x).exp_p::<P>();
978 let s = (Self::ONE + e).reciprocal_p::<P>();
979
980 let y = x * s;
981
982 let dy = (beta * y).mul_adde(e * s, s);
984
985 (y, dy)
986 }
987
988 #[inline(always)]
989 fn algebraic_sigmoid_d<P: Policy, const N: usize>(self) -> (Self, Self) {
990 if const { N == 0 } {
991 return (self, Self::ONE); }
993
994 let pre_root = Self::ONE + self.abs().powi_p::<P>(N as i32); let denom = match N {
997 1 => pre_root,
998 2 => pre_root.sqrt(),
999 3 => pre_root.cbrt_p::<P>(),
1000 4 if const { P::POLICY.precision.le(PrecisionPolicy::Average) } => pre_root.sqrt().sqrt(),
1001 _ => {
1002 let x = pre_root;
1003
1004 let mut y = x.powf_p::<CheckOverflow<LessPrecision<P>, false>>(Self::splat(
1005 E::ONE / E::from_int(N as thermite::LargeInt),
1006 ));
1007
1008 let y_n = y.powi_p::<P>(N as i32);
1009
1010 let np1 = Self::splat(E::from_int((N + 1) as thermite::LargeInt));
1011 let nm1 = Self::splat(E::from_int((N - 1) as thermite::LargeInt));
1012
1013 let n = y * (x - y_n); let d = y_n.mul_adde(np1, x * nm1);
1015
1016 y += (n + n) / d;
1017
1018 y
1019 }
1020 };
1021
1022 let mut y;
1024 let mut dy;
1025
1026 if const { P::POLICY.precision.lt(PrecisionPolicy::Average) } {
1027 let inv_denom = denom.reciprocal_p::<P>();
1028 y = self * inv_denom;
1029 dy = inv_denom / pre_root;
1030 } else {
1031 y = self / denom;
1032 dy = (pre_root * denom).reciprocal_p::<P>();
1033 }
1034
1035 if const { P::POLICY.check_overflow } {
1036 let is_infinite = pre_root.is_infinite();
1037
1038 y = is_infinite.select(self.signum(), y);
1039 dy = dy.nz(is_infinite); }
1041
1042 (y, dy)
1043 }
1044
1045 #[inline(always)]
1046 fn algebraic_swish_d<P: Policy>(self) -> (Self, Self) {
1047 let x = self;
1048
1049 if const { Self::HAS_TRUE_FMA } {
1050 if const { Self::HAS_APPROX_RSQRT } {
1051 let a = x.mul_add(x, Self::ONE);
1052 let y0 = a.rsqrt();
1053 let ay2 = a * y0 * y0;
1054 let ch = ay2.nmul_add(Self::HALF, Self::splat(<E as FloatElement>::ConstRatio::<3, 2>::VALUE));
1055 let r_inv = y0 * ch; let q = x * r_inv;
1057 let xh = Self::HALF * x;
1058 let y = q.mul_add(xh, xh);
1059
1060 let inv_a = r_inv * r_inv;
1061 let qa = q.mul_add(inv_a, q); let dy = qa.mul_add(Self::HALF, Self::HALF); (y, dy)
1065 } else {
1066 let a = x.mul_add(x, Self::ONE);
1067 let q = x / a.sqrt();
1068 let xh = x * Self::HALF;
1069 let y = q.mul_add(xh, xh);
1070
1071 let inv_a = a.reciprocal_p::<P>();
1072 let qa = q.mul_add(inv_a, q);
1073 let dy = qa.mul_add(Self::HALF, Self::HALF);
1074
1075 (y, dy)
1076 }
1077 } else if const { Self::HAS_APPROX_RCP } {
1078 let a = x * x + Self::ONE;
1079 let y0 = a.rsqrt();
1080 let ay2 = a * y0 * y0;
1081 let c = Self::splat(<E as FloatElement>::ConstInt::<3>::VALUE) - ay2;
1082 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;
1086
1087 let inv_a = Self::splat(<E as FloatElement>::ConstRatio::<1, 4>::VALUE) * (r_inv_2 * r_inv_2);
1088 let dy = w + hxy1 * inv_a;
1089
1090 (y, dy)
1091 } else {
1092 let a = x * x + Self::ONE;
1093 let q = x / a.sqrt();
1094 let q1 = q + Self::ONE;
1095 let y = x * Self::HALF * q1;
1096
1097 let dy = Self::HALF * (q1 + q / a);
1098
1099 (y, dy)
1100 }
1101 }
1102}