trellis-runner 0.5.1

Calculation runner
Documentation

trellis-runner

Trellis

Trellis is a generic execution engine for iterative numerical algorithms.

Rather than implementing optimisation loops, integration loops or search loops directly, a procedure describes a single iteration of an algorithm while Trellis manages execution, convergence, termination, observation and checkpointing.

The library separates the concerns of:

  • algorithm implementation (Procedure)
  • problem definition (the problem supplied to the procedure)
  • algorithm state (UserState)
  • execution control (policies)
  • instrumentation (observers)

This separation allows algorithms to focus solely on their numerical method, while Trellis provides a reusable execution framework.

Execution model

Every Trellis calculation follows the same execution model:

                Problem
                   │
                   ▼
             ┌───────────┐
             │ Procedure │
             └─────┬─────┘
                   │
             updates state
                   │
                   ▼
             ┌───────────┐
             │ UserState │
             └─────┬─────┘
                   │
           emits Progress events
                   │
        ┌──────────┴──────────┐
        ▼                     ▼
  Engine Policies        Observers
        │
        ▼
   Engine Actions

The principal abstractions are:

  • Problem — the specific problem instance being solved.
  • Procedure — performs a single iteration of the algorithm.
  • UserState — stores the evolving state of the computation and reports progress.
  • Policies — inspect progress and control execution.
  • Observers — inspect progress without affecting execution.

A simple example

A calculation is configured using the builder returned by [GenerateBuilder::build_for]:

use trellis_runner::{
    GenerateBuilder, MaxIterationPolicy, RelativeTolerancePolicy,
    CancellationGuard, Procedure, Progress, UserState,
};

struct Problem;

#[derive(Default)]
struct State {
    value: f64,
}

impl UserState for State {
    type Float = f64;

    fn progress(&self) -> Progress<Self::Float> {
        Progress::Measure(self.value)
    }
}

struct Solver;

impl Procedure<Problem> for Solver {
    const NAME: &'static str = "Example";

    type State = State;
    type Output = ();

    fn step(
        &self,
        _: &mut Problem,
        _: &mut State,
        _: CancellationGuard<'_>,
    ) {}

    fn finalise(
        &self,
        _: &mut Problem,
        _: &State,
    ) {}
}
let engine = Solver
    .build_for(Problem)
    .with_initial_state(State::default())
    .and_policy(RelativeTolerancePolicy::new(1e-8, 10))
    .and_policy(MaxIterationPolicy::new(10_000))
    .finalise();

let result = engine.run();

The builder configures the execution environment rather than the numerical algorithm itself.

Procedures

A [Procedure] implements the numerical algorithm.

Each call to step() performs a single iteration of the algorithm, while finalise() converts the final algorithm state into the value returned to the caller.

Both infallible and fallible procedures are supported.

User state

[UserState] stores the evolving state of the computation.

In addition to algorithm-specific data, it reports progress to the engine via [Progress], allowing policies and observers to monitor execution.

States implementing [Snapshotable] can additionally participate in checkpointing, allowing long-running computations to be resumed.

Policies

Policies control solver execution.

During a run, the engine collects progress emitted by the procedure and passes it to one or more policies. Policies inspect this information and decide whether the solver should:

  • continue running,
  • terminate successfully,
  • terminate early,
  • request a checkpoint,
  • or perform another engine action.

Policies influence execution.

Observers do not.

Progress ──► Policy ──► Engine Action
           │
           └────► Observer

Multiple policies may be attached simultaneously.

The engine stops as soon as any policy requests termination.

Custom policies can be created implementing the [EnginePolicy] trait.

Built-in policies

Policy Description
MaxIterationPolicy Stops after a fixed number of iterations.
TimeoutPolicy Stops after a maximum wall-clock duration.
AbsoluteTolerancePolicy Stops when the mean absolute error over a rolling window falls below a tolerance.
RelativeTolerancePolicy Stops when the mean relative error over a rolling window falls below a tolerance.
TargetValuePolicy Stops when the mean distance to a target value remains below a tolerance.
NoProgressPolicy Stops when no meaningful improvement has been observed for a specified number of iterations.
StagnationPolicy Stops when improvement over a rolling window falls below a relative threshold.
CheckpointPolicy Requests periodic checkpoint generation.

Observers

Observers receive every event emitted by the engine but never influence execution.

Typical applications include:

  • structured logging,
  • tracing,
  • CSV export,
  • plotting,
  • metrics collection,
  • progress reporting,
  • custom visualisation.

Checkpointing

User states implementing [Snapshotable] may be checkpointed during execution.

Checkpoints may be requested by policies or generated manually, allowing interrupted computations to be resumed.

Extending Trellis

Trellis is designed to be extended through traits.

Most applications only need to implement:

  • [Procedure] to define the numerical algorithm,
  • [UserState] to store algorithm state,
  • [EnginePolicy] for custom stopping criteria,
  • [Observe] for custom instrumentation.

These components compose naturally, allowing new algorithms, policies and observers to be combined without modifying the execution engine itself.

License: MIT