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::SolverScope;
14use crate::stats::SolverStats;
15use crate::termination::Termination;
16
17#[derive(Debug)]
23pub struct SolveResult<S: PlanningSolution> {
24 pub solution: S,
26 pub stats: SolverStats,
28}
29
30impl<S: PlanningSolution> SolveResult<S> {
31 pub fn solution(&self) -> &S {
33 &self.solution
34 }
35
36 pub fn into_solution(self) -> S {
38 self.solution
39 }
40
41 pub fn stats(&self) -> &SolverStats {
43 &self.stats
44 }
45
46 pub fn step_count(&self) -> u64 {
48 self.stats.step_count
49 }
50
51 pub fn moves_evaluated(&self) -> u64 {
53 self.stats.moves_evaluated
54 }
55
56 pub fn moves_accepted(&self) -> u64 {
58 self.stats.moves_accepted
59 }
60}
61
62#[allow(clippy::type_complexity)]
74pub struct Solver<'t, P, T, S, D> {
75 phases: P,
76 termination: T,
77 terminate: Option<&'t AtomicBool>,
78 config: Option<SolverConfig>,
79 time_limit: Option<Duration>,
80 best_solution_callback: Option<Box<dyn Fn(&S) + Send + Sync + 't>>,
82 _phantom: PhantomData<fn(S, D)>,
83}
84
85impl<P: Debug, T: Debug, S, D> Debug for Solver<'_, P, T, S, D> {
86 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87 f.debug_struct("Solver")
88 .field("phases", &self.phases)
89 .field("termination", &self.termination)
90 .finish()
91 }
92}
93
94impl<P, S, D> Solver<'static, P, NoTermination, S, D>
95where
96 S: PlanningSolution,
97{
98 pub fn new(phases: P) -> Self {
100 Solver {
101 phases,
102 termination: NoTermination,
103 terminate: None,
104 config: None,
105 time_limit: None,
106 best_solution_callback: None,
107 _phantom: PhantomData,
108 }
109 }
110
111 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 best_solution_callback: self.best_solution_callback,
120 _phantom: PhantomData,
121 }
122 }
123}
124
125impl<'t, P, T, S, D> Solver<'t, P, T, S, D>
126where
127 S: PlanningSolution,
128{
129 pub fn with_terminate(self, terminate: &'t AtomicBool) -> Solver<'t, P, T, S, D> {
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 best_solution_callback: self.best_solution_callback,
140 _phantom: PhantomData,
141 }
142 }
143
144 pub fn with_time_limit(mut self, limit: Duration) -> Self {
146 self.time_limit = Some(limit);
147 self
148 }
149
150 pub fn with_config(mut self, config: SolverConfig) -> Self {
152 self.config = Some(config);
153 self
154 }
155
156 pub fn with_best_solution_callback(
158 mut self,
159 callback: Box<dyn Fn(&S) + Send + Sync + 't>,
160 ) -> Self {
161 self.best_solution_callback = Some(callback);
162 self
163 }
164
165 pub fn config(&self) -> Option<&SolverConfig> {
167 self.config.as_ref()
168 }
169}
170
171#[derive(Debug, Clone, Copy, Default)]
173pub struct NoTermination;
174
175pub trait MaybeTermination<S: PlanningSolution, D: ScoreDirector<S>>: Send {
177 fn should_terminate(&self, solver_scope: &SolverScope<'_, S, D>) -> bool;
179
180 fn install_inphase_limits(&self, _solver_scope: &mut SolverScope<'_, S, D>) {}
188}
189
190impl<S: PlanningSolution, D: ScoreDirector<S>, T: Termination<S, D>> MaybeTermination<S, D>
191 for Option<T>
192{
193 fn should_terminate(&self, solver_scope: &SolverScope<'_, S, D>) -> bool {
194 match self {
195 Some(t) => t.is_terminated(solver_scope),
196 None => false,
197 }
198 }
199
200 fn install_inphase_limits(&self, solver_scope: &mut SolverScope<'_, S, D>) {
201 if let Some(t) = self {
202 t.install_inphase_limits(solver_scope);
203 }
204 }
205}
206
207impl<S: PlanningSolution, D: ScoreDirector<S>> MaybeTermination<S, D> for NoTermination {
208 fn should_terminate(&self, _solver_scope: &SolverScope<'_, S, D>) -> bool {
209 false
210 }
211
212 }
214
215impl<S: PlanningSolution, D: ScoreDirector<S>> Termination<S, D> for NoTermination {
216 fn is_terminated(&self, _solver_scope: &SolverScope<'_, S, D>) -> bool {
217 false
218 }
219}
220
221macro_rules! impl_solver {
222 ($($idx:tt: $P:ident),+) => {
223 impl<'t, S, D, T, $($P),+> Solver<'t, ($($P,)+), T, S, D>
224 where
225 S: PlanningSolution,
226 D: ScoreDirector<S>,
227 T: MaybeTermination<S, D>,
228 $($P: Phase<S, D>,)+
229 {
230 pub fn solve(&mut self, score_director: D) -> SolveResult<S> {
235 let mut solver_scope = SolverScope::with_terminate(score_director, self.terminate);
236 if let Some(limit) = self.time_limit {
237 solver_scope.set_time_limit(limit);
238 }
239 if let Some(callback) = self.best_solution_callback.take() {
240 solver_scope = solver_scope.with_best_solution_callback(callback);
241 }
242 solver_scope.start_solving();
243
244 self.termination.install_inphase_limits(&mut solver_scope);
247
248 $(
250 if !self.check_termination(&solver_scope) {
251 tracing::debug!(
252 "Starting phase {} ({})",
253 $idx,
254 self.phases.$idx.phase_type_name()
255 );
256 self.phases.$idx.solve(&mut solver_scope);
257 tracing::debug!(
258 "Finished phase {} ({}) with score {:?}",
259 $idx,
260 self.phases.$idx.phase_type_name(),
261 solver_scope.best_score()
262 );
263 }
264 )+
265
266 let (solution, stats) = solver_scope.take_solution_and_stats();
268 SolveResult { solution, stats }
269 }
270
271 fn check_termination(&self, solver_scope: &SolverScope<'_, S, D>) -> bool {
272 if solver_scope.is_terminate_early() {
274 return true;
275 }
276 self.termination.should_terminate(solver_scope)
278 }
279
280
281 }
282 };
283}
284
285macro_rules! impl_solver_with_director {
286 ($($idx:tt: $P:ident),+) => {
287 impl<'t, S, T, $($P),+> Solver<'t, ($($P,)+), T, S, ()>
288 where
289 S: PlanningSolution,
290 T: Send,
291 {
292 pub fn solve_with_director<D>(self, director: D) -> SolveResult<S>
294 where
295 D: ScoreDirector<S>,
296 T: MaybeTermination<S, D>,
297 $($P: Phase<S, D>,)+
298 {
299 let mut solver: Solver<'t, ($($P,)+), T, S, D> = Solver {
300 phases: self.phases,
301 termination: self.termination,
302 terminate: self.terminate,
303 config: self.config,
304 time_limit: self.time_limit,
305 best_solution_callback: self.best_solution_callback,
306 _phantom: PhantomData,
307 };
308 solver.solve(director)
309 }
310 }
311 };
312}
313
314impl_solver_with_director!(0: P0);
315impl_solver_with_director!(0: P0, 1: P1);
316impl_solver_with_director!(0: P0, 1: P1, 2: P2);
317impl_solver_with_director!(0: P0, 1: P1, 2: P2, 3: P3);
318impl_solver_with_director!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4);
319impl_solver_with_director!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5);
320impl_solver_with_director!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5, 6: P6);
321impl_solver_with_director!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5, 6: P6, 7: P7);
322
323impl_solver!(0: P0);
324impl_solver!(0: P0, 1: P1);
325impl_solver!(0: P0, 1: P1, 2: P2);
326impl_solver!(0: P0, 1: P1, 2: P2, 3: P3);
327impl_solver!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4);
328impl_solver!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5);
329impl_solver!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5, 6: P6);
330impl_solver!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5, 6: P6, 7: P7);