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::manager::{SolverRuntime, SolverTerminalReason};
13use crate::phase::Phase;
14use crate::scope::ProgressCallback;
15use crate::scope::SolverScope;
16use crate::stats::SolverStats;
17use crate::termination::Termination;
18
19#[derive(Debug)]
26pub struct SolveResult<S: PlanningSolution> {
27 pub solution: S,
29 pub current_score: Option<S::Score>,
31 pub best_score: S::Score,
33 pub terminal_reason: SolverTerminalReason,
35 pub stats: SolverStats,
37}
38
39impl<S: PlanningSolution> SolveResult<S> {
40 pub fn solution(&self) -> &S {
41 &self.solution
42 }
43
44 pub fn into_solution(self) -> S {
45 self.solution
46 }
47
48 pub fn current_score(&self) -> Option<&S::Score> {
49 self.current_score.as_ref()
50 }
51
52 pub fn best_score(&self) -> &S::Score {
53 &self.best_score
54 }
55
56 pub fn terminal_reason(&self) -> SolverTerminalReason {
57 self.terminal_reason
58 }
59
60 pub fn stats(&self) -> &SolverStats {
61 &self.stats
62 }
63
64 pub fn step_count(&self) -> u64 {
65 self.stats.step_count
66 }
67
68 pub fn moves_evaluated(&self) -> u64 {
69 self.stats.moves_evaluated
70 }
71
72 pub fn moves_accepted(&self) -> u64 {
73 self.stats.moves_accepted
74 }
75}
76
77pub struct Solver<'t, P, T, S: PlanningSolution, D, ProgressCb = ()> {
90 phases: P,
91 termination: T,
92 terminate: Option<&'t AtomicBool>,
93 runtime: Option<SolverRuntime<S>>,
94 config: Option<SolverConfig>,
95 time_limit: Option<Duration>,
96 progress_callback: ProgressCb,
98 _phantom: PhantomData<fn(S, D)>,
99}
100
101impl<P: Debug, T: Debug, S: PlanningSolution, D, ProgressCb> Debug
102 for Solver<'_, P, T, S, D, ProgressCb>
103{
104 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105 f.debug_struct("Solver")
106 .field("phases", &self.phases)
107 .field("termination", &self.termination)
108 .finish()
109 }
110}
111
112impl<P, S, D> Solver<'static, P, NoTermination, S, D, ()>
113where
114 S: PlanningSolution,
115{
116 pub fn new(phases: P) -> Self {
117 Solver {
118 phases,
119 termination: NoTermination,
120 terminate: None,
121 runtime: None,
122 config: None,
123 time_limit: None,
124 progress_callback: (),
125 _phantom: PhantomData,
126 }
127 }
128
129 pub fn with_termination<T>(self, termination: T) -> Solver<'static, P, Option<T>, S, D, ()> {
130 Solver {
131 phases: self.phases,
132 termination: Some(termination),
133 terminate: self.terminate,
134 runtime: self.runtime,
135 config: self.config,
136 time_limit: self.time_limit,
137 progress_callback: self.progress_callback,
138 _phantom: PhantomData,
139 }
140 }
141}
142
143impl<'t, P, T, S, D, ProgressCb> Solver<'t, P, T, S, D, ProgressCb>
144where
145 S: PlanningSolution,
146{
147 pub fn with_terminate(self, terminate: &'t AtomicBool) -> Solver<'t, P, T, S, D, ProgressCb> {
151 Solver {
152 phases: self.phases,
153 termination: self.termination,
154 terminate: Some(terminate),
155 runtime: self.runtime,
156 config: self.config,
157 time_limit: self.time_limit,
158 progress_callback: self.progress_callback,
159 _phantom: PhantomData,
160 }
161 }
162
163 pub fn with_time_limit(mut self, limit: Duration) -> Self {
164 self.time_limit = Some(limit);
165 self
166 }
167
168 pub fn with_config(mut self, config: SolverConfig) -> Self {
169 self.config = Some(config);
170 self
171 }
172
173 pub fn with_progress_callback<F>(self, callback: F) -> Solver<'t, P, T, S, D, F> {
177 Solver {
178 phases: self.phases,
179 termination: self.termination,
180 terminate: self.terminate,
181 runtime: self.runtime,
182 config: self.config,
183 time_limit: self.time_limit,
184 progress_callback: callback,
185 _phantom: PhantomData,
186 }
187 }
188
189 pub fn config(&self) -> Option<&SolverConfig> {
190 self.config.as_ref()
191 }
192
193 pub(crate) fn with_runtime(mut self, runtime: SolverRuntime<S>) -> Self {
194 self.runtime = Some(runtime);
195 self
196 }
197}
198
199#[derive(Debug, Clone, Copy, Default)]
201pub struct NoTermination;
202
203pub trait MaybeTermination<
205 S: PlanningSolution,
206 D: Director<S>,
207 ProgressCb: ProgressCallback<S> = (),
208>: Send
209{
210 fn should_terminate(&self, solver_scope: &SolverScope<'_, S, D, ProgressCb>) -> bool;
212
213 fn install_inphase_limits(&self, _solver_scope: &mut SolverScope<'_, S, D, ProgressCb>) {}
222}
223
224impl<S, D, ProgressCb, T> MaybeTermination<S, D, ProgressCb> for Option<T>
225where
226 S: PlanningSolution,
227 D: Director<S>,
228 ProgressCb: ProgressCallback<S>,
229 T: Termination<S, D, ProgressCb>,
230{
231 fn should_terminate(&self, solver_scope: &SolverScope<'_, S, D, ProgressCb>) -> bool {
232 match self {
233 Some(t) => t.is_terminated(solver_scope),
234 None => false,
235 }
236 }
237
238 fn install_inphase_limits(&self, solver_scope: &mut SolverScope<'_, S, D, ProgressCb>) {
239 if let Some(t) = self {
240 t.install_inphase_limits(solver_scope);
241 }
242 }
243}
244
245impl<S, D, ProgressCb> MaybeTermination<S, D, ProgressCb> for NoTermination
246where
247 S: PlanningSolution,
248 D: Director<S>,
249 ProgressCb: ProgressCallback<S>,
250{
251 fn should_terminate(&self, _solver_scope: &SolverScope<'_, S, D, ProgressCb>) -> bool {
252 false
253 }
254
255 }
257
258impl<S, D, ProgressCb> Termination<S, D, ProgressCb> for NoTermination
259where
260 S: PlanningSolution,
261 D: Director<S>,
262 ProgressCb: ProgressCallback<S>,
263{
264 fn is_terminated(&self, _solver_scope: &SolverScope<'_, S, D, ProgressCb>) -> bool {
265 false
266 }
267}
268
269macro_rules! impl_solver {
270 ($($idx:tt: $P:ident),+) => {
271 impl<'t, S, D, T, ProgressCb, $($P),+> Solver<'t, ($($P,)+), T, S, D, ProgressCb>
272 where
273 S: PlanningSolution,
274 D: Director<S>,
275 T: MaybeTermination<S, D, ProgressCb>,
276 ProgressCb: ProgressCallback<S>,
277 $($P: Phase<S, D, ProgressCb>,)+
278 {
279 pub fn solve(self, score_director: D) -> SolveResult<S> {
284 let Solver {
285 mut phases,
286 termination,
287 terminate,
288 runtime,
289 time_limit,
290 progress_callback,
291 ..
292 } = self;
293
294 let mut solver_scope = SolverScope::new_with_callback(
295 score_director,
296 progress_callback,
297 terminate,
298 runtime,
299 );
300 if let Some(limit) = time_limit {
301 solver_scope.set_time_limit(limit);
302 }
303 solver_scope.start_solving();
304 let initial_score = solver_scope.calculate_score();
305 let initial_solution = solver_scope.score_director().clone_working_solution();
306 solver_scope.set_best_solution(initial_solution, initial_score);
307 solver_scope.report_best_solution();
308 solver_scope.pause_if_requested();
309
310 termination.install_inphase_limits(&mut solver_scope);
313
314 $(
316 solver_scope.pause_if_requested();
317 if !check_termination(&termination, &mut solver_scope) {
318 tracing::debug!(
319 "Starting phase {} ({})",
320 $idx,
321 phases.$idx.phase_type_name()
322 );
323 phases.$idx.solve(&mut solver_scope);
324 solver_scope.pause_if_requested();
325 tracing::debug!(
326 "Finished phase {} ({}) with score {:?}",
327 $idx,
328 phases.$idx.phase_type_name(),
329 solver_scope.best_score()
330 );
331 }
332 )+
333
334 let (solution, current_score, best_score, stats, terminal_reason) =
336 solver_scope.take_solution_and_stats();
337 SolveResult {
338 solution,
339 current_score,
340 best_score,
341 terminal_reason,
342 stats,
343 }
344 }
345 }
346 };
347}
348
349fn check_termination<S, D, ProgressCb, T>(
350 termination: &T,
351 solver_scope: &mut SolverScope<'_, S, D, ProgressCb>,
352) -> bool
353where
354 S: PlanningSolution,
355 D: Director<S>,
356 ProgressCb: ProgressCallback<S>,
357 T: MaybeTermination<S, D, ProgressCb>,
358{
359 if solver_scope.is_terminate_early() {
360 solver_scope.mark_cancelled();
361 return true;
362 }
363 if termination.should_terminate(solver_scope) {
364 solver_scope.mark_terminated_by_config();
365 true
366 } else {
367 false
368 }
369}
370
371macro_rules! impl_solver_with_director {
372 ($($idx:tt: $P:ident),+) => {
373 impl<'t, S, T, ProgressCb, $($P),+> Solver<'t, ($($P,)+), T, S, (), ProgressCb>
374 where
375 S: PlanningSolution,
376 T: Send,
377 ProgressCb: Send + Sync,
378 {
379 pub fn solve_with_director<D>(self, director: D) -> SolveResult<S>
381 where
382 D: Director<S>,
383 ProgressCb: ProgressCallback<S>,
384 T: MaybeTermination<S, D, ProgressCb>,
385 $($P: Phase<S, D, ProgressCb>,)+
386 {
387 let solver: Solver<'t, ($($P,)+), T, S, D, ProgressCb> = Solver {
388 phases: self.phases,
389 termination: self.termination,
390 terminate: self.terminate,
391 runtime: self.runtime,
392 config: self.config,
393 time_limit: self.time_limit,
394 progress_callback: self.progress_callback,
395 _phantom: PhantomData,
396 };
397 solver.solve(director)
398 }
399 }
400 };
401}
402
403impl_solver_with_director!(0: P0);
404impl_solver_with_director!(0: P0, 1: P1);
405impl_solver_with_director!(0: P0, 1: P1, 2: P2);
406impl_solver_with_director!(0: P0, 1: P1, 2: P2, 3: P3);
407impl_solver_with_director!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4);
408impl_solver_with_director!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5);
409impl_solver_with_director!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5, 6: P6);
410impl_solver_with_director!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5, 6: P6, 7: P7);
411
412impl_solver!(0: P0);
413impl_solver!(0: P0, 1: P1);
414impl_solver!(0: P0, 1: P1, 2: P2);
415impl_solver!(0: P0, 1: P1, 2: P2, 3: P3);
416impl_solver!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4);
417impl_solver!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5);
418impl_solver!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5, 6: P6);
419impl_solver!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5, 6: P6, 7: P7);