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