#[macro_export]
macro_rules! get_vpartial_derivative2 {
($f:ident, $func_name:ident) => {
get_vpartial_derivative2!($f, $func_name, [f64]);
};
($f:ident, $func_name:ident, $param_type:ty) => {
fn $func_name<S, V>(x0: &V, k: usize, p: &$param_type) -> V::DVectorf64
where
S: Scalar,
V: Vector<S>,
{
let mut x0_hyperdual = x0.clone().to_hyper_dual_vector();
x0_hyperdual.vset(
k,
HyperDual::new(x0_hyperdual.vget(k).get_a(), 1.0, 1.0, 0.0),
);
let f_x0 = $f(&x0_hyperdual, p);
V::DVectorf64::from_slice(&f_x0.iter().map(|xi| xi.get_d()).collect::<Vec<_>>())
}
};
}
#[cfg(test)]
mod tests {
use crate::{HyperDual, HyperDualVector};
use linalg_traits::{Scalar, Vector};
use nalgebra::SVector;
use numtest::*;
use std::f64::consts::PI;
#[test]
fn test_vpartial_derivative2_basic() {
fn f<S: Scalar, V: Vector<S>>(x: &V, _p: &[f64]) -> Vec<S> {
let f0 = x.vget(0).powi(4) + S::new(2.0) * x.vget(0).powi(2) * x.vget(1);
let f1 = x.vget(1).powi(3) + x.vget(0) * x.vget(1).powi(2);
vec![f0, f1]
}
let x0 = vec![2.0, 1.0];
get_vpartial_derivative2!(f, d2fkk);
let d2f_dx0dx0 = d2fkk(&x0, 0, &[]);
assert_equal_to_decimal!(d2f_dx0dx0[0], 52.0, 15);
assert_equal_to_decimal!(d2f_dx0dx0[1], 0.0, 15);
let d2f_dx1dx1 = d2fkk(&x0, 1, &[]);
assert_equal_to_decimal!(d2f_dx1dx1[0], 0.0, 15);
assert_equal_to_decimal!(d2f_dx1dx1[1], 10.0, 15);
}
#[test]
fn test_vpartial_derivative2_polynomial() {
fn f<S: Scalar, V: Vector<S>>(x: &V, _p: &[f64]) -> Vec<S> {
vec![x.vget(0).powi(2), x.vget(0).powi(3), x.vget(0).powi(4)]
}
let x0 = vec![2.0];
get_vpartial_derivative2!(f, d2fkk);
let result = d2fkk(&x0, 0, &[]);
assert_eq!(result[0], 2.0);
assert_eq!(result[1], 12.0);
assert_eq!(result[2], 48.0);
}
#[test]
fn test_vpartial_derivative2_multivariate() {
fn f<S: Scalar, V: Vector<S>>(x: &V, _p: &[f64]) -> Vec<S> {
vec![x.vget(0).powi(3), x.vget(1).powi(4), x.vget(2).powi(2)]
}
let x0 = vec![1.0, 2.0, 3.0];
get_vpartial_derivative2!(f, d2fkk);
let result_x0 = d2fkk(&x0, 0, &[]);
assert_eq!(result_x0[0], 6.0);
assert_eq!(result_x0[1], 0.0);
assert_eq!(result_x0[2], 0.0);
let result_x1 = d2fkk(&x0, 1, &[]);
assert_eq!(result_x1[0], 0.0);
assert_eq!(result_x1[1], 48.0);
assert_eq!(result_x1[2], 0.0);
let result_x2 = d2fkk(&x0, 2, &[]);
assert_eq!(result_x2[0], 0.0);
assert_eq!(result_x2[1], 0.0);
assert_eq!(result_x2[2], 2.0);
}
#[test]
fn test_vpartial_derivative2_trig() {
fn f<S: Scalar, V: Vector<S>>(x: &V, _p: &[f64]) -> Vec<S> {
vec![x.vget(0).sin(), x.vget(1).cos()]
}
let x0 = vec![PI / 2.0, PI / 4.0];
get_vpartial_derivative2!(f, d2fkk);
let result_x0 = d2fkk(&x0, 0, &[]);
assert_equal_to_decimal!(result_x0[0], -1.0, 15);
assert_eq!(result_x0[1], 0.0);
let expected = -(2.0_f64.sqrt() / 2.0);
let result_x1 = d2fkk(&x0, 1, &[]);
assert_eq!(result_x1[0], 0.0);
assert_equal_to_decimal!(result_x1[1], expected, 15);
}
#[test]
fn test_vpartial_derivative2_exponential() {
fn f<S: Scalar, V: Vector<S>>(x: &V, _p: &[f64]) -> Vec<S> {
vec![x.vget(0).exp(), x.vget(1).powi(2)]
}
let x0 = vec![1.0, 2.0];
get_vpartial_derivative2!(f, d2fkk);
let result_x0 = d2fkk(&x0, 0, &[]);
assert_equal_to_decimal!(result_x0[0], std::f64::consts::E, 15);
assert_eq!(result_x0[1], 0.0);
let result_x1 = d2fkk(&x0, 1, &[]);
assert_eq!(result_x1[0], 0.0);
assert_eq!(result_x1[1], 2.0);
}
#[test]
#[allow(clippy::many_single_char_names)]
fn test_vpartial_derivative2_with_runtime_parameters() {
fn f<S: Scalar, V: Vector<S>>(x: &V, p: &[f64]) -> Vec<S> {
let a = S::new(p[0]);
let b = S::new(p[1]);
let c = S::new(p[2]);
let d = S::new(p[3]);
let e = S::new(p[4]);
let f0 = a * x.vget(0).powi(4) + b * x.vget(0).powi(2) * x.vget(1);
let f1 = c * x.vget(1).powi(3) + d * (e * x.vget(0)).sin();
vec![f0, f1]
}
let a = 1.5;
let b = 2.0;
let c = 0.8;
let d = 3.0;
let e = 0.5;
let p = [a, b, c, d, e];
let x0 = vec![1.0, -0.5];
get_vpartial_derivative2!(f, d2fkk);
let d2f_dx0dx0 = d2fkk(&x0, 0, &p);
let d2f0_dx0dx0_expected = 12.0_f64 * a * (x0[0] as f64).powi(2_i32) + 2.0_f64 * b * x0[1];
let d2f1_dx0dx0_expected = -d * e * e * (e * x0[0]).sin();
assert_equal_to_decimal!(d2f_dx0dx0[0], d2f0_dx0dx0_expected, 14);
assert_equal_to_decimal!(d2f_dx0dx0[1], d2f1_dx0dx0_expected, 14);
let d2f_dx1dx1 = d2fkk(&x0, 1, &p);
let d2f0_dx1dx1_expected = 0.0_f64;
let d2f1_dx1dx1_expected = 6.0_f64 * c * x0[1];
assert_equal_to_decimal!(d2f_dx1dx1[0], d2f0_dx1dx1_expected, 15);
assert_equal_to_decimal!(d2f_dx1dx1[1], d2f1_dx1dx1_expected, 15);
}
#[test]
fn test_vpartial_derivative2_custom_params() {
struct Data {
a: f64,
b: f64,
c: f64,
d: f64,
e: f64,
}
#[allow(clippy::many_single_char_names)]
fn f<S: Scalar, V: Vector<S>>(x: &V, p: &Data) -> Vec<S> {
let a = S::new(p.a);
let b = S::new(p.b);
let c = S::new(p.c);
let d = S::new(p.d);
let e = S::new(p.e);
let f0 = a * x.vget(0).powi(4) + b * x.vget(0).powi(2) * x.vget(1);
let f1 = c * x.vget(1).powi(3) + d * (e * x.vget(0)).sin();
vec![f0, f1]
}
let p = Data {
a: 1.5,
b: 2.0,
c: 0.8,
d: 3.0,
e: 0.5,
};
let x0 = vec![1.0, -0.5];
get_vpartial_derivative2!(f, d2fkk, Data);
let d2f_dx0dx0 = d2fkk(&x0, 0, &p);
let d2f0_dx0dx0_expected =
12.0_f64 * p.a * (x0[0] as f64).powi(2_i32) + 2.0_f64 * p.b * x0[1];
let d2f1_dx0dx0_expected = -p.d * p.e * p.e * (p.e * x0[0]).sin();
assert_equal_to_decimal!(d2f_dx0dx0[0], d2f0_dx0dx0_expected, 14);
assert_equal_to_decimal!(d2f_dx0dx0[1], d2f1_dx0dx0_expected, 14);
let d2f_dx1dx1 = d2fkk(&x0, 1, &p);
let d2f0_dx1dx1_expected = 0.0_f64;
let d2f1_dx1dx1_expected = 6.0_f64 * p.c * x0[1];
assert_equal_to_decimal!(d2f_dx1dx1[0], d2f0_dx1dx1_expected, 15);
assert_equal_to_decimal!(d2f_dx1dx1[1], d2f1_dx1dx1_expected, 15);
}
#[test]
fn test_vpartial_derivative2_vector_types() {
fn f<S: Scalar, V: Vector<S>>(x: &V, _p: &[f64]) -> Vec<S> {
vec![x.vget(0).powi(3), x.vget(1).powi(2)]
}
let x_nalgebra: SVector<f64, 2> = SVector::from([2.0, 3.0]);
get_vpartial_derivative2!(f, d2fkk);
let result_x0 = d2fkk(&x_nalgebra, 0, &[]);
assert_eq!(result_x0[0], 12.0);
assert_eq!(result_x0[1], 0.0);
let result_x1 = d2fkk(&x_nalgebra, 1, &[]);
assert_eq!(result_x1[0], 0.0);
assert_eq!(result_x1[1], 2.0);
}
#[test]
fn test_vpartial_derivative2_single_component() {
fn f<S: Scalar, V: Vector<S>>(x: &V, _p: &[f64]) -> Vec<S> {
vec![x.vget(0).powi(4) + x.vget(1).powi(2)]
}
let x0 = vec![2.0, 1.0];
get_vpartial_derivative2!(f, d2fkk);
let result_x0 = d2fkk(&x0, 0, &[]);
assert_eq!(result_x0[0], 48.0);
let result_x1 = d2fkk(&x0, 1, &[]);
assert_eq!(result_x1[0], 2.0);
}
}