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 {
74 Self {
75 limit,
76 state: RefCell::new(UnimprovedState::default()),
77 _phantom: PhantomData,
78 }
79 }
80}
81
82unsafe impl<S: PlanningSolution> Send for UnimprovedStepCountTermination<S> {}
85
86impl<S: PlanningSolution, D: Director<S>, BestCb: BestSolutionCallback<S>> Termination<S, D, BestCb>
87 for UnimprovedStepCountTermination<S>
88{
89 fn is_terminated(&self, solver_scope: &SolverScope<S, D, BestCb>) -> bool {
90 let mut state = self.state.borrow_mut();
91 let current_step = solver_scope.total_step_count();
92
93 if state.last_checked_step == Some(current_step) {
95 return state.steps_since_improvement >= self.limit;
96 }
97 state.last_checked_step = Some(current_step);
98
99 let current_best = solver_scope.best_score();
100
101 match (&state.last_best_score, current_best) {
102 (None, Some(score)) => {
103 state.last_best_score = Some(*score);
105 state.steps_since_improvement = 0;
106 }
107 (Some(last), Some(current)) => {
108 if *current > *last {
109 state.last_best_score = Some(*current);
111 state.steps_since_improvement = 0;
112 } else {
113 state.steps_since_improvement += 1;
115 }
116 }
117 (Some(_), None) => {
118 state.steps_since_improvement += 1;
120 }
121 (None, None) => {
122 }
124 }
125
126 state.steps_since_improvement >= self.limit
127 }
128}
129
130pub struct UnimprovedTimeTermination<S: PlanningSolution> {
155 limit: Duration,
156 state: RefCell<UnimprovedTimeState<S::Score>>,
157 _phantom: PhantomData<fn() -> S>,
158}
159
160impl<S: PlanningSolution> Debug for UnimprovedTimeTermination<S> {
161 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162 f.debug_struct("UnimprovedTimeTermination")
163 .field("limit", &self.limit)
164 .finish()
165 }
166}
167
168struct UnimprovedTimeState<Sc: Score> {
169 last_best_score: Option<Sc>,
170 last_improvement_time: Option<Instant>,
171}
172
173impl<Sc: Score> Default for UnimprovedTimeState<Sc> {
174 fn default() -> Self {
175 Self {
176 last_best_score: None,
177 last_improvement_time: None,
178 }
179 }
180}
181
182impl<S: PlanningSolution> UnimprovedTimeTermination<S> {
183 pub fn new(limit: Duration) -> Self {
184 Self {
185 limit,
186 state: RefCell::new(UnimprovedTimeState::default()),
187 _phantom: PhantomData,
188 }
189 }
190
191 pub fn millis(ms: u64) -> Self {
192 Self::new(Duration::from_millis(ms))
193 }
194
195 pub fn seconds(secs: u64) -> Self {
196 Self::new(Duration::from_secs(secs))
197 }
198}
199
200unsafe impl<S: PlanningSolution> Send for UnimprovedTimeTermination<S> {}
203
204impl<S: PlanningSolution, D: Director<S>, BestCb: BestSolutionCallback<S>> Termination<S, D, BestCb>
205 for UnimprovedTimeTermination<S>
206{
207 fn is_terminated(&self, solver_scope: &SolverScope<S, D, BestCb>) -> bool {
208 let mut state = self.state.borrow_mut();
209 let current_best = solver_scope.best_score();
210 let now = Instant::now();
211
212 match (&state.last_best_score, current_best) {
213 (None, Some(score)) => {
214 state.last_best_score = Some(*score);
216 state.last_improvement_time = Some(now);
217 false
218 }
219 (Some(last), Some(current)) => {
220 if *current > *last {
221 state.last_best_score = Some(*current);
223 state.last_improvement_time = Some(now);
224 false
225 } else {
226 state
228 .last_improvement_time
229 .map(|t| now.duration_since(t) >= self.limit)
230 .unwrap_or(false)
231 }
232 }
233 (Some(_), None) => {
234 state
236 .last_improvement_time
237 .map(|t| now.duration_since(t) >= self.limit)
238 .unwrap_or(false)
239 }
240 (None, None) => {
241 false
243 }
244 }
245 }
246}