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;