Skip to main content

rustyqlib/core/solvers/
newton_raphson.rs

1//! Newton-Raphson: quadratic convergence near the root, given an analytic
2//! derivative. Used by the Barone-Adesi-Whaley critical-boundary solve.
3
4use super::solver_1d::{Root, Solver1d};
5
6/// Newton-Raphson with an analytic derivative. A vanishing or non-finite
7/// derivative stops the iteration with `converged: false`.
8pub fn newton_raphson(
9    cfg: &Solver1d,
10    f: impl Fn(f64) -> f64,
11    df: impl Fn(f64) -> f64,
12    x0: f64,
13) -> Root {
14    let mut x = x0;
15    for i in 0..cfg.max_iter {
16        let fx = f(x);
17        if fx.abs() <= cfg.tol {
18            return Root { x, iterations: i, converged: true };
19        }
20        let dfx = df(x);
21        if dfx == 0.0 || !dfx.is_finite() {
22            return Root { x, iterations: i, converged: false };
23        }
24        x -= fx / dfx;
25    }
26    Root { x, iterations: cfg.max_iter, converged: f(x).abs() <= cfg.tol }
27}
28
29#[cfg(test)]
30mod tests {
31    use super::*;
32
33    #[test]
34    fn finds_sqrt_two() {
35        let r = newton_raphson(&Solver1d::default(), |x| x * x - 2.0, |x| 2.0 * x, 1.0);
36        assert!(r.converged && (r.x - std::f64::consts::SQRT_2).abs() < 1e-12, "{r:?}");
37    }
38
39    #[test]
40    fn reports_failure_on_zero_derivative() {
41        // f'(0) = 0: Newton cannot move
42        let r = newton_raphson(&Solver1d::default(), |x| x * x - 2.0, |x| 2.0 * x, 0.0);
43        assert!(!r.converged);
44    }
45}