use super::bracket::{find_bracket, mov3};
const MIN_TOLERANCE: f64 = 3.0e-8_f64;
pub fn brent_df_search<F: Fn (f64) -> (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 ZEPS: f64 = f64::EPSILON * 1.0e-3;
let bracket = find_bracket(|x| fun(x).0, a, b);
let ax = bracket.a;
let _b = bracket.b;
let c = bracket.c;
let mut a = if ax < c { ax } else { c };
let mut b = if ax > c { ax } else { c };
let mut e: f64 = 0.0;
let mut d: f64 = 0.0;
let mut x = b; let mut w = b; let mut v = b;
let (mut fx, mut dx) = fun(x);
let mut fw = fx;
let mut fv = fx;
let mut dw = dx;
let mut dv = dx;
let mut nr_iterations: usize = 0;
for _i in 0..max_iterations {
let xm = 0.5 * (a+b);
let tol1 = tol * x.abs() + ZEPS;
let tol2 = 2.0 * (tol1 + ZEPS);
if (x - xm).abs() <= (tol2 - 0.5*(b - a)) {
break;
}
if e.abs() > tol1 {
let mut d1 = 2.0 * (b-a); let mut d2 = d1;
if dw != dx { d1 = (w-x)*dx/(dx-dw); } if dv != dx { d2 = (v-x)*dx/(dx-dv); }
let u1 = x + d1;
let u2 = x + d2;
let ok1 = (a-u1)*(u1-b) > 0.0 && dx*d1 <= 0.0;
let ok2 = (a-u2)*(u2-b) > 0.0 && dx*d2 <= 0.0;
let olde = e; e = d;
if ok1 || ok2 {
if ok1 && ok2 {
d = if d1.abs() < d2.abs() { d1 } else { d2 };
} else if ok1 {
d = d1;
} else {
d = d2;
}
if d.abs() <= (0.5*olde).abs() {
let u = x + d;
if u-a < tol2 || b-u < tol2 {
d = tol1.copysign(xm-x);
}
}
else { e = if dx >= 0.0 { a-x } else { b-x };
d = 0.5 * e;
}
}
else {
e = if dx >= 0.0 { a-x } else { b-x };
d = 0.5 * e;
}
}
else {
e = if dx >= 0.0 { a-x } else { b-x };
d = 0.5 * e;
}
let u: f64;
let fu: f64;
if d.abs() >= tol1 {
u = x + d;
(fu, _) = fun(u);
}
else {
u = x + tol1.copysign(d);
(fu, _) = fun(u);
if fu > fx {
break;
}
}
let du: f64;
(_, du) = fun(u);
if fu <= fx {
if u >= x { a = x; } else { b = x; }
mov3(&mut v, &mut fv, &mut dv, w, fw, dw);
mov3(&mut w, &mut fw, &mut dw, x, fx, dx);
mov3(&mut x, &mut fx, &mut dx, u, fu, du);
}
else {
if u < x { a = u; } else { b = u; }
if fu <= fw || w == x {
mov3(&mut v, &mut fv, &mut dv, w, fw, dw);
mov3(&mut w, &mut fw, &mut dw, u, fu, du);
}
else if fu < fv || v == x || v == w {
mov3(&mut v, &mut fv, &mut dv, u, fu, du);
}
}
nr_iterations += 1;
}
(x, fx, nr_iterations)
}
#[cfg(test)]
#[test]
fn test_cosine() {
use super::{golden_section_search, brent_search};
let cosine = |x: f64| (x.cos(), -(x.sin()));
let ranges = vec![(0.01, 1.0)];
for range in ranges {
let (xmin, f, nr_iterations) =
brent_df_search(cosine, range.0, range.1, 0.0, 0);
let (xmin_golden, _, nr_iterations_golden) =
golden_section_search(|x| cosine(x).0, range.0, range.1, 0.0, 0);
let (xmin_brent, _, nr_iterations_brent) =
brent_search(|x| cosine(x).0, range.0, range.1, 0.0, 0);
println!("xmin: {:.8} f(xmin): {:6.2} iterations: {} vs brent {} vs golden {}",
xmin, f, nr_iterations, nr_iterations_brent, nr_iterations_golden
);
assert_float_relative_eq!(xmin, std::f64::consts::PI, 1.0e-8);
assert_float_relative_eq!(xmin_brent, std::f64::consts::PI, 1.0e-8);
assert_float_relative_eq!(xmin_golden, std::f64::consts::PI, 1.0e-8);
}
}
#[cfg(test)]
#[test]
fn test_poly2() {
use super::{golden_section_search, brent_search};
let poly2 = |x: f64| ((x-1.0)*(x-2.0), 2.0*x-3.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) = brent_df_search(poly2, range.0, range.1, 0.0, 0);
let (xmin_golden, _, nr_iterations_golden) =
golden_section_search(|x| poly2(x).0, range.0, range.1, 0.0, 0);
let (xmin_brent, _, nr_iterations_brent) =
brent_search(|x| poly2(x).0, range.0, range.1, 0.0, 0);
println!("xmin: {:.8} f(xmin): {:6.2} iterations: {} vs brent {} vs golden {}",
xmin, f, nr_iterations, nr_iterations_brent, nr_iterations_golden
);
assert_float_relative_eq!(xmin, 1.5, 1.0e-8);
assert_float_relative_eq!(xmin_brent, 1.5, 1.0e-8);
assert_float_relative_eq!(xmin_golden, 1.5, 1.0e-8);
}
}