trellis_runner/state/mod.rs
1/// Core solver state representation.
2///
3/// This module defines the runtime state used by the engine during execution.
4///
5/// The state is split into three components:
6///
7/// - `user`: application-defined state implementing [`UserState`]
8/// - `runtime`: engine execution metadata (iteration count, timing)
9/// - `convergence`: tracking of numerical convergence behaviour
10///
11/// ## Design principles
12///
13/// - The engine owns a single mutable `State<S>` during execution
14/// - External code interacts with state via [`StateView`], not direct access
15/// - Persistence is handled via [`Snapshotable`] + checkpointing systems
16///
17/// ## Separation of concerns
18///
19/// - `UserState` defines problem-specific behaviour
20/// - `RuntimeState` tracks execution lifecycle
21/// - `ConvergenceState` tracks numerical progress
22///
23/// These components are intentionally isolated to avoid coupling
24/// solver logic with infrastructure concerns.
25///
26/// ## Notes
27///
28/// - `State` is not exposed mutably outside the engine
29/// - Access is mediated via `StateView<'_>`
30/// - Persistence uses snapshot types derived from `Snapshotable`
31mod convergence;
32mod runtime;
33mod user;
34mod view;
35
36pub use user::{Snapshotable, StateRestorer, UserState};
37
38pub(crate) use convergence::ConvergenceState;
39pub(crate) use runtime::RuntimeState;
40
41pub(crate) use view::StateView;
42
43use num_traits::float::FloatCore;
44
45/// Internal execution state of the solver.
46///
47/// This struct is owned exclusively by the Engine during execution
48/// and is not intended to be modified directly by users.
49///
50/// Access to state is provided through StateView.
51///
52/// # Fields
53/// - `user`: user-defined state implementing UserState
54/// - `runtime`: execution metadata (iteration count, duration)
55/// - `convergence`: convergence tracking for policies
56#[derive(Clone, Debug)]
57#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
58#[cfg_attr(
59 feature = "serde",
60 serde(bound(
61 serialize = "S: serde::Serialize, S::Float: serde::Serialize",
62 deserialize = "S: serde::Deserialize<'de>, S::Float: serde::Deserialize<'de>"
63 ))
64)]
65pub struct State<S: UserState> {
66 pub(crate) runtime: RuntimeState,
67
68 pub(crate) convergence: ConvergenceState<S::Float>,
69 /// The user component of the state implements the application specific code
70 pub user: S,
71}
72
73impl<S> State<S>
74where
75 S: UserState,
76 <S as UserState>::Float: FloatCore,
77{
78 /// Creates a fresh solver state.
79 ///
80 /// Initializes:
81 /// - runtime counters at zero
82 /// - convergence tracking at initial values
83 pub(crate) fn new(user: S) -> Self {
84 Self {
85 user,
86 runtime: RuntimeState::new(),
87 convergence: ConvergenceState::new(),
88 }
89 }
90
91 // TODO: More elegant to not expose these methods, or to return a state at all and construct
92 // directly on the error type
93 pub fn run_summary(&self) -> crate::RunSummary<<S as UserState>::Float> {
94 let view = StateView::new(self);
95
96 crate::RunSummary::new(view)
97 }
98
99 pub fn into_user(self) -> S {
100 self.user
101 }
102}