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
// Copyright 2018-2020 argmin developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! # Termination
//!
//! Defines reasons for termination.
//!
//! TODO:
//!   * Maybe it is better to define a trait (with `terminated` and `text` methods), because it
//!     would allow implementers of solvers to define their own `TerminationReason`s. However, this
//!     would require a lot of work.

use serde::{Deserialize, Serialize};

/// Indicates why the optimization algorithm stopped
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum TerminationReason {
    /// In case it has not terminated yet
    NotTerminated,
    /// Maximum number of iterations reached
    MaxItersReached,
    /// Target cost function value reached
    TargetCostReached,
    /// Target precision reached
    TargetPrecisionReached,
    /// Cost function value did not change
    NoChangeInCost,
    /// Acceped stall iter exceeded
    AcceptedStallIterExceeded,
    /// Best stall iter exceeded
    BestStallIterExceeded,
    /// Condition for Line search met
    LineSearchConditionMet,
    /// Target tolerance reached
    TargetToleranceReached,
    /// Aborted
    Aborted,
}

impl TerminationReason {
    /// Returns `true` if a solver terminated and `false` otherwise
    pub fn terminated(self) -> bool {
        match self {
            TerminationReason::NotTerminated => false,
            _ => true,
        }
    }

    /// Returns a texual representation of what happened
    pub fn text(&self) -> &str {
        match *self {
            TerminationReason::NotTerminated => "Not terminated",
            TerminationReason::MaxItersReached => "Maximum number of iterations reached",
            TerminationReason::TargetCostReached => "Target cost value reached",
            TerminationReason::TargetPrecisionReached => "Target precision reached",
            TerminationReason::NoChangeInCost => "No change in cost function value",
            TerminationReason::AcceptedStallIterExceeded => "Accepted stall iterations exceeded",
            TerminationReason::BestStallIterExceeded => "Best stall iterations exceeded",
            TerminationReason::LineSearchConditionMet => "Line search condition met",
            TerminationReason::TargetToleranceReached => "Target tolerance reached",
            TerminationReason::Aborted => "Optimization aborted",
        }
    }
}

impl std::fmt::Display for TerminationReason {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}", self.text())
    }
}

impl Default for TerminationReason {
    fn default() -> Self {
        TerminationReason::NotTerminated
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    send_sync_test!(termination_reason, TerminationReason);
}