use crate::dual::forward_elementary_methods;
use std::fmt::{self, Display};
use crate::kernels;
use crate::Float;
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct DualVec<F: Float, const N: usize> {
pub re: F,
pub eps: [F; N],
}
impl<F: Float, const N: usize> Default for DualVec<F, N> {
fn default() -> Self {
Self::constant(F::zero())
}
}
impl<F: Float, const N: usize> Display for DualVec<F, N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.re)?;
for (i, e) in self.eps.iter().enumerate() {
write!(f, " + {e}\u{03b5}{i}")?;
}
Ok(())
}
}
impl<F: Float, const N: usize> From<F> for DualVec<F, N> {
#[inline]
fn from(val: F) -> Self {
DualVec::constant(val)
}
}
impl<F: Float, const N: usize> DualVec<F, N> {
#[inline]
#[must_use]
pub fn new(re: F, eps: [F; N]) -> Self {
DualVec { re, eps }
}
#[inline]
#[must_use]
pub fn constant(re: F) -> Self {
DualVec {
re,
eps: [F::zero(); N],
}
}
#[inline]
#[must_use]
pub fn with_tangent(re: F, lane: usize) -> Self {
DualVec {
re,
eps: std::array::from_fn(|k| if k == lane { F::one() } else { F::zero() }),
}
}
#[inline]
fn chain(self, f_val: F, f_deriv: F) -> Self {
DualVec {
re: f_val,
eps: std::array::from_fn(|k| {
if self.eps[k].is_all_zero() {
F::zero()
} else {
self.eps[k] * f_deriv
}
}),
}
}
forward_elementary_methods!();
#[inline]
pub fn powf(self, n: Self) -> Self {
if n.eps.iter().all(|e| e.is_all_zero()) {
if let Some(ni) = n.re.to_i32() {
if F::from(ni).unwrap() == n.re {
return self.powi(ni);
}
}
}
if n.re == F::zero() {
let dy = if self.re > F::zero() {
self.re.ln()
} else {
F::zero()
};
return DualVec {
re: F::one(),
eps: std::array::from_fn(|k| {
if n.eps[k].is_all_zero() {
F::zero()
} else {
dy * n.eps[k]
}
}),
};
}
let val = self.re.powf(n.re);
let dx_factor =
if self.re == F::zero() || val == F::zero() || !self.re.is_finite() || !val.is_finite()
{
n.re * self.re.powf(n.re - F::one())
} else {
n.re * val / self.re
};
let dy_factor = if val == F::zero() || self.re <= F::zero() {
F::zero()
} else {
val * self.re.ln()
};
DualVec {
re: val,
eps: std::array::from_fn(|k| {
let dx = if self.eps[k].is_all_zero() {
F::zero()
} else {
dx_factor * self.eps[k]
};
let dy = if n.eps[k].is_all_zero() {
F::zero()
} else {
dy_factor * n.eps[k]
};
dx + dy
}),
}
}
#[inline]
pub fn atan2(self, other: Self) -> Self {
let (d_self, d_other) = kernels::atan2_partials(self.re, other.re);
DualVec {
re: self.re.atan2(other.re),
eps: std::array::from_fn(|k| d_self * self.eps[k] + d_other * other.eps[k]),
}
}
#[inline]
pub fn mul_add(self, a: Self, b: Self) -> Self {
DualVec {
re: self.re.mul_add(a.re, b.re),
eps: std::array::from_fn(|k| self.eps[k] * a.re + self.re * a.eps[k] + b.eps[k]),
}
}
#[inline]
pub fn hypot(self, other: Self) -> Self {
let h = self.re.hypot(other.re);
if h == F::zero() {
return Self::constant(h);
}
let (da, db) = kernels::hypot_partials(self.re, other.re, h);
DualVec {
re: h,
eps: std::array::from_fn(|k| da * self.eps[k] + db * other.eps[k]),
}
}
}