argmin/core/
termination.rs

1// Copyright 2018-2024 argmin developers
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8#[cfg(feature = "serde1")]
9use serde::{Deserialize, Serialize};
10
11/// Status of optimization execution
12#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
13#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
14pub enum TerminationStatus {
15    /// Execution is terminated
16    Terminated(TerminationReason),
17    /// Execution is running
18    #[default]
19    NotTerminated,
20}
21
22impl TerminationStatus {
23    /// Returns `true` if a solver terminated and `false` otherwise.
24    ///
25    /// # Example
26    ///
27    /// ```
28    /// use argmin::core::{TerminationStatus, TerminationReason};
29    ///
30    /// assert!(TerminationStatus::Terminated(TerminationReason::MaxItersReached).terminated());
31    /// assert!(TerminationStatus::Terminated(TerminationReason::TargetCostReached).terminated());
32    /// assert!(TerminationStatus::Terminated(TerminationReason::SolverConverged).terminated());
33    /// assert!(TerminationStatus::Terminated(TerminationReason::Interrupt).terminated());
34    /// assert!(TerminationStatus::Terminated(TerminationReason::Timeout).terminated());
35    /// assert!(TerminationStatus::Terminated(TerminationReason::SolverExit("Exit reason".to_string())).terminated());
36    /// ```
37    pub fn terminated(&self) -> bool {
38        matches!(self, TerminationStatus::Terminated(_))
39    }
40}
41
42impl std::fmt::Display for TerminationStatus {
43    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
44        match self {
45            TerminationStatus::Terminated(reason) => f.write_str(reason.text()),
46            TerminationStatus::NotTerminated => f.write_str("Running"),
47        }
48    }
49}
50
51/// Reasons for optimization algorithms to stop
52#[derive(Debug, Clone, Eq, PartialEq, Hash)]
53#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
54pub enum TerminationReason {
55    /// Reached maximum number of iterations
56    MaxItersReached,
57    /// Reached target cost function value
58    TargetCostReached,
59    /// Algorithm manually interrupted with SIGINT (Ctrl+C), SIGTERM or SIGHUP
60    Interrupt,
61    /// Converged
62    SolverConverged,
63    /// Timeout reached
64    Timeout,
65    /// Solver exit with given reason
66    SolverExit(String),
67}
68
69impl TerminationReason {
70    /// Returns a textual representation of what happened.
71    ///
72    /// # Example
73    ///
74    /// ```
75    /// use argmin::core::TerminationReason;
76    ///
77    /// assert_eq!(
78    ///     TerminationReason::MaxItersReached.text(),
79    ///     "Maximum number of iterations reached"
80    /// );
81    /// assert_eq!(
82    ///     TerminationReason::TargetCostReached.text(),
83    ///     "Target cost value reached"
84    /// );
85    /// assert_eq!(
86    ///     TerminationReason::Interrupt.text(),
87    ///     "Interrupt"
88    /// );
89    /// assert_eq!(
90    ///     TerminationReason::SolverConverged.text(),
91    ///     "Solver converged"
92    /// );
93    /// assert_eq!(
94    ///     TerminationReason::Timeout.text(),
95    ///     "Timeout reached"
96    /// );
97    /// assert_eq!(
98    ///     TerminationReason::SolverExit("Aborted".to_string()).text(),
99    ///     "Aborted"
100    /// );
101    /// ```
102    pub fn text(&self) -> &str {
103        match self {
104            TerminationReason::MaxItersReached => "Maximum number of iterations reached",
105            TerminationReason::TargetCostReached => "Target cost value reached",
106            TerminationReason::Interrupt => "Interrupt",
107            TerminationReason::SolverConverged => "Solver converged",
108            TerminationReason::Timeout => "Timeout reached",
109            TerminationReason::SolverExit(reason) => reason.as_ref(),
110        }
111    }
112}
113
114impl std::fmt::Display for TerminationReason {
115    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
116        write!(f, "{}", self.text())
117    }
118}
119
120impl Default for TerminationReason {
121    fn default() -> Self {
122        TerminationReason::SolverExit("Undefined".to_string())
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129
130    send_sync_test!(termination_reason, TerminationReason);
131}