Skip to main content

loeres_device/
config.rs

1//! Bounded execution configuration (RFC 005).
2//!
3//! Runtime policy data โ€” iteration cap, tolerance, timing mode โ€” kept as data,
4//! not type-level const generics. RFC 005 validates *structural* config rules;
5//! *solver-specific* validation (e.g. whether a kernel forbids zero tolerance)
6//! is RFC 006-owned.
7
8use loeres::{FiniteScalar, OrderedScalar, SolverError};
9
10/// Execution timing policy.
11///
12/// `EarlyExitAllowed` is always available. The `ConstantIteration` variant is
13/// gated behind the `constant-iteration` feature, so requesting constant-iteration
14/// without that feature fails at compile time (decision M5). `TimingMode` is
15/// `#[non_exhaustive]`, so downstream `match`es must include a wildcard arm and
16/// stay robust regardless of which features compiled the variant in.
17#[non_exhaustive]
18#[derive(Copy, Clone, Debug, Eq, PartialEq)]
19pub enum TimingMode {
20    /// Bounded run that may exit early once converged.
21    EarlyExitAllowed,
22    /// Fixed iteration count for timing stability (requires `constant-iteration`).
23    #[cfg(feature = "constant-iteration")]
24    ConstantIteration,
25}
26
27/// Runtime device-solve configuration.
28#[derive(Copy, Clone, Debug)]
29pub struct DeviceSolveConfig<S> {
30    /// Maximum solver iterations (must be `> 0`).
31    pub max_iterations: u32,
32    /// Convergence tolerance.
33    pub tolerance: S,
34    /// Execution timing policy.
35    pub timing_mode: TimingMode,
36}
37
38impl<S: FiniteScalar + OrderedScalar> DeviceSolveConfig<S> {
39    /// Validate the *structural* config rules (RFC 005 ยง6): `max_iterations > 0`
40    /// and a finite, non-negative tolerance.
41    ///
42    /// Zero tolerance is **not** rejected here โ€” whether a concrete solver forbids
43    /// it is RFC 006's decision (decision M6). Returns a structured
44    /// [`SolverError`]; never panics.
45    pub fn validate(&self) -> Result<(), SolverError> {
46        if self.max_iterations == 0 {
47            return Err(SolverError::InvalidInput);
48        }
49        if !self.tolerance.is_finite() {
50            return Err(SolverError::NonFiniteInput);
51        }
52        if self.tolerance < S::zero() {
53            return Err(SolverError::InvalidInput);
54        }
55        Ok(())
56    }
57}
58
59#[cfg(test)]
60mod tests;