Skip to content

Commit

Permalink
Added derivative and Hessian for sphere test function
Browse files Browse the repository at this point in the history
  • Loading branch information
stefan-k committed Feb 4, 2024
1 parent d5cb6a7 commit 88326ca
Show file tree
Hide file tree
Showing 2 changed files with 257 additions and 4 deletions.
24 changes: 20 additions & 4 deletions crates/argmin-testfunctions/src/rastrigin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,17 +186,33 @@ mod tests {
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);
let derivative = rastrigin_derivative(&[1.0_f64, -1.0_f64]);
let derivative_a = rastrigin_a_derivative(&[1.0_f64, -1.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);
let derivative = rastrigin_derivative_const(&[1.0_f64, -1.0_f64]);
let derivative_a = rastrigin_a_derivative_const(&[1.0_f64, -1.0_f64], 10.0);
for i in 0..derivative.len() {
assert_relative_eq!(derivative[i], derivative_a[i], epsilon = f64::EPSILON);
}

let hessian = rastrigin_hessian(&[1.0_f64, -1.0_f64]);
let hessian_a = rastrigin_a_hessian(&[1.0_f64, -1.0_f64], 10.0);
for i in 0..hessian.len() {
for j in 0..hessian.len() {
assert_relative_eq!(hessian[i][j], hessian_a[i][j], epsilon = f64::EPSILON);
}
}

let hessian = rastrigin_hessian_const(&[1.0_f64, -1.0_f64]);
let hessian_a: [[_; 2]; 2] = rastrigin_a_hessian_const(&[1.0_f64, -1.0_f64], 10.0);
for i in 0..hessian.len() {
for j in 0..hessian.len() {
assert_relative_eq!(hessian[i][j], hessian_a[i][j], epsilon = f64::EPSILON);
}
}
}

#[test]
Expand Down
237 changes: 237 additions & 0 deletions crates/argmin-testfunctions/src/sphere.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,62 @@ where
param.iter().map(|x| num2 * *x).collect()
}

/// Derivative of sphere test function
///
/// Defined as
///
/// `f(x_1, x_2, ..., x_n) = (2 * x_1, 2 * x_2, ... 2 * x_n)`
///
/// where `x_i \in (-\infty, \infty)` and `n > 0`.
///
/// This is the const generics version, which requires the number of parameters to be known
/// at compile time.
pub fn sphere_derivative_const<const N: usize, T>(param: &[T; N]) -> [T; N]
where
T: Float + FromPrimitive,
{
let num2 = T::from_f64(2.0).unwrap();
let mut deriv = [T::from_f64(0.0).unwrap(); N];
for i in 0..N {
deriv[i] = num2 * param[i];
}
deriv
}

/// Hessian of sphere test function
pub fn sphere_hessian<T>(param: &[T]) -> Vec<Vec<T>>
where
T: Float + FromPrimitive,
{
let n = param.len();
let mut hessian = vec![vec![T::from_f64(0.0).unwrap(); n]; n];
for (i, row) in hessian.iter_mut().enumerate().take(n) {
row[i] = T::from_f64(2.0).unwrap();
}
hessian
}

/// Hessian of sphere test function
///
/// This is the const generics version, which requires the number of parameters to be known
/// at compile time.
pub fn sphere_hessian_const<const N: usize, T>(_param: &[T; N]) -> [[T; N]; N]
where
T: Float + FromPrimitive,
{
let mut hessian = [[T::from_f64(0.0).unwrap(); N]; N];
for (i, row) in hessian.iter_mut().enumerate().take(N) {
row[i] = T::from_f64(2.0).unwrap();
}
hessian
}

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

#[test]
Expand All @@ -68,4 +120,189 @@ mod tests {
epsilon = std::f64::EPSILON
);
}

proptest! {
#[test]
fn test_sphere(a in -10.0..10.0,
b in -10.0..10.0,
c in -10.0..10.0,
d in -10.0..10.0,
e in -10.0..10.0,
f in -10.0..10.0,
g in -10.0..10.0,
h in -10.0..10.0) {
let param: [f64; 8] = [a, b, c, d, e, f, g, h];
let v1 = sphere(&param);
let v2 = a.powi(2) + b.powi(2) + c.powi(2) + d.powi(2) + e.powi(2) + f.powi(2) + g.powi(2) + h.powi(2);
assert_relative_eq!(v1, v2, epsilon = std::f64::EPSILON);
}
}

proptest! {
#[test]
fn test_sphere_derivative(a in -10.0..10.0,
b in -10.0..10.0,
c in -10.0..10.0,
d in -10.0..10.0,
e in -10.0..10.0,
f in -10.0..10.0,
g in -10.0..10.0,
h in -10.0..10.0) {
let param = [a, b, c, d, e, f, g, h];
let derivative = sphere_derivative(&param);
let derivative_fd =
[2.0 * a, 2.0 * b, 2.0 * c, 2.0 * d, 2.0 * e, 2.0 * f, 2.0 * g, 2.0 * h];
for i in 0..derivative.len() {
assert_relative_eq!(derivative[i], derivative_fd[i], epsilon = std::f64::EPSILON);
}
}
}

proptest! {
#[test]
fn test_sphere_derivative_const(a in -10.0..10.0,
b in -10.0..10.0,
c in -10.0..10.0,
d in -10.0..10.0,
e in -10.0..10.0,
f in -10.0..10.0,
g in -10.0..10.0,
h in -10.0..10.0) {
let param = [a, b, c, d, e, f, g, h];
let derivative = sphere_derivative_const(&param);
let derivative_fd =
[2.0 * a, 2.0 * b, 2.0 * c, 2.0 * d, 2.0 * e, 2.0 * f, 2.0 * g, 2.0 * h];
for i in 0..derivative.len() {
assert_relative_eq!(derivative[i], derivative_fd[i], epsilon = std::f64::EPSILON);
}
}
}

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

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

proptest! {
#[test]
fn test_sphere_hessian(a in -10.0..10.0,
b in -10.0..10.0,
c in -10.0..10.0,
d in -10.0..10.0,
e in -10.0..10.0,
f in -10.0..10.0,
g in -10.0..10.0,
h in -10.0..10.0) {
let param = [a, b, c, d, e, f, g, h];
let hessian = sphere_hessian(&param);
for i in 0..hessian.len() {
for j in 0..hessian.len() {
if i == j {
assert_relative_eq!(hessian[i][j], 2.0, epsilon = std::f64::EPSILON);
} else {
assert_relative_eq!(hessian[i][j], 0.0, epsilon = std::f64::EPSILON);
}
}
}
}
}

proptest! {
#[test]
fn test_sphere_hessian_const(a in -10.0..10.0,
b in -10.0..10.0,
c in -10.0..10.0,
d in -10.0..10.0,
e in -10.0..10.0,
f in -10.0..10.0,
g in -10.0..10.0,
h in -10.0..10.0) {
let param = [a, b, c, d, e, f, g, h];
let hessian = sphere_hessian_const(&param);
for i in 0..hessian.len() {
for j in 0..hessian.len() {
if i == j {
assert_relative_eq!(hessian[i][j], 2.0, epsilon = std::f64::EPSILON);
} else {
assert_relative_eq!(hessian[i][j], 0.0, epsilon = std::f64::EPSILON);
}
}
}
}
}

proptest! {
#[test]
fn test_sphere_hessian_finitediff(a in -10.0..10.0,
b in -10.0..10.0,
c in -10.0..10.0,
d in -10.0..10.0,
e in -10.0..10.0,
f in -10.0..10.0,
g in -10.0..10.0,
h in -10.0..10.0) {
let param = [a, b, c, d, e, f, g, h];
let hessian = sphere_hessian(&param);
let hessian_fd = Vec::from(param).central_hessian(&|x| sphere_derivative(&x));
for i in 0..hessian.len() {
for j in 0..hessian.len() {
assert_relative_eq!(hessian[i][j], hessian_fd[i][j], epsilon = std::f64::EPSILON);
}
}
}
}

proptest! {
#[test]
fn test_sphere_hessian_const_finitediff(a in -10.0..10.0,
b in -10.0..10.0,
c in -10.0..10.0,
d in -10.0..10.0,
e in -10.0..10.0,
f in -10.0..10.0,
g in -10.0..10.0,
h in -10.0..10.0) {
let param = [a, b, c, d, e, f, g, h];
let hessian = sphere_hessian_const(&param);
let hessian_fd = Vec::from(param).central_hessian(&|x| sphere_derivative(&x));
for i in 0..hessian.len() {
for j in 0..hessian.len() {
assert_relative_eq!(hessian[i][j], hessian_fd[i][j], epsilon = std::f64::EPSILON);
}
}
}
}
}

0 comments on commit 88326ca

Please sign in to comment.