#[macro_export]
macro_rules! get_spartial_derivative2 {
($f:ident, $func_name:ident) => {
get_spartial_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) -> f64
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);
f_x0.get_d()
}
};
}
#[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_spartial_derivative2_basic() {
fn f<S: Scalar, V: Vector<S>>(x: &V, _p: &[f64]) -> S {
x.vget(0).powi(4) + S::new(2.0) * x.vget(0).powi(2) * x.vget(1) + x.vget(1).powi(3)
}
let x0 = vec![2.0, 1.0];
get_spartial_derivative2!(f, d2fkk);
let d2f_dx0dx0 = d2fkk(&x0, 0, &[]);
assert_equal_to_decimal!(d2f_dx0dx0, 52.0, 15);
let d2f_dx1dx1 = d2fkk(&x0, 1, &[]);
assert_equal_to_decimal!(d2f_dx1dx1, 6.0, 15);
}
#[test]
fn test_spartial_derivative2_polynomial() {
fn f<S: Scalar, V: Vector<S>>(x: &V, _p: &[f64]) -> S {
x.vget(0).powi(2) + x.vget(0).powi(3) + x.vget(0).powi(4)
}
let x0 = vec![2.0];
get_spartial_derivative2!(f, d2fkk);
let d2f_dx0dx0 = d2fkk(&x0, 0, &[]);
assert_eq!(d2f_dx0dx0, 62.0);
}
#[test]
fn test_spartial_derivative2_multivariate() {
fn f<S: Scalar, V: Vector<S>>(x: &V, _p: &[f64]) -> S {
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_spartial_derivative2!(f, d2fkk);
let d2f_dx0dx0 = d2fkk(&x0, 0, &[]);
assert_eq!(d2f_dx0dx0, 6.0);
let d2f_dx1dx1 = d2fkk(&x0, 1, &[]);
assert_eq!(d2f_dx1dx1, 48.0);
let d2f_dx2dx2 = d2fkk(&x0, 2, &[]);
assert_eq!(d2f_dx2dx2, 2.0);
}
#[test]
fn test_spartial_derivative2_trig() {
fn f<S: Scalar, V: Vector<S>>(x: &V, _p: &[f64]) -> S {
x.vget(0).sin() + x.vget(1).cos()
}
let x0 = vec![PI / 2.0, PI / 4.0];
get_spartial_derivative2!(f, d2fkk);
let d2f_dx0dx0 = d2fkk(&x0, 0, &[]);
assert_equal_to_decimal!(d2f_dx0dx0, -1.0, 15);
let expected = -(2.0_f64.sqrt() / 2.0);
let d2f_dx1dx1 = d2fkk(&x0, 1, &[]);
assert_equal_to_decimal!(d2f_dx1dx1, expected, 15);
}
#[test]
fn test_spartial_derivative2_exponential() {
fn f<S: Scalar, V: Vector<S>>(x: &V, _p: &[f64]) -> S {
x.vget(0).exp() + x.vget(1).powi(2)
}
let x0 = vec![1.0, 2.0];
get_spartial_derivative2!(f, d2fkk);
let d2f_dx0dx0 = d2fkk(&x0, 0, &[]);
assert_equal_to_decimal!(d2f_dx0dx0, std::f64::consts::E, 15);
let d2f_dx1dx1 = d2fkk(&x0, 1, &[]);
assert_eq!(d2f_dx1dx1, 2.0);
}
#[test]
#[allow(clippy::many_single_char_names)]
fn test_spartial_derivative2_with_runtime_parameters() {
fn f<S: Scalar, V: Vector<S>>(x: &V, p: &[f64]) -> 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]);
a * x.vget(0).powi(4)
+ b * x.vget(0).powi(2) * x.vget(1)
+ c * x.vget(1).powi(3)
+ d * (e * x.vget(0)).sin()
}
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_spartial_derivative2!(f, d2fkk);
let d2f_dx0dx0_expected = 12.0_f64 * a * (x0[0] as f64).powi(2_i32) + 2.0_f64 * b * x0[1]
- d * e * e * (e * x0[0]).sin();
let d2f_dx1dx1_expected = 6.0_f64 * c * x0[1];
let d2f_dx0dx0 = d2fkk(&x0, 0, &p);
let d2f_dx1dx1 = d2fkk(&x0, 1, &p);
assert_equal_to_decimal!(d2f_dx0dx0, d2f_dx0dx0_expected, 14);
assert_equal_to_decimal!(d2f_dx1dx1, d2f_dx1dx1_expected, 15);
}
#[test]
fn test_spartial_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) -> 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);
a * x.vget(0).powi(4)
+ b * x.vget(0).powi(2) * x.vget(1)
+ c * x.vget(1).powi(3)
+ d * (e * x.vget(0)).sin()
}
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_spartial_derivative2!(f, d2fkk, Data);
let d2f_dx0dx0_expected = 12.0_f64 * p.a * (x0[0] as f64).powi(2_i32)
+ 2.0_f64 * p.b * x0[1]
- p.d * p.e * p.e * (p.e * x0[0]).sin();
let d2f_dx1dx1_expected = 6.0_f64 * p.c * x0[1];
let d2f_dx0dx0 = d2fkk(&x0, 0, &p);
let d2f_dx1dx1 = d2fkk(&x0, 1, &p);
assert_equal_to_decimal!(d2f_dx0dx0, d2f_dx0dx0_expected, 14);
assert_equal_to_decimal!(d2f_dx1dx1, d2f_dx1dx1_expected, 15);
}
#[test]
fn test_spartial_derivative2_vector_types() {
fn f<S: Scalar, V: Vector<S>>(x: &V, _p: &[f64]) -> S {
x.vget(0).powi(3) + x.vget(1).powi(2)
}
get_spartial_derivative2!(f, d2fkk);
let x_nalgebra: SVector<f64, 2> = SVector::from([2.0, 3.0]);
assert_eq!(d2fkk(&x_nalgebra, 0, &[]), 12.0);
assert_eq!(d2fkk(&x_nalgebra, 1, &[]), 2.0);
}
}