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