use crate::constants::CBRT_EPS;
pub fn sderivative2(f: &impl Fn(f64) -> f64, x0: f64, h: Option<f64>) -> f64 {
let h = h.unwrap_or(*CBRT_EPS);
let dx = h * (1.0 + x0.abs());
(f(x0 + 2.0 * dx) - 2.0 * f(x0 + dx) + f(x0)) / dx.powi(2)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils;
use linalg_traits::RealField;
use numtest::*;
use std::f64::consts::PI;
#[test]
fn test_product_rule() {
let f = |x: f64| x.powi(3);
let df = |x: f64| 3.0 * x.powi(2);
let d2f = |x: f64| 6.0 * x;
let g = |x: f64| x.sin();
let dg = |x: f64| x.cos();
let d2g = |x: f64| -x.sin();
let h = |x: f64| f(x) * g(x);
let d2h = |x: f64| d2f(x) * g(x) + 2.0 * df(x) * dg(x) + f(x) * d2g(x);
let d2h_approx = |x: f64| sderivative2(&h, x, None);
assert_equal_to_decimal!(d2h_approx(-1.5), d2h(-1.5), 2);
assert_equal_to_decimal!(d2h_approx(1.5), d2h(1.5), 2);
}
#[test]
fn test_chain_rule_one_composition() {
let f = |x: f64| x.powi(3);
let df = |x: f64| 3.0 * x.powi(2);
let d2f = |x: f64| 6.0 * x;
let g = |x: f64| x.sin();
let dg = |x: f64| x.cos();
let d2g = |x: f64| -x.sin();
let h = |x: f64| g(f(x));
let d2h = |x: f64| d2g(f(x)) * df(x).powi(2) + dg(f(x)) * d2f(x);
let d2h_approx = |x: f64| sderivative2(&h, x, None);
assert_equal_to_decimal!(d2h_approx(-1.5), d2h(-1.5), 1);
assert_equal_to_decimal!(d2h_approx(1.5), d2h(1.5), 1);
}
#[test]
fn test_sderivative2_polynomial() {
let f = |x: f64| x.powi(4);
let d2f = |x: f64| 12.0 * x.powi(2);
assert_equal_to_decimal!(sderivative2(&f, -2.0, None), d2f(-2.0), 2);
assert_equal_to_decimal!(sderivative2(&f, 2.0, None), d2f(2.0), 2);
}
#[test]
fn test_sderivative2_sine() {
assert_equal_to_decimal!(
sderivative2(&f64::sin, PI / 4.0, None),
test_utils::sin_deriv2(PI / 4.0),
3
);
}
#[test]
fn test_sderivative2_exponential() {
assert_equal_to_decimal!(
sderivative2(&f64::exp, 1.0, None),
test_utils::exp_deriv2(1.0),
3
);
}
#[test]
fn test_sderivative2_inverse_secant() {
let f = |x: f64| x.asec();
assert_equal_to_decimal!(sderivative2(&f, 1.5, None), test_utils::asec_deriv2(1.5), 2);
assert_equal_to_decimal!(
sderivative2(&f, -1.5, None),
test_utils::asec_deriv2(-1.5),
2
);
}
#[test]
fn test_sderivative2_inverse_cosecant() {
let f = |x: f64| x.acsc();
assert_equal_to_decimal!(sderivative2(&f, 1.5, None), test_utils::acsc_deriv2(1.5), 2);
assert_equal_to_decimal!(
sderivative2(&f, -1.5, None),
test_utils::acsc_deriv2(-1.5),
2
);
}
}