Added golden section search

This commit is contained in:
Jérôme 2023-03-26 22:21:30 +02:00
parent 5f56865f33
commit 6134559471
2 changed files with 78 additions and 7 deletions

View file

@ -1,6 +1,7 @@
extern crate colored;
use colored::Colorize;
mod univariate_solvers;
mod univariate_minimizers;
/// x sin(x)
/// e + ──────
@ -43,7 +44,7 @@ fn check_result(x : f64, x_true : f64, tol : f64, test_name: &str, verbose: bool
return 1;
} else {
if verbose {
println!("{}\t: x = {}\tf(x) = {}\t{} (expected {})", test_name_padded, format!("{:<20}", x), format!("{:<40}", fct(x)), "failed".red(), x_true);
println!("{}\t: x = {}\tf(x) = {}\t{} (expected {}, delta = {})", test_name_padded, format!("{:<20}", x), format!("{:<40}", fct(x)), "failed".red(), x_true, (x - x_true));
} else {
println!("{} {} : expected {}, got {}", test_name_padded, "failed".red(), x_true, x);
}
@ -60,18 +61,18 @@ fn print_test_results(num_tests_passed: u32, num_tests_total: u32) {
}
}
fn main() {
println!("Testing Rust numerical solvers.");
let mut num_tests_passed : u32 = 0;
let num_tests_total : u32 = 7;
let verbose : bool = true;
fn test_univariate_solvers(verbose: bool) {
println!("Testing univariate numerical solvers.");
let x0 : f64 = 1.0;
let tol : f64 = 1e-10;
let max_iter : u32 = 100;
let dx_num : f64 = 1e-6;
let mut num_tests_passed : u32 = 0;
let num_tests_total : u32 = 7;
let x_mathematica: f64 = -3.26650043678562449167148755288;// 30 digits of precision
let x_mathematica_2: f64 = -6.27133405258685307845641527902;// 30 digits of precision
let x_mathematica_3: f64 = -9.42553801930504142668603949182;// 30 digits of precision
// let x_mathematica_3: f64 = -9.42553801930504142668603949182;// 30 digits of precision
let x_newton: f64 = univariate_solvers::newton_solve(&(fct as fn(f64) -> f64), &(dfct as fn(f64) -> f64), x0, tol, max_iter);
let x_newton_num: f64 = univariate_solvers::newton_solve_num(&(fct as fn(f64) -> f64), x0, tol, dx_num, max_iter);
let x_halley: f64 = univariate_solvers::halley_solve(&(fct as fn(f64) -> f64), &(dfct as fn(f64) -> f64), &(ddfct as fn(f64) -> f64), x0, tol, max_iter, false).unwrap();
@ -89,3 +90,24 @@ fn main() {
print_test_results(num_tests_passed, num_tests_total);
}
fn test_univariate_optimizers(verbose: bool) {
let tol : f64 = 1e-10;
let max_iter : u32 = 100;
let dx_num : f64 = 1e-6;
let mut num_tests_passed : u32 = 0;
let num_tests_total : u32 = 1;
let x_mathematica: f64 = -4.54295618675514754103476876324;// 30 digits of precision
let y_mathematica: f64 = -0.206327079359226884630654987440;// 30 digits of precision
let x_golden_section: f64 = univariate_minimizers::golden_section_minimize(&(fct as fn(f64) -> f64), -7.0, -1.0, tol);
num_tests_passed += check_result(x_golden_section, x_mathematica, tol*1e2, "Golden section search", verbose);
print_test_results(num_tests_passed, num_tests_total);
}
fn main() {
println!("Testing Rust numerical solvers.");
let verbose : bool = true;
test_univariate_solvers(verbose);
test_univariate_optimizers(verbose);
}

View file

@ -0,0 +1,49 @@
/// Golden section search for minimizing a function f(x)
/// @param f function to minimize
/// @param a left bracket
/// @param b right bracket
/// @param tol tolerance
/// @return solution
/// @note The interval [a, b] must bracket the minimum, and the function must have f''(x) > 0 over the interval [a, b] to garantee convergence.
pub fn golden_section_minimize<F>(f : F, mut a: f64, mut b: f64, tol: f64) -> f64
where F : Fn(f64) -> f64
{
let invphi: f64 = (f64::sqrt(5.0) - 1.0) / 2.0; // 1 / phi
let invphi2: f64 = (3.0 - f64::sqrt(5.0)) / 2.0; // 1 / phi^2
a = f64::min(a, b);
b = f64::max(a, b);
let mut h: f64 = b - a;
if h <= tol {
return (a + b)/2.0;
}
// Required steps to achieve tolerance
let n: u32 = (f64::ceil(f64::ln(tol / h) / f64::ln(invphi))) as u32;
let mut c: f64 = a + invphi2 * h;
let mut d: f64 = a + invphi * h;
let mut yc: f64 = f(c);
let mut yd: f64 = f(d);
for _ in 0..n {
if yc < yd { // yc > yd to find the maximum
b = d;
d = c;
yd = yc;
h = invphi * h;
c = a + invphi2 * h;
yc = f(c);
} else {
a = c;
c = d;
yc = yd;
h = invphi * h;
d = a + invphi * h;
yd = f(d);
}
}
if yc < yd { return (a + d)/2.0; }
else { return (c + b)/2.0; }
}