use std::iter::Sum;
use ndarray::{Array1, Array2};
use num::{Float, FromPrimitive};
pub fn rosenbrock<T: Float + FromPrimitive>(param: &[T], a: T, b: T) -> T {
let num2 = T::from_f64(2.0).unwrap();
(a - param[0]).powf(num2) + b * (param[1] - param[0].powf(num2)).powf(num2)
}
pub fn rosenbrock_derivative<T: Float + FromPrimitive>(param: &[T], a: T, b: T) -> Vec<T> {
let num2 = T::from_f64(2.0).unwrap();
let num3 = T::from_f64(3.0).unwrap();
let num4 = T::from_f64(4.0).unwrap();
let x = param[0];
let y = param[1];
let mut out = vec![];
out.push(-num2 * a + num4 * b * x.powf(num3) - num4 * b * x * y + num2 * x);
out.push(num2 * b * (y - x.powf(num2)));
out
}
pub fn rosenbrock_hessian<T: Float + FromPrimitive>(param: &[T], _a: T, b: T) -> Vec<T> {
let num2 = T::from_f64(2.0).unwrap();
let num4 = T::from_f64(4.0).unwrap();
let num12 = T::from_f64(12.0).unwrap();
let x = param[0];
let y = param[1];
let mut out = vec![];
out.push(num12 * b * x.powf(num2) - num4 * b * y + num2);
out.push(-num4 * b * x);
out.push(-num4 * b * x);
out.push(num2 * b);
out
}
pub fn rosenbrock_nd<T: Float + FromPrimitive>(param: &Array1<T>, a: T, b: T) -> T {
let num2 = T::from_f64(2.0).unwrap();
(a - param[0]).powf(num2) + b * (param[1] - param[0].powf(num2)).powf(num2)
}
pub fn rosenbrock_derivative_nd<T: Float + FromPrimitive>(
param: &Array1<T>,
a: T,
b: T,
) -> Array1<T> {
let num2 = T::from_f64(2.0).unwrap();
let num3 = T::from_f64(3.0).unwrap();
let num4 = T::from_f64(4.0).unwrap();
let x = param[0];
let y = param[1];
let mut out = Array1::zeros(2);
out[[0]] = -num2 * a + num4 * b * x.powf(num3) - num4 * b * x * y + num2 * x;
out[[1]] = num2 * b * (y - x.powf(num2));
out
}
pub fn rosenbrock_hessian_nd<T: Float + FromPrimitive>(
param: &Array1<T>,
_a: T,
b: T,
) -> Array2<T> {
let num2 = T::from_f64(2.0).unwrap();
let num4 = T::from_f64(4.0).unwrap();
let num12 = T::from_f64(12.0).unwrap();
let x = param[0];
let y = param[1];
let mut out = Array2::zeros((2, 2));
out[[0, 0]] = num12 * b * x.powf(num2) - num4 * b * y + num2;
out[[0, 1]] = -num4 * b * x;
out[[1, 0]] = -num4 * b * x;
out[[1, 1]] = num2 * b;
out
}
pub fn sphere<T: Float + FromPrimitive + Sum>(param: &[T]) -> T {
let num2 = T::from_f64(2.0).unwrap();
param.iter().map(|x| x.powf(num2)).sum()
}
pub fn sphere_derivative<T: Float + FromPrimitive>(param: &[T]) -> Vec<T> {
let num2 = T::from_f64(2.0).unwrap();
param.iter().map(|x| num2 * *x).collect()
}
#[cfg(test)]
mod tests {
use super::*;
use std;
use ndarray::arr1;
#[test]
fn test_compare_rosenbrock_funcs() {
assert!(
(rosenbrock(&[1.0, 1.0], 1.0, 100.0) - rosenbrock_nd(&arr1(&[1.0, 1.0]), 1.0, 100.0))
.abs() < std::f64::EPSILON
);
assert!(
(rosenbrock(&[1.5, -0.9], 1.0, 100.0) - rosenbrock_nd(&arr1(&[1.5, -0.9]), 1.0, 100.0))
.abs() < std::f64::EPSILON
);
assert!(
(rosenbrock(&[1.5_f32, -0.9_f32], 1.0_f32, 100.0_f32)
- rosenbrock_nd(&arr1(&[1.5_f32, -0.9_f32]), 1.0_f32, 100.0_f32))
.abs() < std::f32::EPSILON
);
assert!(
(rosenbrock(&[-10.5, 23.4], 1.0, 100.0)
- rosenbrock_nd(&arr1(&[-10.5, 23.4]), 1.0, 100.0))
.abs() < std::f64::EPSILON
);
}
#[test]
fn test_compare_rosenbrock_derivative_funcs() {
let inputs = vec![vec![1.0, 1.0], vec![1.5, -0.9], vec![-10.5, 23.4]];
for input in &inputs {
let diff: Vec<f64> = rosenbrock_derivative(input, 1.0, 100.0)
.iter()
.zip(
rosenbrock_derivative_nd(&arr1(input), 1.0, 100.0)
.to_vec()
.iter(),
)
.map(|(a, b): (&f64, &f64)| (a - b).abs())
.collect();
for elem in diff {
assert!(elem < std::f64::EPSILON);
}
}
let inputs = vec![
vec![1.0_f32, 1.0_f32],
vec![1.5_f32, -0.9_f32],
vec![-10.5_f32, 23.4_f32],
];
for input in &inputs {
let diff: Vec<f32> = rosenbrock_derivative(input, 1.0, 100.0)
.iter()
.zip(
rosenbrock_derivative_nd(&arr1(input), 1.0, 100.0)
.to_vec()
.iter(),
)
.map(|(a, b): (&f32, &f32)| (a - b).abs())
.collect();
for elem in diff {
assert!(elem < std::f32::EPSILON);
}
}
}
#[test]
fn test_rosenbrock_optimum() {
assert!(rosenbrock(&[1.0_f32, 1.0_f32], 1.0, 100.0).abs() < std::f32::EPSILON);
assert!(rosenbrock(&[1.0, 1.0], 1.0, 100.0).abs() < std::f64::EPSILON);
}
#[test]
fn test_rosenbrock_nd_optimum() {
assert!(rosenbrock_nd(&arr1(&[1.0, 1.0]), 1.0, 100.0).abs() < std::f64::EPSILON);
assert!(
rosenbrock_nd(&arr1(&[1.0_f32, 1.0_f32]), 1.0_f32, 100.0_f32).abs() < std::f32::EPSILON
);
}
#[test]
fn test_rosenbrock_derivative() {
let res: Vec<f64> = rosenbrock_derivative(&[1.0, 1.0], 1.0, 100.0);
for elem in &res {
assert!((elem - 0.0).abs() < std::f64::EPSILON);
}
let res: Vec<f32> = rosenbrock_derivative(&[1.0_f32, 1.0_f32], 1.0_f32, 100.0_f32);
for elem in &res {
assert!((elem - 0.0).abs() < std::f32::EPSILON);
}
}
#[test]
fn test_rosenbrock_nd_derivative() {
let res: Array1<f64> = rosenbrock_derivative_nd(&arr1(&[1.0, 1.0]), 1.0, 100.0);
for elem in res.iter() {
assert!((elem - 0.0).abs() < std::f64::EPSILON);
}
let res: Array1<f32> =
rosenbrock_derivative_nd(&arr1(&[1.0_f32, 1.0_f32]), 1.0_f32, 100.0_f32);
for elem in res.iter() {
assert!((elem - 0.0).abs() < std::f32::EPSILON);
}
}
#[test]
fn test_sphere_optimum() {
assert!(sphere(&[0.0_f32, 0.0_f32]).abs() < std::f32::EPSILON);
assert!(sphere(&[0.0_f64, 0.0_f64]).abs() < std::f64::EPSILON);
}
}