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::termination::Termination;
15
16pub struct Solver<'t, P, T, S, D> {
28 phases: P,
29 termination: T,
30 terminate: Option<&'t AtomicBool>,
31 config: Option<SolverConfig>,
32 time_limit: Option<Duration>,
33 _phantom: PhantomData<fn(S, D)>,
34}
35
36impl<P: Debug, T: Debug, S, D> Debug for Solver<'_, P, T, S, D> {
37 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38 f.debug_struct("Solver")
39 .field("phases", &self.phases)
40 .field("termination", &self.termination)
41 .finish()
42 }
43}
44
45impl<P, S, D> Solver<'static, P, NoTermination, S, D>
46where
47 S: PlanningSolution,
48{
49 pub fn new(phases: P) -> Self {
51 Solver {
52 phases,
53 termination: NoTermination,
54 terminate: None,
55 config: None,
56 time_limit: None,
57 _phantom: PhantomData,
58 }
59 }
60
61 pub fn with_termination<T>(self, termination: T) -> Solver<'static, P, Option<T>, S, D> {
63 Solver {
64 phases: self.phases,
65 termination: Some(termination),
66 terminate: self.terminate,
67 config: self.config,
68 time_limit: self.time_limit,
69 _phantom: PhantomData,
70 }
71 }
72}
73
74impl<'t, P, T, S, D> Solver<'t, P, T, S, D>
75where
76 S: PlanningSolution,
77{
78 pub fn with_terminate(self, terminate: &AtomicBool) -> Solver<'_, P, T, S, D> {
82 Solver {
83 phases: self.phases,
84 termination: self.termination,
85 terminate: Some(terminate),
86 config: self.config,
87 time_limit: self.time_limit,
88 _phantom: PhantomData,
89 }
90 }
91
92 pub fn with_time_limit(mut self, limit: Duration) -> Self {
94 self.time_limit = Some(limit);
95 self
96 }
97
98 pub fn with_config(mut self, config: SolverConfig) -> Self {
100 self.config = Some(config);
101 self
102 }
103
104 pub fn config(&self) -> Option<&SolverConfig> {
106 self.config.as_ref()
107 }
108}
109
110#[derive(Debug, Clone, Copy, Default)]
112pub struct NoTermination;
113
114pub trait MaybeTermination<S: PlanningSolution, D: ScoreDirector<S>>: Send {
116 fn should_terminate(&self, solver_scope: &SolverScope<'_, S, D>) -> bool;
118}
119
120impl<S: PlanningSolution, D: ScoreDirector<S>, T: Termination<S, D>> MaybeTermination<S, D>
121 for Option<T>
122{
123 fn should_terminate(&self, solver_scope: &SolverScope<'_, S, D>) -> bool {
124 match self {
125 Some(t) => t.is_terminated(solver_scope),
126 None => false,
127 }
128 }
129}
130
131impl<S: PlanningSolution, D: ScoreDirector<S>> MaybeTermination<S, D> for NoTermination {
132 fn should_terminate(&self, _solver_scope: &SolverScope<'_, S, D>) -> bool {
133 false
134 }
135}
136
137impl<S: PlanningSolution, D: ScoreDirector<S>> Termination<S, D> for NoTermination {
138 fn is_terminated(&self, _solver_scope: &SolverScope<'_, S, D>) -> bool {
139 false
140 }
141}
142
143macro_rules! impl_solver {
144 ($($idx:tt: $P:ident),+) => {
145 impl<'t, S, D, T, $($P),+> Solver<'t, ($($P,)+), T, S, D>
146 where
147 S: PlanningSolution,
148 D: ScoreDirector<S>,
149 T: MaybeTermination<S, D>,
150 $($P: Phase<S, D>,)+
151 {
152 pub fn solve(&mut self, score_director: D) -> S {
154 let mut solver_scope = SolverScope::with_terminate(score_director, self.terminate);
155 if let Some(limit) = self.time_limit {
156 solver_scope.set_time_limit(limit);
157 }
158 solver_scope.start_solving();
159
160 $(
162 if !self.check_termination(&solver_scope) {
163 tracing::debug!(
164 "Starting phase {} ({})",
165 $idx,
166 self.phases.$idx.phase_type_name()
167 );
168 self.phases.$idx.solve(&mut solver_scope);
169 tracing::debug!(
170 "Finished phase {} ({}) with score {:?}",
171 $idx,
172 self.phases.$idx.phase_type_name(),
173 solver_scope.best_score()
174 );
175 }
176 )+
177
178 solver_scope.take_best_or_working_solution()
179 }
180
181 fn check_termination(&self, solver_scope: &SolverScope<'_, S, D>) -> bool {
182 if solver_scope.is_terminate_early() {
184 return true;
185 }
186 self.termination.should_terminate(solver_scope)
188 }
189 }
190 };
191}
192
193macro_rules! impl_solver_with_director {
194 ($($idx:tt: $P:ident),+) => {
195 impl<'t, S, T, $($P),+> Solver<'t, ($($P,)+), T, S, ()>
196 where
197 S: PlanningSolution,
198 T: Send,
199 {
200 pub fn solve_with_director<D>(self, director: D) -> S
202 where
203 D: ScoreDirector<S>,
204 T: MaybeTermination<S, D>,
205 $($P: Phase<S, D>,)+
206 {
207 let mut solver: Solver<'t, ($($P,)+), T, S, D> = Solver {
208 phases: self.phases,
209 termination: self.termination,
210 terminate: self.terminate,
211 config: self.config,
212 time_limit: self.time_limit,
213 _phantom: PhantomData,
214 };
215 solver.solve(director)
216 }
217 }
218 };
219}
220
221impl_solver_with_director!(0: P0);
222impl_solver_with_director!(0: P0, 1: P1);
223impl_solver_with_director!(0: P0, 1: P1, 2: P2);
224impl_solver_with_director!(0: P0, 1: P1, 2: P2, 3: P3);
225impl_solver_with_director!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4);
226impl_solver_with_director!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5);
227impl_solver_with_director!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5, 6: P6);
228impl_solver_with_director!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5, 6: P6, 7: P7);
229
230impl_solver!(0: P0);
231impl_solver!(0: P0, 1: P1);
232impl_solver!(0: P0, 1: P1, 2: P2);
233impl_solver!(0: P0, 1: P1, 2: P2, 3: P3);
234impl_solver!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4);
235impl_solver!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5);
236impl_solver!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5, 6: P6);
237impl_solver!(0: P0, 1: P1, 2: P2, 3: P3, 4: P4, 5: P5, 6: P6, 7: P7);