1use std::fmt::Debug;
4use std::marker::PhantomData;
5use std::sync::atomic::AtomicBool;
6use std::time::Duration;
7
8use solverforge_config::SolverConfig;
9use solverforge_core::domain::PlanningSolution;
10use solverforge_scoring::Director;
11
12use crate::phase::Phase;
13use crate::scope::ProgressCallback;
14use crate::scope::SolverScope;
15use crate::stats::SolverStats;
16use crate::termination::Termination;
17
18#[derive(Debug)]
25pub struct SolveResult<S: PlanningSolution> {
26 pub solution: S,
28 pub best_score: S::Score,
30 pub stats: SolverStats,
32}
33
34impl<S: PlanningSolution> SolveResult<S> {
35 pub fn solution(&self) -> &S {
36 &self.solution
37 }
38
39 pub fn into_solution(self) -> S {
40 self.solution
41 }
42
43 pub fn best_score(&self) -> &S::Score {
44 &self.best_score
45 }
46
47 pub fn stats(&self) -> &SolverStats {
48 &self.stats
49 }
50
51 pub fn step_count(&self) -> u64 {
52 self.stats.step_count
53 }
54
55 pub fn moves_evaluated(&self) -> u64 {
56 self.stats.moves_evaluated
57 }
58
59 pub fn moves_accepted(&self) -> u64 {
60 self.stats.moves_accepted
61 }
62}
63
64pub struct Solver<'t, P, T, S, D, ProgressCb = ()> {
77 phases: P,
78 termination: T,
79 terminate: Option<&'t AtomicBool>,
80 config: Option<SolverConfig>,
81 time_limit: Option<Duration>,
82 progress_callback: ProgressCb,
84 _phantom: PhantomData<fn(S, D)>,
85}
86
87impl<P: Debug, T: Debug, S, D, ProgressCb> Debug for Solver<'_, P, T, S, D, ProgressCb> {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 f.debug_struct("Solver")
90 .field("phases", &self.phases)
91 .field("termination", &self.termination)
92 .finish()
93 }
94}
95
96impl<P, S, D> Solver<'static, P, NoTermination, S, D, ()>
97where
98 S: PlanningSolution,
99{
100 pub fn new(phases: P) -> Self {
101 Solver {
102 phases,
103 termination: NoTermination,
104 terminate: None,
105 config: None,
106 time_limit: None,
107 progress_callback: (),
108 _phantom: PhantomData,
109 }
110 }
111
112 pub fn with_termination<T>(self, termination: T) -> Solver<'static, P, Option<T>, S, D, ()> {
113 Solver {
114 phases: self.phases,
115 termination: Some(termination),
116 terminate: self.terminate,
117 config: self.config,
118 time_limit: self.time_limit,
119 progress_callback: self.progress_callback,
120 _phantom: PhantomData,
121 }
122 }
123}
124
125impl<'t, P, T, S, D, ProgressCb> Solver<'t, P, T, S, D, ProgressCb>
126where
127 S: PlanningSolution,
128{
129 pub fn with_terminate(self, terminate: &'t AtomicBool) -> Solver<'t, P, T, S, D, ProgressCb> {
133 Solver {
134 phases: self.phases,
135 termination: self.termination,
136 terminate: Some(terminate),
137 config: self.config,
138 time_limit: self.time_limit,
139 progress_callback: self.progress_callback,
140 _phantom: PhantomData,
141 }
142 }
143
144 pub fn with_time_limit(mut self, limit: Duration) -> Self {
145 self.time_limit = Some(limit);
146 self
147 }
148
149 pub fn with_config(mut self, config: SolverConfig) -> Self {
150 self.config = Some(config);
151 self
152 }
153
154 pub fn with_progress_callback<F>(self, callback: F) -> Solver<'t, P, T, S, D, F> {
158 Solver {
159 phases: self.phases,
160 termination: self.termination,
161 terminate: self.terminate,
162 config: self.config,
163 time_limit: self.time_limit,
164 progress_callback: callback,
165 _phantom: PhantomData,
166 }
167 }
168
169 pub fn config(&self) -> Option<&SolverConfig> {
170 self.config.as_ref()
171 }
172}
173
174#[derive(Debug, Clone, Copy, Default)]
176pub struct NoTermination;
177
178pub trait MaybeTermination<
180 S: PlanningSolution,
181 D: Director<S>,
182 ProgressCb: ProgressCallback<S> = (),
183>: Send
184{
185 fn should_terminate(&self, solver_scope: &SolverScope<'_, S, D, ProgressCb>) -> bool;
187
188 fn install_inphase_limits(&self, _solver_scope: &mut SolverScope<'_, S, D, ProgressCb>) {}
197}
198
199impl<S, D, ProgressCb, T> MaybeTermination<S, D, ProgressCb> for Option<T>
200where
201 S: PlanningSolution,
202 D: Director<S>,
203 ProgressCb: ProgressCallback<S>,
204 T: Termination<S, D, ProgressCb>,
205{
206 fn should_terminate(&self, solver_scope: &SolverScope<'_, S, D, ProgressCb>) -> bool {
207 match self {
208 Some(t) => t.is_terminated(solver_scope),
209 None => false,
210 }
211 }
212
213 fn install_inphase_limits(&self, solver_scope: &mut SolverScope<'_, S, D, ProgressCb>) {
214 if let Some(t) = self {
215 t.install_inphase_limits(solver_scope);
216 }
217 }
218}
219
220impl<S, D, ProgressCb> MaybeTermination<S, D, ProgressCb> for NoTermination
221where
222 S: PlanningSolution,
223 D: Director<S>,
224 ProgressCb: ProgressCallback<S>,
225{
226 fn should_terminate(&self, _solver_scope: &SolverScope<'_, S, D, ProgressCb>) -> bool {
227 false
228 }
229
230 }
232
233impl<S, D, ProgressCb> Termination<S, D, ProgressCb> for NoTermination
234where
235 S: PlanningSolution,
236 D: Director<S>,
237 ProgressCb: ProgressCallback<S>,
238{
239 fn is_terminated(&self, _solver_scope: &SolverScope<'_, S, D, ProgressCb>) -> bool {
240 false
241 }
242}
243
244macro_rules! impl_solver {
245 ($($idx:tt: $P:ident),+) => {
246 impl<'t, S, D, T, ProgressCb, $($P),+> Solver<'t, ($($P,)+), T, S, D, ProgressCb>
247 where
248 S: PlanningSolution,
249 D: Director<S>,
250 T: MaybeTermination<S, D, ProgressCb>,
251 ProgressCb: ProgressCallback<S>,
252 $($P: Phase<S, D, ProgressCb>,)+
253 {
254 pub fn solve(self, score_director: D) -> SolveResult<S> {
259 let Solver {
260 mut phases,
261 termination,
262 terminate,
263 time_limit,
264 progress_callback,
265 ..
266 } = self;
267
268 let mut solver_scope = SolverScope::new_with_callback(
269 score_director,
270 progress_callback,
271 terminate,
272 );
273 if let Some(limit) = time_limit {
274 solver_scope.set_time_limit(limit);
275 }
276 solver_scope.start_solving();
277 let initial_score = solver_scope.calculate_score();
278 let initial_solution = solver_scope.score_director().clone_working_solution();
279 solver_scope.set_best_solution(initial_solution, initial_score);
280
281 termination.install_inphase_limits(&mut solver_scope);
284
285 $(
287 if !check_termination(&termination, &solver_scope) {
288 tracing::debug!(
289 "Starting phase {} ({})",
290 $idx,
291 phases.$idx.phase_type_name()
292 );
293 phases.$idx.solve(&mut solver_scope);
294 tracing::debug!(
295 "Finished phase {} ({}) with score {:?}",
296 $idx,
297 phases.$idx.phase_type_name(),
298 solver_scope.best_score()
299 );
300 }
301 )+
302
303 let (solution, best_score, stats) = solver_scope.take_solution_and_stats();
305 SolveResult {
306 solution,
307 best_score,
308 stats,
309 }
310 }
311 }
312 };
313}
314
315fn check_termination<S, D, ProgressCb, T>(
316 termination: &T,
317 solver_scope: &SolverScope<'_, S, D, ProgressCb>,
318) -> bool
319where
320 S: PlanningSolution,
321 D: Director<S>,
322 ProgressCb: ProgressCallback<S>,
323 T: MaybeTermination<S, D, ProgressCb>,
324{
325 if solver_scope.is_terminate_early() {
326 return true;
327 }
328 termination.should_terminate(solver_scope)
329}
330
331macro_rules! impl_solver_with_director {
332 ($($idx:tt: $P:ident),+) => {
333 impl<'t, S, T, ProgressCb, $($P),+> Solver<'t, ($($P,)+), T, S, (), ProgressCb>
334 where
335 S: PlanningSolution,
336 T: Send,
337 ProgressCb: Send + Sync,
338 {
339 pub fn solve_with_director<D>(self, director: D) -> SolveResult<S>
341 where
342 D: Director<S>,
343 ProgressCb: ProgressCallback<S>,
344 T: MaybeTermination<S, D, ProgressCb>,
345 $($P: Phase<S, D, ProgressCb>,)+
346 {
347 let solver: Solver<'t, ($($P,)+), T, S, D, ProgressCb> = Solver {
348 phases: self.phases,
349 termination: self.termination,
350 terminate: self.terminate,
351 config: self.config,
352 time_limit: self.time_limit,
353 progress_callback: self.progress_callback,
354 _phantom: PhantomData,
355 };
356 solver.solve(director)
357 }
358 }
359 };
360}
361
362impl_solver_with_director!(0: P0);
363impl_solver_with_director!(0: P0, 1: P1);
364impl_solver_with_director!(0: P0, 1: P1, 2: P2);
365impl_solver_with_director!(0: P0, 1: P1, 2: P2, 3: P3);
366impl_solver_with_director!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4);
367impl_solver_with_director!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5);
368impl_solver_with_director!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5, 6: P6);
369impl_solver_with_director!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5, 6: P6, 7: P7);
370
371impl_solver!(0: P0);
372impl_solver!(0: P0, 1: P1);
373impl_solver!(0: P0, 1: P1, 2: P2);
374impl_solver!(0: P0, 1: P1, 2: P2, 3: P3);
375impl_solver!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4);
376impl_solver!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5);
377impl_solver!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5, 6: P6);
378impl_solver!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5, 6: P6, 7: P7);