numerics_rs/root_finding/
builder.rs

1use super::*;
2/// Builder pattern for RootFinder configuration.
3pub struct RootFinderBuilder<'a> {
4    method: RootFindingMethod,
5    initial_guess: Option<f64>,
6    boundaries: Option<(f64, f64)>,
7    tolerance: Option<f64>,
8    max_iterations: Option<usize>,
9    log_convergence: Option<bool>,
10    function: Option<&'a F>,   // Target function
11    derivative: Option<&'a F>, // Derivative of the target function
12}
13
14impl<'a> RootFinderBuilder<'a> {
15    /// Creates a new instance of `RootFinderBuilder`.
16    pub fn new(method: RootFindingMethod) -> Self {
17        Self {
18            method,
19            initial_guess: None,
20            boundaries: None,
21            tolerance: None,
22            max_iterations: None,
23            log_convergence: None,
24            function: None,
25            derivative: None,
26        }
27    }
28
29    /// Sets the initial guess for methods that require one (e.g., Newton-Raphson).
30    pub fn initial_guess(mut self, guess: f64) -> Self {
31        self.initial_guess = Some(guess);
32        self
33    }
34
35    /// Sets the boundaries for methods that require bounded intervals (e.g., Bisection).
36    pub fn boundaries(mut self, x0: f64, x1: f64) -> Self {
37        self.boundaries = Some((x0, x1));
38        self
39    }
40
41    /// Sets the tolerance for the root-finding process.
42    pub fn tolerance(mut self, tol: f64) -> Self {
43        self.tolerance = Some(tol);
44        self
45    }
46
47    /// Sets the maximum number of iterations.
48    pub fn max_iterations(mut self, max: usize) -> Self {
49        self.max_iterations = Some(max);
50        self
51    }
52
53    /// Enables or disables logging of convergence steps.
54    pub fn log_convergence(mut self, log: bool) -> Self {
55        self.log_convergence = Some(log);
56        self
57    }
58
59    /// Sets the target function to be used by the root finder.
60    pub fn function(mut self, function: &'a F) -> Self {
61        self.function = Some(function);
62        self
63    }
64
65    /// Sets the derivative of the target function (required for Newton-Raphson).
66    pub fn derivative(mut self, derivative: &'a F) -> Self {
67        self.derivative = Some(derivative);
68        self
69    }
70
71    /// Builds and returns the `RootFinder` instance.
72    pub fn build(self) -> Result<RootFindingIterationDecorator<'a>, String> {
73        let function = self.function.ok_or("Function must be specified")?;
74        let tolerance = self.tolerance.ok_or("Tolerance must be specified.")?;
75        let max_iterations = self
76            .max_iterations
77            .ok_or("Max iterations must be specified.")?;
78        let log_convergence = self.log_convergence.unwrap_or(false);
79        // Validate the build configuration based on the selected method
80        let rf: Result<Box<dyn RootFinder + 'a>, String> = match self.method {
81            RootFindingMethod::NewtonRaphson => {
82                // let derivative = self.derivative.ok_or("Derivative must be specified")?;
83                let initial_guess = self
84                    .initial_guess
85                    .ok_or("Initial guess must be specified")?;
86
87                Ok(Box::new(newton_raphson::NewtonRaphsonRootFinder {
88                    x0: initial_guess,
89                    tolerance,
90                }))
91            }
92            RootFindingMethod::Secant => {
93                let boundaries = self
94                    .boundaries
95                    .ok_or("Derivative must be specified for Secant method.")?;
96
97                Ok(Box::new(secant::SecantRootFinder {
98                    x0: boundaries.0,
99                    x1: boundaries.1,
100                    x2: f64::NAN,
101                    tolerance,
102                }))
103            }
104            RootFindingMethod::Bisection => {
105                let boundaries = self
106                    .boundaries
107                    .ok_or("Derivative must be specified for Bisection method.")?;
108
109                Ok(Box::new(bisection::BisectionRootFinder {
110                    x0: boundaries.0,
111                    x1: boundaries.1,
112                    tolerance,
113                    search_left: true,
114                }))
115            }
116            // Handle other methods if needed
117            _ => Err("Unsupported method in this example.".to_string()),
118        };
119        Ok(RootFindingIterationDecorator::new(
120            function,
121            self.derivative,
122            rf?,
123            max_iterations,
124            log_convergence,
125        ))
126    }
127}