use crate::constants::CBRT_EPS;
use linalg_traits::Vector;
#[allow(clippy::many_single_char_names)]
pub fn mixed_spartial_derivative2<V>(
f: &impl Fn(&V) -> f64,
x0: &V,
i: usize,
j: usize,
h: Option<f64>,
) -> f64
where
V: Vector<f64>,
{
let mut x0 = x0.clone();
let h = h.unwrap_or(*CBRT_EPS);
let x0i = x0[i];
let x0j = x0[j];
let dxi = h * (1.0 + x0i.abs());
let dxj = h * (1.0 + x0j.abs());
x0[i] += dxi;
x0[j] += dxj;
let b = f(&x0);
x0[i] = x0i;
x0[j] = x0j;
x0[i] += dxi;
x0[j] -= dxj;
let c = f(&x0);
x0[i] = x0i;
x0[j] = x0j;
x0[i] -= dxi;
x0[j] += dxj;
let d = f(&x0);
x0[i] = x0i;
x0[j] = x0j;
x0[i] -= dxi;
x0[j] -= dxj;
let e = f(&x0);
(b - c - d + e) / (4.0 * dxi * dxj)
}
#[cfg(test)]
mod tests {
use super::*;
use numtest::*;
#[test]
fn test_mixed_spartial_derivative2_basic() {
let f = |x: &Vec<f64>| x[0].powi(4) + 2.0 * x[0].powi(2) * x[1] + x[1].powi(3);
let x0 = vec![2.0, 1.0];
assert_equal_to_decimal!(mixed_spartial_derivative2(&f, &x0, 0, 1, None), 8.0, 5);
assert_equal_to_decimal!(mixed_spartial_derivative2(&f, &x0, 1, 0, None), 8.0, 5);
}
#[test]
fn test_mixed_spartial_derivative2_same_index() {
let f = |x: &Vec<f64>| x[0].powi(4) + 2.0 * x[0].powi(2) * x[1] + x[1].powi(3);
let x0 = vec![2.0, 1.0];
assert_equal_to_decimal!(mixed_spartial_derivative2(&f, &x0, 0, 0, None), 52.0, 3);
assert_equal_to_decimal!(mixed_spartial_derivative2(&f, &x0, 1, 1, None), 6.0, 3);
}
}