use super::bracket::{find_bracket, shft3, shft2};
const MIN_TOLERANCE: f64 = 3.0e-8_f64;
pub fn golden_section_search<F: Fn (f64) -> f64>(
fun: F,
a: f64,
b: f64,
tol: f64,
max_iterations: usize
) -> (f64, f64, usize)
{
let tol = tol.max(MIN_TOLERANCE);
let max_iterations = if max_iterations < 1 { 500 } else { max_iterations.min(1000) };
const R: f64 = 0.61803399_f64;
const C: f64 = 1.0 - R;
let bracket = find_bracket(&fun, a, b);
let a = bracket.a;
let b = bracket.b;
let c = bracket.c;
let mut x1: f64;
let mut x2: f64;
let mut x0 = a;
let mut x3 = c;
if (c-b).abs() > (b-a).abs() {
x1 = b;
x2 = b + C*(c-b);
} else {
x2 = b;
x1 = b - C*(b-a);
}
let mut f1 = fun(x1);
let mut f2 = fun(x2);
let mut nr_iterations: usize = 0;
while (x3-x0).abs() > tol*(x1.abs() + x2.abs()) {
if f2 < f1 {
let d = R*x2 + C*x3;
shft3(&mut x0, &mut x1, &mut x2, d);
shft2(&mut f1, &mut f2, fun(x2));
}
else {
let d = R*x1 + C*x0;
shft3(&mut x3, &mut x2, &mut x1, d);
shft2(&mut f2, &mut f1, fun(x1));
}
nr_iterations += 1;
if nr_iterations >= max_iterations { break; }
if nr_iterations > 10 && (x3-x0).abs() < tol && (f1 - f2).abs() < tol { break; }
}
if f1 < f2 {
(x1, f1, nr_iterations)
}
else {
(x2, f2, nr_iterations)
}
}
#[cfg(test)]
#[test]
fn test_poly2() {
let poly2 = |x: f64| (x-1.0)*(x-2.0);
let ranges = vec![(10.0, 20.0), (20.0, 10.0), (-10.0, 0.0),
(-2000.0, -1000.0), (-10_000.0, 30_000.0), (0.0001, 0.0002), (-0.00001, 1.4999)];
for range in ranges {
let (xmin, f, nr_iterations) = golden_section_search(poly2, range.0, range.1, 0.0, 0);
println!("MIN: {:.8} f(xmin): {:6.2} iterations:{}",
xmin, f, nr_iterations
);
assert_float_relative_eq!(xmin, 1.5, 1.0e-8);
}
}
#[cfg(test)]
#[test]
fn test_cosine() {
let cosine = |x: f64| x.cos();
let ranges = vec![(0.01, 1.0)];
for range in ranges {
let (xmin, f, nr_iterations) = golden_section_search(cosine, range.0, range.1, 0.0, 0);
println!("MIN: {:.8} f(xmin): {:6.2} iterations:{}",
xmin, f, nr_iterations
);
assert_float_relative_eq!(xmin, std::f64::consts::PI, 1.0e-8);
}
}
#[cfg(test)]
#[test]
fn test_saw() {
let saw = |x: f64| if x >= 0.0 { x*x*x } else { -x / 1000.0 } ;
let ranges = vec![(10.0, 20.0), (20.0, 10.0), (-10.0, 0.0),
(-2000.0, -1000.0), (-10_000.0, 30_000.0), (0.0001, 0.0002), (-0.00001, 1.4999)];
for range in ranges {
let (xmin, f, nr_iterations) = golden_section_search(saw, range.0, range.1, 1.0e-5, 0);
println!("MIN: {:.8} f(xmin): {:6.2} iterations:{}",
xmin, f, nr_iterations
);
assert_float_absolute_eq!(xmin, 0.0, 1.0e-5);
}
}