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 config,
290 time_limit,
291 progress_callback,
292 ..
293 } = self;
294
295 let mut solver_scope = SolverScope::new_with_callback(
296 score_director,
297 progress_callback,
298 terminate,
299 runtime,
300 );
301 if let Some(seed) = config.as_ref().and_then(|cfg| cfg.random_seed) {
302 solver_scope = solver_scope.with_seed(seed);
303 }
304 if let Some(limit) = time_limit {
305 solver_scope.set_time_limit(limit);
306 }
307 solver_scope.start_solving();
308 let initial_score = solver_scope.calculate_score();
309 let initial_solution = solver_scope.score_director().clone_working_solution();
310 solver_scope.set_best_solution(initial_solution, initial_score);
311 solver_scope.report_best_solution();
312 solver_scope.pause_if_requested();
313
314 termination.install_inphase_limits(&mut solver_scope);
317
318 $(
320 solver_scope.pause_if_requested();
321 if !check_termination(&termination, &mut solver_scope) {
322 tracing::debug!(
323 "Starting phase {} ({})",
324 $idx,
325 phases.$idx.phase_type_name()
326 );
327 phases.$idx.solve(&mut solver_scope);
328 solver_scope.pause_if_requested();
329 tracing::debug!(
330 "Finished phase {} ({}) with score {:?}",
331 $idx,
332 phases.$idx.phase_type_name(),
333 solver_scope.best_score()
334 );
335 }
336 )+
337
338 let (solution, current_score, best_score, stats, terminal_reason) =
340 solver_scope.take_solution_and_stats();
341 SolveResult {
342 solution,
343 current_score,
344 best_score,
345 terminal_reason,
346 stats,
347 }
348 }
349 }
350 };
351}
352
353fn check_termination<S, D, ProgressCb, T>(
354 termination: &T,
355 solver_scope: &mut SolverScope<'_, S, D, ProgressCb>,
356) -> bool
357where
358 S: PlanningSolution,
359 D: Director<S>,
360 ProgressCb: ProgressCallback<S>,
361 T: MaybeTermination<S, D, ProgressCb>,
362{
363 if solver_scope.is_terminate_early() {
364 solver_scope.mark_cancelled();
365 return true;
366 }
367 if termination.should_terminate(solver_scope) {
368 solver_scope.mark_terminated_by_config();
369 true
370 } else {
371 false
372 }
373}
374
375macro_rules! impl_solver_with_director {
376 ($($idx:tt: $P:ident),+) => {
377 impl<'t, S, T, ProgressCb, $($P),+> Solver<'t, ($($P,)+), T, S, (), ProgressCb>
378 where
379 S: PlanningSolution,
380 T: Send,
381 ProgressCb: Send + Sync,
382 {
383 pub fn solve_with_director<D>(self, director: D) -> SolveResult<S>
385 where
386 D: Director<S>,
387 ProgressCb: ProgressCallback<S>,
388 T: MaybeTermination<S, D, ProgressCb>,
389 $($P: Phase<S, D, ProgressCb>,)+
390 {
391 let solver: Solver<'t, ($($P,)+), T, S, D, ProgressCb> = Solver {
392 phases: self.phases,
393 termination: self.termination,
394 terminate: self.terminate,
395 runtime: self.runtime,
396 config: self.config,
397 time_limit: self.time_limit,
398 progress_callback: self.progress_callback,
399 _phantom: PhantomData,
400 };
401 solver.solve(director)
402 }
403 }
404 };
405}
406
407impl_solver_with_director!(0: P0);
408impl_solver_with_director!(0: P0, 1: P1);
409impl_solver_with_director!(0: P0, 1: P1, 2: P2);
410impl_solver_with_director!(0: P0, 1: P1, 2: P2, 3: P3);
411impl_solver_with_director!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4);
412impl_solver_with_director!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5);
413impl_solver_with_director!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5, 6: P6);
414impl_solver_with_director!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5, 6: P6, 7: P7);
415
416impl_solver!(0: P0);
417impl_solver!(0: P0, 1: P1);
418impl_solver!(0: P0, 1: P1, 2: P2);
419impl_solver!(0: P0, 1: P1, 2: P2, 3: P3);
420impl_solver!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4);
421impl_solver!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5);
422impl_solver!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5, 6: P6);
423impl_solver!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5, 6: P6, 7: P7);