solverforge_solver/termination/
unimproved.rs1use std::fmt::Debug;
4use std::marker::PhantomData;
5use std::time::Duration;
6
7use solverforge_core::domain::PlanningSolution;
8use solverforge_core::score::Score;
9use solverforge_scoring::Director;
10
11use super::Termination;
12use crate::scope::ProgressCallback;
13use crate::scope::SolverScope;
14
15pub struct UnimprovedStepCountTermination<S: PlanningSolution> {
39 limit: u64,
40 state: UnimprovedState<S::Score>,
41 _phantom: PhantomData<fn() -> S>,
42}
43
44impl<S: PlanningSolution> Debug for UnimprovedStepCountTermination<S> {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 f.debug_struct("UnimprovedStepCountTermination")
47 .field("limit", &self.limit)
48 .field(
49 "steps_since_improvement",
50 &self.state.steps_since_improvement,
51 )
52 .finish()
53 }
54}
55
56#[derive(Clone)]
57struct UnimprovedState<Sc: Score> {
58 last_best_score: Option<Sc>,
59 steps_since_improvement: u64,
60 last_checked_step: Option<u64>,
61}
62
63impl<Sc: Score> Default for UnimprovedState<Sc> {
64 fn default() -> Self {
65 Self {
66 last_best_score: None,
67 steps_since_improvement: 0,
68 last_checked_step: None,
69 }
70 }
71}
72
73impl<S: PlanningSolution> UnimprovedStepCountTermination<S> {
74 pub fn new(limit: u64) -> Self {
75 Self {
76 limit,
77 state: UnimprovedState::default(),
78 _phantom: PhantomData,
79 }
80 }
81}
82
83impl<S: PlanningSolution, D: Director<S>, BestCb: ProgressCallback<S>> Termination<S, D, BestCb>
84 for UnimprovedStepCountTermination<S>
85{
86 fn is_terminated(&mut self, solver_scope: &SolverScope<S, D, BestCb>) -> bool {
87 let state = &mut self.state;
88 let current_step = solver_scope.total_step_count();
89
90 if state.last_checked_step == Some(current_step) {
92 return state.steps_since_improvement >= self.limit;
93 }
94 state.last_checked_step = Some(current_step);
95
96 let current_best = solver_scope.best_score();
97
98 match (&state.last_best_score, current_best) {
99 (None, Some(score)) => {
100 state.last_best_score = Some(*score);
102 state.steps_since_improvement = 0;
103 }
104 (Some(last), Some(current)) => {
105 if *current > *last {
106 state.last_best_score = Some(*current);
108 state.steps_since_improvement = 0;
109 } else {
110 state.steps_since_improvement += 1;
112 }
113 }
114 (Some(_), None) => {
115 state.steps_since_improvement += 1;
117 }
118 (None, None) => {
119 }
121 }
122
123 state.steps_since_improvement >= self.limit
124 }
125}
126
127pub struct UnimprovedTimeTermination<S: PlanningSolution> {
152 limit: Duration,
153 state: UnimprovedTimeState<S::Score>,
154 _phantom: PhantomData<fn() -> S>,
155}
156
157impl<S: PlanningSolution> Debug for UnimprovedTimeTermination<S> {
158 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
159 f.debug_struct("UnimprovedTimeTermination")
160 .field("limit", &self.limit)
161 .finish()
162 }
163}
164
165struct UnimprovedTimeState<Sc: Score> {
166 last_best_score: Option<Sc>,
167 last_improvement_elapsed: Option<Duration>,
168}
169
170impl<Sc: Score> Default for UnimprovedTimeState<Sc> {
171 fn default() -> Self {
172 Self {
173 last_best_score: None,
174 last_improvement_elapsed: None,
175 }
176 }
177}
178
179impl<S: PlanningSolution> UnimprovedTimeTermination<S> {
180 pub fn new(limit: Duration) -> Self {
181 Self {
182 limit,
183 state: UnimprovedTimeState::default(),
184 _phantom: PhantomData,
185 }
186 }
187
188 pub fn millis(ms: u64) -> Self {
189 Self::new(Duration::from_millis(ms))
190 }
191
192 pub fn seconds(secs: u64) -> Self {
193 Self::new(Duration::from_secs(secs))
194 }
195}
196
197impl<S: PlanningSolution, D: Director<S>, BestCb: ProgressCallback<S>> Termination<S, D, BestCb>
198 for UnimprovedTimeTermination<S>
199{
200 fn is_terminated(&mut self, solver_scope: &SolverScope<S, D, BestCb>) -> bool {
201 let state = &mut self.state;
202 let current_best = solver_scope.best_score();
203 let now = solver_scope.elapsed().unwrap_or_default();
204
205 match (&state.last_best_score, current_best) {
206 (None, Some(score)) => {
207 state.last_best_score = Some(*score);
209 state.last_improvement_elapsed = Some(now);
210 false
211 }
212 (Some(last), Some(current)) => {
213 if *current > *last {
214 state.last_best_score = Some(*current);
216 state.last_improvement_elapsed = Some(now);
217 false
218 } else {
219 state
221 .last_improvement_elapsed
222 .map(|t| now.saturating_sub(t) >= self.limit)
223 .unwrap_or(false)
224 }
225 }
226 (Some(_), None) => {
227 state
229 .last_improvement_elapsed
230 .map(|t| now.saturating_sub(t) >= self.limit)
231 .unwrap_or(false)
232 }
233 (None, None) => {
234 false
236 }
237 }
238 }
239}