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}