use core::cmp::Ordering;
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use crate::scalar::Numeric;
#[derive(Debug, Clone, Copy)]
pub struct Dual<T: Numeric> {
pub value: T,
pub deriv: T,
}
impl<T: Numeric> Dual<T> {
#[inline]
pub fn new(value: T, deriv: T) -> Self {
Dual { value, deriv }
}
#[inline]
pub fn constant(value: T) -> Self {
Dual {
value,
deriv: T::ZERO,
}
}
#[inline]
pub fn variable(value: T) -> Self {
Dual {
value,
deriv: T::ONE,
}
}
}
impl<T: Numeric> Add for Dual<T> {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
Dual {
value: self.value + rhs.value,
deriv: self.deriv + rhs.deriv,
}
}
}
impl<T: Numeric> Sub for Dual<T> {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
Dual {
value: self.value - rhs.value,
deriv: self.deriv - rhs.deriv,
}
}
}
impl<T: Numeric> Mul for Dual<T> {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
Dual {
value: self.value * rhs.value,
deriv: self.value * rhs.deriv + self.deriv * rhs.value,
}
}
}
impl<T: Numeric> Div for Dual<T> {
type Output = Self;
#[inline]
fn div(self, rhs: Self) -> Self {
Dual {
value: self.value / rhs.value,
deriv: (self.deriv * rhs.value - self.value * rhs.deriv) / (rhs.value * rhs.value),
}
}
}
impl<T: Numeric> Neg for Dual<T> {
type Output = Self;
#[inline]
fn neg(self) -> Self {
Dual {
value: -self.value,
deriv: -self.deriv,
}
}
}
impl<T: Numeric> AddAssign for Dual<T> {
#[inline]
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl<T: Numeric> SubAssign for Dual<T> {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl<T: Numeric> MulAssign for Dual<T> {
#[inline]
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl<T: Numeric> DivAssign for Dual<T> {
#[inline]
fn div_assign(&mut self, rhs: Self) {
*self = *self / rhs;
}
}
impl<T: Numeric> PartialEq for Dual<T> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl<T: Numeric> PartialOrd for Dual<T> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.value.partial_cmp(&other.value)
}
}
impl<T: Numeric> Numeric for Dual<T> {
const ZERO: Self = Dual {
value: T::ZERO,
deriv: T::ZERO,
};
const ONE: Self = Dual {
value: T::ONE,
deriv: T::ZERO,
};
const TWO: Self = Dual {
value: T::TWO,
deriv: T::ZERO,
};
const HALF: Self = Dual {
value: T::HALF,
deriv: T::ZERO,
};
const PI: Self = Dual {
value: T::PI,
deriv: T::ZERO,
};
const EPSILON: Self = Dual {
value: T::EPSILON,
deriv: T::ZERO,
};
const NAN: Self = Dual {
value: T::NAN,
deriv: T::ZERO,
};
const INFINITY: Self = Dual {
value: T::INFINITY,
deriv: T::ZERO,
};
const NEG_INFINITY: Self = Dual {
value: T::NEG_INFINITY,
deriv: T::ZERO,
};
const MAX: Self = Dual {
value: T::MAX,
deriv: T::ZERO,
};
const MIN_POSITIVE: Self = Dual {
value: T::MIN_POSITIVE,
deriv: T::ZERO,
};
#[inline]
fn from_f64(value: f64) -> Self {
Dual::constant(T::from_f64(value))
}
#[inline]
fn from_u64(value: u64) -> Self {
Dual::constant(T::from_u64(value))
}
#[inline]
fn from_usize(value: usize) -> Self {
Dual::constant(T::from_usize(value))
}
#[inline]
fn abs(self) -> Self {
let deriv = if self.value < T::ZERO {
-self.deriv
} else {
self.deriv
};
Dual {
value: self.value.abs(),
deriv,
}
}
#[inline]
fn sqrt(self) -> Self {
let root = self.value.sqrt();
Dual {
value: root,
deriv: self.deriv / (T::TWO * root),
}
}
#[inline]
fn sin(self) -> Self {
Dual {
value: self.value.sin(),
deriv: self.value.cos() * self.deriv,
}
}
#[inline]
fn cos(self) -> Self {
Dual {
value: self.value.cos(),
deriv: -(self.value.sin()) * self.deriv,
}
}
#[inline]
fn tan(self) -> Self {
let t = self.value.tan();
Dual {
value: t,
deriv: (T::ONE + t * t) * self.deriv,
}
}
#[inline]
fn exp(self) -> Self {
let e = self.value.exp();
Dual {
value: e,
deriv: e * self.deriv,
}
}
#[inline]
fn ln(self) -> Self {
Dual {
value: self.value.ln(),
deriv: self.deriv / self.value,
}
}
#[inline]
fn is_nan(self) -> bool {
self.value.is_nan()
}
#[inline]
fn is_finite(self) -> bool {
self.value.is_finite()
}
}