#![allow(clippy::needless_lifetimes, clippy::suspicious_arithmetic_impl)]
use std::fmt;
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
#[derive(Clone, Debug)]
pub struct Dual {
pub real: f64,
pub dual: Vec<f64>,
}
#[inline]
fn len_check(a: &Dual, b: &Dual) {
debug_assert_eq!(a.dual.len(), b.dual.len(), "Dual tangent length mismatch");
}
impl Dual {
#[inline]
pub fn new(real: f64, dual: Vec<f64>) -> Self {
Dual { real, dual }
}
#[inline]
pub fn constant(real: f64, n: usize) -> Self {
Dual {
real,
dual: vec![0.0; n],
}
}
#[inline]
pub fn variable(value: f64, i: usize, n: usize) -> Self {
let mut dual = vec![0.0; n];
dual[i] = 1.0;
Dual { real: value, dual }
}
#[inline]
pub fn real(&self) -> f64 {
self.real
}
#[inline]
pub fn dual(&self) -> &[f64] {
&self.dual
}
#[inline]
pub fn partial(&self, i: usize) -> f64 {
self.dual[i]
}
#[inline]
pub fn num_vars(&self) -> usize {
self.dual.len()
}
#[inline]
fn scaled(&self, k: f64) -> Vec<f64> {
self.dual.iter().map(|&d| d * k).collect()
}
#[inline]
pub fn powf(&self, n: f64) -> Dual {
let vn = self.real.powf(n);
let gp = n * self.real.powf(n - 1.0);
Dual {
real: vn,
dual: self.scaled(gp),
}
}
#[inline]
pub fn powi(&self, n: i32) -> Dual {
let vn = self.real.powi(n);
let gp = (n as f64) * self.real.powi(n - 1);
Dual {
real: vn,
dual: self.scaled(gp),
}
}
#[inline]
pub fn exp(&self) -> Dual {
let e = self.real.exp();
Dual {
real: e,
dual: self.scaled(e),
}
}
#[inline]
pub fn ln(&self) -> Dual {
let inv = 1.0 / self.real;
Dual {
real: self.real.ln(),
dual: self.scaled(inv),
}
}
#[inline]
pub fn sqrt(&self) -> Dual {
let s = self.real.sqrt();
Dual {
real: s,
dual: self.scaled(0.5 / s),
}
}
#[inline]
pub fn sin(&self) -> Dual {
Dual {
real: self.real.sin(),
dual: self.scaled(self.real.cos()),
}
}
#[inline]
pub fn cos(&self) -> Dual {
Dual {
real: self.real.cos(),
dual: self.scaled(-self.real.sin()),
}
}
#[inline]
pub fn tan(&self) -> Dual {
let t = self.real.tan();
let sec2 = 1.0 + t * t;
Dual {
real: t,
dual: self.scaled(sec2),
}
}
#[inline]
pub fn tanh(&self) -> Dual {
let t = self.real.tanh();
Dual {
real: t,
dual: self.scaled(1.0 - t * t),
}
}
#[inline]
pub fn abs(&self) -> Dual {
let sign = if self.real >= 0.0 { 1.0 } else { -1.0 };
Dual {
real: self.real.abs(),
dual: self.scaled(sign),
}
}
#[inline]
pub fn erf(&self) -> Dual {
let v = self.real;
let r = crate::math::erf(v);
let g = std::f64::consts::FRAC_2_SQRT_PI * (-v * v).exp();
Dual {
real: r,
dual: self.scaled(g),
}
}
#[inline]
pub fn norm_cdf(&self) -> Dual {
let v = self.real;
let r = crate::math::norm_cdf(v);
let g = crate::math::norm_pdf(v);
Dual {
real: r,
dual: self.scaled(g),
}
}
#[inline]
pub fn inv_norm_cdf(&self) -> Dual {
let v = self.real;
let r = crate::math::inv_norm_cdf(v);
let g = 1.0 / crate::math::norm_pdf(r);
Dual {
real: r,
dual: self.scaled(g),
}
}
}
impl<'a, 'b> Add<&'b Dual> for &'a Dual {
type Output = Dual;
#[inline]
fn add(self, rhs: &'b Dual) -> Dual {
len_check(self, rhs);
Dual {
real: self.real + rhs.real,
dual: self
.dual
.iter()
.zip(&rhs.dual)
.map(|(a, b)| a + b)
.collect(),
}
}
}
impl Add for Dual {
type Output = Dual;
#[inline]
fn add(mut self, rhs: Dual) -> Dual {
self += &rhs;
self
}
}
impl Add<&Dual> for Dual {
type Output = Dual;
#[inline]
fn add(mut self, rhs: &Dual) -> Dual {
self += rhs;
self
}
}
impl Add<Dual> for &Dual {
type Output = Dual;
#[inline]
fn add(self, mut rhs: Dual) -> Dual {
rhs += self;
rhs
}
}
impl Add<f64> for &Dual {
type Output = Dual;
#[inline]
fn add(self, rhs: f64) -> Dual {
Dual {
real: self.real + rhs,
dual: self.dual.clone(),
}
}
}
impl Add<f64> for Dual {
type Output = Dual;
#[inline]
fn add(mut self, rhs: f64) -> Dual {
self.real += rhs;
self
}
}
impl Add<&Dual> for f64 {
type Output = Dual;
#[inline]
fn add(self, rhs: &Dual) -> Dual {
rhs + self
}
}
impl Add<Dual> for f64 {
type Output = Dual;
#[inline]
fn add(self, rhs: Dual) -> Dual {
rhs + self
}
}
impl<'a, 'b> Sub<&'b Dual> for &'a Dual {
type Output = Dual;
#[inline]
fn sub(self, rhs: &'b Dual) -> Dual {
len_check(self, rhs);
Dual {
real: self.real - rhs.real,
dual: self
.dual
.iter()
.zip(&rhs.dual)
.map(|(a, b)| a - b)
.collect(),
}
}
}
impl Sub for Dual {
type Output = Dual;
#[inline]
fn sub(mut self, rhs: Dual) -> Dual {
self -= &rhs;
self
}
}
impl Sub<&Dual> for Dual {
type Output = Dual;
#[inline]
fn sub(mut self, rhs: &Dual) -> Dual {
self -= rhs;
self
}
}
impl Sub<Dual> for &Dual {
type Output = Dual;
#[inline]
fn sub(self, mut rhs: Dual) -> Dual {
len_check(self, &rhs);
rhs.real = self.real - rhs.real;
for (b, &a) in rhs.dual.iter_mut().zip(&self.dual) {
*b = a - *b;
}
rhs
}
}
impl Sub<f64> for &Dual {
type Output = Dual;
#[inline]
fn sub(self, rhs: f64) -> Dual {
Dual {
real: self.real - rhs,
dual: self.dual.clone(),
}
}
}
impl Sub<f64> for Dual {
type Output = Dual;
#[inline]
fn sub(mut self, rhs: f64) -> Dual {
self.real -= rhs;
self
}
}
impl Sub<&Dual> for f64 {
type Output = Dual;
#[inline]
fn sub(self, rhs: &Dual) -> Dual {
Dual {
real: self - rhs.real,
dual: rhs.dual.iter().map(|&d| -d).collect(),
}
}
}
impl Sub<Dual> for f64 {
type Output = Dual;
#[inline]
fn sub(self, mut rhs: Dual) -> Dual {
rhs.real = self - rhs.real;
for d in rhs.dual.iter_mut() {
*d = -*d;
}
rhs
}
}
impl<'a, 'b> Mul<&'b Dual> for &'a Dual {
type Output = Dual;
#[inline]
fn mul(self, rhs: &'b Dual) -> Dual {
len_check(self, rhs);
let a = self.real;
let b = rhs.real;
Dual {
real: a * b,
dual: self
.dual
.iter()
.zip(&rhs.dual)
.map(|(&da, &db)| da * b + db * a)
.collect(),
}
}
}
impl Mul for Dual {
type Output = Dual;
#[inline]
fn mul(mut self, rhs: Dual) -> Dual {
len_check(&self, &rhs);
let a = self.real;
let b = rhs.real;
self.real = a * b;
for (da, &db) in self.dual.iter_mut().zip(&rhs.dual) {
*da = *da * b + db * a;
}
self
}
}
impl Mul<&Dual> for Dual {
type Output = Dual;
#[inline]
fn mul(mut self, rhs: &Dual) -> Dual {
len_check(&self, rhs);
let a = self.real;
let b = rhs.real;
self.real = a * b;
for (da, &db) in self.dual.iter_mut().zip(&rhs.dual) {
*da = *da * b + db * a;
}
self
}
}
impl Mul<Dual> for &Dual {
type Output = Dual;
#[inline]
fn mul(self, mut rhs: Dual) -> Dual {
len_check(self, &rhs);
let a = self.real;
let b = rhs.real;
rhs.real = a * b;
for (db, &da) in rhs.dual.iter_mut().zip(&self.dual) {
*db = da * b + *db * a;
}
rhs
}
}
impl Mul<f64> for &Dual {
type Output = Dual;
#[inline]
fn mul(self, rhs: f64) -> Dual {
Dual {
real: self.real * rhs,
dual: self.dual.iter().map(|&d| d * rhs).collect(),
}
}
}
impl Mul<f64> for Dual {
type Output = Dual;
#[inline]
fn mul(mut self, rhs: f64) -> Dual {
self.real *= rhs;
for d in self.dual.iter_mut() {
*d *= rhs;
}
self
}
}
impl Mul<&Dual> for f64 {
type Output = Dual;
#[inline]
fn mul(self, rhs: &Dual) -> Dual {
rhs * self
}
}
impl Mul<Dual> for f64 {
type Output = Dual;
#[inline]
fn mul(self, rhs: Dual) -> Dual {
rhs * self
}
}
impl<'a, 'b> Div<&'b Dual> for &'a Dual {
type Output = Dual;
#[inline]
fn div(self, rhs: &'b Dual) -> Dual {
len_check(self, rhs);
let inv = 1.0 / rhs.real;
let inv2 = inv * inv;
let a_inv2 = self.real * inv2;
Dual {
real: self.real * inv,
dual: self
.dual
.iter()
.zip(&rhs.dual)
.map(|(&da, &db)| da * inv - db * a_inv2)
.collect(),
}
}
}
impl Div for Dual {
type Output = Dual;
#[inline]
fn div(mut self, rhs: Dual) -> Dual {
len_check(&self, &rhs);
let inv = 1.0 / rhs.real;
let inv2 = inv * inv;
let a_inv2 = self.real * inv2;
self.real *= inv;
for (da, &db) in self.dual.iter_mut().zip(&rhs.dual) {
*da = *da * inv - db * a_inv2;
}
self
}
}
impl Div<&Dual> for Dual {
type Output = Dual;
#[inline]
fn div(mut self, rhs: &Dual) -> Dual {
len_check(&self, rhs);
let inv = 1.0 / rhs.real;
let inv2 = inv * inv;
let a_inv2 = self.real * inv2;
self.real *= inv;
for (da, &db) in self.dual.iter_mut().zip(&rhs.dual) {
*da = *da * inv - db * a_inv2;
}
self
}
}
impl Div<Dual> for &Dual {
type Output = Dual;
#[inline]
fn div(self, rhs: Dual) -> Dual {
self / &rhs
}
}
impl Div<f64> for &Dual {
type Output = Dual;
#[inline]
fn div(self, rhs: f64) -> Dual {
let inv = 1.0 / rhs;
Dual {
real: self.real * inv,
dual: self.dual.iter().map(|&d| d * inv).collect(),
}
}
}
impl Div<f64> for Dual {
type Output = Dual;
#[inline]
fn div(mut self, rhs: f64) -> Dual {
let inv = 1.0 / rhs;
self.real *= inv;
for d in self.dual.iter_mut() {
*d *= inv;
}
self
}
}
impl Div<&Dual> for f64 {
type Output = Dual;
#[inline]
fn div(self, rhs: &Dual) -> Dual {
let inv = 1.0 / rhs.real;
let neg_scale = -self * inv * inv;
Dual {
real: self * inv,
dual: rhs.dual.iter().map(|&d| d * neg_scale).collect(),
}
}
}
impl Div<Dual> for f64 {
type Output = Dual;
#[inline]
fn div(self, mut rhs: Dual) -> Dual {
let inv = 1.0 / rhs.real;
let neg_scale = -self * inv * inv;
rhs.real = self * inv;
for d in rhs.dual.iter_mut() {
*d *= neg_scale;
}
rhs
}
}
impl Neg for &Dual {
type Output = Dual;
#[inline]
fn neg(self) -> Dual {
Dual {
real: -self.real,
dual: self.dual.iter().map(|&d| -d).collect(),
}
}
}
impl Neg for Dual {
type Output = Dual;
#[inline]
fn neg(mut self) -> Dual {
self.real = -self.real;
for d in self.dual.iter_mut() {
*d = -*d;
}
self
}
}
impl AddAssign<&Dual> for Dual {
#[inline]
fn add_assign(&mut self, rhs: &Dual) {
len_check(self, rhs);
self.real += rhs.real;
for (a, &b) in self.dual.iter_mut().zip(&rhs.dual) {
*a += b;
}
}
}
impl AddAssign for Dual {
#[inline]
fn add_assign(&mut self, rhs: Dual) {
len_check(self, &rhs);
self.real += rhs.real;
for (a, &b) in self.dual.iter_mut().zip(&rhs.dual) {
*a += b;
}
}
}
impl AddAssign<f64> for Dual {
#[inline]
fn add_assign(&mut self, rhs: f64) {
self.real += rhs;
}
}
impl SubAssign<&Dual> for Dual {
#[inline]
fn sub_assign(&mut self, rhs: &Dual) {
len_check(self, rhs);
self.real -= rhs.real;
for (a, &b) in self.dual.iter_mut().zip(&rhs.dual) {
*a -= b;
}
}
}
impl SubAssign for Dual {
#[inline]
fn sub_assign(&mut self, rhs: Dual) {
len_check(self, &rhs);
self.real -= rhs.real;
for (a, &b) in self.dual.iter_mut().zip(&rhs.dual) {
*a -= b;
}
}
}
impl SubAssign<f64> for Dual {
#[inline]
fn sub_assign(&mut self, rhs: f64) {
self.real -= rhs;
}
}
impl MulAssign<&Dual> for Dual {
#[inline]
fn mul_assign(&mut self, rhs: &Dual) {
len_check(self, rhs);
let a = self.real;
let b = rhs.real;
self.real = a * b;
for (da, &db) in self.dual.iter_mut().zip(&rhs.dual) {
*da = *da * b + db * a;
}
}
}
impl MulAssign<f64> for Dual {
#[inline]
fn mul_assign(&mut self, rhs: f64) {
self.real *= rhs;
for d in self.dual.iter_mut() {
*d *= rhs;
}
}
}
impl DivAssign<&Dual> for Dual {
#[inline]
fn div_assign(&mut self, rhs: &Dual) {
len_check(self, rhs);
let inv = 1.0 / rhs.real;
let inv2 = inv * inv;
let a_inv2 = self.real * inv2;
self.real *= inv;
for (da, &db) in self.dual.iter_mut().zip(&rhs.dual) {
*da = *da * inv - db * a_inv2;
}
}
}
impl DivAssign<f64> for Dual {
#[inline]
fn div_assign(&mut self, rhs: f64) {
let inv = 1.0 / rhs;
self.real *= inv;
for d in self.dual.iter_mut() {
*d *= inv;
}
}
}
impl PartialEq for Dual {
fn eq(&self, other: &Self) -> bool {
self.real == other.real
}
}
impl PartialEq<f64> for Dual {
fn eq(&self, other: &f64) -> bool {
self.real == *other
}
}
impl PartialOrd for Dual {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.real.partial_cmp(&other.real)
}
}
impl PartialOrd<f64> for Dual {
fn partial_cmp(&self, other: &f64) -> Option<std::cmp::Ordering> {
self.real.partial_cmp(other)
}
}
impl fmt::Display for Dual {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.real)
}
}
#[derive(Clone, Debug)]
pub struct NamedDual {
pub(crate) inner: Dual,
#[cfg(debug_assertions)]
pub(crate) gen_id: u64,
}
impl NamedDual {
#[inline]
pub(crate) fn __from_inner(inner: Dual) -> Self {
Self {
inner,
#[cfg(debug_assertions)]
gen_id: crate::forward_tape::current_gen(),
}
}
#[inline]
pub fn real(&self) -> f64 {
self.inner.real
}
pub fn partial(&self, name: &str) -> f64 {
let idx = crate::forward_tape::with_active_registry(|r| {
let r =
r.expect("NamedDual::partial called outside a frozen NamedForwardTape scope");
r.index_of(name).unwrap_or_else(|| {
panic!(
"NamedDual::partial: name {:?} not present in registry",
name
)
})
});
self.inner.partial(idx)
}
pub fn gradient(&self) -> Vec<(String, f64)> {
crate::forward_tape::with_active_registry(|r| {
let r =
r.expect("NamedDual::gradient called outside a frozen NamedForwardTape scope");
let n = r.len();
let mut out = Vec::with_capacity(n);
for (i, name) in r.iter().enumerate() {
out.push((name.to_string(), self.inner.partial(i)));
}
out
})
}
#[inline]
pub fn inner(&self) -> &Dual {
&self.inner
}
#[inline]
pub fn exp(&self) -> Self {
Self {
inner: self.inner.exp(),
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
#[inline]
pub fn ln(&self) -> Self {
Self {
inner: self.inner.ln(),
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
#[inline]
pub fn sqrt(&self) -> Self {
Self {
inner: self.inner.sqrt(),
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
#[inline]
pub fn sin(&self) -> Self {
Self {
inner: self.inner.sin(),
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
#[inline]
pub fn cos(&self) -> Self {
Self {
inner: self.inner.cos(),
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
#[inline]
pub fn tan(&self) -> Self {
Self {
inner: self.inner.tan(),
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
#[inline]
pub fn norm_cdf(&self) -> Self {
Self {
inner: self.inner.norm_cdf(),
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
#[inline]
pub fn inv_norm_cdf(&self) -> Self {
Self {
inner: self.inner.inv_norm_cdf(),
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
#[inline]
pub fn powf(&self, n: f64) -> Self {
Self {
inner: self.inner.powf(n),
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
#[inline]
pub fn powi(&self, n: i32) -> Self {
Self {
inner: self.inner.powi(n),
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
}
impl fmt::Display for NamedDual {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "NamedDual({})", self.inner.real)
}
}
macro_rules! __named_dual_binop {
($trait:ident, $method:ident, $op:tt) => {
impl ::core::ops::$trait<NamedDual> for NamedDual {
type Output = NamedDual;
#[inline]
fn $method(self, rhs: NamedDual) -> NamedDual {
#[cfg(debug_assertions)]
crate::forward_tape::check_gen(self.gen_id, rhs.gen_id);
NamedDual {
inner: self.inner $op rhs.inner,
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
}
impl ::core::ops::$trait<&NamedDual> for &NamedDual {
type Output = NamedDual;
#[inline]
fn $method(self, rhs: &NamedDual) -> NamedDual {
#[cfg(debug_assertions)]
crate::forward_tape::check_gen(self.gen_id, rhs.gen_id);
NamedDual {
inner: &self.inner $op &rhs.inner,
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
}
impl ::core::ops::$trait<&NamedDual> for NamedDual {
type Output = NamedDual;
#[inline]
fn $method(self, rhs: &NamedDual) -> NamedDual {
#[cfg(debug_assertions)]
crate::forward_tape::check_gen(self.gen_id, rhs.gen_id);
NamedDual {
inner: self.inner $op &rhs.inner,
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
}
impl ::core::ops::$trait<NamedDual> for &NamedDual {
type Output = NamedDual;
#[inline]
fn $method(self, rhs: NamedDual) -> NamedDual {
#[cfg(debug_assertions)]
crate::forward_tape::check_gen(self.gen_id, rhs.gen_id);
NamedDual {
inner: &self.inner $op rhs.inner,
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
}
impl ::core::ops::$trait<f64> for NamedDual {
type Output = NamedDual;
#[inline]
fn $method(self, rhs: f64) -> NamedDual {
NamedDual {
inner: self.inner $op rhs,
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
}
impl ::core::ops::$trait<f64> for &NamedDual {
type Output = NamedDual;
#[inline]
fn $method(self, rhs: f64) -> NamedDual {
NamedDual {
inner: &self.inner $op rhs,
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
}
};
}
__named_dual_binop!(Add, add, +);
__named_dual_binop!(Sub, sub, -);
__named_dual_binop!(Mul, mul, *);
__named_dual_binop!(Div, div, /);
impl ::core::ops::AddAssign<NamedDual> for NamedDual {
#[inline]
fn add_assign(&mut self, rhs: NamedDual) {
#[cfg(debug_assertions)]
crate::forward_tape::check_gen(self.gen_id, rhs.gen_id);
self.inner += rhs.inner;
}
}
impl ::core::ops::AddAssign<&NamedDual> for NamedDual {
#[inline]
fn add_assign(&mut self, rhs: &NamedDual) {
#[cfg(debug_assertions)]
crate::forward_tape::check_gen(self.gen_id, rhs.gen_id);
self.inner += &rhs.inner;
}
}
impl ::core::ops::Neg for NamedDual {
type Output = NamedDual;
#[inline]
fn neg(self) -> NamedDual {
NamedDual {
inner: -self.inner,
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
}
impl ::core::ops::Neg for &NamedDual {
type Output = NamedDual;
#[inline]
fn neg(self) -> NamedDual {
NamedDual {
inner: -&self.inner,
#[cfg(debug_assertions)]
gen_id: self.gen_id,
}
}
}
impl ::core::ops::Add<NamedDual> for f64 {
type Output = NamedDual;
#[inline]
fn add(self, rhs: NamedDual) -> NamedDual {
NamedDual {
inner: self + rhs.inner,
#[cfg(debug_assertions)]
gen_id: rhs.gen_id,
}
}
}
impl ::core::ops::Add<&NamedDual> for f64 {
type Output = NamedDual;
#[inline]
fn add(self, rhs: &NamedDual) -> NamedDual {
NamedDual {
inner: self + &rhs.inner,
#[cfg(debug_assertions)]
gen_id: rhs.gen_id,
}
}
}
impl ::core::ops::Sub<NamedDual> for f64 {
type Output = NamedDual;
#[inline]
fn sub(self, rhs: NamedDual) -> NamedDual {
NamedDual {
inner: self - rhs.inner,
#[cfg(debug_assertions)]
gen_id: rhs.gen_id,
}
}
}
impl ::core::ops::Sub<&NamedDual> for f64 {
type Output = NamedDual;
#[inline]
fn sub(self, rhs: &NamedDual) -> NamedDual {
NamedDual {
inner: self - &rhs.inner,
#[cfg(debug_assertions)]
gen_id: rhs.gen_id,
}
}
}
impl ::core::ops::Mul<NamedDual> for f64 {
type Output = NamedDual;
#[inline]
fn mul(self, rhs: NamedDual) -> NamedDual {
NamedDual {
inner: self * rhs.inner,
#[cfg(debug_assertions)]
gen_id: rhs.gen_id,
}
}
}
impl ::core::ops::Mul<&NamedDual> for f64 {
type Output = NamedDual;
#[inline]
fn mul(self, rhs: &NamedDual) -> NamedDual {
NamedDual {
inner: self * &rhs.inner,
#[cfg(debug_assertions)]
gen_id: rhs.gen_id,
}
}
}
impl ::core::ops::Div<NamedDual> for f64 {
type Output = NamedDual;
#[inline]
fn div(self, rhs: NamedDual) -> NamedDual {
NamedDual {
inner: self / rhs.inner,
#[cfg(debug_assertions)]
gen_id: rhs.gen_id,
}
}
}
impl ::core::ops::Div<&NamedDual> for f64 {
type Output = NamedDual;
#[inline]
fn div(self, rhs: &NamedDual) -> NamedDual {
NamedDual {
inner: self / &rhs.inner,
#[cfg(debug_assertions)]
gen_id: rhs.gen_id,
}
}
}