Skip to content

Commit

Permalink
Added derivative and Hessian of Rastrigin test function
Browse files Browse the repository at this point in the history
  • Loading branch information
stefan-k committed Feb 4, 2024
1 parent fa33b04 commit d5cb6a7
Show file tree
Hide file tree
Showing 2 changed files with 231 additions and 4 deletions.
227 changes: 227 additions & 0 deletions crates/argmin-testfunctions/src/rastrigin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,121 @@ where
.sum()
}

/// Derivative of Rastrigin test function where `a` can be chosen freely
pub fn rastrigin_a_derivative<T>(param: &[T], a: T) -> Vec<T>
where
T: Float + FromPrimitive + Sum + Into<f64>,
{
let npi2 = T::from_f64(2.0 * PI).unwrap();
let n2 = T::from_f64(2.0).unwrap();
param
.iter()
.map(|x| n2 * *x + npi2 * a * T::from_f64(f64::sin((npi2 * *x).into())).unwrap())
.collect()
}

/// Derivative of Rastrigin test function
pub fn rastrigin_derivative<T>(param: &[T]) -> Vec<T>
where
T: Float + FromPrimitive + Sum + Into<f64>,
{
rastrigin_a_derivative(param, T::from_f64(10.0).unwrap())
}

/// Derivative of Rastrigin test function where `a` can be chosen freely
///
/// This is the const generics version, which requires the number of parameters to be known
/// at compile time.
pub fn rastrigin_a_derivative_const<const N: usize, T>(param: &[T; N], a: T) -> [T; N]
where
T: Float + FromPrimitive + Sum + Into<f64>,
{
let npi2 = T::from_f64(2.0 * PI).unwrap();
let n2 = T::from_f64(2.0).unwrap();
let mut result = [T::from_f64(0.0).unwrap(); N];
for i in 0..N {
result[i] =
n2 * param[i] + npi2 * a * T::from_f64(f64::sin((npi2 * param[i]).into())).unwrap();
}
result
}

/// Derivative of Rastrigin test function
///
/// This is the const generics version, which requires the number of parameters to be known
/// at compile time.
pub fn rastrigin_derivative_const<const N: usize, T>(param: &[T; N]) -> [T; N]
where
T: Float + FromPrimitive + Sum + Into<f64>,
{
rastrigin_a_derivative_const(param, T::from_f64(10.0).unwrap())
}

/// Hessian of Rastrigin test function where `a` can be chosen freely
pub fn rastrigin_a_hessian<T>(param: &[T], a: T) -> Vec<Vec<T>>
where
T: Float + FromPrimitive + Sum + Into<f64>,
{
let npi2 = T::from_f64(2.0 * PI).unwrap();
let n4pisq = T::from_f64(4.0 * PI.powi(2)).unwrap();
let n2 = T::from_f64(2.0).unwrap();
let n0 = T::from_f64(0.0).unwrap();

let n = param.len();
let mut hessian = vec![vec![n0; n]; n];

for i in 0..n {
hessian[i][i] = n2 + n4pisq * a * T::from_f64(f64::cos((npi2 * param[i]).into())).unwrap();
}
hessian
}

/// Hessian of Rastrigin test function
pub fn rastrigin_hessian<T>(param: &[T]) -> Vec<Vec<T>>
where
T: Float + FromPrimitive + Sum + Into<f64>,
{
rastrigin_a_hessian(param, T::from_f64(10.0).unwrap())
}

/// Hessian of Rastrigin test function where `a` can be chosen freely
///
/// This is the const generics version, which requires the number of parameters to be known
/// at compile time.
pub fn rastrigin_a_hessian_const<const N: usize, T>(param: &[T], a: T) -> [[T; N]; N]
where
T: Float + FromPrimitive + Sum + Into<f64>,
{
let npi2 = T::from_f64(2.0 * PI).unwrap();
let n4pisq = T::from_f64(4.0 * PI.powi(2)).unwrap();
let n2 = T::from_f64(2.0).unwrap();
let n0 = T::from_f64(0.0).unwrap();

let mut hessian = [[n0; N]; N];

for i in 0..N {
hessian[i][i] = n2 + n4pisq * a * T::from_f64(f64::cos((npi2 * param[i]).into())).unwrap();
}
hessian
}

/// Hessian of Rastrigin test function
///
/// This is the const generics version, which requires the number of parameters to be known
/// at compile time.
pub fn rastrigin_hessian_const<const N: usize, T>(param: &[T; N]) -> [[T; N]; N]
where
T: Float + FromPrimitive + Sum + Into<f64>,
{
rastrigin_a_hessian_const(param, T::from_f64(10.0).unwrap())
}

#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
use finitediff::FiniteDiff;
use proptest::prelude::*;
use std::{f32, f64};

#[test]
Expand All @@ -68,10 +179,126 @@ mod tests {
rastrigin_a(&[0.0_f32, 0.0_f32], 10.0),
epsilon = f32::EPSILON
);

assert_relative_eq!(
rastrigin(&[0.0_f64, 0.0_f64]),
rastrigin_a(&[0.0_f64, 0.0_f64], 10.0),
epsilon = f64::EPSILON
);

let derivative = rastrigin_derivative(&[0.0_f64, 0.0_f64]);
let derivative_a = rastrigin_a_derivative(&[0.0_f64, 0.0_f64], 10.0);
for i in 0..derivative.len() {
assert_relative_eq!(derivative[i], derivative_a[i], epsilon = f64::EPSILON);
}

let derivative = rastrigin_derivative_const(&[0.0_f64, 0.0_f64]);
let derivative_a = rastrigin_a_derivative_const(&[0.0_f64, 0.0_f64], 10.0);
for i in 0..derivative.len() {
assert_relative_eq!(derivative[i], derivative_a[i], epsilon = f64::EPSILON);
}
}

#[test]
fn test_rastrigin_a_derivative_optimum() {
let derivative = rastrigin_a_derivative(&[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], 10.0);
for elem in derivative {
assert_relative_eq!(elem, 0.0, epsilon = std::f64::EPSILON);
}
}

#[test]
fn test_rastrigin_a_derivative_const_optimum() {
let derivative =
rastrigin_a_derivative_const(&[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], 10.0);
for elem in derivative {
assert_relative_eq!(elem, 0.0, epsilon = std::f64::EPSILON);
}
}

proptest! {
#[test]
fn test_rastrigin_derivative_finitediff(a in -5.12..5.12,
b in -5.12..5.12,
c in -5.12..5.12,
d in -5.12..5.12,
e in -5.12..5.12,
f in -5.12..5.12,
g in -5.12..5.12,
h in -5.12..5.12) {
let param = [a, b, c, d, e, f, g, h];
let derivative = rastrigin_derivative(&param);
let derivative_fd = Vec::from(param).central_diff(&|x| rastrigin(&x));
for i in 0..derivative.len() {
assert_relative_eq!(derivative[i], derivative_fd[i], epsilon = 1e-5);
}
}
}

proptest! {
#[test]
fn test_rastrigin_derivative_const_finitediff(a in -5.12..5.12,
b in -5.12..5.12,
c in -5.12..5.12,
d in -5.12..5.12,
e in -5.12..5.12,
f in -5.12..5.12,
g in -5.12..5.12,
h in -5.12..5.12) {
let param = [a, b, c, d, e, f, g, h];
let derivative = rastrigin_derivative_const(&param);
let derivative_fd = Vec::from(param).central_diff(&|x| rastrigin(&x));
for i in 0..derivative.len() {
assert_relative_eq!(derivative[i], derivative_fd[i], epsilon = 1e-5);
}
}
}

proptest! {
#[test]
fn test_rastrigin_hessian_finitediff(a in -5.12..5.12,
b in -5.12..5.12,
c in -5.12..5.12,
d in -5.12..5.12,
e in -5.12..5.12,
f in -5.12..5.12,
g in -5.12..5.12,
h in -5.12..5.12) {
let param = [a, b, c, d, e, f, g, h];
let hessian = rastrigin_hessian(&param);
let hessian_fd =
Vec::from(param).forward_hessian(&|x| rastrigin_derivative(&x));
let n = hessian.len();
for i in 0..n {
assert_eq!(hessian[i].len(), n);
for j in 0..n {
assert_relative_eq!(hessian[i][j], hessian_fd[i][j], epsilon = 1e-4);
}
}
}
}

proptest! {
#[test]
fn test_rastrigin_hessian_const_finitediff(a in -5.12..5.12,
b in -5.12..5.12,
c in -5.12..5.12,
d in -5.12..5.12,
e in -5.12..5.12,
f in -5.12..5.12,
g in -5.12..5.12,
h in -5.12..5.12) {
let param = [a, b, c, d, e, f, g, h];
let hessian = rastrigin_hessian_const(&param);
let hessian_fd =
Vec::from(param).forward_hessian(&|x| rastrigin_derivative(&x));
let n = hessian.len();
for i in 0..n {
assert_eq!(hessian[i].len(), n);
for j in 0..n {
assert_relative_eq!(hessian[i][j], hessian_fd[i][j], epsilon = 1e-4);
}
}
}
}
}
8 changes: 4 additions & 4 deletions crates/argmin-testfunctions/src/rosenbrock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ where
}

/// Hessian of the multidimensional Rosenbrock test function
pub fn rosenbrock_hessian<T>(x: &[T], a: T, b: T) -> Vec<Vec<T>>
pub fn rosenbrock_hessian<T>(param: &[T], a: T, b: T) -> Vec<Vec<T>>
where
T: Float + FromPrimitive + AddAssign,
{
Expand All @@ -78,12 +78,12 @@ where
let n4 = T::from_f64(4.0).unwrap();
let n12 = T::from_f64(12.0).unwrap();

let n = x.len();
let n = param.len();
let mut hessian = vec![vec![n0; n]; n];

for i in 0..n - 1 {
let xi = x[i];
let xi1 = x[i + 1];
let xi = param[i];
let xi1 = param[i + 1];

hessian[i][i] += n12 * b * xi.powi(2) - n4 * b * xi1 + n2 * a;
hessian[i + 1][i + 1] = n2 * b;
Expand Down

0 comments on commit d5cb6a7

Please sign in to comment.