#![warn(clippy::all)]
#![warn(clippy::allow_attributes)]
use nalgebra::allocator::Allocator;
use nalgebra::{DefaultAllocator, Dim, OMatrix, Scalar};
#[cfg(feature = "ndarray")]
use ndarray::ScalarOperand;
use num_traits::{Float, FloatConst, FromPrimitive, Inv, NumAssignOps, NumOps, Signed};
use std::collections::HashMap;
use std::fmt;
use std::hash::Hash;
use std::iter::{Product, Sum};
#[macro_use]
mod macros;
#[macro_use]
mod impl_derivatives;
mod bessel;
mod datatypes;
mod explicit;
mod implicit;
pub use bessel::BesselDual;
pub use datatypes::derivative::Derivative;
pub use datatypes::dual::{Dual, Dual32, Dual64};
pub use datatypes::dual_vec::{
DualDVec32, DualDVec64, DualSVec, DualSVec32, DualSVec64, DualVec, DualVec32, DualVec64,
};
pub use datatypes::dual2::{Dual2, Dual2_32, Dual2_64};
pub use datatypes::dual2_vec::{
Dual2DVec, Dual2DVec32, Dual2DVec64, Dual2SVec, Dual2SVec32, Dual2SVec64, Dual2Vec, Dual2Vec32,
Dual2Vec64,
};
pub use datatypes::dual3::{Dual3, Dual3_32, Dual3_64};
pub use datatypes::hyperdual::{HyperDual, HyperDual32, HyperDual64};
pub use datatypes::hyperdual_vec::{
HyperDualDVec32, HyperDualDVec64, HyperDualSVec32, HyperDualSVec64, HyperDualVec,
HyperDualVec32, HyperDualVec64,
};
pub use datatypes::hyperhyperdual::{HyperHyperDual, HyperHyperDual32, HyperHyperDual64};
pub use datatypes::real::Real;
pub use explicit::{
Gradients, first_derivative, gradient, hessian, jacobian, partial, partial_hessian, partial2,
partial3, second_derivative, second_partial_derivative, third_derivative,
third_partial_derivative, third_partial_derivative_vec, zeroth_derivative,
};
pub use implicit::{
ImplicitDerivative, ImplicitFunction, implicit_derivative, implicit_derivative_binary,
implicit_derivative_sp, implicit_derivative_vec,
};
pub mod linalg;
#[cfg(feature = "python")]
pub mod python;
#[cfg(feature = "python_macro")]
mod python_macro;
#[cfg(feature = "ndarray")]
pub trait DualNum<F>:
NumOps
+ for<'r> NumOps<&'r Self>
+ Signed
+ NumOps<F>
+ NumAssignOps
+ NumAssignOps<F>
+ Clone
+ Inv<Output = Self>
+ Sum
+ Product
+ FromPrimitive
+ From<F>
+ DualStruct<Self, F, Real = F>
+ Mappable<Self>
+ fmt::Display
+ PartialOrd
+ PartialOrd<F>
+ fmt::Debug
+ ScalarOperand
+ 'static
{
const NDERIV: usize;
fn recip(&self) -> Self;
fn powi(&self, n: i32) -> Self;
fn powf(&self, n: F) -> Self;
fn sqrt(&self) -> Self;
fn cbrt(&self) -> Self;
fn exp(&self) -> Self;
fn exp2(&self) -> Self;
fn exp_m1(&self) -> Self;
fn ln(&self) -> Self;
fn log(&self, base: F) -> Self;
fn log2(&self) -> Self;
fn log10(&self) -> Self;
fn ln_1p(&self) -> Self;
fn sin(&self) -> Self;
fn cos(&self) -> Self;
fn tan(&self) -> Self;
fn sin_cos(&self) -> (Self, Self);
fn asin(&self) -> Self;
fn acos(&self) -> Self;
fn atan(&self) -> Self;
fn atan2(&self, other: Self) -> Self;
fn sinh(&self) -> Self;
fn cosh(&self) -> Self;
fn tanh(&self) -> Self;
fn asinh(&self) -> Self;
fn acosh(&self) -> Self;
fn atanh(&self) -> Self;
fn sph_j0(&self) -> Self;
fn sph_j1(&self) -> Self;
fn sph_j2(&self) -> Self;
#[inline]
fn mul_add(&self, a: Self, b: Self) -> Self {
self.clone() * a + b
}
#[inline]
fn powd(&self, exp: Self) -> Self {
(self.ln() * exp).exp()
}
}
#[cfg(not(feature = "ndarray"))]
pub trait DualNum<F>:
NumOps
+ for<'r> NumOps<&'r Self>
+ Signed
+ NumOps<F>
+ NumAssignOps
+ NumAssignOps<F>
+ Clone
+ Inv<Output = Self>
+ Sum
+ Product
+ FromPrimitive
+ From<F>
+ DualStruct<Self, F, Real = F>
+ Mappable<Self>
+ fmt::Display
+ PartialOrd
+ PartialOrd<F>
+ fmt::Debug
+ 'static
{
const NDERIV: usize;
fn recip(&self) -> Self;
fn powi(&self, n: i32) -> Self;
fn powf(&self, n: F) -> Self;
fn sqrt(&self) -> Self;
fn cbrt(&self) -> Self;
fn exp(&self) -> Self;
fn exp2(&self) -> Self;
fn exp_m1(&self) -> Self;
fn ln(&self) -> Self;
fn log(&self, base: F) -> Self;
fn log2(&self) -> Self;
fn log10(&self) -> Self;
fn ln_1p(&self) -> Self;
fn sin(&self) -> Self;
fn cos(&self) -> Self;
fn tan(&self) -> Self;
fn sin_cos(&self) -> (Self, Self);
fn asin(&self) -> Self;
fn acos(&self) -> Self;
fn atan(&self) -> Self;
fn atan2(&self, other: Self) -> Self;
fn sinh(&self) -> Self;
fn cosh(&self) -> Self;
fn tanh(&self) -> Self;
fn asinh(&self) -> Self;
fn acosh(&self) -> Self;
fn atanh(&self) -> Self;
fn sph_j0(&self) -> Self;
fn sph_j1(&self) -> Self;
fn sph_j2(&self) -> Self;
#[inline]
fn mul_add(&self, a: Self, b: Self) -> Self {
self.clone() * a + b
}
#[inline]
fn powd(&self, exp: Self) -> Self {
(self.ln() * exp).exp()
}
}
pub trait DualNumFloat:
Float + FloatConst + FromPrimitive + Signed + fmt::Display + fmt::Debug + Sync + Send + 'static
{
}
impl<T> DualNumFloat for T where
T: Float
+ FloatConst
+ FromPrimitive
+ Signed
+ fmt::Display
+ fmt::Debug
+ Sync
+ Send
+ 'static
{
}
macro_rules! impl_dual_num_float {
($float:ty) => {
impl DualNum<$float> for $float {
const NDERIV: usize = 0;
fn mul_add(&self, a: Self, b: Self) -> Self {
<$float>::mul_add(*self, a, b)
}
fn recip(&self) -> Self {
<$float>::recip(*self)
}
fn powi(&self, n: i32) -> Self {
<$float>::powi(*self, n)
}
fn powf(&self, n: Self) -> Self {
<$float>::powf(*self, n)
}
fn powd(&self, n: Self) -> Self {
<$float>::powf(*self, n)
}
fn sqrt(&self) -> Self {
<$float>::sqrt(*self)
}
fn exp(&self) -> Self {
<$float>::exp(*self)
}
fn exp2(&self) -> Self {
<$float>::exp2(*self)
}
fn ln(&self) -> Self {
<$float>::ln(*self)
}
fn log(&self, base: Self) -> Self {
<$float>::log(*self, base)
}
fn log2(&self) -> Self {
<$float>::log2(*self)
}
fn log10(&self) -> Self {
<$float>::log10(*self)
}
fn cbrt(&self) -> Self {
<$float>::cbrt(*self)
}
fn sin(&self) -> Self {
<$float>::sin(*self)
}
fn cos(&self) -> Self {
<$float>::cos(*self)
}
fn tan(&self) -> Self {
<$float>::tan(*self)
}
fn asin(&self) -> Self {
<$float>::asin(*self)
}
fn acos(&self) -> Self {
<$float>::acos(*self)
}
fn atan(&self) -> Self {
<$float>::atan(*self)
}
fn atan2(&self, other: $float) -> Self {
<$float>::atan2(*self, other)
}
fn sin_cos(&self) -> (Self, Self) {
<$float>::sin_cos(*self)
}
fn exp_m1(&self) -> Self {
<$float>::exp_m1(*self)
}
fn ln_1p(&self) -> Self {
<$float>::ln_1p(*self)
}
fn sinh(&self) -> Self {
<$float>::sinh(*self)
}
fn cosh(&self) -> Self {
<$float>::cosh(*self)
}
fn tanh(&self) -> Self {
<$float>::tanh(*self)
}
fn asinh(&self) -> Self {
<$float>::asinh(*self)
}
fn acosh(&self) -> Self {
<$float>::acosh(*self)
}
fn atanh(&self) -> Self {
<$float>::atanh(*self)
}
fn sph_j0(&self) -> Self {
if self.abs() < <$float>::EPSILON {
1.0 - self * self / 6.0
} else {
self.sin() / self
}
}
fn sph_j1(&self) -> Self {
if self.abs() < <$float>::EPSILON {
self / 3.0
} else {
let sc = self.sin_cos();
let rec = self.recip();
(sc.0 * rec - sc.1) * rec
}
}
fn sph_j2(&self) -> Self {
if self.abs() < <$float>::EPSILON {
self * self / 15.0
} else {
let sc = self.sin_cos();
let s2 = self * self;
((3.0 - s2) * sc.0 - 3.0 * self * sc.1) / (self * s2)
}
}
}
};
}
impl_dual_num_float!(f32);
impl_dual_num_float!(f64);
pub trait DualStruct<D, F> {
type Real;
type Inner;
fn re(&self) -> Self::Real;
fn from_inner(inner: &Self::Inner) -> Self;
}
pub trait Mappable<D> {
type Output<O>;
fn map_dual<M: Fn(D) -> O, O>(self, f: M) -> Self::Output<O>;
}
impl<D, F> DualStruct<D, F> for () {
type Real = ();
type Inner = ();
fn re(&self) {}
fn from_inner(_: &Self::Inner) -> Self {}
}
impl<D> Mappable<D> for () {
type Output<O> = ();
fn map_dual<M: FnOnce(D) -> O, O>(self, _: M) {}
}
impl DualStruct<f32, f32> for f32 {
type Real = f32;
type Inner = f32;
fn re(&self) -> f32 {
*self
}
fn from_inner(inner: &Self::Inner) -> Self {
*inner
}
}
impl Mappable<f32> for f32 {
type Output<O> = O;
fn map_dual<M: FnOnce(f32) -> O, O>(self, f: M) -> Self::Output<O> {
f(self)
}
}
impl DualStruct<f64, f64> for f64 {
type Real = f64;
type Inner = f64;
fn re(&self) -> f64 {
*self
}
fn from_inner(inner: &Self::Inner) -> Self {
*inner
}
}
impl Mappable<f64> for f64 {
type Output<O> = O;
fn map_dual<M: FnOnce(f64) -> O, O>(self, f: M) -> Self::Output<O> {
f(self)
}
}
impl<D, F, T1: DualStruct<D, F>, T2: DualStruct<D, F>> DualStruct<D, F> for (T1, T2) {
type Real = (T1::Real, T2::Real);
type Inner = (T1::Inner, T2::Inner);
fn re(&self) -> Self::Real {
let (s1, s2) = self;
(s1.re(), s2.re())
}
fn from_inner(re: &Self::Inner) -> Self {
let (r1, r2) = re;
(T1::from_inner(r1), T2::from_inner(r2))
}
}
impl<D, T1: Mappable<D>, T2: Mappable<D>> Mappable<D> for (T1, T2) {
type Output<O> = (T1::Output<O>, T2::Output<O>);
fn map_dual<M: Fn(D) -> O, O>(self, f: M) -> Self::Output<O> {
let (s1, s2) = self;
(s1.map_dual(&f), s2.map_dual(&f))
}
}
impl<D, F, T1: DualStruct<D, F>, T2: DualStruct<D, F>, T3: DualStruct<D, F>> DualStruct<D, F>
for (T1, T2, T3)
{
type Real = (T1::Real, T2::Real, T3::Real);
type Inner = (T1::Inner, T2::Inner, T3::Inner);
fn re(&self) -> Self::Real {
let (s1, s2, s3) = self;
(s1.re(), s2.re(), s3.re())
}
fn from_inner(inner: &Self::Inner) -> Self {
let (r1, r2, r3) = inner;
(T1::from_inner(r1), T2::from_inner(r2), T3::from_inner(r3))
}
}
impl<D, T1: Mappable<D>, T2: Mappable<D>, T3: Mappable<D>> Mappable<D> for (T1, T2, T3) {
type Output<O> = (T1::Output<O>, T2::Output<O>, T3::Output<O>);
fn map_dual<M: Fn(D) -> O, O>(self, f: M) -> Self::Output<O> {
let (s1, s2, s3) = self;
(s1.map_dual(&f), s2.map_dual(&f), s3.map_dual(&f))
}
}
impl<D, F, T1: DualStruct<D, F>, T2: DualStruct<D, F>, T3: DualStruct<D, F>, T4: DualStruct<D, F>>
DualStruct<D, F> for (T1, T2, T3, T4)
{
type Real = (T1::Real, T2::Real, T3::Real, T4::Real);
type Inner = (T1::Inner, T2::Inner, T3::Inner, T4::Inner);
fn re(&self) -> Self::Real {
let (s1, s2, s3, s4) = self;
(s1.re(), s2.re(), s3.re(), s4.re())
}
fn from_inner(inner: &Self::Inner) -> Self {
let (r1, r2, r3, r4) = inner;
(
T1::from_inner(r1),
T2::from_inner(r2),
T3::from_inner(r3),
T4::from_inner(r4),
)
}
}
impl<D, T1: Mappable<D>, T2: Mappable<D>, T3: Mappable<D>, T4: Mappable<D>> Mappable<D>
for (T1, T2, T3, T4)
{
type Output<O> = (T1::Output<O>, T2::Output<O>, T3::Output<O>, T4::Output<O>);
fn map_dual<M: Fn(D) -> O, O>(self, f: M) -> Self::Output<O> {
let (s1, s2, s3, s4) = self;
(
s1.map_dual(&f),
s2.map_dual(&f),
s3.map_dual(&f),
s4.map_dual(&f),
)
}
}
impl<
D,
F,
T1: DualStruct<D, F>,
T2: DualStruct<D, F>,
T3: DualStruct<D, F>,
T4: DualStruct<D, F>,
T5: DualStruct<D, F>,
> DualStruct<D, F> for (T1, T2, T3, T4, T5)
{
type Real = (T1::Real, T2::Real, T3::Real, T4::Real, T5::Real);
type Inner = (T1::Inner, T2::Inner, T3::Inner, T4::Inner, T5::Inner);
fn re(&self) -> Self::Real {
let (s1, s2, s3, s4, s5) = self;
(s1.re(), s2.re(), s3.re(), s4.re(), s5.re())
}
fn from_inner(inner: &Self::Inner) -> Self {
let (r1, r2, r3, r4, r5) = inner;
(
T1::from_inner(r1),
T2::from_inner(r2),
T3::from_inner(r3),
T4::from_inner(r4),
T5::from_inner(r5),
)
}
}
impl<D, T1: Mappable<D>, T2: Mappable<D>, T3: Mappable<D>, T4: Mappable<D>, T5: Mappable<D>>
Mappable<D> for (T1, T2, T3, T4, T5)
{
type Output<O> = (
T1::Output<O>,
T2::Output<O>,
T3::Output<O>,
T4::Output<O>,
T5::Output<O>,
);
fn map_dual<M: Fn(D) -> O, O>(self, f: M) -> Self::Output<O> {
let (s1, s2, s3, s4, s5) = self;
(
s1.map_dual(&f),
s2.map_dual(&f),
s3.map_dual(&f),
s4.map_dual(&f),
s5.map_dual(&f),
)
}
}
impl<D, F, T: DualStruct<D, F>, const N: usize> DualStruct<D, F> for [T; N] {
type Real = [T::Real; N];
type Inner = [T::Inner; N];
fn re(&self) -> Self::Real {
self.each_ref().map(|x| x.re())
}
fn from_inner(re: &Self::Inner) -> Self {
re.each_ref().map(T::from_inner)
}
}
impl<D, T: Mappable<D>, const N: usize> Mappable<D> for [T; N] {
type Output<O> = [T::Output<O>; N];
fn map_dual<M: Fn(D) -> O, O>(self, f: M) -> Self::Output<O> {
self.map(|x| x.map_dual(&f))
}
}
impl<D, F, T: DualStruct<D, F>> DualStruct<D, F> for Option<T> {
type Real = Option<T::Real>;
type Inner = Option<T::Inner>;
fn re(&self) -> Self::Real {
self.as_ref().map(|x| x.re())
}
fn from_inner(inner: &Self::Inner) -> Self {
inner.as_ref().map(|x| T::from_inner(x))
}
}
impl<D, T: Mappable<D>> Mappable<D> for Option<T> {
type Output<O> = Option<T::Output<O>>;
fn map_dual<M: Fn(D) -> O, O>(self, f: M) -> Self::Output<O> {
self.map(|x| x.map_dual(f))
}
}
impl<D, T: Mappable<D>, E> Mappable<D> for Result<T, E> {
type Output<O> = Result<T::Output<O>, E>;
fn map_dual<M: Fn(D) -> O, O>(self, f: M) -> Self::Output<O> {
self.map(|x| x.map_dual(f))
}
}
impl<D, F, T: DualStruct<D, F>> DualStruct<D, F> for Vec<T> {
type Real = Vec<T::Real>;
type Inner = Vec<T::Inner>;
fn re(&self) -> Self::Real {
self.iter().map(|x| x.re()).collect()
}
fn from_inner(inner: &Self::Inner) -> Self {
inner.iter().map(|x| T::from_inner(x)).collect()
}
}
impl<D, T: Mappable<D>> Mappable<D> for Vec<T> {
type Output<O> = Vec<T::Output<O>>;
fn map_dual<M: Fn(D) -> O, O>(self, f: M) -> Self::Output<O> {
self.into_iter().map(|x| x.map_dual(&f)).collect()
}
}
impl<D, F, T: DualStruct<D, F>, K: Clone + Eq + Hash> DualStruct<D, F> for HashMap<K, T> {
type Real = HashMap<K, T::Real>;
type Inner = HashMap<K, T::Inner>;
fn re(&self) -> Self::Real {
self.iter().map(|(k, x)| (k.clone(), x.re())).collect()
}
fn from_inner(inner: &Self::Inner) -> Self {
inner
.iter()
.map(|(k, x)| (k.clone(), T::from_inner(x)))
.collect()
}
}
impl<D, T: Mappable<D>, K: Eq + Hash> Mappable<D> for HashMap<K, T> {
type Output<O> = HashMap<K, T::Output<O>>;
fn map_dual<M: Fn(D) -> O, O>(self, f: M) -> Self::Output<O> {
self.into_iter().map(|(k, x)| (k, x.map_dual(&f))).collect()
}
}
impl<D: DualNum<F>, F: DualNumFloat, R: Dim, C: Dim> DualStruct<D, F> for OMatrix<D, R, C>
where
DefaultAllocator: Allocator<R, C>,
D::Inner: DualNum<F>,
{
type Real = OMatrix<F, R, C>;
type Inner = OMatrix<D::Inner, R, C>;
fn re(&self) -> Self::Real {
self.map(|x| x.re())
}
fn from_inner(inner: &Self::Inner) -> Self {
inner.map(|x| D::from_inner(&x))
}
}
impl<D: Scalar, R: Dim, C: Dim> Mappable<Self> for OMatrix<D, R, C>
where
DefaultAllocator: Allocator<R, C>,
{
type Output<O> = O;
fn map_dual<M: Fn(Self) -> O, O>(self, f: M) -> O {
f(self)
}
}