Skip to main content

trellis_runner/
lib.rs

1//! # Trellis
2//!
3//! Trellis is a generic execution engine for iterative numerical algorithms.
4//!
5//! Rather than implementing optimisation loops, integration loops or search loops
6//! directly, a procedure describes a single iteration of an algorithm while
7//! Trellis manages execution, convergence, termination, observation and
8//! checkpointing.
9//!
10//! The library separates the concerns of:
11//!
12//! - **algorithm implementation** (`Procedure`)
13//! - **problem definition** (the problem supplied to the procedure)
14//! - **algorithm state** (`UserState`)
15//! - **execution control** (policies)
16//! - **instrumentation** (observers)
17//!
18//! This separation allows algorithms to focus solely on their numerical method,
19//! while Trellis provides a reusable execution framework.
20//!
21//! ## Execution model
22//!
23//! Every Trellis calculation follows the same execution model:
24//!
25//! ```text
26//!                 Problem
27//!                    │
28//!                    ▼
29//!              ┌───────────┐
30//!              │ Procedure │
31//!              └─────┬─────┘
32//!                    │
33//!              updates state
34//!                    │
35//!                    ▼
36//!              ┌───────────┐
37//!              │ UserState │
38//!              └─────┬─────┘
39//!                    │
40//!            emits Progress events
41//!                    │
42//!         ┌──────────┴──────────┐
43//!         ▼                     ▼
44//!   Engine Policies        Observers
45//!         │
46//!         ▼
47//!    Engine Actions
48//! ```
49//!
50//! The principal abstractions are:
51//!
52//! - **Problem** — the specific problem instance being solved.
53//! - **Procedure** — performs a single iteration of the algorithm.
54//! - **UserState** — stores the evolving state of the computation and reports
55//!   progress.
56//! - **Policies** — inspect progress and control execution.
57//! - **Observers** — inspect progress without affecting execution.
58//!
59//! ## A simple example
60//!
61//! A calculation is configured using the builder returned by
62//! [`GenerateBuilder::build_for`]:
63//!
64//! ```
65//! use trellis_runner::{
66//!     GenerateBuilder, MaxIterationPolicy, RelativeTolerancePolicy,
67//!     CancellationGuard, Procedure, Progress, UserState,
68//! };
69//!
70//! struct Problem;
71//!
72//! #[derive(Default)]
73//! struct State {
74//!     value: f64,
75//! }
76//!
77//! impl UserState for State {
78//!     type Float = f64;
79//!
80//!     fn progress(&self) -> Progress<Self::Float> {
81//!         Progress::Measure(self.value)
82//!     }
83//! }
84//!
85//! struct Solver;
86//!
87//! impl Procedure<Problem> for Solver {
88//!     const NAME: &'static str = "Example";
89//!
90//!     type State = State;
91//!     type Output = ();
92//!
93//!     fn step(
94//!         &self,
95//!         _: &mut Problem,
96//!         _: &mut State,
97//!         _: CancellationGuard<'_>,
98//!     ) {}
99//!
100//!     fn finalise(
101//!         &self,
102//!         _: &mut Problem,
103//!         _: &State,
104//!     ) {}
105//! }
106//! let engine = Solver
107//!     .build_for(Problem)
108//!     .with_initial_state(State::default())
109//!     .and_policy(RelativeTolerancePolicy::new(1e-8, 10))
110//!     .and_policy(MaxIterationPolicy::new(10_000))
111//!     .finalise();
112//!
113//! let result = engine.run();
114//! ```
115//!
116//! The builder configures the execution environment rather than the numerical
117//! algorithm itself.
118//!
119//! ## Procedures
120//!
121//! A [`Procedure`] implements the numerical algorithm.
122//!
123//! Each call to `step()` performs a single iteration of the algorithm, while
124//! `finalise()` converts the final algorithm state into the value returned to the
125//! caller.
126//!
127//! Both infallible and fallible procedures are supported.
128//!
129//! ## User state
130//!
131//! [`UserState`] stores the evolving state of the computation.
132//!
133//! In addition to algorithm-specific data, it reports progress to the engine via
134//! [`Progress`], allowing policies and observers to monitor execution.
135//!
136//! States implementing [`Snapshotable`] can additionally participate in
137//! checkpointing, allowing long-running computations to be resumed.
138//!
139//! ## Policies
140//!
141//! Policies control solver execution.
142//!
143//! During a run, the engine collects progress emitted by the procedure and
144//! passes it to one or more policies. Policies inspect this information and
145//! decide whether the solver should:
146//!
147//! - continue running,
148//! - terminate successfully,
149//! - terminate early,
150//! - request a checkpoint,
151//! - or perform another engine action.
152//!
153//! Policies influence execution.
154//!
155//! Observers do not.
156//!
157//! ```text
158//! Progress ──► Policy ──► Engine Action
159//!            │
160//!            └────► Observer
161//! ```
162//!
163//! Multiple policies may be attached simultaneously.
164//!
165//! The engine stops as soon as any policy requests termination.
166//!
167//! Custom policies can be created implementing the [`EnginePolicy`] trait.
168//!
169//! ### Built-in policies
170//!
171//! | Policy | Description |
172//! |---------|-------------|
173//! | `MaxIterationPolicy` | Stops after a fixed number of iterations. |
174//! | `TimeoutPolicy` | Stops after a maximum wall-clock duration. |
175//! | `AbsoluteTolerancePolicy` | Stops when the mean absolute error over a rolling window falls below a tolerance. |
176//! | `RelativeTolerancePolicy` | Stops when the mean relative error over a rolling window falls below a tolerance. |
177//! | `TargetValuePolicy` | Stops when the mean distance to a target value remains below a tolerance. |
178//! | `NoProgressPolicy` | Stops when no meaningful improvement has been observed for a specified number of iterations. |
179//! | `StagnationPolicy` | Stops when improvement over a rolling window falls below a relative threshold. |
180//! | `CheckpointPolicy` | Requests periodic checkpoint generation. |
181//!
182//! ## Observers
183//!
184//! Observers receive every event emitted by the engine but never influence
185//! execution.
186//!
187//! Typical applications include:
188//!
189//! - structured logging,
190//! - tracing,
191//! - CSV export,
192//! - plotting,
193//! - metrics collection,
194//! - progress reporting,
195//! - custom visualisation.
196//!
197//! ## Checkpointing
198//!
199//! User states implementing [`Snapshotable`] may be checkpointed during
200//! execution.
201//!
202//! Checkpoints may be requested by policies or generated manually, allowing
203//! interrupted computations to be resumed.
204//!
205//! ## Extending Trellis
206//!
207//! Trellis is designed to be extended through traits.
208//!
209//! Most applications only need to implement:
210//!
211//! - [`Procedure`] to define the numerical algorithm,
212//! - [`UserState`] to store algorithm state,
213//! - [`EnginePolicy`] for custom stopping criteria,
214//! - [`Observe`] for custom instrumentation.
215//!
216//! These components compose naturally, allowing new algorithms, policies and
217//! observers to be combined without modifying the execution engine itself.
218#![allow(dead_code)]
219
220mod procedure;
221
222mod engine;
223mod progress;
224mod result;
225mod watchers;
226
227mod state;
228
229pub(crate) use procedure::Infallible;
230
231pub use procedure::{FallibleProcedure, Procedure};
232pub use tokio_util::sync::CancellationToken;
233
234pub use engine::{
235    AbsoluteTolerancePolicy, CancellationGuard, CheckpointPolicy, EngineFailure, EnginePolicy,
236    GenerateBuilder, GenerateBuilderFallible, InMemoryCheckpointStore, MaxIterationPolicy,
237    NoProgressPolicy, RelativeTolerancePolicy, StagnationPolicy, TargetValuePolicy, Termination,
238    TimeoutPolicy,
239};
240
241#[cfg(feature = "writing")]
242pub use engine::JsonCheckpointStore;
243
244pub use result::{EngineOutput, EngineOutputWithSnapshot, RunSummary, TrellisError};
245
246pub use state::{Snapshotable, State, StateRestorer, UserState};
247
248pub use progress::{Progress, ProgressDiagnostics};
249
250pub use watchers::{Frequency, Observe, Tracer};
251
252#[cfg(feature = "writing")]
253pub use watchers::CsvProgressWriter;
254
255#[cfg(feature = "plotting")]
256pub use watchers::PlotObserver;
257
258pub trait TrellisFloat: std::fmt::Display + std::fmt::Debug + num_traits::float::FloatCore {}
259
260impl TrellisFloat for f32 {}
261impl TrellisFloat for f64 {}