Skip to main content

sim_lib_numbers_numeric/
traits.rs

1//! Backend plugin traits and option types: `Differentiator`, `Quadrature`, and
2//! `OdeSolver`, plus the `DiffOpts`/`QuadOpts`/`OdeOpts` and problem records.
3
4use sim_kernel::{Cx, Result, Symbol, Value};
5use sim_lib_numbers_func::Func;
6
7/// Common interface for every numeric backend plugin: it reports its method
8/// name and the kind of operation it implements.
9pub trait NumericPlugin: Send + Sync + 'static {
10    /// The method name this plugin registers under (for example, `central` or `rk4`).
11    fn name(&self) -> Symbol;
12    /// The numeric operation kind this plugin implements.
13    fn kind(&self) -> NumericKind;
14}
15
16/// The category of numeric backend, used to route a plugin to the right slot in
17/// the registry.
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19pub enum NumericKind {
20    /// A differentiator backend (numeric derivative at a point).
21    Differentiator,
22    /// A fixed-rule quadrature backend (definite integral with a set rule).
23    QuadratureFixed,
24    /// An adaptive quadrature backend (definite integral to a tolerance).
25    QuadratureAdaptive,
26    /// A fixed-step ODE solver backend.
27    OdeFixed,
28    /// An adaptive-step ODE solver backend.
29    OdeAdaptive,
30    /// An implicit differential-algebraic equation solver backend.
31    DaeImplicit,
32}
33
34/// Options controlling a numeric-differentiation call.
35#[derive(Clone, Debug)]
36pub struct DiffOpts {
37    /// The differentiator method to use, or `auto` to let the registry choose.
38    pub method: Symbol,
39    /// The finite-difference step size.
40    pub h: f64,
41}
42
43impl DiffOpts {
44    /// Returns default options: the `auto` method with a small default step.
45    ///
46    /// # Examples
47    ///
48    /// ```
49    /// use sim_lib_numbers_numeric::DiffOpts;
50    ///
51    /// let opts = DiffOpts::auto();
52    /// assert_eq!(opts.method.to_string(), "auto");
53    /// assert!(opts.h > 0.0);
54    /// ```
55    pub fn auto() -> Self {
56        Self {
57            method: Symbol::new("auto"),
58            h: 1.0e-6,
59        }
60    }
61}
62
63/// Options controlling an integration (quadrature) call.
64#[derive(Clone, Debug)]
65pub struct QuadOpts {
66    /// The quadrature method to use, or `auto` to let the registry choose.
67    pub method: Symbol,
68    /// The number of subdivisions, for fixed-rule quadrature.
69    pub n: Option<usize>,
70    /// The error tolerance, for adaptive quadrature.
71    pub tol: Option<f64>,
72}
73
74impl QuadOpts {
75    /// Returns default options for fixed-rule integration (`auto`, no tolerance).
76    pub fn fixed_default() -> Self {
77        Self {
78            method: Symbol::new("auto"),
79            n: None,
80            tol: None,
81        }
82    }
83
84    /// Returns default options for adaptive integration (`auto` with a tolerance).
85    ///
86    /// # Examples
87    ///
88    /// ```
89    /// use sim_lib_numbers_numeric::QuadOpts;
90    ///
91    /// let fixed = QuadOpts::fixed_default();
92    /// assert!(fixed.tol.is_none());
93    ///
94    /// let adaptive = QuadOpts::adaptive_default();
95    /// assert!(adaptive.tol.is_some());
96    /// ```
97    pub fn adaptive_default() -> Self {
98        Self {
99            method: Symbol::new("auto"),
100            n: None,
101            tol: Some(1.0e-10),
102        }
103    }
104}
105
106/// Options controlling an ODE-solve call.
107#[derive(Clone, Debug)]
108pub struct OdeOpts {
109    /// The ODE solver method to use, or `auto` to let the registry choose.
110    pub method: Symbol,
111    /// The fixed step size, for fixed-step solvers.
112    pub h: Option<f64>,
113    /// The error tolerance, for adaptive solvers.
114    pub tol: Option<f64>,
115    /// An optional cap on the number of integration steps.
116    pub max_steps: Option<usize>,
117}
118
119impl OdeOpts {
120    /// Returns default options for an adaptive ODE solve (`auto` with a tolerance).
121    ///
122    /// # Examples
123    ///
124    /// ```
125    /// use sim_lib_numbers_numeric::OdeOpts;
126    ///
127    /// let opts = OdeOpts::default_adaptive();
128    /// assert_eq!(opts.method.to_string(), "auto");
129    /// assert!(opts.tol.is_some());
130    /// assert!(opts.h.is_none());
131    /// ```
132    pub fn default_adaptive() -> Self {
133        Self {
134            method: Symbol::new("auto"),
135            h: None,
136            tol: Some(1.0e-8),
137            max_steps: None,
138        }
139    }
140}
141
142/// A first-order initial-value ODE problem `dy/dx = f(x, y)` handed to an
143/// [`OdeSolver`].
144pub struct OdeProblem<'a> {
145    /// The right-hand-side function giving the derivative.
146    pub dy: &'a Func,
147    /// The independent-variable symbol (typically `x`).
148    pub var: &'a Symbol,
149    /// The dependent-variable symbol (typically `y`).
150    pub y_var: &'a Symbol,
151    /// The initial value of the independent variable.
152    pub x0: &'a Value,
153    /// The initial value of the dependent variable.
154    pub y0: &'a Value,
155    /// The end value of the independent variable to integrate toward.
156    pub x_end: &'a Value,
157}
158
159/// A numeric differentiation backend: computes `df/dvar` at a point.
160pub trait Differentiator: NumericPlugin {
161    /// Evaluates the numeric derivative of `f` with respect to `var` at `point`.
162    fn diff_at(
163        &self,
164        cx: &mut Cx,
165        f: &Func,
166        var: &Symbol,
167        point: &Value,
168        opt: DiffOpts,
169    ) -> Result<Value>;
170}
171
172/// A numeric integration (quadrature) backend: computes a definite integral.
173pub trait Quadrature: NumericPlugin {
174    /// Integrates `f` over `var` from `lo` to `hi`.
175    fn integrate(
176        &self,
177        cx: &mut Cx,
178        f: &Func,
179        var: &Symbol,
180        lo: &Value,
181        hi: &Value,
182        opt: QuadOpts,
183    ) -> Result<Value>;
184}
185
186/// A numeric ODE-solving backend: integrates an initial-value problem.
187pub trait OdeSolver: NumericPlugin {
188    /// Solves `problem`, returning the sampled `(x, y)` points of the trajectory.
189    fn solve(
190        &self,
191        cx: &mut Cx,
192        problem: OdeProblem<'_>,
193        opt: OdeOpts,
194    ) -> Result<Vec<(Value, Value)>>;
195}