#[macro_export]
macro_rules! get_sderivative {
($f:ident, $func_name:ident) => {
get_sderivative!($f, $func_name, [f64]);
};
($f:ident, $func_name:ident, $param_type:ty) => {
fn $func_name<R: RealField>(x0: R, p: &$param_type) -> f64 {
let x0 = $crate::Dual::new(x0.into(), 1.0);
let f_x0 = $f(x0, p);
f_x0.get_dual()
}
};
}
#[cfg(test)]
mod tests {
use crate::test_utils;
use linalg_traits::RealField;
use numtest::*;
use std::f64::consts::PI;
#[test]
fn test_product_rule() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.powi(3)
}
fn df<R: RealField>(x: R, _p: &[f64]) -> R {
3.0 * x.powi(2)
}
fn g<R: RealField>(x: R, _p: &[f64]) -> R {
x.sin()
}
fn dg<R: RealField>(x: R, _p: &[f64]) -> R {
x.cos()
}
fn h<R: RealField>(x: R, p: &[f64]) -> R {
f(x, p) * g(x, p)
}
fn dh<R: RealField>(x: R, p: &[f64]) -> R {
df(x, p) * g(x, p) + f(x, p) * dg(x, p)
}
let p = [];
get_sderivative!(h, dh_autodiff);
assert_eq!(dh_autodiff(-1.5, &p), dh(-1.5, &p));
assert_eq!(dh_autodiff(1.5, &p), dh(1.5, &p));
}
#[test]
fn test_quotient_rule() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.powi(3)
}
fn df<R: RealField>(x: R, _p: &[f64]) -> R {
3.0 * x.powi(2)
}
fn g<R: RealField>(x: R, _p: &[f64]) -> R {
x.sin()
}
fn dg<R: RealField>(x: R, _p: &[f64]) -> R {
x.cos()
}
fn h<R: RealField>(x: R, p: &[f64]) -> R {
f(x, p) / g(x, p)
}
fn dh<R: RealField>(x: R, p: &[f64]) -> R {
(g(x, p) * df(x, p) - f(x, p) * dg(x, p)) / g(x, p).powi(2)
}
let p = [];
get_sderivative!(h, dh_autodiff);
assert_eq!(dh_autodiff(-1.5, &p), dh(-1.5, &p));
assert_eq!(dh_autodiff(1.5, &p), dh(1.5, &p));
}
#[test]
fn test_chain_rule_one_composition() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.powi(3)
}
fn df<R: RealField>(x: R) -> R {
3.0 * x.powi(2)
}
fn g<R: RealField>(x: R, _p: &[f64]) -> R {
x.sin()
}
fn dg<R: RealField>(x: R) -> R {
x.cos()
}
fn h<R: RealField>(x: R, p: &[f64]) -> R {
g(f(x, p), p)
}
fn dh<R: RealField>(x: R) -> R {
dg(f(x, &[])) * df(x)
}
get_sderivative!(h, dh_autodiff);
assert_eq!(dh_autodiff(-1.5, &[]), dh(-1.5));
assert_eq!(dh_autodiff(1.5, &[]), dh(1.5));
}
#[test]
fn test_chain_rule_two_compositions() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.powi(3)
}
fn df<R: RealField>(x: R) -> R {
3.0 * x.powi(2)
}
fn g<R: RealField>(x: R, _p: &[f64]) -> R {
x.sin()
}
fn dg<R: RealField>(x: R) -> R {
x.cos()
}
fn h<R: RealField>(x: R, _p: &[f64]) -> R {
5.0 / x.powi(2)
}
fn dh<R: RealField>(x: R) -> R {
-10.0 / x.powi(3)
}
fn j<R: RealField>(x: R, p: &[f64]) -> R {
h(g(f(x, p), p), p)
}
fn dj<R: RealField>(x: R) -> R {
dh(g(f(x, &[]), &[])) * dg(f(x, &[])) * df(x)
}
get_sderivative!(j, dj_autodiff);
assert_equal_to_decimal!(dj_autodiff(1.5, &[]), dj(1.5), 12);
}
#[test]
#[allow(clippy::items_after_statements)]
fn test_sderivative_polynomial() {
fn f1<R: RealField>(_x: R, _p: &[f64]) -> R {
R::one()
}
get_sderivative!(f1, df1);
assert_eq!(df1(2.0, &[]), test_utils::polyi_deriv(0, 2.0));
fn f2<R: RealField>(x: R, _p: &[f64]) -> R {
x
}
get_sderivative!(f2, df2);
assert_eq!(df2(2.0, &[]), test_utils::polyi_deriv(1, 2.0));
fn f3<R: RealField>(x: R, _p: &[f64]) -> R {
x.powi(2)
}
get_sderivative!(f3, df3);
assert_eq!(df3(2.0, &[]), test_utils::polyi_deriv(2, 2.0));
fn f4<R: RealField>(x: R, _p: &[f64]) -> R {
x.powi(3)
}
get_sderivative!(f4, df4);
assert_eq!(df4(2.0, &[]), test_utils::polyi_deriv(3, 2.0));
fn f5<R: RealField>(x: R, _p: &[f64]) -> R {
x.powi(4)
}
get_sderivative!(f5, df5);
assert_eq!(df5(2.0, &[]), test_utils::polyi_deriv(4, 2.0));
fn f6<R: RealField>(x: R, _p: &[f64]) -> R {
x.powi(5)
}
get_sderivative!(f6, df6);
assert_eq!(df6(2.0, &[]), test_utils::polyi_deriv(5, 2.0));
fn f7<R: RealField>(x: R, _p: &[f64]) -> R {
x.powi(6)
}
get_sderivative!(f7, df7);
assert_eq!(df7(2.0, &[]), test_utils::polyi_deriv(6, 2.0));
fn f8<R: RealField>(x: R, _p: &[f64]) -> R {
x.powi(7)
}
get_sderivative!(f8, df8);
assert_eq!(df8(2.0, &[]), test_utils::polyi_deriv(7, 2.0));
fn f9<R: RealField>(x: R, _p: &[f64]) -> R {
x.powi(-1)
}
get_sderivative!(f9, df9);
assert_eq!(df9(2.0, &[]), test_utils::polyi_deriv(-1, 2.0));
fn f10<R: RealField>(x: R, _p: &[f64]) -> R {
x.powi(-2)
}
get_sderivative!(f10, df10);
assert_eq!(df10(2.0, &[]), test_utils::polyi_deriv(-2, 2.0));
fn f11<R: RealField>(x: R, _p: &[f64]) -> R {
x.powi(-3)
}
get_sderivative!(f11, df11);
assert_eq!(df11(2.0, &[]), test_utils::polyi_deriv(-3, 2.0));
fn f12<R: RealField>(x: R, _p: &[f64]) -> R {
x.powi(-7)
}
get_sderivative!(f12, df12);
assert_eq!(df12(2.0, &[]), test_utils::polyi_deriv(-7, 2.0));
fn f13<R: RealField>(x: R, _p: &[f64]) -> R {
x.powf(R::from(1.0 / 3.0))
}
get_sderivative!(f13, df13);
assert_eq!(df13(2.0, &[]), test_utils::polyf_deriv(1.0 / 3.0, 2.0));
fn f14<R: RealField>(x: R, _p: &[f64]) -> R {
x.powf(R::from(7.0 / 3.0))
}
get_sderivative!(f14, df14);
assert_equal_to_decimal!(df14(2.0, &[]), test_utils::polyf_deriv(7.0 / 3.0, 2.0), 15);
fn f15<R: RealField>(x: R, _p: &[f64]) -> R {
x.powf(R::from(-1.0 / 3.0))
}
get_sderivative!(f15, df15);
assert_eq!(df15(2.0, &[]), test_utils::polyf_deriv(-1.0 / 3.0, 2.0));
fn f16<R: RealField>(x: R, _p: &[f64]) -> R {
x.powf(R::from(-7.0 / 3.0))
}
get_sderivative!(f16, df16);
assert_eq!(df16(2.0, &[]), test_utils::polyf_deriv(-7.0 / 3.0, 2.0));
}
#[test]
fn test_sderivative_square_root() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.sqrt()
}
get_sderivative!(f, df);
assert_eq!(df(0.5, &[]), test_utils::sqrt_deriv(0.5));
assert_eq!(df(1.5, &[]), test_utils::sqrt_deriv(1.5));
}
#[test]
fn test_sderivative_exponential() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.exp()
}
get_sderivative!(f, df);
assert_eq!(df(-1.0, &[]), test_utils::exp_deriv(-1.0));
assert_eq!(df(0.0, &[]), test_utils::exp_deriv(0.0));
assert_eq!(df(1.0, &[]), test_utils::exp_deriv(1.0));
}
#[test]
fn test_sderivative_power() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
R::from(5.0).powf(x)
}
get_sderivative!(f, df);
assert_eq!(df(-1.0, &[]), test_utils::power_deriv(5.0, -1.0));
assert_eq!(df(0.0, &[]), test_utils::power_deriv(5.0, 0.0));
assert_equal_to_decimal!(df(1.0, &[]), test_utils::power_deriv(5.0, 1.0), 14);
}
#[test]
fn test_sderivative_natural_logarithm() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.ln()
}
get_sderivative!(f, df);
assert_eq!(df(0.5, &[]), test_utils::ln_deriv(0.5));
assert_eq!(df(1.0, &[]), test_utils::ln_deriv(1.0));
assert_eq!(df(1.5, &[]), test_utils::ln_deriv(1.5));
}
#[test]
fn test_sderivative_base_10_logarithm() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.log10()
}
get_sderivative!(f, df);
assert_eq!(df(0.5, &[]), test_utils::log10_deriv(0.5));
assert_eq!(df(1.0, &[]), test_utils::log10_deriv(1.0));
assert_eq!(df(1.5, &[]), test_utils::log10_deriv(1.5));
}
#[test]
fn test_sderivative_sine() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.sin()
}
get_sderivative!(f, df);
assert_eq!(df(0.0, &[]), test_utils::sin_deriv(0.0));
assert_eq!(df(PI / 4.0, &[]), test_utils::sin_deriv(PI / 4.0));
assert_eq!(df(PI / 2.0, &[]), test_utils::sin_deriv(PI / 2.0));
assert_eq!(
df(3.0 * PI / 4.0, &[]),
test_utils::sin_deriv(3.0 * PI / 4.0)
);
assert_eq!(df(PI, &[]), test_utils::sin_deriv(PI));
assert_eq!(
df(5.0 * PI / 4.0, &[]),
test_utils::sin_deriv(5.0 * PI / 4.0)
);
assert_eq!(
df(3.0 * PI / 2.0, &[]),
test_utils::sin_deriv(3.0 * PI / 2.0)
);
assert_eq!(
df(7.0 * PI / 4.0, &[]),
test_utils::sin_deriv(7.0 * PI / 4.0)
);
assert_eq!(df(2.0 * PI, &[]), test_utils::sin_deriv(2.0 * PI));
}
#[test]
fn test_sderivative_cosine() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.cos()
}
get_sderivative!(f, df);
assert_eq!(df(0.0, &[]), test_utils::cos_deriv(0.0));
assert_eq!(df(PI / 4.0, &[]), test_utils::cos_deriv(PI / 4.0));
assert_eq!(df(PI / 2.0, &[]), test_utils::cos_deriv(PI / 2.0));
assert_eq!(
df(3.0 * PI / 4.0, &[]),
test_utils::cos_deriv(3.0 * PI / 4.0)
);
assert_eq!(df(PI, &[]), test_utils::cos_deriv(PI));
assert_eq!(
df(5.0 * PI / 4.0, &[]),
test_utils::cos_deriv(5.0 * PI / 4.0)
);
assert_eq!(
df(3.0 * PI / 2.0, &[]),
test_utils::cos_deriv(3.0 * PI / 2.0)
);
assert_eq!(
df(7.0 * PI / 4.0, &[]),
test_utils::cos_deriv(7.0 * PI / 4.0)
);
assert_eq!(df(2.0 * PI, &[]), test_utils::cos_deriv(2.0 * PI));
}
#[test]
fn test_sderivative_tangent() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.tan()
}
get_sderivative!(f, df);
assert_eq!(df(0.0, &[]), test_utils::tan_deriv(0.0));
assert_eq!(df(PI / 4.0, &[]), test_utils::tan_deriv(PI / 4.0));
assert_eq!(
df(3.0 * PI / 4.0, &[]),
test_utils::tan_deriv(3.0 * PI / 4.0)
);
assert_eq!(df(PI, &[]), test_utils::tan_deriv(PI));
assert_eq!(
df(5.0 * PI / 4.0, &[]),
test_utils::tan_deriv(5.0 * PI / 4.0)
);
assert_eq!(
df(7.0 * PI / 4.0, &[]),
test_utils::tan_deriv(7.0 * PI / 4.0)
);
assert_eq!(df(2.0 * PI, &[]), test_utils::tan_deriv(2.0 * PI));
}
#[test]
fn test_sderivative_cosecant() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.csc()
}
get_sderivative!(f, df);
assert_equal_to_decimal!(df(PI / 4.0, &[]), test_utils::csc_deriv(PI / 4.0), 15);
assert_equal_to_decimal!(
df(3.0 * PI / 4.0, &[]),
test_utils::csc_deriv(3.0 * PI / 4.0),
15
);
assert_equal_to_decimal!(
df(5.0 * PI / 4.0, &[]),
test_utils::csc_deriv(5.0 * PI / 4.0),
15
);
assert_equal_to_decimal!(
df(7.0 * PI / 4.0, &[]),
test_utils::csc_deriv(7.0 * PI / 4.0),
15
);
}
#[test]
fn test_sderivative_secant() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.sec()
}
get_sderivative!(f, df);
assert_eq!(df(0.0, &[]), test_utils::sec_deriv(0.0));
assert_eq!(df(PI / 4.0, &[]), test_utils::sec_deriv(PI / 4.0));
assert_eq!(
df(3.0 * PI / 4.0, &[]),
test_utils::sec_deriv(3.0 * PI / 4.0)
);
assert_eq!(df(PI, &[]), test_utils::sec_deriv(PI));
assert_eq!(
df(5.0 * PI / 4.0, &[]),
test_utils::sec_deriv(5.0 * PI / 4.0)
);
assert_eq!(
df(7.0 * PI / 4.0, &[]),
test_utils::sec_deriv(7.0 * PI / 4.0)
);
assert_eq!(df(2.0 * PI, &[]), test_utils::sec_deriv(2.0 * PI));
}
#[test]
fn test_sderivative_cotangent() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.cot()
}
get_sderivative!(f, df);
assert_equal_to_decimal!(df(PI / 4.0, &[]), test_utils::cot_deriv(PI / 4.0), 15);
assert_equal_to_decimal!(df(PI / 2.0, &[]), test_utils::cot_deriv(PI / 2.0), 16);
assert_eq!(
df(3.0 * PI / 4.0, &[]),
test_utils::cot_deriv(3.0 * PI / 4.0)
);
assert_eq!(
df(5.0 * PI / 4.0, &[]),
test_utils::cot_deriv(5.0 * PI / 4.0)
);
assert_equal_to_decimal!(
df(3.0 * PI / 2.0, &[]),
test_utils::cot_deriv(3.0 * PI / 2.0),
15
);
assert_eq!(
df(7.0 * PI / 4.0, &[]),
test_utils::cot_deriv(7.0 * PI / 4.0)
);
}
#[test]
fn test_sderivative_inverse_sine() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.asin()
}
get_sderivative!(f, df);
assert_eq!(df(-0.5, &[]), test_utils::asin_deriv(-0.5));
assert_eq!(df(0.0, &[]), test_utils::asin_deriv(0.0));
assert_eq!(df(0.5, &[]), test_utils::asin_deriv(0.5));
}
#[test]
fn test_sderivative_inverse_cosine() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.acos()
}
get_sderivative!(f, df);
assert_eq!(df(-0.5, &[]), test_utils::acos_deriv(-0.5));
assert_eq!(df(0.0, &[]), test_utils::acos_deriv(0.0));
assert_eq!(df(0.5, &[]), test_utils::acos_deriv(0.5));
}
#[test]
fn test_sderivative_inverse_tangent() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.atan()
}
get_sderivative!(f, df);
assert_eq!(df(-1.5, &[]), test_utils::atan_deriv(-1.5));
assert_eq!(df(-1.0, &[]), test_utils::atan_deriv(-1.0));
assert_eq!(df(-0.5, &[]), test_utils::atan_deriv(-0.5));
assert_eq!(df(0.0, &[]), test_utils::atan_deriv(0.0));
assert_eq!(df(0.5, &[]), test_utils::atan_deriv(0.5));
assert_eq!(df(1.0, &[]), test_utils::atan_deriv(1.0));
assert_eq!(df(1.5, &[]), test_utils::atan_deriv(1.5));
}
#[test]
fn test_sderivative_inverse_cosecant() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.acsc()
}
get_sderivative!(f, df);
assert_eq!(df(-1.5, &[]), test_utils::acsc_deriv(-1.5));
assert_eq!(df(1.5, &[]), test_utils::acsc_deriv(1.5));
}
#[test]
fn test_sderivative_inverse_secant() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.asec()
}
get_sderivative!(f, df);
assert_eq!(df(-1.5, &[]), test_utils::asec_deriv(-1.5));
assert_eq!(df(1.5, &[]), test_utils::asec_deriv(1.5));
}
#[test]
fn test_sderivative_inverse_cotangent() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.acot()
}
get_sderivative!(f, df);
assert_equal_to_decimal!(df(-1.5, &[]), test_utils::acot_deriv(-1.5), 16);
assert_eq!(df(-1.0, &[]), test_utils::acot_deriv(-1.0));
assert_eq!(df(-0.5, &[]), test_utils::acot_deriv(-0.5));
assert_eq!(df(0.5, &[]), test_utils::acot_deriv(0.5));
assert_eq!(df(1.0, &[]), test_utils::acot_deriv(1.0));
assert_equal_to_decimal!(df(1.5, &[]), test_utils::acot_deriv(1.5), 16);
}
#[test]
fn test_sderivative_hyperbolic_sine() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.sinh()
}
get_sderivative!(f, df);
assert_eq!(df(-1.0, &[]), test_utils::sinh_deriv(-1.0));
assert_eq!(df(0.0, &[]), test_utils::sinh_deriv(0.0));
assert_eq!(df(1.0, &[]), test_utils::sinh_deriv(1.0));
}
#[test]
fn test_sderivative_hyperbolic_cosine() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.cosh()
}
get_sderivative!(f, df);
assert_eq!(df(-1.0, &[]), test_utils::cosh_deriv(-1.0));
assert_eq!(df(0.0, &[]), test_utils::cosh_deriv(0.0));
assert_eq!(df(1.0, &[]), test_utils::cosh_deriv(1.0));
}
#[test]
fn test_sderivative_hyperbolic_tangent() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.tanh()
}
get_sderivative!(f, df);
assert_eq!(df(-1.0, &[]), test_utils::tanh_deriv(-1.0));
assert_eq!(df(0.0, &[]), test_utils::tanh_deriv(0.0));
assert_eq!(df(1.0, &[]), test_utils::tanh_deriv(1.0));
}
#[test]
fn test_sderivative_hyperbolic_cosecant() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.csch()
}
get_sderivative!(f, df);
assert_equal_to_decimal!(df(-1.0, &[]), test_utils::csch_deriv(-1.0), 15);
assert_equal_to_decimal!(df(1.0, &[]), test_utils::csch_deriv(1.0), 15);
}
#[test]
fn test_sderivative_hyperbolic_secant() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.sech()
}
get_sderivative!(f, df);
assert_equal_to_decimal!(df(-1.0, &[]), test_utils::sech_deriv(-1.0), 16);
assert_eq!(df(0.0, &[]), test_utils::sech_deriv(0.0));
assert_equal_to_decimal!(df(1.0, &[]), test_utils::sech_deriv(1.0), 16);
}
#[test]
fn test_sderivative_hyperbolic_cotangent() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.coth()
}
get_sderivative!(f, df);
assert_equal_to_decimal!(df(-1.0, &[]), test_utils::coth_deriv(-1.0), 15);
assert_equal_to_decimal!(df(1.0, &[]), test_utils::coth_deriv(1.0), 15);
}
#[test]
fn test_sderivative_inverse_hyperbolic_sine() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.asinh()
}
get_sderivative!(f, df);
assert_eq!(df(-1.5, &[]), test_utils::asinh_deriv(-1.5));
assert_eq!(df(-1.0, &[]), test_utils::asinh_deriv(-1.0));
assert_eq!(df(-0.5, &[]), test_utils::asinh_deriv(-0.5));
assert_eq!(df(0.0, &[]), test_utils::asinh_deriv(0.0));
assert_eq!(df(0.5, &[]), test_utils::asinh_deriv(0.5));
assert_eq!(df(1.0, &[]), test_utils::asinh_deriv(1.0));
assert_eq!(df(1.5, &[]), test_utils::asinh_deriv(1.5));
}
#[test]
fn test_sderivative_inverse_hyperbolic_cosine() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.acosh()
}
get_sderivative!(f, df);
assert_eq!(df(1.5, &[]), test_utils::acosh_deriv(1.5));
}
#[test]
fn test_sderivative_inverse_hyperbolic_tangent() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.atanh()
}
get_sderivative!(f, df);
assert_eq!(df(-0.5, &[]), test_utils::atanh_deriv(-0.5));
assert_eq!(df(0.0, &[]), test_utils::atanh_deriv(0.0));
assert_eq!(df(0.5, &[]), test_utils::atanh_deriv(0.5));
}
#[test]
fn test_sderivative_inverse_hyperbolic_cosecant() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.acsch()
}
get_sderivative!(f, df);
assert_equal_to_decimal!(df(-1.5, &[]), test_utils::acsch_deriv(-1.5), 16);
assert_eq!(df(-1.0, &[]), test_utils::acsch_deriv(-1.0));
assert_eq!(df(-0.5, &[]), test_utils::acsch_deriv(-0.5));
assert_eq!(df(0.5, &[]), test_utils::acsch_deriv(0.5));
assert_eq!(df(1.0, &[]), test_utils::acsch_deriv(1.0));
assert_equal_to_decimal!(df(1.5, &[]), test_utils::acsch_deriv(1.5), 16);
}
#[test]
fn test_sderivative_inverse_hyperbolic_secant() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.asech()
}
get_sderivative!(f, df);
assert_eq!(df(0.5, &[]), test_utils::asech_deriv(0.5));
}
#[test]
fn test_sderivative_inverse_hyperbolic_cotangent() {
fn f<R: RealField>(x: R, _p: &[f64]) -> R {
x.acoth()
}
get_sderivative!(f, df);
assert_equal_to_decimal!(df(-1.5, &[]), test_utils::acoth_deriv(-1.5), 16);
assert_equal_to_decimal!(df(1.5, &[]), test_utils::acoth_deriv(1.5), 16);
}
#[test]
fn test_sderivative_with_runtime_parameters() {
fn f<R: RealField>(x: R, p: &[f64]) -> R {
let alpha = R::from(p[0]);
let beta = R::from(p[1]);
(alpha * x).exp() * (beta * x).cos()
}
fn df<R: RealField>(x: R, p: &[f64]) -> R {
let alpha = R::from(p[0]);
let beta = R::from(p[1]);
let exp_term = (alpha * x).exp();
let cos_term = (beta * x).cos();
let sin_term = (beta * x).sin();
exp_term * (alpha * cos_term - beta * sin_term)
}
let x0 = 0.3;
let p = [0.5, 2.0];
get_sderivative!(f, df_autodiff);
let df_eval_autodiff: f64 = df_autodiff(x0, &p);
let df_eval: f64 = df(x0, &p);
assert_equal_to_decimal!(df_eval_autodiff, df_eval, 16);
}
#[test]
fn test_sderivative_custom_params() {
struct Data {
a: f64,
b: f64,
c: f64,
}
#[allow(clippy::many_single_char_names)]
fn f<R: RealField>(x: R, p: &Data) -> R {
let a = R::from(p.a);
let b = R::from(p.b);
let c = R::from(p.c);
a * x.powi(2) + b * x + c
}
let p = Data {
a: 2.5,
b: -1.3,
c: 4.7,
};
get_sderivative!(f, df, Data);
let df_true = |x: f64| 2.0 * p.a * x + p.b;
let x0 = 1.0;
let df_eval: f64 = df(x0, &p);
let df_eval_true: f64 = df_true(x0);
assert_equal_to_decimal!(df_eval, df_eval_true, 15);
}
}