1use std::fmt::{self, Debug};
2use std::marker::PhantomData;
3
4use solverforge_config::PartitionedSearchConfig;
5use solverforge_core::domain::PlanningSolution;
6use solverforge_scoring::Director;
7
8use crate::heuristic::r#move::Move;
9use crate::heuristic::MoveSelector;
10use crate::phase::localsearch::{Acceptor, LocalSearchForager, LocalSearchPhase};
11use crate::phase::partitioned::{ChildPhases, PartitionedSearchPhase, SolutionPartitioner};
12use crate::phase::Phase;
13use crate::scope::{ProgressCallback, SolverScope};
14
15use super::SearchContext;
16
17pub trait CustomSearchPhase<S>: Debug + Send
18where
19 S: PlanningSolution,
20{
21 fn solve<D, ProgressCb>(&mut self, solver_scope: &mut SolverScope<'_, S, D, ProgressCb>)
22 where
23 D: Director<S>,
24 ProgressCb: ProgressCallback<S>;
25
26 fn phase_type_name(&self) -> &'static str {
27 "CustomSearchPhase"
28 }
29}
30
31impl<S, M, MS, A, Fo> CustomSearchPhase<S> for LocalSearchPhase<S, M, MS, A, Fo>
32where
33 S: PlanningSolution,
34 M: Move<S>,
35 MS: MoveSelector<S, M> + Debug,
36 A: Acceptor<S> + Debug,
37 Fo: LocalSearchForager<S, M> + Debug,
38{
39 fn solve<D, ProgressCb>(&mut self, solver_scope: &mut SolverScope<'_, S, D, ProgressCb>)
40 where
41 D: Director<S>,
42 ProgressCb: ProgressCallback<S>,
43 {
44 Phase::solve(self, solver_scope);
45 }
46
47 fn phase_type_name(&self) -> &'static str {
48 "LocalSearchPhase"
49 }
50}
51
52impl<S, PD, Part, SDF, PF, CP> CustomSearchPhase<S>
53 for PartitionedSearchPhase<S, PD, Part, SDF, PF, CP>
54where
55 S: PlanningSolution + 'static,
56 PD: Director<S> + 'static,
57 Part: SolutionPartitioner<S>,
58 SDF: Fn(S) -> PD + Send + Sync,
59 PF: Fn() -> CP + Send + Sync,
60 CP: ChildPhases<S, PD> + Send,
61{
62 fn solve<D, ProgressCb>(&mut self, solver_scope: &mut SolverScope<'_, S, D, ProgressCb>)
63 where
64 D: Director<S>,
65 ProgressCb: ProgressCallback<S>,
66 {
67 Phase::solve(self, solver_scope);
68 }
69
70 fn phase_type_name(&self) -> &'static str {
71 "PartitionedSearch"
72 }
73}
74
75pub trait CustomPhaseRegistry<S, V, DM, IDM>
76where
77 S: PlanningSolution,
78{
79 type Phase: CustomSearchPhase<S>;
80
81 fn contains(&self, name: &str) -> bool;
82
83 fn build_named(
84 &self,
85 name: &str,
86 context: &SearchContext<S, V, DM, IDM>,
87 ) -> Option<Self::Phase>;
88
89 fn contains_partitioned(&self, name: &str) -> bool;
90
91 fn build_partitioned_named(
92 &self,
93 name: &str,
94 config: &PartitionedSearchConfig,
95 context: &SearchContext<S, V, DM, IDM>,
96 ) -> Option<Self::Phase>;
97}
98
99pub struct NoCustomPhases;
100
101impl<S, V, DM, IDM> CustomPhaseRegistry<S, V, DM, IDM> for NoCustomPhases
102where
103 S: PlanningSolution,
104{
105 type Phase = NoCustomPhase;
106
107 fn contains(&self, _name: &str) -> bool {
108 false
109 }
110
111 fn build_named(
112 &self,
113 _name: &str,
114 _context: &SearchContext<S, V, DM, IDM>,
115 ) -> Option<Self::Phase> {
116 None
117 }
118
119 fn contains_partitioned(&self, _name: &str) -> bool {
120 false
121 }
122
123 fn build_partitioned_named(
124 &self,
125 _name: &str,
126 _config: &PartitionedSearchConfig,
127 _context: &SearchContext<S, V, DM, IDM>,
128 ) -> Option<Self::Phase> {
129 None
130 }
131}
132
133#[derive(Clone, Copy)]
134pub enum NoCustomPhase {}
135
136impl Debug for NoCustomPhase {
137 fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
138 match *self {}
139 }
140}
141
142impl<S> CustomSearchPhase<S> for NoCustomPhase
143where
144 S: PlanningSolution,
145{
146 fn solve<D, ProgressCb>(&mut self, _solver_scope: &mut SolverScope<'_, S, D, ProgressCb>)
147 where
148 D: Director<S>,
149 ProgressCb: ProgressCallback<S>,
150 {
151 match *self {}
152 }
153
154 fn phase_type_name(&self) -> &'static str {
155 match *self {}
156 }
157}
158
159impl<S, D, ProgressCb> Phase<S, D, ProgressCb> for NoCustomPhase
160where
161 S: PlanningSolution,
162 D: Director<S>,
163 ProgressCb: ProgressCallback<S>,
164{
165 fn solve(&mut self, _solver_scope: &mut SolverScope<'_, S, D, ProgressCb>) {
166 match *self {}
167 }
168
169 fn phase_type_name(&self) -> &'static str {
170 match *self {}
171 }
172}
173
174pub struct CustomPhaseNode<Previous, Builder, Phase> {
175 previous: Previous,
176 name: &'static str,
177 builder: Builder,
178 _marker: PhantomData<fn() -> Phase>,
179}
180
181pub struct PartitionedPhaseNode<Previous, Builder, Phase> {
182 previous: Previous,
183 name: &'static str,
184 builder: Builder,
185 _marker: PhantomData<fn() -> Phase>,
186}
187
188impl<Previous, Builder, Phase> PartitionedPhaseNode<Previous, Builder, Phase> {
189 pub fn new(previous: Previous, name: &'static str, builder: Builder) -> Self {
190 Self {
191 previous,
192 name,
193 builder,
194 _marker: PhantomData,
195 }
196 }
197}
198
199impl<Previous, Builder, Phase> CustomPhaseNode<Previous, Builder, Phase> {
200 pub fn new(previous: Previous, name: &'static str, builder: Builder) -> Self {
201 Self {
202 previous,
203 name,
204 builder,
205 _marker: PhantomData,
206 }
207 }
208}
209
210pub enum CustomPhaseUnion<PreviousPhase, CurrentPhase> {
211 Previous(PreviousPhase),
212 Current(CurrentPhase),
213}
214
215impl<PreviousPhase: Debug, CurrentPhase: Debug> Debug
216 for CustomPhaseUnion<PreviousPhase, CurrentPhase>
217{
218 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219 match self {
220 Self::Previous(phase) => f.debug_tuple("CustomPhase::Previous").field(phase).finish(),
221 Self::Current(phase) => f.debug_tuple("CustomPhase::Current").field(phase).finish(),
222 }
223 }
224}
225
226impl<S, PreviousPhase, CurrentPhase> CustomSearchPhase<S>
227 for CustomPhaseUnion<PreviousPhase, CurrentPhase>
228where
229 S: PlanningSolution,
230 PreviousPhase: CustomSearchPhase<S>,
231 CurrentPhase: CustomSearchPhase<S>,
232{
233 fn solve<D, ProgressCb>(&mut self, solver_scope: &mut SolverScope<'_, S, D, ProgressCb>)
234 where
235 D: Director<S>,
236 ProgressCb: ProgressCallback<S>,
237 {
238 match self {
239 Self::Previous(phase) => phase.solve(solver_scope),
240 Self::Current(phase) => phase.solve(solver_scope),
241 }
242 }
243
244 fn phase_type_name(&self) -> &'static str {
245 "CustomPhase"
246 }
247}
248
249impl<S, D, ProgressCb, PreviousPhase, CurrentPhase> Phase<S, D, ProgressCb>
250 for CustomPhaseUnion<PreviousPhase, CurrentPhase>
251where
252 S: PlanningSolution,
253 D: Director<S>,
254 ProgressCb: ProgressCallback<S>,
255 PreviousPhase: CustomSearchPhase<S>,
256 CurrentPhase: CustomSearchPhase<S>,
257{
258 fn solve(&mut self, solver_scope: &mut SolverScope<'_, S, D, ProgressCb>) {
259 CustomSearchPhase::solve(self, solver_scope);
260 }
261
262 fn phase_type_name(&self) -> &'static str {
263 CustomSearchPhase::<S>::phase_type_name(self)
264 }
265}
266
267impl<S, V, DM, IDM, Previous, Builder, CurrentPhase> CustomPhaseRegistry<S, V, DM, IDM>
268 for CustomPhaseNode<Previous, Builder, CurrentPhase>
269where
270 S: PlanningSolution,
271 Previous: CustomPhaseRegistry<S, V, DM, IDM>,
272 Builder: Fn(&SearchContext<S, V, DM, IDM>) -> CurrentPhase,
273 CurrentPhase: CustomSearchPhase<S>,
274{
275 type Phase = CustomPhaseUnion<Previous::Phase, CurrentPhase>;
276
277 fn contains(&self, name: &str) -> bool {
278 self.name == name || self.previous.contains(name)
279 }
280
281 fn build_named(
282 &self,
283 name: &str,
284 context: &SearchContext<S, V, DM, IDM>,
285 ) -> Option<Self::Phase> {
286 if self.name == name {
287 return Some(CustomPhaseUnion::Current((self.builder)(context)));
288 }
289 self.previous
290 .build_named(name, context)
291 .map(CustomPhaseUnion::Previous)
292 }
293
294 fn contains_partitioned(&self, name: &str) -> bool {
295 self.previous.contains_partitioned(name)
296 }
297
298 fn build_partitioned_named(
299 &self,
300 name: &str,
301 config: &PartitionedSearchConfig,
302 context: &SearchContext<S, V, DM, IDM>,
303 ) -> Option<Self::Phase> {
304 self.previous
305 .build_partitioned_named(name, config, context)
306 .map(CustomPhaseUnion::Previous)
307 }
308}
309
310impl<S, V, DM, IDM, Previous, Builder, CurrentPhase> CustomPhaseRegistry<S, V, DM, IDM>
311 for PartitionedPhaseNode<Previous, Builder, CurrentPhase>
312where
313 S: PlanningSolution,
314 Previous: CustomPhaseRegistry<S, V, DM, IDM>,
315 Builder: Fn(&SearchContext<S, V, DM, IDM>, &PartitionedSearchConfig) -> CurrentPhase,
316 CurrentPhase: CustomSearchPhase<S>,
317{
318 type Phase = CustomPhaseUnion<Previous::Phase, CurrentPhase>;
319
320 fn contains(&self, name: &str) -> bool {
321 self.previous.contains(name)
322 }
323
324 fn build_named(
325 &self,
326 name: &str,
327 context: &SearchContext<S, V, DM, IDM>,
328 ) -> Option<Self::Phase> {
329 self.previous
330 .build_named(name, context)
331 .map(CustomPhaseUnion::Previous)
332 }
333
334 fn contains_partitioned(&self, name: &str) -> bool {
335 self.name == name || self.previous.contains_partitioned(name)
336 }
337
338 fn build_partitioned_named(
339 &self,
340 name: &str,
341 config: &PartitionedSearchConfig,
342 context: &SearchContext<S, V, DM, IDM>,
343 ) -> Option<Self::Phase> {
344 if self.name == name {
345 return Some(CustomPhaseUnion::Current((self.builder)(context, config)));
346 }
347 self.previous
348 .build_partitioned_named(name, config, context)
349 .map(CustomPhaseUnion::Previous)
350 }
351}