trellis_runner/
result.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
//! This module defines the default output type for a trellis calculation, in addition to the error
//! wrapper.

use crate::{State, UserState};
use num_traits::float::FloatCore;
use std::fmt;

/// The output of a calculation
///
/// The calculation output is user defined in the finalise step of the [`Calculation`] trait, but
/// this is presented as a good verbose option in situations where the caller wants granular
/// information about the calculation and its progress. It returns the entire original problem,
/// solver and state object.
pub struct Output<R, S>
where
    S: UserState,
{
    /// The original calculation carried out by `trellis`
    pub result: R,
    /// Solver state after the last iterationn
    pub state: State<S>,
}

impl<R, S> std::fmt::Display for Output<R, S>
where
    R: fmt::Display,
    S: UserState,
    <S as UserState>::Float: FloatCore,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if let Some(cause) = self.state.termination_cause() {
            use crate::Cause::*;
            match cause {
                Converged => {
                    writeln!(
                        f,
                        "Solver converged after {} iterations",
                        self.state.current_iteration()
                    )?;
                    if let Some(duration) = self.state.duration() {
                        writeln!(f, "Duration {:?}", duration)?;
                    }
                    writeln!(f, "{}", self.result)?;
                }
                ControlC => {
                    writeln!(
                        f,
                        "Solver converged by ctrl-c intervention after {} iterations",
                        self.state.current_iteration()
                    )?;
                }
                Parent => {
                    writeln!(
                        f,
                        "Solver converged by parent intervention after {} iterations",
                        self.state.current_iteration()
                    )?;
                }
                ExceededMaxIterations => {
                    writeln!(
                        f,
                        "Solver exceeded maximum iterations ({})",
                        self.state.current_iteration()
                    )?;
                }
            }
        } else {
            writeln!(f, "Solver is still in progress.")?;
        }
        Ok(())
    }
}

impl<R, S> Output<R, S>
where
    S: UserState,
{
    pub(crate) fn new(result: R, state: State<S>) -> Self {
        Self { result, state }
    }
}

#[derive(thiserror::Error, Debug)]
/// An error wrapper for trellis calculations
///
/// The error wraps the underlying error type [`ErrorCause`], which contains information about the
/// reason the calculation failed. In addition it can optionally return the an output from the
/// calculation. This is useful in situations where a failure occured due to running out of
/// iterations, or termination from the parent thread, but the state of the calculation at that
/// point may still contain meaningful information. Maybe the calculation ran out of iterations
/// because it was unable to reach the required tolerance, but is still at convergence?
pub struct TrellisError<O, E> {
    #[source]
    /// The underlying error cause.
    pub cause: ErrorCause<E>,
    /// An optional result which can be extracted by the caller
    pub result: Option<O>,
}

impl<O, E> From<E> for TrellisError<O, E> {
    fn from(cause: E) -> Self {
        Self {
            cause: ErrorCause::User(cause),
            result: None,
        }
    }
}

#[derive(thiserror::Error, Debug)]
pub enum ErrorCause<E> {
    #[error("error in user defined calculation: {0}")]
    User(#[from] E),
    #[error("exceeded maximum number of iterations")]
    MaxIterExceeded,
    #[error("calculation cancelled due to ctrl-c")]
    ControlC,
    #[error("calculation cancelled due to cancelled token")]
    CancellationToken,
}