use thermite::math::FloatConsts;
use thermite::math::RealMathWithPolicy;
use thermite::math::algorithms::reduce_in_place;
use thermite::math::policy::Policy;
use thermite::math::specialized::{
SpecializedCoreMath, SpecializedRealMath, SpecializedSpatialMath, SpecializedTranscendentalMath,
};
use thermite::prelude::*;
use thermite::vector::AsFloatVectorWithBitsKernel;
use crate::Dual;
use crate::vector::DualFloatVector;
pub trait DualMathVector: DualFloatVector + RealMathWithPolicy + FloatConsts {}
impl<V> DualMathVector for V where V: DualFloatVector + RealMathWithPolicy + FloatConsts {}
impl<V: DualMathVector, const N: usize> SpecializedCoreMath<Dual<V::Element, N>> for Dual<V, N> {
#[inline(always)]
fn inverse_sqrt<P: Policy>(self) -> Self {
let r = self.re.inverse_sqrt_p::<P>();
self.chain(r, (V::HALF * r * r * r).neg())
}
}
impl<V: DualMathVector, const N: usize> SpecializedTranscendentalMath<Dual<V::Element, N>> for Dual<V, N> {
#[inline(always)]
fn sin_cos<P: Policy>(self) -> (Self, Self) {
let (s, c) = self.re.sin_cos_p::<P>();
(self.chain(s, c), self.chain(c, s.neg()))
}
#[inline(always)]
fn tan<P: Policy>(self) -> Self {
let t = self.re.tan_p::<P>();
self.chain(t, t.mul_adde(t, V::ONE))
}
#[inline(always)]
fn sincos_pi<P: Policy>(self) -> (Self, Self) {
let (s, c) = self.re.sincos_pi_p::<P>();
let pi = V::PI;
(self.chain(s, pi * c), self.chain(c, (pi * s).neg()))
}
#[inline(always)]
fn tan_pi<P: Policy>(self) -> Self {
let t = self.re.tan_pi_p::<P>();
self.chain(t, V::PI * t.mul_adde(t, V::ONE))
}
#[inline(always)]
fn sinh_cosh<P: Policy>(self) -> (Self, Self) {
let (sh, ch) = self.re.sinh_cosh_p::<P>();
(self.chain(sh, ch), self.chain(ch, sh))
}
#[inline(always)]
fn tanh<P: Policy>(self) -> Self {
let v = self.re.tanh_p::<P>();
self.chain(v, v.nmul_adde(v, V::ONE))
}
#[inline(always)]
fn asin<P: Policy>(self) -> Self {
let v = self.re.asin_p::<P>();
self.chain(v, self.re.nmul_adde(self.re, V::ONE).inverse_sqrt_p::<P>())
}
#[inline(always)]
fn acos<P: Policy>(self) -> Self {
let v = self.re.acos_p::<P>();
self.chain(v, self.re.nmul_adde(self.re, V::ONE).inverse_sqrt_p::<P>().neg())
}
#[inline(always)]
fn atan<P: Policy>(self) -> Self {
let v = self.re.atan_p::<P>();
self.chain(v, self.re.mul_adde(self.re, V::ONE).reciprocal_p::<P>())
}
#[inline(always)]
fn asinh<P: Policy>(self) -> Self {
let v = self.re.asinh_p::<P>();
self.chain(v, self.re.mul_adde(self.re, V::ONE).inverse_sqrt_p::<P>())
}
#[inline(always)]
fn acosh<P: Policy>(self) -> Self {
let v = self.re.acosh_p::<P>();
self.chain(v, self.re.mul_sube(self.re, V::ONE).inverse_sqrt_p::<P>())
}
#[inline(always)]
fn atanh<P: Policy>(self) -> Self {
let v = self.re.atanh_p::<P>();
self.chain(v, self.re.nmul_adde(self.re, V::ONE).reciprocal_p::<P>())
}
#[inline(always)]
fn exp<P: Policy>(self) -> Self {
let v = self.re.exp_p::<P>();
self.chain(v, v)
}
#[inline(always)]
fn exph<P: Policy>(self) -> Self {
let v = self.re.exph_p::<P>();
self.chain(v, v)
}
#[inline(always)]
fn exp2<P: Policy>(self) -> Self {
let v = self.re.exp2_p::<P>();
self.chain(v, V::LN_2 * v)
}
#[inline(always)]
fn exp10<P: Policy>(self) -> Self {
let v = self.re.exp10_p::<P>();
self.chain(v, V::LN_10 * v)
}
#[inline(always)]
fn exp_m1<P: Policy>(self) -> Self {
let v = self.re.exp_m1_p::<P>();
self.chain(v, v + V::ONE)
}
#[inline(always)]
fn exp2_m1<P: Policy>(self) -> Self {
let v = self.re.exp2_m1_p::<P>();
self.chain(v, v.mul_adde(V::LN_2, V::LN_2))
}
#[inline(always)]
fn exp10_m1<P: Policy>(self) -> Self {
let v = self.re.exp10_m1_p::<P>();
self.chain(v, v.mul_adde(V::LN_10, V::LN_10))
}
#[inline(always)]
fn powf<P: Policy>(self, e: Self) -> Self {
let v = self.re.powf_p::<P>(e.re);
let a = e.re * v / self.re;
struct ExpIsConstKernel;
impl<O: FloatVector, const N: usize> AsFloatVectorWithBitsKernel<O, N> for ExpIsConstKernel {
type Output = bool;
#[inline(always)]
fn with_bits<
W: FloatVectorWithBits<
Element = O::Element,
Lanes = O::Lanes,
Mask = O::Mask,
Signed = O::Signed,
Unsigned = O::Unsigned,
ExtendedPrecision = O::ExtendedPrecision,
> + CastVector<O>,
>(
self,
mut v: [W; N],
) -> bool {
reduce_in_place(&mut v, |x, y| x | y);
v[0].is_all_zero()
}
}
let exp_is_const = match <V as FloatVector>::with_bits(e.dual, ExpIsConstKernel) {
Some(is_const) => is_const,
None => {
let mut is_const = true;
let mut i = 0;
while i < N {
is_const &= e.dual[i].is_all_zero();
i += 1;
}
is_const
}
};
if thermite::likely(exp_is_const) {
return self.chain(v, a);
}
let b = (v * self.re.ln_p::<P>()).nz(self.re.cmp_le(V::ZERO));
let mut dual = self.dual;
let mut i = 0;
while i < N {
dual[i] = a.mul_adde(self.dual[i], b * e.dual[i]);
i += 1;
}
Dual { re: v, dual }
}
#[inline(always)]
fn cbrt<P: Policy>(self) -> Self {
let v = self.re.cbrt_p::<P>();
let three: V = thermite::const_splat!(int <V::Element>: 3);
self.chain(v, (three * v * v).reciprocal_p::<P>())
}
#[inline(always)]
fn nth_root<P: Policy, const M: usize>(self) -> Self {
let v = self.re.nth_root_p::<P, M>();
let m_v = V::splat(<V::Element as FloatElement>::from_int(M as thermite::LargeInt));
self.chain(v, v / (m_v * self.re))
}
#[inline(always)]
fn ln<P: Policy>(self) -> Self {
let v = self.re.ln_p::<P>();
self.chain(v, self.re.reciprocal_p::<P>())
}
#[inline(always)]
fn ln_1p<P: Policy>(self) -> Self {
let v = self.re.ln_1p_p::<P>();
self.chain(v, (V::ONE + self.re).reciprocal_p::<P>())
}
#[inline(always)]
fn log2<P: Policy>(self) -> Self {
let v = self.re.log2_p::<P>();
self.chain(v, self.re.reciprocal_p::<P>() * V::LOG2_E)
}
#[inline(always)]
fn log10<P: Policy>(self) -> Self {
let v = self.re.log10_p::<P>();
self.chain(v, self.re.reciprocal_p::<P>() * V::LOG10_E)
}
#[inline(always)]
fn log2_p1<P: Policy>(self) -> Self {
let v = self.re.log2_p1_p::<P>();
self.chain(v, (V::ONE + self.re).reciprocal_p::<P>() * V::LOG2_E)
}
#[inline(always)]
fn log10_p1<P: Policy>(self) -> Self {
let v = self.re.log10_p1_p::<P>();
self.chain(v, (V::ONE + self.re).reciprocal_p::<P>() * V::LOG10_E)
}
#[inline(always)]
fn log_n<P: Policy, const M: usize>(self) -> Self {
let v = self.re.log_n_p::<P, M>();
let ln_m = V::splat(<V::Element as FloatElement>::from_int(M as thermite::LargeInt)).ln_p::<P>();
self.chain(v, (self.re * ln_m).reciprocal_p::<P>())
}
#[inline(always)]
fn sinc<P: Policy>(self) -> Self {
let v = self.re.sinc_p::<P>();
let c = self.re.cos_p::<P>();
let factor = (c - v) / self.re;
let factor = self.re.is_zero().select(V::ZERO, factor);
self.chain(v, factor)
}
#[inline(always)]
fn sinc_pi<P: Policy>(self) -> Self {
let v = self.re.sinc_pi_p::<P>();
let c = self.re.cos_pi_p::<P>();
let factor = (c - v) / self.re;
let factor = self.re.is_zero().select(V::ZERO, factor);
self.chain(v, factor)
}
#[inline(always)]
fn ln1m_expnx<P: Policy>(self) -> Self {
let v = self.re.ln1m_expnx_p::<P>();
self.chain(v, self.re.exp_m1_p::<P>().reciprocal_p::<P>())
}
#[inline(always)]
fn ln1m_expnx_ext<P: Policy>(self, lnx: Self) -> Self {
let v = self.re.ln1m_expnx_ext_p::<P>(lnx.re);
self.chain(v, self.re.exp_m1_p::<P>().reciprocal_p::<P>())
}
}
impl<V: DualMathVector, const N: usize> SpecializedSpatialMath<Dual<V::Element, N>> for Dual<V, N> {
#[inline(always)]
fn l1_norm<P: Policy>(self) -> Self {
self.abs()
}
#[inline(always)]
fn l2_norm_squared<P: Policy>(self) -> Self {
self * self
}
#[inline(always)]
fn l2_norm<P: Policy>(self) -> Self {
self.abs()
}
#[inline(always)]
fn hypot_n<P: Policy, const K: usize>(values: [Self; K]) -> Self {
let mut re = [V::ZERO; K];
let mut k = 0;
while k < K {
re[k] = values[k].re;
k += 1;
}
let h = <V as thermite::math::SpatialMathWithPolicy>::hypot_n_p::<P, K>(re);
let inv = h.reciprocal_p::<P>().nz(h.is_zero());
let mut dual = [V::ZERO; N];
let mut i = 0;
while i < N {
let mut acc = V::ZERO;
let mut k = 0;
while k < K {
acc = re[k].mul_adde(values[k].dual[i], acc);
k += 1;
}
dual[i] = acc * inv;
i += 1;
}
Dual { re: h, dual }
}
#[inline(always)]
fn inv_hypot_n<P: Policy, const K: usize>(values: [Self; K]) -> Self {
let mut re = [V::ZERO; K];
let mut k = 0;
while k < K {
re[k] = values[k].re;
k += 1;
}
let ih = <V as thermite::math::SpatialMathWithPolicy>::inv_hypot_n_p::<P, K>(re);
let factor = (ih * ih * ih).neg();
let factor = factor.zz(factor.is_finite());
let mut dual = [V::ZERO; N];
let mut i = 0;
while i < N {
let mut acc = V::ZERO;
let mut k = 0;
while k < K {
acc = re[k].mul_adde(values[k].dual[i], acc);
k += 1;
}
dual[i] = factor * acc;
i += 1;
}
Dual { re: ih, dual }
}
}
impl<V: DualMathVector, const N: usize> SpecializedRealMath<Dual<V::Element, N>> for Dual<V, N> {
#[inline(always)]
fn atan2<P: Policy>(self, x: Self) -> Self {
let v = self.re.atan2_p::<P>(x.re);
let denom = self.re.mul_adde(self.re, x.re * x.re);
let inv = denom.reciprocal_p::<P>();
let mut dual = self.dual;
let mut i = 0;
while i < N {
dual[i] = self.re.nmul_adde(x.dual[i], x.re * self.dual[i]) * inv;
i += 1;
}
Dual { re: v, dual }
}
#[inline(always)]
fn inverse_smoothstep<P: Policy, const M: usize>(y: Self, edges: Option<(Self, Self)>) -> Self {
let edges_re = edges.map(|(a, b)| (a.re, b.re));
let t = y.re.inverse_smoothstep_p::<P, M>(edges_re);
let dprime = t.smoothstep_derivative_p::<P, M>(edges_re);
y.chain(t, dprime.reciprocal_p::<P>())
}
}