#[macro_export]
macro_rules! get_sderivative2 {
($f:ident, $func_name:ident) => {
get_sderivative2!($f, $func_name, [f64]);
};
($f:ident, $func_name:ident, $param_type:ty) => {
fn $func_name<S: Scalar>(x0: S, p: &$param_type) -> f64 {
let x0 = HyperDual::new(x0.to_f64().unwrap(), 1.0, 1.0, 0.0);
let f_x0 = $f(x0, p);
f_x0.get_d()
}
};
}
#[cfg(test)]
mod tests {
use crate::{HyperDual, test_utils};
use linalg_traits::Scalar;
use numtest::*;
use std::f64::consts::PI;
#[cfg(feature = "trig")]
use trig::Trig;
#[test]
fn test_product_rule() {
fn f<S: Scalar>(x: S, _p: &[f64]) -> S {
x.powi(3)
}
fn d2f<S: Scalar>(x: S) -> S {
S::new(6.0) * x
}
fn g<S: Scalar>(x: S, _p: &[f64]) -> S {
x.sin()
}
fn d2g<S: Scalar>(x: S) -> S {
-x.sin()
}
fn h<S: Scalar>(x: S, p: &[f64]) -> S {
f(x, p) * g(x, p)
}
#[allow(clippy::similar_names)]
fn d2h<S: Scalar>(x: S) -> S {
let f_val = x.powi(3);
let df_val = S::new(3.0) * x.powi(2);
let d2f_val = d2f(x);
let g_val = x.sin();
let dg_val = x.cos();
let d2g_val = d2g(x);
d2f_val * g_val + S::new(2.0) * df_val * dg_val + f_val * d2g_val
}
let p = [];
get_sderivative2!(h, d2h_autodiff);
assert_equal_to_decimal!(d2h_autodiff(-1.5, &p), d2h(-1.5), 14);
assert_equal_to_decimal!(d2h_autodiff(1.5, &p), d2h(1.5), 14);
}
#[test]
fn test_quotient_rule() {
fn f<S: Scalar>(x: S, _p: &[f64]) -> S {
x.powi(3)
}
fn d2f<S: Scalar>(x: S) -> S {
S::new(6.0) * x
}
fn g<S: Scalar>(x: S, _p: &[f64]) -> S {
x.sin()
}
fn d2g<S: Scalar>(x: S) -> S {
-x.sin()
}
fn h<S: Scalar>(x: S, p: &[f64]) -> S {
f(x, p) / g(x, p)
}
#[allow(clippy::similar_names)]
fn d2h<S: Scalar>(x: S) -> S {
let f_val = x.powi(3);
let df_val = S::new(3.0) * x.powi(2);
let d2f_val = d2f(x);
let g_val = x.sin();
let dg_val = x.cos();
let d2g_val = d2g(x);
let g_squared = g_val.powi(2);
let g_cubed = g_val.powi(3);
(d2f_val * g_val - f_val * d2g_val) / g_squared
- S::new(2.0) * (df_val * g_val - f_val * dg_val) * dg_val / g_cubed
}
let p = [];
get_sderivative2!(h, d2h_autodiff);
assert_equal_to_decimal!(d2h_autodiff(-1.5, &p), d2h(-1.5), 14);
assert_equal_to_decimal!(d2h_autodiff(1.5, &p), d2h(1.5), 14);
}
#[test]
fn test_chain_rule_one_composition() {
fn f<S: Scalar>(x: S, _p: &[f64]) -> S {
x.powi(3)
}
fn d2f<S: Scalar>(x: S) -> S {
S::new(6.0) * x
}
fn g<S: Scalar>(x: S, _p: &[f64]) -> S {
x.sin()
}
fn d2g<S: Scalar>(x: S) -> S {
-x.sin()
}
fn h<S: Scalar>(x: S, p: &[f64]) -> S {
g(f(x, p), p)
}
#[allow(clippy::similar_names)]
fn d2h<S: Scalar>(x: S) -> S {
let f_val = f(x, &[]);
let df_val = S::new(3.0) * x.powi(2);
let d2f_val = d2f(x);
let dg_val = f_val.cos();
let d2g_val = d2g(f_val);
d2g_val * df_val.powi(2) + dg_val * d2f_val
}
get_sderivative2!(h, d2h_autodiff);
assert_equal_to_decimal!(d2h_autodiff(-1.5, &[]), d2h(-1.5), 12);
assert_equal_to_decimal!(d2h_autodiff(1.5, &[]), d2h(1.5), 12);
}
#[test]
fn test_chain_rule_two_compositions() {
fn f<S: Scalar>(x: S, _p: &[f64]) -> S {
x.powi(3)
}
fn d2f<S: Scalar>(x: S) -> S {
S::new(6.0) * x
}
fn g<S: Scalar>(x: S, _p: &[f64]) -> S {
x.sin()
}
fn d2g<S: Scalar>(x: S) -> S {
-x.sin()
}
fn h<S: Scalar>(x: S, _p: &[f64]) -> S {
S::new(5.0) / x.powi(2)
}
fn d2h<S: Scalar>(x: S) -> S {
S::new(30.0) / x.powi(4)
}
fn j<S: Scalar>(x: S, p: &[f64]) -> S {
h(g(f(x, p), p), p)
}
#[allow(clippy::similar_names)]
fn d2j<S: Scalar>(x: S) -> S {
let f_val = f(x, &[]);
let df_val = S::new(3.0) * x.powi(2);
let d2f_val = d2f(x);
let g_val = g(f_val, &[]);
let dg_val = f_val.cos();
let d2g_val = d2g(f_val);
let dh_val = S::new(-10.0) / g_val.powi(3);
let d2h_val = d2h(g_val);
d2h_val * dg_val.powi(2) * df_val.powi(2)
+ dh_val * d2g_val * df_val.powi(2)
+ dh_val * dg_val * d2f_val
}
get_sderivative2!(j, d2j_autodiff);
assert_equal_to_decimal!(d2j_autodiff(1.5, &[]), d2j(1.5), 10);
}
#[test]
#[allow(clippy::items_after_statements)]
fn test_sderivative2_polynomial() {
fn f1<S: Scalar>(_x: S, _p: &[f64]) -> S {
S::one()
}
get_sderivative2!(f1, d2f1);
assert_eq!(d2f1(2.0, &[]), test_utils::polyi_deriv2(0, 2.0));
fn f2<S: Scalar>(x: S, _p: &[f64]) -> S {
x
}
get_sderivative2!(f2, d2f2);
assert_eq!(d2f2(2.0, &[]), test_utils::polyi_deriv2(1, 2.0));
fn f3<S: Scalar>(x: S, _p: &[f64]) -> S {
x.powi(2)
}
get_sderivative2!(f3, d2f3);
assert_eq!(d2f3(2.0, &[]), test_utils::polyi_deriv2(2, 2.0));
fn f4<S: Scalar>(x: S, _p: &[f64]) -> S {
x.powi(3)
}
get_sderivative2!(f4, d2f4);
assert_eq!(d2f4(2.0, &[]), test_utils::polyi_deriv2(3, 2.0));
fn f5<S: Scalar>(x: S, _p: &[f64]) -> S {
x.powi(4)
}
get_sderivative2!(f5, d2f5);
assert_eq!(d2f5(2.0, &[]), test_utils::polyi_deriv2(4, 2.0));
fn f6<S: Scalar>(x: S, _p: &[f64]) -> S {
x.powi(5)
}
get_sderivative2!(f6, d2f6);
assert_eq!(d2f6(2.0, &[]), test_utils::polyi_deriv2(5, 2.0));
fn f7<S: Scalar>(x: S, _p: &[f64]) -> S {
x.powi(6)
}
get_sderivative2!(f7, d2f7);
assert_eq!(d2f7(2.0, &[]), test_utils::polyi_deriv2(6, 2.0));
fn f8<S: Scalar>(x: S, _p: &[f64]) -> S {
x.powi(7)
}
get_sderivative2!(f8, d2f8);
assert_eq!(d2f8(2.0, &[]), test_utils::polyi_deriv2(7, 2.0));
fn f9<S: Scalar>(x: S, _p: &[f64]) -> S {
x.powi(-1)
}
get_sderivative2!(f9, d2f9);
assert_eq!(d2f9(2.0, &[]), test_utils::polyi_deriv2(-1, 2.0));
fn f10<S: Scalar>(x: S, _p: &[f64]) -> S {
x.powi(-2)
}
get_sderivative2!(f10, d2f10);
assert_eq!(d2f10(2.0, &[]), test_utils::polyi_deriv2(-2, 2.0));
fn f11<S: Scalar>(x: S, _p: &[f64]) -> S {
x.powi(-3)
}
get_sderivative2!(f11, d2f11);
assert_eq!(d2f11(2.0, &[]), test_utils::polyi_deriv2(-3, 2.0));
fn f12<S: Scalar>(x: S, _p: &[f64]) -> S {
x.powi(-7)
}
get_sderivative2!(f12, d2f12);
assert_eq!(d2f12(2.0, &[]), test_utils::polyi_deriv2(-7, 2.0));
fn f13<S: Scalar>(x: S, _p: &[f64]) -> S {
x.powf(S::new(1.0 / 3.0))
}
get_sderivative2!(f13, d2f13);
assert_equal_to_decimal!(
d2f13(2.0, &[]),
test_utils::polyf_deriv2(1.0 / 3.0, 2.0),
16
);
fn f14<S: Scalar>(x: S, _p: &[f64]) -> S {
x.powf(S::new(7.0 / 3.0))
}
get_sderivative2!(f14, d2f14);
assert_equal_to_decimal!(
d2f14(2.0, &[]),
test_utils::polyf_deriv2(7.0 / 3.0, 2.0),
14
);
fn f15<S: Scalar>(x: S, _p: &[f64]) -> S {
x.powf(S::new(-1.0 / 3.0))
}
get_sderivative2!(f15, d2f15);
assert_equal_to_decimal!(
d2f15(2.0, &[]),
test_utils::polyf_deriv2(-1.0 / 3.0, 2.0),
16
);
fn f16<S: Scalar>(x: S, _p: &[f64]) -> S {
x.powf(S::new(-7.0 / 3.0))
}
get_sderivative2!(f16, d2f16);
assert_equal_to_decimal!(
d2f16(2.0, &[]),
test_utils::polyf_deriv2(-7.0 / 3.0, 2.0),
16
);
}
#[test]
fn test_sderivative2_square_root() {
fn f<S: Scalar>(x: S, _p: &[f64]) -> S {
x.sqrt()
}
get_sderivative2!(f, d2f);
assert_eq!(d2f(0.5, &[]), test_utils::sqrt_deriv2(0.5));
assert_eq!(d2f(1.5, &[]), test_utils::sqrt_deriv2(1.5));
}
#[test]
fn test_sderivative2_exponential() {
fn f<S: Scalar>(x: S, _p: &[f64]) -> S {
x.exp()
}
get_sderivative2!(f, d2f);
assert_eq!(d2f(-1.0, &[]), test_utils::exp_deriv2(-1.0));
assert_eq!(d2f(0.0, &[]), test_utils::exp_deriv2(0.0));
assert_eq!(d2f(1.0, &[]), test_utils::exp_deriv2(1.0));
}
#[test]
fn test_sderivative2_power() {
fn f<S: Scalar>(x: S, _p: &[f64]) -> S {
S::new(5.0).powf(x)
}
get_sderivative2!(f, d2f);
assert_eq!(d2f(-1.0, &[]), test_utils::power_deriv2(5.0, -1.0));
assert_eq!(d2f(0.0, &[]), test_utils::power_deriv2(5.0, 0.0));
assert_equal_to_decimal!(d2f(1.0, &[]), test_utils::power_deriv2(5.0, 1.0), 13);
}
#[test]
fn test_sderivative2_natural_logarithm() {
fn f<S: Scalar>(x: S, _p: &[f64]) -> S {
x.ln()
}
get_sderivative2!(f, d2f);
assert_eq!(d2f(0.5, &[]), test_utils::ln_deriv2(0.5));
assert_eq!(d2f(1.0, &[]), test_utils::ln_deriv2(1.0));
assert_eq!(d2f(1.5, &[]), test_utils::ln_deriv2(1.5));
}
#[test]
fn test_sderivative2_base_10_logarithm() {
fn f<S: Scalar>(x: S, _p: &[f64]) -> S {
x.log10()
}
get_sderivative2!(f, d2f);
assert_eq!(d2f(0.5, &[]), test_utils::log10_deriv2(0.5));
assert_eq!(d2f(1.0, &[]), test_utils::log10_deriv2(1.0));
assert_eq!(d2f(1.5, &[]), test_utils::log10_deriv2(1.5));
}
#[test]
fn test_sderivative2_sine() {
fn f<S: Scalar>(x: S, _p: &[f64]) -> S {
x.sin()
}
get_sderivative2!(f, d2f);
assert_eq!(d2f(0.0, &[]), test_utils::sin_deriv2(0.0));
assert_eq!(d2f(PI / 4.0, &[]), test_utils::sin_deriv2(PI / 4.0));
assert_eq!(d2f(PI / 2.0, &[]), test_utils::sin_deriv2(PI / 2.0));
assert_eq!(
d2f(3.0 * PI / 4.0, &[]),
test_utils::sin_deriv2(3.0 * PI / 4.0)
);
assert_eq!(d2f(PI, &[]), test_utils::sin_deriv2(PI));
assert_eq!(
d2f(5.0 * PI / 4.0, &[]),
test_utils::sin_deriv2(5.0 * PI / 4.0)
);
assert_eq!(
d2f(3.0 * PI / 2.0, &[]),
test_utils::sin_deriv2(3.0 * PI / 2.0)
);
assert_eq!(
d2f(7.0 * PI / 4.0, &[]),
test_utils::sin_deriv2(7.0 * PI / 4.0)
);
assert_eq!(d2f(2.0 * PI, &[]), test_utils::sin_deriv2(2.0 * PI));
}
#[test]
fn test_sderivative2_cosine() {
fn f<S: Scalar>(x: S, _p: &[f64]) -> S {
x.cos()
}
get_sderivative2!(f, d2f);
assert_eq!(d2f(0.0, &[]), test_utils::cos_deriv2(0.0));
assert_eq!(d2f(PI / 4.0, &[]), test_utils::cos_deriv2(PI / 4.0));
assert_eq!(d2f(PI / 2.0, &[]), test_utils::cos_deriv2(PI / 2.0));
assert_eq!(
d2f(3.0 * PI / 4.0, &[]),
test_utils::cos_deriv2(3.0 * PI / 4.0)
);
assert_eq!(d2f(PI, &[]), test_utils::cos_deriv2(PI));
assert_eq!(
d2f(5.0 * PI / 4.0, &[]),
test_utils::cos_deriv2(5.0 * PI / 4.0)
);
assert_eq!(
d2f(3.0 * PI / 2.0, &[]),
test_utils::cos_deriv2(3.0 * PI / 2.0)
);
assert_eq!(
d2f(7.0 * PI / 4.0, &[]),
test_utils::cos_deriv2(7.0 * PI / 4.0)
);
assert_eq!(d2f(2.0 * PI, &[]), test_utils::cos_deriv2(2.0 * PI));
}
#[test]
#[cfg(feature = "trig")]
fn test_sderivative2_tangent() {
fn f<S: Scalar>(x: S, _p: &[f64]) -> S {
x.tan()
}
get_sderivative2!(f, d2f);
assert_eq!(d2f(0.0, &[]), test_utils::tan_deriv2(0.0));
assert_equal_to_decimal!(d2f(PI / 4.0, &[]), test_utils::tan_deriv2(PI / 4.0), 15);
assert_equal_to_decimal!(
d2f(3.0 * PI / 4.0, &[]),
test_utils::tan_deriv2(3.0 * PI / 4.0),
15
);
assert_eq!(d2f(PI, &[]), test_utils::tan_deriv2(PI));
assert_equal_to_decimal!(
d2f(5.0 * PI / 4.0, &[]),
test_utils::tan_deriv2(5.0 * PI / 4.0),
15
);
assert_equal_to_decimal!(
d2f(7.0 * PI / 4.0, &[]),
test_utils::tan_deriv2(7.0 * PI / 4.0),
15
);
assert_eq!(d2f(2.0 * PI, &[]), test_utils::tan_deriv2(2.0 * PI));
}
#[test]
#[cfg(feature = "trig")]
fn test_sderivative2_cosecant() {
fn f<S: Scalar + Trig>(x: S, _p: &[f64]) -> S {
x.csc()
}
get_sderivative2!(f, d2f);
assert_equal_to_decimal!(d2f(PI / 4.0, &[]), test_utils::csc_deriv2(PI / 4.0), 14);
assert_equal_to_decimal!(
d2f(3.0 * PI / 4.0, &[]),
test_utils::csc_deriv2(3.0 * PI / 4.0),
14
);
assert_equal_to_decimal!(
d2f(5.0 * PI / 4.0, &[]),
test_utils::csc_deriv2(5.0 * PI / 4.0),
14
);
assert_equal_to_decimal!(
d2f(7.0 * PI / 4.0, &[]),
test_utils::csc_deriv2(7.0 * PI / 4.0),
14
);
}
#[test]
#[cfg(feature = "trig")]
fn test_sderivative2_secant() {
fn f<S: Scalar + Trig>(x: S, _p: &[f64]) -> S {
x.sec()
}
get_sderivative2!(f, d2f);
assert_eq!(d2f(0.0, &[]), test_utils::sec_deriv2(0.0));
assert_equal_to_decimal!(d2f(PI / 4.0, &[]), test_utils::sec_deriv2(PI / 4.0), 15);
assert_equal_to_decimal!(
d2f(3.0 * PI / 4.0, &[]),
test_utils::sec_deriv2(3.0 * PI / 4.0),
15
);
assert_eq!(d2f(PI, &[]), test_utils::sec_deriv2(PI));
assert_equal_to_decimal!(
d2f(5.0 * PI / 4.0, &[]),
test_utils::sec_deriv2(5.0 * PI / 4.0),
15
);
assert_equal_to_decimal!(
d2f(7.0 * PI / 4.0, &[]),
test_utils::sec_deriv2(7.0 * PI / 4.0),
15
);
assert_eq!(d2f(2.0 * PI, &[]), test_utils::sec_deriv2(2.0 * PI));
}
#[test]
#[cfg(feature = "trig")]
fn test_sderivative2_cotangent() {
fn f<S: Scalar + Trig>(x: S, _p: &[f64]) -> S {
x.cot()
}
get_sderivative2!(f, d2f);
assert_equal_to_decimal!(d2f(PI / 4.0, &[]), test_utils::cot_deriv2(PI / 4.0), 14);
assert_equal_to_decimal!(d2f(PI / 2.0, &[]), test_utils::cot_deriv2(PI / 2.0), 15);
assert_equal_to_decimal!(
d2f(3.0 * PI / 4.0, &[]),
test_utils::cot_deriv2(3.0 * PI / 4.0),
14
);
assert_equal_to_decimal!(
d2f(5.0 * PI / 4.0, &[]),
test_utils::cot_deriv2(5.0 * PI / 4.0),
14
);
assert_equal_to_decimal!(
d2f(3.0 * PI / 2.0, &[]),
test_utils::cot_deriv2(3.0 * PI / 2.0),
14
);
assert_equal_to_decimal!(
d2f(7.0 * PI / 4.0, &[]),
test_utils::cot_deriv2(7.0 * PI / 4.0),
14
);
}
#[test]
fn test_sderivative2_inverse_sine() {
fn f<S: Scalar>(x: S, _p: &[f64]) -> S {
x.asin()
}
get_sderivative2!(f, d2f);
assert_eq!(d2f(-0.5, &[]), test_utils::asin_deriv2(-0.5));
assert_eq!(d2f(0.0, &[]), test_utils::asin_deriv2(0.0));
assert_eq!(d2f(0.5, &[]), test_utils::asin_deriv2(0.5));
}
#[test]
fn test_sderivative2_inverse_cosine() {
fn f<S: Scalar>(x: S, _p: &[f64]) -> S {
x.acos()
}
get_sderivative2!(f, d2f);
assert_eq!(d2f(-0.5, &[]), test_utils::acos_deriv2(-0.5));
assert_eq!(d2f(0.0, &[]), test_utils::acos_deriv2(0.0));
assert_eq!(d2f(0.5, &[]), test_utils::acos_deriv2(0.5));
}
#[test]
fn test_sderivative2_inverse_tangent() {
fn f<S: Scalar>(x: S, _p: &[f64]) -> S {
x.atan()
}
get_sderivative2!(f, d2f);
assert_eq!(d2f(-1.5, &[]), test_utils::atan_deriv2(-1.5));
assert_eq!(d2f(-1.0, &[]), test_utils::atan_deriv2(-1.0));
assert_eq!(d2f(-0.5, &[]), test_utils::atan_deriv2(-0.5));
assert_eq!(d2f(0.0, &[]), test_utils::atan_deriv2(0.0));
assert_eq!(d2f(0.5, &[]), test_utils::atan_deriv2(0.5));
assert_eq!(d2f(1.0, &[]), test_utils::atan_deriv2(1.0));
assert_eq!(d2f(1.5, &[]), test_utils::atan_deriv2(1.5));
}
#[test]
#[cfg(feature = "trig")]
fn test_sderivative2_inverse_cosecant() {
fn f<S: Scalar + Trig>(x: S, _p: &[f64]) -> S {
x.acsc()
}
get_sderivative2!(f, d2f);
assert_equal_to_decimal!(d2f(-1.5, &[]), test_utils::acsc_deriv2(-1.5), 16);
assert_equal_to_decimal!(d2f(1.5, &[]), test_utils::acsc_deriv2(1.5), 16);
}
#[test]
#[cfg(feature = "trig")]
fn test_sderivative2_inverse_secant() {
fn f<S: Scalar + Trig>(x: S, _p: &[f64]) -> S {
x.asec()
}
get_sderivative2!(f, d2f);
assert_equal_to_decimal!(d2f(-1.5, &[]), test_utils::asec_deriv2(-1.5), 16);
assert_equal_to_decimal!(d2f(1.5, &[]), test_utils::asec_deriv2(1.5), 16);
}
#[test]
#[cfg(feature = "trig")]
fn test_sderivative2_inverse_cotangent() {
fn f<S: Scalar + Trig>(x: S, _p: &[f64]) -> S {
x.acot()
}
get_sderivative2!(f, d2f);
assert_equal_to_decimal!(d2f(-1.5, &[]), test_utils::acot_deriv2(-1.5), 15);
assert_eq!(d2f(-1.0, &[]), test_utils::acot_deriv2(-1.0));
assert_eq!(d2f(-0.5, &[]), test_utils::acot_deriv2(-0.5));
assert_eq!(d2f(0.5, &[]), test_utils::acot_deriv2(0.5));
assert_eq!(d2f(1.0, &[]), test_utils::acot_deriv2(1.0));
assert_equal_to_decimal!(d2f(1.5, &[]), test_utils::acot_deriv2(1.5), 15);
}
#[test]
fn test_sderivative2_hyperbolic_sine() {
fn f<S: Scalar>(x: S, _p: &[f64]) -> S {
x.sinh()
}
get_sderivative2!(f, d2f);
assert_eq!(d2f(-1.0, &[]), test_utils::sinh_deriv2(-1.0));
assert_eq!(d2f(0.0, &[]), test_utils::sinh_deriv2(0.0));
assert_eq!(d2f(1.0, &[]), test_utils::sinh_deriv2(1.0));
}
#[test]
fn test_sderivative2_hyperbolic_cosine() {
fn f<S: Scalar>(x: S, _p: &[f64]) -> S {
x.cosh()
}
get_sderivative2!(f, d2f);
assert_eq!(d2f(-1.0, &[]), test_utils::cosh_deriv2(-1.0));
assert_eq!(d2f(0.0, &[]), test_utils::cosh_deriv2(0.0));
assert_eq!(d2f(1.0, &[]), test_utils::cosh_deriv2(1.0));
}
#[test]
#[cfg(feature = "trig")]
fn test_sderivative2_hyperbolic_tangent() {
fn f<S: Scalar>(x: S, _p: &[f64]) -> S {
x.tanh()
}
get_sderivative2!(f, d2f);
assert_eq!(d2f(-1.0, &[]), test_utils::tanh_deriv2(-1.0));
assert_eq!(d2f(0.0, &[]), test_utils::tanh_deriv2(0.0));
assert_eq!(d2f(1.0, &[]), test_utils::tanh_deriv2(1.0));
}
#[test]
#[cfg(feature = "trig")]
fn test_sderivative2_hyperbolic_cosecant() {
fn f<S: Scalar + Trig>(x: S, _p: &[f64]) -> S {
x.csch()
}
get_sderivative2!(f, d2f);
assert_equal_to_decimal!(d2f(-1.0, &[]), test_utils::csch_deriv2(-1.0), 16);
assert_equal_to_decimal!(d2f(1.0, &[]), test_utils::csch_deriv2(1.0), 16);
}
#[test]
#[cfg(feature = "trig")]
fn test_sderivative2_hyperbolic_secant() {
fn f<S: Scalar + Trig>(x: S, _p: &[f64]) -> S {
x.sech()
}
get_sderivative2!(f, d2f);
assert_equal_to_decimal!(d2f(-1.0, &[]), test_utils::sech_deriv2(-1.0), 15);
assert_eq!(d2f(0.0, &[]), test_utils::sech_deriv2(0.0));
assert_equal_to_decimal!(d2f(1.0, &[]), test_utils::sech_deriv2(1.0), 15);
}
#[test]
#[cfg(feature = "trig")]
fn test_sderivative2_hyperbolic_cotangent() {
fn f<S: Scalar + Trig>(x: S, _p: &[f64]) -> S {
x.coth()
}
get_sderivative2!(f, d2f);
assert_equal_to_decimal!(d2f(-1.0, &[]), test_utils::coth_deriv2(-1.0), 15);
assert_equal_to_decimal!(d2f(1.0, &[]), test_utils::coth_deriv2(1.0), 15);
}
#[test]
fn test_sderivative2_inverse_hyperbolic_sine() {
fn f<S: Scalar>(x: S, _p: &[f64]) -> S {
x.asinh()
}
get_sderivative2!(f, d2f);
assert_eq!(d2f(-1.5, &[]), test_utils::asinh_deriv2(-1.5));
assert_eq!(d2f(-1.0, &[]), test_utils::asinh_deriv2(-1.0));
assert_eq!(d2f(-0.5, &[]), test_utils::asinh_deriv2(-0.5));
assert_eq!(d2f(0.0, &[]), test_utils::asinh_deriv2(0.0));
assert_eq!(d2f(0.5, &[]), test_utils::asinh_deriv2(0.5));
assert_eq!(d2f(1.0, &[]), test_utils::asinh_deriv2(1.0));
assert_eq!(d2f(1.5, &[]), test_utils::asinh_deriv2(1.5));
}
#[test]
fn test_sderivative2_inverse_hyperbolic_cosine() {
fn f<S: Scalar>(x: S, _p: &[f64]) -> S {
x.acosh()
}
get_sderivative2!(f, d2f);
assert_eq!(d2f(1.5, &[]), test_utils::acosh_deriv2(1.5));
}
#[test]
fn test_sderivative2_inverse_hyperbolic_tangent() {
fn f<S: Scalar>(x: S, _p: &[f64]) -> S {
x.atanh()
}
get_sderivative2!(f, d2f);
assert_eq!(d2f(-0.5, &[]), test_utils::atanh_deriv2(-0.5));
assert_eq!(d2f(0.0, &[]), test_utils::atanh_deriv2(0.0));
assert_eq!(d2f(0.5, &[]), test_utils::atanh_deriv2(0.5));
}
#[test]
#[cfg(feature = "trig")]
fn test_sderivative2_inverse_hyperbolic_cosecant() {
fn f<S: Scalar + Trig>(x: S, _p: &[f64]) -> S {
x.acsch()
}
get_sderivative2!(f, d2f);
assert_equal_to_decimal!(d2f(-1.5, &[]), test_utils::acsch_deriv2(-1.5), 15);
assert_eq!(d2f(-1.0, &[]), test_utils::acsch_deriv2(-1.0));
assert_eq!(d2f(-0.5, &[]), test_utils::acsch_deriv2(-0.5));
assert_eq!(d2f(0.5, &[]), test_utils::acsch_deriv2(0.5));
assert_eq!(d2f(1.0, &[]), test_utils::acsch_deriv2(1.0));
assert_equal_to_decimal!(d2f(1.5, &[]), test_utils::acsch_deriv2(1.5), 15);
}
#[test]
#[cfg(feature = "trig")]
fn test_sderivative2_inverse_hyperbolic_secant() {
fn f<S: Scalar + Trig>(x: S, _p: &[f64]) -> S {
x.asech()
}
get_sderivative2!(f, d2f);
assert_eq!(d2f(0.5, &[]), test_utils::asech_deriv2(0.5));
}
#[test]
#[cfg(feature = "trig")]
fn test_sderivative2_inverse_hyperbolic_cotangent() {
fn f<S: Scalar + Trig>(x: S, _p: &[f64]) -> S {
x.acoth()
}
get_sderivative2!(f, d2f);
assert_equal_to_decimal!(d2f(-1.5, &[]), test_utils::acoth_deriv2(-1.5), 15);
assert_equal_to_decimal!(d2f(1.5, &[]), test_utils::acoth_deriv2(1.5), 15);
}
#[test]
fn test_sderivative2_with_runtime_parameters() {
fn f<S: Scalar>(x: S, p: &[f64]) -> S {
let alpha = S::new(p[0]);
let beta = S::new(p[1]);
(alpha * x).exp() * (beta * x).cos()
}
fn d2f<S: Scalar>(x: S, p: &[f64]) -> S {
let alpha = S::new(p[0]);
let beta = S::new(p[1]);
let exp_term = (alpha * x).exp();
let cos_term = (beta * x).cos();
let sin_term = (beta * x).sin();
exp_term
* ((alpha.powi(2) - beta.powi(2)) * cos_term
- S::new(2.0) * alpha * beta * sin_term)
}
let x0 = 0.3;
let p = [0.5, 2.0];
get_sderivative2!(f, d2f_autodiff);
let d2f_eval_autodiff: f64 = d2f_autodiff(x0, &p);
let d2f_eval: f64 = d2f(x0, &p);
assert_equal_to_decimal!(d2f_eval_autodiff, d2f_eval, 15);
}
#[test]
fn test_sderivative2_custom_params() {
struct Data {
a: f64,
b: f64,
c: f64,
}
#[allow(clippy::many_single_char_names)]
fn f<S: Scalar>(x: S, p: &Data) -> S {
let a = S::new(p.a);
let b = S::new(p.b);
let c = S::new(p.c);
a * x.powi(2) + b * x + c
}
let p = Data {
a: 2.5,
b: -1.3,
c: 4.7,
};
get_sderivative2!(f, d2f, Data);
let d2f_true = |_x: f64| 2.0 * p.a;
let x0 = 1.0;
let d2f_eval: f64 = d2f(x0, &p);
let d2f_eval_true: f64 = d2f_true(x0);
assert_equal_to_decimal!(d2f_eval, d2f_eval_true, 15);
}
}