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::phase::localsearch::MoveCursorSource;
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 on_solver_terminal<D, ProgressCb>(
33 &mut self,
34 _solver_scope: &mut SolverScope<'_, S, D, ProgressCb>,
35 ) where
36 D: Director<S>,
37 ProgressCb: ProgressCallback<S>,
38 {
39 }
40
41 fn phase_type_name(&self) -> &'static str {
42 "CustomSearchPhase"
43 }
44}
45
46impl<S, M, Source, A, Fo> CustomSearchPhase<S> for LocalSearchPhase<S, M, Source, A, Fo>
47where
48 S: PlanningSolution,
49 M: Move<S>,
50 Source: MoveCursorSource<S, M> + Debug + Send,
51 A: Acceptor<S> + Debug,
52 Fo: LocalSearchForager<S, M> + Debug,
53{
54 fn solve<D, ProgressCb>(&mut self, solver_scope: &mut SolverScope<'_, S, D, ProgressCb>)
55 where
56 D: Director<S>,
57 ProgressCb: ProgressCallback<S>,
58 {
59 Phase::solve(self, solver_scope);
60 }
61
62 fn phase_type_name(&self) -> &'static str {
63 "LocalSearchPhase"
64 }
65
66 fn on_solver_terminal<D, ProgressCb>(
67 &mut self,
68 solver_scope: &mut SolverScope<'_, S, D, ProgressCb>,
69 ) where
70 D: Director<S>,
71 ProgressCb: ProgressCallback<S>,
72 {
73 Phase::on_solver_terminal(self, solver_scope);
74 }
75}
76
77impl<S, PD, Part, SDF, PF, CP> CustomSearchPhase<S>
78 for PartitionedSearchPhase<S, PD, Part, SDF, PF, CP>
79where
80 S: PlanningSolution + 'static,
81 PD: Director<S> + 'static,
82 Part: SolutionPartitioner<S>,
83 SDF: Fn(S) -> PD + Send + Sync,
84 PF: Fn() -> CP + Send + Sync,
85 CP: ChildPhases<S, PD> + Send,
86{
87 fn solve<D, ProgressCb>(&mut self, solver_scope: &mut SolverScope<'_, S, D, ProgressCb>)
88 where
89 D: Director<S>,
90 ProgressCb: ProgressCallback<S>,
91 {
92 Phase::solve(self, solver_scope);
93 }
94
95 fn phase_type_name(&self) -> &'static str {
96 "PartitionedSearch"
97 }
98
99 fn on_solver_terminal<D, ProgressCb>(
100 &mut self,
101 solver_scope: &mut SolverScope<'_, S, D, ProgressCb>,
102 ) where
103 D: Director<S>,
104 ProgressCb: ProgressCallback<S>,
105 {
106 Phase::on_solver_terminal(self, solver_scope);
107 }
108}
109
110#[derive(Clone, Copy, Debug, PartialEq, Eq)]
116pub enum RuntimeExtensionPolicy {
117 Typed,
118 Dynamic,
119}
120
121pub trait RuntimeExtensionRegistry<S, V, DM, IDM>
127where
128 S: PlanningSolution,
129{
130 type Phase: CustomSearchPhase<S>;
131
132 fn policy(&self) -> RuntimeExtensionPolicy;
133
134 fn contains_custom(&self, name: &str) -> bool;
135
136 fn instantiate_custom(
137 &self,
138 name: &str,
139 context: &SearchContext<S, V, DM, IDM>,
140 ) -> Option<Self::Phase>;
141
142 fn contains_partitioned(&self, name: &str) -> bool;
143
144 fn instantiate_partitioned(
145 &self,
146 name: &str,
147 config: &PartitionedSearchConfig,
148 context: &SearchContext<S, V, DM, IDM>,
149 ) -> Option<Self::Phase>;
150}
151
152pub struct NoTypedExtensions;
153
154impl<S, V, DM, IDM> RuntimeExtensionRegistry<S, V, DM, IDM> for NoTypedExtensions
155where
156 S: PlanningSolution,
157{
158 type Phase = NoRuntimeExtensionPhase;
159
160 fn policy(&self) -> RuntimeExtensionPolicy {
161 RuntimeExtensionPolicy::Typed
162 }
163
164 fn contains_custom(&self, _name: &str) -> bool {
165 false
166 }
167
168 fn instantiate_custom(
169 &self,
170 _name: &str,
171 _context: &SearchContext<S, V, DM, IDM>,
172 ) -> Option<Self::Phase> {
173 None
174 }
175
176 fn contains_partitioned(&self, _name: &str) -> bool {
177 false
178 }
179
180 fn instantiate_partitioned(
181 &self,
182 _name: &str,
183 _config: &PartitionedSearchConfig,
184 _context: &SearchContext<S, V, DM, IDM>,
185 ) -> Option<Self::Phase> {
186 None
187 }
188}
189
190pub struct NoDynamicExtensions;
195
196impl<S, V, DM, IDM> RuntimeExtensionRegistry<S, V, DM, IDM> for NoDynamicExtensions
197where
198 S: PlanningSolution,
199{
200 type Phase = NoRuntimeExtensionPhase;
201
202 fn policy(&self) -> RuntimeExtensionPolicy {
203 RuntimeExtensionPolicy::Dynamic
204 }
205
206 fn contains_custom(&self, _name: &str) -> bool {
207 false
208 }
209
210 fn instantiate_custom(
211 &self,
212 _name: &str,
213 _context: &SearchContext<S, V, DM, IDM>,
214 ) -> Option<Self::Phase> {
215 None
216 }
217
218 fn contains_partitioned(&self, _name: &str) -> bool {
219 false
220 }
221
222 fn instantiate_partitioned(
223 &self,
224 _name: &str,
225 _config: &PartitionedSearchConfig,
226 _context: &SearchContext<S, V, DM, IDM>,
227 ) -> Option<Self::Phase> {
228 None
229 }
230}
231
232#[derive(Clone, Copy)]
233pub enum NoRuntimeExtensionPhase {}
234
235impl Debug for NoRuntimeExtensionPhase {
236 fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
237 match *self {}
238 }
239}
240
241impl<S> CustomSearchPhase<S> for NoRuntimeExtensionPhase
242where
243 S: PlanningSolution,
244{
245 fn solve<D, ProgressCb>(&mut self, _solver_scope: &mut SolverScope<'_, S, D, ProgressCb>)
246 where
247 D: Director<S>,
248 ProgressCb: ProgressCallback<S>,
249 {
250 match *self {}
251 }
252
253 fn phase_type_name(&self) -> &'static str {
254 match *self {}
255 }
256}
257
258impl<S, D, ProgressCb> Phase<S, D, ProgressCb> for NoRuntimeExtensionPhase
259where
260 S: PlanningSolution,
261 D: Director<S>,
262 ProgressCb: ProgressCallback<S>,
263{
264 fn solve(&mut self, _solver_scope: &mut SolverScope<'_, S, D, ProgressCb>) {
265 match *self {}
266 }
267
268 fn phase_type_name(&self) -> &'static str {
269 match *self {}
270 }
271}
272
273pub struct CustomPhaseNode<Previous, Builder, Phase> {
274 previous: Previous,
275 name: &'static str,
276 builder: Builder,
277 _marker: PhantomData<fn() -> Phase>,
278}
279
280pub struct PartitionedPhaseNode<Previous, Builder, Phase> {
281 previous: Previous,
282 name: &'static str,
283 builder: Builder,
284 _marker: PhantomData<fn() -> Phase>,
285}
286
287impl<Previous, Builder, Phase> PartitionedPhaseNode<Previous, Builder, Phase> {
288 pub fn new(previous: Previous, name: &'static str, builder: Builder) -> Self {
289 Self {
290 previous,
291 name,
292 builder,
293 _marker: PhantomData,
294 }
295 }
296}
297
298impl<Previous, Builder, Phase> CustomPhaseNode<Previous, Builder, Phase> {
299 pub fn new(previous: Previous, name: &'static str, builder: Builder) -> Self {
300 Self {
301 previous,
302 name,
303 builder,
304 _marker: PhantomData,
305 }
306 }
307}
308
309pub enum CustomPhaseUnion<PreviousPhase, CurrentPhase> {
310 Previous(PreviousPhase),
311 Current(CurrentPhase),
312}
313
314impl<PreviousPhase: Debug, CurrentPhase: Debug> Debug
315 for CustomPhaseUnion<PreviousPhase, CurrentPhase>
316{
317 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
318 match self {
319 Self::Previous(phase) => f.debug_tuple("CustomPhase::Previous").field(phase).finish(),
320 Self::Current(phase) => f.debug_tuple("CustomPhase::Current").field(phase).finish(),
321 }
322 }
323}
324
325impl<S, PreviousPhase, CurrentPhase> CustomSearchPhase<S>
326 for CustomPhaseUnion<PreviousPhase, CurrentPhase>
327where
328 S: PlanningSolution,
329 PreviousPhase: CustomSearchPhase<S>,
330 CurrentPhase: CustomSearchPhase<S>,
331{
332 fn solve<D, ProgressCb>(&mut self, solver_scope: &mut SolverScope<'_, S, D, ProgressCb>)
333 where
334 D: Director<S>,
335 ProgressCb: ProgressCallback<S>,
336 {
337 match self {
338 Self::Previous(phase) => phase.solve(solver_scope),
339 Self::Current(phase) => phase.solve(solver_scope),
340 }
341 }
342
343 fn phase_type_name(&self) -> &'static str {
344 "CustomPhase"
345 }
346
347 fn on_solver_terminal<D, ProgressCb>(
348 &mut self,
349 solver_scope: &mut SolverScope<'_, S, D, ProgressCb>,
350 ) where
351 D: Director<S>,
352 ProgressCb: ProgressCallback<S>,
353 {
354 match self {
355 Self::Previous(phase) => phase.on_solver_terminal(solver_scope),
356 Self::Current(phase) => phase.on_solver_terminal(solver_scope),
357 }
358 }
359}
360
361impl<S, D, ProgressCb, PreviousPhase, CurrentPhase> Phase<S, D, ProgressCb>
362 for CustomPhaseUnion<PreviousPhase, CurrentPhase>
363where
364 S: PlanningSolution,
365 D: Director<S>,
366 ProgressCb: ProgressCallback<S>,
367 PreviousPhase: CustomSearchPhase<S>,
368 CurrentPhase: CustomSearchPhase<S>,
369{
370 fn solve(&mut self, solver_scope: &mut SolverScope<'_, S, D, ProgressCb>) {
371 CustomSearchPhase::solve(self, solver_scope);
372 }
373
374 fn phase_type_name(&self) -> &'static str {
375 CustomSearchPhase::<S>::phase_type_name(self)
376 }
377
378 fn on_solver_terminal(&mut self, solver_scope: &mut SolverScope<'_, S, D, ProgressCb>) {
379 CustomSearchPhase::on_solver_terminal(self, solver_scope);
380 }
381}
382
383impl<S, V, DM, IDM, Previous, Builder, CurrentPhase> RuntimeExtensionRegistry<S, V, DM, IDM>
384 for CustomPhaseNode<Previous, Builder, CurrentPhase>
385where
386 S: PlanningSolution,
387 Previous: RuntimeExtensionRegistry<S, V, DM, IDM>,
388 Builder: Fn(&SearchContext<S, V, DM, IDM>) -> CurrentPhase,
389 CurrentPhase: CustomSearchPhase<S>,
390{
391 type Phase = CustomPhaseUnion<Previous::Phase, CurrentPhase>;
392
393 fn policy(&self) -> RuntimeExtensionPolicy {
394 self.previous.policy()
395 }
396
397 fn contains_custom(&self, name: &str) -> bool {
398 self.name == name || self.previous.contains_custom(name)
399 }
400
401 fn instantiate_custom(
402 &self,
403 name: &str,
404 context: &SearchContext<S, V, DM, IDM>,
405 ) -> Option<Self::Phase> {
406 if self.name == name {
407 return Some(CustomPhaseUnion::Current((self.builder)(context)));
408 }
409 self.previous
410 .instantiate_custom(name, context)
411 .map(CustomPhaseUnion::Previous)
412 }
413
414 fn contains_partitioned(&self, name: &str) -> bool {
415 self.previous.contains_partitioned(name)
416 }
417
418 fn instantiate_partitioned(
419 &self,
420 name: &str,
421 config: &PartitionedSearchConfig,
422 context: &SearchContext<S, V, DM, IDM>,
423 ) -> Option<Self::Phase> {
424 self.previous
425 .instantiate_partitioned(name, config, context)
426 .map(CustomPhaseUnion::Previous)
427 }
428}
429
430impl<S, V, DM, IDM, Previous, Builder, CurrentPhase> RuntimeExtensionRegistry<S, V, DM, IDM>
431 for PartitionedPhaseNode<Previous, Builder, CurrentPhase>
432where
433 S: PlanningSolution,
434 Previous: RuntimeExtensionRegistry<S, V, DM, IDM>,
435 Builder: Fn(&SearchContext<S, V, DM, IDM>, &PartitionedSearchConfig) -> CurrentPhase,
436 CurrentPhase: CustomSearchPhase<S>,
437{
438 type Phase = CustomPhaseUnion<Previous::Phase, CurrentPhase>;
439
440 fn policy(&self) -> RuntimeExtensionPolicy {
441 self.previous.policy()
442 }
443
444 fn contains_custom(&self, name: &str) -> bool {
445 self.previous.contains_custom(name)
446 }
447
448 fn instantiate_custom(
449 &self,
450 name: &str,
451 context: &SearchContext<S, V, DM, IDM>,
452 ) -> Option<Self::Phase> {
453 self.previous
454 .instantiate_custom(name, context)
455 .map(CustomPhaseUnion::Previous)
456 }
457
458 fn contains_partitioned(&self, name: &str) -> bool {
459 self.name == name || self.previous.contains_partitioned(name)
460 }
461
462 fn instantiate_partitioned(
463 &self,
464 name: &str,
465 config: &PartitionedSearchConfig,
466 context: &SearchContext<S, V, DM, IDM>,
467 ) -> Option<Self::Phase> {
468 if self.name == name {
469 return Some(CustomPhaseUnion::Current((self.builder)(context, config)));
470 }
471 self.previous
472 .instantiate_partitioned(name, config, context)
473 .map(CustomPhaseUnion::Previous)
474 }
475}