use crate::passive::Passive;
use std::fmt;
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
#[derive(Clone, Copy)]
pub struct Jet2<T: Passive> {
value: T,
d1: T,
d2: T,
}
impl<T: Passive> Jet2<T> {
#[inline]
pub fn new(value: T, d1: T, d2: T) -> Self {
Jet2 { value, d1, d2 }
}
#[inline]
pub fn constant(value: T) -> Self {
Jet2 {
value,
d1: T::zero(),
d2: T::zero(),
}
}
#[inline]
pub fn variable(value: T) -> Self {
Jet2 {
value,
d1: T::one(),
d2: T::zero(),
}
}
#[inline]
pub fn value(&self) -> T {
self.value
}
#[inline]
pub fn first_derivative(&self) -> T {
self.d1
}
#[inline]
pub fn second_derivative(&self) -> T {
self.d2
}
pub fn powf(self, n: T) -> Jet2<T> {
let v = self.value;
let two = T::from(2.0).unwrap();
let vn = v.powf(n);
let gp = n * v.powf(n - T::one());
let gpp = n * (n - T::one()) * v.powf(n - two);
Jet2 {
value: vn,
d1: gp * self.d1,
d2: gpp * self.d1 * self.d1 + gp * self.d2,
}
}
pub fn exp(self) -> Jet2<T> {
let e = self.value.exp();
Jet2 {
value: e,
d1: e * self.d1,
d2: e * self.d1 * self.d1 + e * self.d2,
}
}
pub fn ln(self) -> Jet2<T> {
let v = self.value;
let inv = T::one() / v;
let inv2 = inv * inv;
Jet2 {
value: v.ln(),
d1: inv * self.d1,
d2: -inv2 * self.d1 * self.d1 + inv * self.d2,
}
}
pub fn sqrt(self) -> Jet2<T> {
let v = self.value;
let s = v.sqrt();
let half = T::from(0.5).unwrap();
let quarter = T::from(0.25).unwrap();
let gp = half / s;
let gpp = -quarter / (s * v);
Jet2 {
value: s,
d1: gp * self.d1,
d2: gpp * self.d1 * self.d1 + gp * self.d2,
}
}
pub fn sin(self) -> Jet2<T> {
let v = self.value;
let (s, c) = (v.sin(), v.cos());
Jet2 {
value: s,
d1: c * self.d1,
d2: -s * self.d1 * self.d1 + c * self.d2,
}
}
pub fn cos(self) -> Jet2<T> {
let v = self.value;
let (s, c) = (v.sin(), v.cos());
Jet2 {
value: c,
d1: -s * self.d1,
d2: -c * self.d1 * self.d1 - s * self.d2,
}
}
pub fn erf(self) -> Jet2<T> {
let v = self.value;
let r = crate::math::erf(v);
let two = T::from(2.0).unwrap();
let two_over_sqrt_pi =
T::from(std::f64::consts::FRAC_2_SQRT_PI).unwrap();
let gp = two_over_sqrt_pi * (-v * v).exp();
let gpp = -two * v * gp;
Jet2 {
value: r,
d1: gp * self.d1,
d2: gpp * self.d1 * self.d1 + gp * self.d2,
}
}
pub fn norm_cdf(self) -> Jet2<T> {
let v = self.value;
let r = crate::math::norm_cdf(v);
let gp = crate::math::norm_pdf(v);
let gpp = -v * gp;
Jet2 {
value: r,
d1: gp * self.d1,
d2: gpp * self.d1 * self.d1 + gp * self.d2,
}
}
pub fn inv_norm_cdf(self) -> Jet2<T> {
let v = self.value;
let r = crate::math::inv_norm_cdf(v);
let gp = T::one() / crate::math::norm_pdf(r);
let gpp = r * gp * gp;
Jet2 {
value: r,
d1: gp * self.d1,
d2: gpp * self.d1 * self.d1 + gp * self.d2,
}
}
}
impl<T: Passive> Add for Jet2<T> {
type Output = Jet2<T>;
#[inline]
fn add(self, rhs: Self) -> Self {
Jet2 {
value: self.value + rhs.value,
d1: self.d1 + rhs.d1,
d2: self.d2 + rhs.d2,
}
}
}
impl<T: Passive> Add<T> for Jet2<T> {
type Output = Jet2<T>;
#[inline]
fn add(self, rhs: T) -> Self {
Jet2 {
value: self.value + rhs,
d1: self.d1,
d2: self.d2,
}
}
}
impl Add<Jet2<f64>> for f64 {
type Output = Jet2<f64>;
#[inline]
fn add(self, rhs: Jet2<f64>) -> Jet2<f64> {
Jet2 {
value: self + rhs.value,
d1: rhs.d1,
d2: rhs.d2,
}
}
}
impl Add<Jet2<f32>> for f32 {
type Output = Jet2<f32>;
#[inline]
fn add(self, rhs: Jet2<f32>) -> Jet2<f32> {
Jet2 {
value: self + rhs.value,
d1: rhs.d1,
d2: rhs.d2,
}
}
}
impl<T: Passive> Sub for Jet2<T> {
type Output = Jet2<T>;
#[inline]
fn sub(self, rhs: Self) -> Self {
Jet2 {
value: self.value - rhs.value,
d1: self.d1 - rhs.d1,
d2: self.d2 - rhs.d2,
}
}
}
impl<T: Passive> Sub<T> for Jet2<T> {
type Output = Jet2<T>;
#[inline]
fn sub(self, rhs: T) -> Self {
Jet2 {
value: self.value - rhs,
d1: self.d1,
d2: self.d2,
}
}
}
impl Sub<Jet2<f64>> for f64 {
type Output = Jet2<f64>;
#[inline]
fn sub(self, rhs: Jet2<f64>) -> Jet2<f64> {
Jet2 {
value: self - rhs.value,
d1: -rhs.d1,
d2: -rhs.d2,
}
}
}
impl Sub<Jet2<f32>> for f32 {
type Output = Jet2<f32>;
#[inline]
fn sub(self, rhs: Jet2<f32>) -> Jet2<f32> {
Jet2 {
value: self - rhs.value,
d1: -rhs.d1,
d2: -rhs.d2,
}
}
}
impl<T: Passive> Mul for Jet2<T> {
type Output = Jet2<T>;
#[inline]
fn mul(self, rhs: Self) -> Self {
let two = T::from(2.0).unwrap();
Jet2 {
value: self.value * rhs.value,
d1: self.d1 * rhs.value + self.value * rhs.d1,
d2: self.d2 * rhs.value + two * self.d1 * rhs.d1 + self.value * rhs.d2,
}
}
}
impl<T: Passive> Mul<T> for Jet2<T> {
type Output = Jet2<T>;
#[inline]
fn mul(self, rhs: T) -> Self {
Jet2 {
value: self.value * rhs,
d1: self.d1 * rhs,
d2: self.d2 * rhs,
}
}
}
impl Mul<Jet2<f64>> for f64 {
type Output = Jet2<f64>;
#[inline]
fn mul(self, rhs: Jet2<f64>) -> Jet2<f64> {
Jet2 {
value: self * rhs.value,
d1: self * rhs.d1,
d2: self * rhs.d2,
}
}
}
impl Mul<Jet2<f32>> for f32 {
type Output = Jet2<f32>;
#[inline]
fn mul(self, rhs: Jet2<f32>) -> Jet2<f32> {
Jet2 {
value: self * rhs.value,
d1: self * rhs.d1,
d2: self * rhs.d2,
}
}
}
impl<T: Passive> Div for Jet2<T> {
type Output = Jet2<T>;
#[inline]
fn div(self, rhs: Self) -> Self {
let two = T::from(2.0).unwrap();
let inv_b = T::one() / rhs.value;
let inv_b2 = inv_b * inv_b;
let inv_b3 = inv_b2 * inv_b;
let recip = Jet2 {
value: inv_b,
d1: -rhs.d1 * inv_b2,
d2: two * rhs.d1 * rhs.d1 * inv_b3 - rhs.d2 * inv_b2,
};
self * recip
}
}
impl<T: Passive> Div<T> for Jet2<T> {
type Output = Jet2<T>;
#[inline]
fn div(self, rhs: T) -> Self {
let inv = T::one() / rhs;
Jet2 {
value: self.value * inv,
d1: self.d1 * inv,
d2: self.d2 * inv,
}
}
}
impl Div<Jet2<f64>> for f64 {
type Output = Jet2<f64>;
#[inline]
fn div(self, rhs: Jet2<f64>) -> Jet2<f64> {
Jet2::constant(self) / rhs
}
}
impl Div<Jet2<f32>> for f32 {
type Output = Jet2<f32>;
#[inline]
fn div(self, rhs: Jet2<f32>) -> Jet2<f32> {
Jet2::constant(self) / rhs
}
}
impl<T: Passive> Neg for Jet2<T> {
type Output = Jet2<T>;
#[inline]
fn neg(self) -> Self {
Jet2 {
value: -self.value,
d1: -self.d1,
d2: -self.d2,
}
}
}
impl<T: Passive> AddAssign for Jet2<T> {
#[inline]
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl<T: Passive> AddAssign<T> for Jet2<T> {
#[inline]
fn add_assign(&mut self, rhs: T) {
self.value = self.value + rhs;
}
}
impl<T: Passive> SubAssign for Jet2<T> {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl<T: Passive> SubAssign<T> for Jet2<T> {
#[inline]
fn sub_assign(&mut self, rhs: T) {
self.value = self.value - rhs;
}
}
impl<T: Passive> MulAssign for Jet2<T> {
#[inline]
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl<T: Passive> MulAssign<T> for Jet2<T> {
#[inline]
fn mul_assign(&mut self, rhs: T) {
*self = *self * rhs;
}
}
impl<T: Passive> DivAssign for Jet2<T> {
#[inline]
fn div_assign(&mut self, rhs: Self) {
*self = *self / rhs;
}
}
impl<T: Passive> DivAssign<T> for Jet2<T> {
#[inline]
fn div_assign(&mut self, rhs: T) {
*self = *self / rhs;
}
}
impl<T: Passive> fmt::Display for Jet2<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.value)
}
}
impl<T: Passive> fmt::Debug for Jet2<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Jet2(v={}, d1={}, d2={})",
self.value, self.d1, self.d2
)
}
}
impl<T: Passive> Default for Jet2<T> {
fn default() -> Self {
Jet2::constant(T::zero())
}
}
impl<T: Passive> From<T> for Jet2<T> {
fn from(value: T) -> Self {
Jet2::constant(value)
}
}
impl From<i32> for Jet2<f64> {
fn from(value: i32) -> Self {
Jet2::constant(value as f64)
}
}
impl From<i32> for Jet2<f32> {
fn from(value: i32) -> Self {
Jet2::constant(value as f32)
}
}
impl<T: Passive> PartialEq for Jet2<T> {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl<T: Passive> PartialOrd for Jet2<T> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.value.partial_cmp(&other.value)
}
}
#[derive(Clone)]
pub struct NamedJet2<T: Passive> {
pub(crate) inner: Jet2<T>,
pub(crate) seeded: Option<usize>,
#[cfg(debug_assertions)]
pub(crate) gen_id: u64,
}
impl<T: Passive> NamedJet2<T> {
#[inline]
pub(crate) fn __from_parts(inner: Jet2<T>, seeded: Option<usize>) -> Self {
Self {
inner,
seeded,
#[cfg(debug_assertions)]
gen_id: crate::forward_tape::current_gen(),
}
}
#[inline]
pub fn value(&self) -> T {
self.inner.value()
}
pub fn first_derivative(&self, name: &str) -> T {
let idx = crate::forward_tape::with_active_registry(|r| {
let r = r.expect(
"NamedJet2::first_derivative called outside a frozen NamedForwardTape scope",
);
r.index_of(name).unwrap_or_else(|| {
panic!(
"NamedJet2::first_derivative: name {:?} not present in registry",
name
)
})
});
if self.seeded == Some(idx) {
self.inner.first_derivative()
} else {
T::zero()
}
}
pub fn second_derivative(&self, name: &str) -> T {
let idx = crate::forward_tape::with_active_registry(|r| {
let r = r.expect(
"NamedJet2::second_derivative called outside a frozen NamedForwardTape scope",
);
r.index_of(name).unwrap_or_else(|| {
panic!(
"NamedJet2::second_derivative: name {:?} not present in registry",
name
)
})
});
if self.seeded == Some(idx) {
self.inner.second_derivative()
} else {
T::zero()
}
}
#[inline]
pub fn inner(&self) -> &Jet2<T> {
&self.inner
}
#[inline]
pub fn exp(&self) -> Self {
Self {
inner: self.inner.exp(),
seeded: self.seeded,
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
#[inline]
pub fn ln(&self) -> Self {
Self {
inner: self.inner.ln(),
seeded: self.seeded,
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
#[inline]
pub fn sqrt(&self) -> Self {
Self {
inner: self.inner.sqrt(),
seeded: self.seeded,
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
#[inline]
pub fn sin(&self) -> Self {
Self {
inner: self.inner.sin(),
seeded: self.seeded,
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
#[inline]
pub fn cos(&self) -> Self {
Self {
inner: self.inner.cos(),
seeded: self.seeded,
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
#[inline]
pub fn norm_cdf(&self) -> Self {
Self {
inner: self.inner.norm_cdf(),
seeded: self.seeded,
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
#[inline]
pub fn inv_norm_cdf(&self) -> Self {
Self {
inner: self.inner.inv_norm_cdf(),
seeded: self.seeded,
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
}
impl<T: Passive> fmt::Debug for NamedJet2<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("NamedJet2")
.field("value", &self.inner.value())
.field("first", &self.inner.first_derivative())
.field("second", &self.inner.second_derivative())
.field("seeded", &self.seeded)
.finish()
}
}
impl<T: Passive> fmt::Display for NamedJet2<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "NamedJet2({})", self.inner.value())
}
}
#[inline]
pub(crate) fn merge_seeded(a: Option<usize>, b: Option<usize>) -> Option<usize> {
match (a, b) {
(None, None) => None,
(Some(x), None) | (None, Some(x)) => Some(x),
(Some(x), Some(y)) if x == y => Some(x),
(Some(_), Some(_)) => {
#[cfg(debug_assertions)]
panic!(
"NamedJet2: operation between two differently-seeded variables; \
seeded Jet2 supports only one active direction"
);
#[cfg(not(debug_assertions))]
a
}
}
}
macro_rules! __named_d2_binop {
($trait_:ident, $method:ident, $op:tt) => {
impl<T: Passive> ::core::ops::$trait_<NamedJet2<T>> for NamedJet2<T> {
type Output = NamedJet2<T>;
#[inline]
fn $method(self, rhs: NamedJet2<T>) -> NamedJet2<T> {
#[cfg(debug_assertions)]
crate::forward_tape::check_gen(self.gen_id, rhs.gen_id);
NamedJet2 {
inner: self.inner $op rhs.inner,
seeded: merge_seeded(self.seeded, rhs.seeded),
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
}
impl<T: Passive> ::core::ops::$trait_<&NamedJet2<T>> for &NamedJet2<T> {
type Output = NamedJet2<T>;
#[inline]
fn $method(self, rhs: &NamedJet2<T>) -> NamedJet2<T> {
#[cfg(debug_assertions)]
crate::forward_tape::check_gen(self.gen_id, rhs.gen_id);
NamedJet2 {
inner: self.inner $op rhs.inner,
seeded: merge_seeded(self.seeded, rhs.seeded),
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
}
impl<T: Passive> ::core::ops::$trait_<&NamedJet2<T>> for NamedJet2<T> {
type Output = NamedJet2<T>;
#[inline]
fn $method(self, rhs: &NamedJet2<T>) -> NamedJet2<T> {
#[cfg(debug_assertions)]
crate::forward_tape::check_gen(self.gen_id, rhs.gen_id);
NamedJet2 {
inner: self.inner $op rhs.inner,
seeded: merge_seeded(self.seeded, rhs.seeded),
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
}
impl<T: Passive> ::core::ops::$trait_<NamedJet2<T>> for &NamedJet2<T> {
type Output = NamedJet2<T>;
#[inline]
fn $method(self, rhs: NamedJet2<T>) -> NamedJet2<T> {
#[cfg(debug_assertions)]
crate::forward_tape::check_gen(self.gen_id, rhs.gen_id);
NamedJet2 {
inner: self.inner $op rhs.inner,
seeded: merge_seeded(self.seeded, rhs.seeded),
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
}
impl<T: Passive> ::core::ops::$trait_<T> for NamedJet2<T> {
type Output = NamedJet2<T>;
#[inline]
fn $method(self, rhs: T) -> NamedJet2<T> {
NamedJet2 {
inner: self.inner $op rhs,
seeded: self.seeded,
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
}
impl<T: Passive> ::core::ops::$trait_<T> for &NamedJet2<T> {
type Output = NamedJet2<T>;
#[inline]
fn $method(self, rhs: T) -> NamedJet2<T> {
NamedJet2 {
inner: self.inner $op rhs,
seeded: self.seeded,
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
}
};
}
__named_d2_binop!(Add, add, +);
__named_d2_binop!(Sub, sub, -);
__named_d2_binop!(Mul, mul, *);
__named_d2_binop!(Div, div, /);
impl<T: Passive> ::core::ops::Neg for NamedJet2<T> {
type Output = NamedJet2<T>;
#[inline]
fn neg(self) -> NamedJet2<T> {
NamedJet2 {
inner: -self.inner,
seeded: self.seeded,
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
}
impl<T: Passive> ::core::ops::Neg for &NamedJet2<T> {
type Output = NamedJet2<T>;
#[inline]
fn neg(self) -> NamedJet2<T> {
NamedJet2 {
inner: -self.inner,
seeded: self.seeded,
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
}
macro_rules! __named_d2_scalar_lhs {
($scalar:ty) => {
impl ::core::ops::Add<NamedJet2<$scalar>> for $scalar {
type Output = NamedJet2<$scalar>;
#[inline]
fn add(self, rhs: NamedJet2<$scalar>) -> NamedJet2<$scalar> {
NamedJet2 {
inner: self + rhs.inner,
seeded: rhs.seeded,
#[cfg(debug_assertions)]
gen_id: rhs.gen_id,
}
}
}
impl ::core::ops::Add<&NamedJet2<$scalar>> for $scalar {
type Output = NamedJet2<$scalar>;
#[inline]
fn add(self, rhs: &NamedJet2<$scalar>) -> NamedJet2<$scalar> {
NamedJet2 {
inner: self + rhs.inner,
seeded: rhs.seeded,
#[cfg(debug_assertions)]
gen_id: rhs.gen_id,
}
}
}
impl ::core::ops::Sub<NamedJet2<$scalar>> for $scalar {
type Output = NamedJet2<$scalar>;
#[inline]
fn sub(self, rhs: NamedJet2<$scalar>) -> NamedJet2<$scalar> {
NamedJet2 {
inner: self - rhs.inner,
seeded: rhs.seeded,
#[cfg(debug_assertions)]
gen_id: rhs.gen_id,
}
}
}
impl ::core::ops::Sub<&NamedJet2<$scalar>> for $scalar {
type Output = NamedJet2<$scalar>;
#[inline]
fn sub(self, rhs: &NamedJet2<$scalar>) -> NamedJet2<$scalar> {
NamedJet2 {
inner: self - rhs.inner,
seeded: rhs.seeded,
#[cfg(debug_assertions)]
gen_id: rhs.gen_id,
}
}
}
impl ::core::ops::Mul<NamedJet2<$scalar>> for $scalar {
type Output = NamedJet2<$scalar>;
#[inline]
fn mul(self, rhs: NamedJet2<$scalar>) -> NamedJet2<$scalar> {
NamedJet2 {
inner: self * rhs.inner,
seeded: rhs.seeded,
#[cfg(debug_assertions)]
gen_id: rhs.gen_id,
}
}
}
impl ::core::ops::Mul<&NamedJet2<$scalar>> for $scalar {
type Output = NamedJet2<$scalar>;
#[inline]
fn mul(self, rhs: &NamedJet2<$scalar>) -> NamedJet2<$scalar> {
NamedJet2 {
inner: self * rhs.inner,
seeded: rhs.seeded,
#[cfg(debug_assertions)]
gen_id: rhs.gen_id,
}
}
}
impl ::core::ops::Div<NamedJet2<$scalar>> for $scalar {
type Output = NamedJet2<$scalar>;
#[inline]
fn div(self, rhs: NamedJet2<$scalar>) -> NamedJet2<$scalar> {
NamedJet2 {
inner: self / rhs.inner,
seeded: rhs.seeded,
#[cfg(debug_assertions)]
gen_id: rhs.gen_id,
}
}
}
impl ::core::ops::Div<&NamedJet2<$scalar>> for $scalar {
type Output = NamedJet2<$scalar>;
#[inline]
fn div(self, rhs: &NamedJet2<$scalar>) -> NamedJet2<$scalar> {
NamedJet2 {
inner: self / rhs.inner,
seeded: rhs.seeded,
#[cfg(debug_assertions)]
gen_id: rhs.gen_id,
}
}
}
};
}
__named_d2_scalar_lhs!(f64);
__named_d2_scalar_lhs!(f32);
impl crate::real::Real for Jet2<f64> {
type Passive = f64;
#[inline]
fn value(&self) -> f64 {
self.value
}
#[inline]
fn ln(&self) -> Self {
Jet2::ln(*self)
}
#[inline]
fn exp(&self) -> Self {
Jet2::exp(*self)
}
#[inline]
fn sqrt(&self) -> Self {
Jet2::sqrt(*self)
}
#[inline]
fn powf(&self, exponent: Self) -> Self {
Jet2::exp(exponent * Jet2::ln(*self))
}
#[inline]
fn powi(&self, exponent: i32) -> Self {
Jet2::powf(*self, exponent as f64)
}
#[inline]
fn sin(&self) -> Self {
Jet2::sin(*self)
}
#[inline]
fn cos(&self) -> Self {
Jet2::cos(*self)
}
}