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