rustyqlib/core/solvers/
halley.rs1use super::solver_1d::{Root, Solver1d};
6
7pub fn halley(
9 cfg: &Solver1d,
10 f: impl Fn(f64) -> f64,
11 df: impl Fn(f64) -> f64,
12 d2f: impl Fn(f64) -> f64,
13 x0: f64,
14) -> Root {
15 let mut x = x0;
16 for i in 0..cfg.max_iter {
17 let fx = f(x);
18 if fx.abs() <= cfg.tol {
19 return Root { x, iterations: i, converged: true };
20 }
21 let dfx = df(x);
22 let denom = 2.0 * dfx * dfx - fx * d2f(x);
23 if denom == 0.0 || !denom.is_finite() {
24 return Root { x, iterations: i, converged: false };
25 }
26 x -= 2.0 * fx * dfx / denom;
27 }
28 Root { x, iterations: cfg.max_iter, converged: f(x).abs() <= cfg.tol }
29}
30
31#[cfg(test)]
32mod tests {
33 use super::*;
34
35 fn f(x: f64) -> f64 {
36 x * x - 2.0
37 }
38 fn df(x: f64) -> f64 {
39 2.0 * x
40 }
41 fn d2f(_: f64) -> f64 {
42 2.0
43 }
44
45 #[test]
46 fn finds_sqrt_two() {
47 let r = halley(&Solver1d::default(), f, df, d2f, 1.0);
48 assert!(r.converged && (r.x - std::f64::consts::SQRT_2).abs() < 1e-12, "{r:?}");
49 }
50
51 #[test]
52 fn converges_in_fewer_iterations_than_newton() {
53 let cfg = Solver1d::new(1e-14, 200);
54 let n = super::super::newton_raphson::newton_raphson(&cfg, f, df, 100.0);
55 let h = halley(&cfg, f, df, d2f, 100.0);
56 assert!(n.converged && h.converged);
57 assert!(h.iterations <= n.iterations, "halley {} vs newton {}", h.iterations, n.iterations);
58 }
59}