use std::convert::Into;
use std::fmt;
use std::ops::{Add, Div, Mul, Neg, Rem, Sub};
use crate::traits::{
fp::FPVector,
math::{Vector, Normed, Norm, InnerProduct},
pointer::{Redox, Oxide},
num::{ExpLogOps, PowOps, TrigOps, Real},
};
use crate::structure::hyper_dual::HyperDual;
#[derive(Debug, Copy, Clone, Default, PartialOrd)]
pub struct Dual {
x: f64,
dx: f64,
}
impl fmt::Display for Dual {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s1 = format!("value: {}\nslope: {}", self.x, self.dx);
write!(f, "{}", s1)
}
}
impl PartialEq for Dual {
fn eq(&self, other: &Dual) -> bool {
self.x == other.x && self.dx == other.dx
}
}
impl Dual {
pub fn new<T: Into<f64> + Copy>(x: T, dx: T) -> Dual {
Dual {
x: x.into(),
dx: dx.into(),
}
}
pub fn value(&self) -> f64 {
self.x
}
pub fn slope(&self) -> f64 {
self.dx
}
pub fn extract(&self) -> (f64, f64) {
(self.x, self.dx)
}
pub fn set_value(&mut self, v: f64) {
self.x = v
}
pub fn set_slope(&mut self, dv: f64) {
self.dx = dv
}
}
pub fn dual<T: Into<f64> + Copy>(x: T, dx: T) -> Dual {
Dual::new(x, dx)
}
impl Neg for Dual {
type Output = Dual;
fn neg(self) -> Dual {
Dual::new(-self.x, -self.dx)
}
}
impl Add<Dual> for Dual {
type Output = Dual;
fn add(self, other: Dual) -> Dual {
Dual::new(self.x + other.x, self.dx + other.dx)
}
}
impl Sub<Dual> for Dual {
type Output = Dual;
fn sub(self, other: Dual) -> Dual {
Dual::new(self.x - other.x, self.dx - other.dx)
}
}
impl Mul<Dual> for Dual {
type Output = Dual;
fn mul(self, other: Dual) -> Dual {
let v1 = self.x;
let v2 = other.x;
let dv1 = self.dx;
let dv2 = other.dx;
Dual::new(v1 * v2, v1 * dv2 + v2 * dv1)
}
}
impl Div<Dual> for Dual {
type Output = Dual;
fn div(self, other: Dual) -> Dual {
let v1 = self.x;
let v2 = other.x;
let dv1 = self.dx;
let dv2 = other.dx;
Dual::new(v1 / v2, (dv1 * v2 - v1 * dv2) / (v2 * v2))
}
}
impl Rem<Dual> for Dual {
type Output = Dual;
fn rem(self, _rhs: Dual) -> Dual {
unimplemented!()
}
}
impl<'a> Neg for &'a Dual {
type Output = Dual;
fn neg(self) -> Self::Output {
Dual {
x: -self.x,
dx: -self.dx,
}
}
}
impl<'a, 'b> Add<&'b Dual> for &'a Dual {
type Output = Dual;
fn add(self, rhs: &Dual) -> Self::Output {
Dual {
x: self.x + rhs.x,
dx: self.dx + rhs.dx,
}
}
}
impl<'a, 'b> Sub<&'b Dual> for &'a Dual {
type Output = Dual;
fn sub(self, rhs: &Dual) -> Self::Output {
Dual {
x: self.x - rhs.x,
dx: self.dx - rhs.dx,
}
}
}
impl<'a, 'b> Mul<&'b Dual> for &'a Dual {
type Output = Dual;
fn mul(self, rhs: &Dual) -> Self::Output {
Dual {
x: self.x * rhs.x,
dx: self.x * rhs.dx + self.dx * rhs.x,
}
}
}
impl<'a, 'b> Div<&'b Dual> for &'a Dual {
type Output = Dual;
fn div(self, rhs: &Dual) -> Self::Output {
Dual {
x: self.x / rhs.x,
dx: (self.dx * rhs.x - self.x * rhs.dx) / (rhs.x * rhs.x),
}
}
}
impl Add<f64> for Dual {
type Output = Dual;
fn add(self, other: f64) -> Dual {
self + Dual::new(other, 0.)
}
}
impl Sub<f64> for Dual {
type Output = Dual;
fn sub(self, other: f64) -> Dual {
self - Dual::new(other, 0.)
}
}
impl Mul<f64> for Dual {
type Output = Dual;
fn mul(self, other: f64) -> Dual {
self * Dual::new(other, 0.)
}
}
impl Div<f64> for Dual {
type Output = Dual;
fn div(self, other: f64) -> Dual {
self / Dual::new(other, 0.)
}
}
impl Add<Dual> for f64 {
type Output = Dual;
fn add(self, other: Dual) -> Dual {
other.add(self)
}
}
impl Sub<Dual> for f64 {
type Output = Dual;
fn sub(self, other: Dual) -> Dual {
dual(self, 0.) - other
}
}
impl Mul<Dual> for f64 {
type Output = Dual;
fn mul(self, other: Dual) -> Dual {
other.mul(self)
}
}
impl Div<Dual> for f64 {
type Output = Dual;
fn div(self, other: Dual) -> Dual {
dual(self, 0.) / other
}
}
impl<'a> Add<f64> for &'a Dual {
type Output = Dual;
fn add(self, rhs: f64) -> Self::Output {
Dual {
x: self.x + rhs,
dx: self.dx,
}
}
}
impl<'a> Sub<f64> for &'a Dual {
type Output = Dual;
fn sub(self, rhs: f64) -> Self::Output {
Dual {
x: self.x - rhs,
dx: self.dx,
}
}
}
impl<'a> Mul<f64> for &'a Dual {
type Output = Dual;
fn mul(self, rhs: f64) -> Self::Output {
Dual {
x: self.x * rhs,
dx: self.dx * rhs,
}
}
}
impl<'a> Div<f64> for &'a Dual {
type Output = Dual;
fn div(self, rhs: f64) -> Self::Output {
Dual {
x: self.x / rhs,
dx: self.dx / rhs,
}
}
}
impl TrigOps for Dual {
fn sin(&self) -> Self {
let val = self.x.sin();
let dval = self.dx * self.x.cos();
Dual::new(val, dval)
}
fn cos(&self) -> Self {
let val = self.x.cos();
let dval = -self.dx * self.x.sin();
Dual::new(val, dval)
}
fn tan(&self) -> Self {
let val = self.x.tan();
let dval = self.dx * (1. + val * val); Dual::new(val, dval)
}
fn asin(&self) -> Self {
let val = self.x.asin();
let dval = self.dx / (1f64 - self.x.powi(2)).sqrt();
Dual::new(val, dval)
}
fn acos(&self) -> Self {
let val = self.x.acos();
let dval = -self.dx / (1f64 - self.x.powi(2)).sqrt();
Dual::new(val, dval)
}
fn atan(&self) -> Self {
let val = self.x.atan();
let dval = self.dx / (1f64 + self.x.powi(2));
Dual::new(val, dval)
}
fn sinh(&self) -> Self {
let val = self.x.sinh();
let dval = self.dx * self.x.cosh();
Dual::new(val, dval)
}
fn cosh(&self) -> Self {
let val = self.x.cosh();
let dval = self.dx * self.x.sinh();
Dual::new(val, dval)
}
fn tanh(&self) -> Self {
let val = self.x.tanh();
let dval = self.dx / self.x.cosh().powi(2);
Dual::new(val, dval)
}
fn asinh(&self) -> Self {
let val = self.x.asinh();
let dval = self.dx / (1f64 + self.x.powi(2)).sqrt();
Dual::new(val, dval)
}
fn acosh(&self) -> Self {
let val = self.x.acosh();
let dval = self.dx / (self.x.powi(2) - 1f64).sqrt();
Dual::new(val, dval)
}
fn atanh(&self) -> Self {
let val = self.x.atanh();
let dval = self.dx / (1f64 - self.x.powi(2));
Dual::new(val, dval)
}
fn sin_cos(&self) -> (Self, Self) {
let vals = self.x.sin_cos();
let dvals = (self.dx * vals.1, -self.dx * vals.0);
let d1 = Dual::new(vals.0, dvals.0);
let d2 = Dual::new(vals.1, dvals.1);
(d1, d2)
}
}
impl ExpLogOps for Dual {
fn exp(&self) -> Self {
let val = self.value().exp();
let dval = val * self.slope();
Dual::new(val, dval)
}
fn ln(&self) -> Self {
let val = self.value().ln();
let dval = self.slope() / self.value();
Dual::new(val, dval)
}
fn log(&self, base: f64) -> Self {
self.ln() / base.ln()
}
fn log2(&self) -> Self {
self.log(2f64)
}
fn log10(&self) -> Self {
self.log(10f64)
}
}
impl PowOps for Dual {
fn powi(&self, n: i32) -> Self {
let x = self.x;
let val = x.powi(n);
let dval = (n as f64) * x.powi(n - 1) * self.dx;
Dual::new(val, dval)
}
fn powf(&self, f: f64) -> Self {
if f == 0f64 {
return Dual::new(1f64, 0f64);
}
let x = self.x;
let val = x.powf(f);
let dval = f * x.powf(f - 1f64) * self.dx;
Dual::new(val, dval)
}
fn pow(&self, f: Self) -> Self {
let x = self.value();
let dx = self.slope();
let n = f.value();
let dn = f.slope();
Dual::new(x.powf(n), x.powf(n - 1f64) * (n * dx + x * x.ln() * dn))
}
fn sqrt(&self) -> Self {
self.powf(0.5)
}
}
impl Real for Dual {
fn to_f64(&self) -> f64 {
self.value()
}
fn from_f64(f: f64) -> Self {
Dual::new(f, 0f64)
}
fn to_dual(&self) -> Dual {
*self
}
fn from_dual(d: Dual) -> Self {
d
}
fn to_hyper_dual(&self) -> HyperDual {
HyperDual::new(self.value(), self.slope(), 0f64)
}
fn from_hyper_dual(h: HyperDual) -> Self {
Dual::new(h.value(), h.slope())
}
}
pub trait VecWithDual {
type Item;
fn conv_dual(&self) -> Self::Item;
}
impl VecWithDual for Vec<f64> {
type Item = Vec<Dual>;
fn conv_dual(&self) -> Vec<Dual> {
self.clone()
.into_iter()
.map(|x| Dual::new(x, 0.))
.collect::<Vec<Dual>>()
}
}
impl VecWithDual for Vec<Dual> {
type Item = Vec<f64>;
fn conv_dual(&self) -> Vec<f64> {
self.clone()
.into_iter()
.map(|x| x.value())
.collect::<Vec<f64>>()
}
}
pub trait Dualist {
fn values(&self) -> Vec<f64>;
fn slopes(&self) -> Vec<f64>;
}
impl Dualist for Vec<Dual> {
fn values(&self) -> Vec<f64> {
self.conv_dual()
}
fn slopes(&self) -> Vec<f64> {
self.clone()
.into_iter()
.map(|t| t.slope())
.collect::<Vec<f64>>()
}
}
pub fn merge_dual(y: &Vec<f64>, dy: &Vec<f64>) -> Vec<Dual> {
let mut result = vec![dual(0, 0); y.len()];
for i in 0 .. y.len() {
result[i] = dual(y[i], dy[i]);
}
result
}
impl FPVector for Vec<Dual> {
type Scalar = Dual;
fn fmap<F>(&self, f: F) -> Self
where
F: Fn(Self::Scalar) -> Self::Scalar,
{
self.clone()
.into_iter()
.map(|x| f(x))
.collect::<Vec<Dual>>()
}
fn reduce<F, T>(&self, _init: T, _f: F) -> Self::Scalar
where
F: Fn(Self::Scalar, Self::Scalar) -> Self::Scalar,
T: Into<Self::Scalar>,
{
unimplemented!()
}
fn zip_with<F>(&self, f: F, other: &Self) -> Self
where
F: Fn(Self::Scalar, Self::Scalar) -> Self::Scalar,
{
self.into_iter()
.zip(other)
.map(|(x, y)| f(*x, *y))
.collect::<Vec<Dual>>()
}
fn filter<F>(&self, _f: F) -> Self
where
F: Fn(Self::Scalar) -> bool,
{
unimplemented!()
}
fn take(&self, n: usize) -> Self {
self.clone().into_iter().take(n).collect::<Vec<Dual>>()
}
fn skip(&self, n: usize) -> Self {
self.clone().into_iter().skip(n).collect::<Vec<Dual>>()
}
fn sum(&self) -> Self::Scalar {
let mut s = dual(0f64, 0f64);
for x in self {
s = s + *x;
}
s
}
fn prod(&self) -> Self::Scalar {
let mut p = dual(1f64, 0f64);
for x in self {
p = p * *x;
}
p
}
}
#[allow(unused_variables)]
impl Vector for Vec<Dual> {
fn add_vec(&self, other: &Self) -> Self {
self.zip_with(|x, y| x + y, other)
}
fn sub_vec(&self, other: &Self) -> Self {
self.zip_with(|x, y| x - y, other)
}
fn mul_scalar<T: Into<f64>>(&self, other: T) -> Self {
let scalar = other.into();
self.fmap(|x| x * scalar)
}
}
impl Normed for Vec<Dual> {
type Scalar = Dual;
fn norm(&self, _kind: Norm) -> Self::Scalar {
unimplemented!()
}
fn normalize(&self, _kind: Norm) -> Self where Self: Sized {
unimplemented!()
}
}
impl InnerProduct for Vec<Dual> {
fn dot(&self, _other: &Self) -> Self::Scalar {
self.clone()
.into_iter()
.zip(_other.clone())
.map(|(x, y)| x.mul(y))
.fold(Self::Scalar::new(0., 0.), |sum: Self::Scalar, x| sum.add(x))
}
}
impl Oxide for Vec<Dual> {
fn ox(self) -> Redox<Vec<Dual>> {
Redox::<Vec<Dual>>::from_vec(self)
}
}