solverforge_solver/runtime/
phases.rs1use std::fmt::{self, Debug};
2use std::hash::Hash;
3
4use solverforge_config::{ConstructionHeuristicConfig, PhaseConfig, SolverConfig};
5use solverforge_core::domain::{PlanningSolution, SolutionDescriptor};
6use solverforge_core::score::{ParseableScore, Score};
7
8use super::construction::{ListConstructionArgs, UnifiedConstruction};
9use crate::builder::ListContext;
10use crate::heuristic::selector::nearby_list_change::CrossEntityDistanceMeter;
11use crate::phase::{sequence::PhaseSequence, Phase};
12use crate::unified_search::{
13 build_unified_local_search, build_unified_vnd, UnifiedLocalSearch, UnifiedVnd,
14};
15
16pub enum RuntimePhase<C, LS, VND> {
17 Construction(C),
18 LocalSearch(LS),
19 Vnd(VND),
20}
21
22impl<C, LS, VND> Debug for RuntimePhase<C, LS, VND>
23where
24 C: Debug,
25 LS: Debug,
26 VND: Debug,
27{
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 match self {
30 Self::Construction(phase) => write!(f, "RuntimePhase::Construction({phase:?})"),
31 Self::LocalSearch(phase) => write!(f, "RuntimePhase::LocalSearch({phase:?})"),
32 Self::Vnd(phase) => write!(f, "RuntimePhase::Vnd({phase:?})"),
33 }
34 }
35}
36
37impl<S, D, ProgressCb, C, LS, VND> Phase<S, D, ProgressCb> for RuntimePhase<C, LS, VND>
38where
39 S: PlanningSolution,
40 D: solverforge_scoring::Director<S>,
41 ProgressCb: crate::scope::ProgressCallback<S>,
42 C: Phase<S, D, ProgressCb> + Debug,
43 LS: Phase<S, D, ProgressCb> + Debug,
44 VND: Phase<S, D, ProgressCb> + Debug,
45{
46 fn solve(&mut self, solver_scope: &mut crate::scope::SolverScope<'_, S, D, ProgressCb>) {
47 match self {
48 Self::Construction(phase) => phase.solve(solver_scope),
49 Self::LocalSearch(phase) => phase.solve(solver_scope),
50 Self::Vnd(phase) => phase.solve(solver_scope),
51 }
52 }
53
54 fn phase_type_name(&self) -> &'static str {
55 "RuntimePhase"
56 }
57}
58
59pub type UnifiedRuntimePhase<S, V, DM, IDM> = RuntimePhase<
60 UnifiedConstruction<S, V>,
61 UnifiedLocalSearch<S, V, DM, IDM>,
62 UnifiedVnd<S, V, DM, IDM>,
63>;
64
65pub fn build_phases<S, V, DM, IDM>(
66 config: &SolverConfig,
67 descriptor: &SolutionDescriptor,
68 list_ctx: Option<&ListContext<S, V, DM, IDM>>,
69 list_construction: Option<ListConstructionArgs<S, V>>,
70 list_variable_name: Option<&'static str>,
71) -> PhaseSequence<UnifiedRuntimePhase<S, V, DM, IDM>>
72where
73 S: PlanningSolution + 'static,
74 S::Score: Score + ParseableScore,
75 V: Clone + Copy + PartialEq + Eq + Hash + Into<usize> + Send + Sync + Debug + 'static,
76 DM: CrossEntityDistanceMeter<S> + Clone + Debug + 'static,
77 IDM: CrossEntityDistanceMeter<S> + Clone + Debug + 'static,
78{
79 let mut phases = Vec::new();
80
81 if config.phases.is_empty() {
82 phases.push(default_construction_phase(
83 descriptor,
84 list_construction.as_ref(),
85 list_variable_name,
86 ));
87 phases.push(RuntimePhase::LocalSearch(build_unified_local_search(
88 None,
89 descriptor,
90 list_ctx,
91 config.random_seed,
92 )));
93 return PhaseSequence::new(phases);
94 }
95
96 for phase in &config.phases {
97 match phase {
98 PhaseConfig::ConstructionHeuristic(ch) => {
99 phases.push(build_construction_phase(
100 ch,
101 descriptor,
102 list_construction.as_ref(),
103 list_variable_name,
104 ));
105 }
106 PhaseConfig::LocalSearch(ls) => {
107 phases.push(RuntimePhase::LocalSearch(build_unified_local_search(
108 Some(ls),
109 descriptor,
110 list_ctx,
111 config.random_seed,
112 )));
113 }
114 PhaseConfig::Vnd(vnd) => {
115 phases.push(RuntimePhase::Vnd(build_unified_vnd(
116 vnd,
117 descriptor,
118 list_ctx,
119 config.random_seed,
120 )));
121 }
122 _ => {
123 panic!("unsupported phase in the unified runtime");
124 }
125 }
126 }
127
128 PhaseSequence::new(phases)
129}
130
131fn default_construction_phase<S, V, DM, IDM>(
132 descriptor: &SolutionDescriptor,
133 list_construction: Option<&ListConstructionArgs<S, V>>,
134 list_variable_name: Option<&'static str>,
135) -> UnifiedRuntimePhase<S, V, DM, IDM>
136where
137 S: PlanningSolution + 'static,
138 S::Score: Score + ParseableScore,
139 V: Clone + Copy + PartialEq + Eq + Hash + Into<usize> + Send + Sync + Debug + 'static,
140 DM: CrossEntityDistanceMeter<S> + Clone + Debug + 'static,
141 IDM: CrossEntityDistanceMeter<S> + Clone + Debug + 'static,
142{
143 RuntimePhase::Construction(UnifiedConstruction::new(
144 None,
145 descriptor.clone(),
146 list_construction.copied(),
147 list_variable_name,
148 ))
149}
150
151fn build_construction_phase<S, V, DM, IDM>(
152 config: &ConstructionHeuristicConfig,
153 descriptor: &SolutionDescriptor,
154 list_construction: Option<&ListConstructionArgs<S, V>>,
155 list_variable_name: Option<&'static str>,
156) -> UnifiedRuntimePhase<S, V, DM, IDM>
157where
158 S: PlanningSolution + 'static,
159 S::Score: Score + ParseableScore,
160 V: Clone + Copy + PartialEq + Eq + Hash + Into<usize> + Send + Sync + Debug + 'static,
161 DM: CrossEntityDistanceMeter<S> + Clone + Debug + 'static,
162 IDM: CrossEntityDistanceMeter<S> + Clone + Debug + 'static,
163{
164 RuntimePhase::Construction(UnifiedConstruction::new(
165 Some(config.clone()),
166 descriptor.clone(),
167 list_construction.copied(),
168 list_variable_name,
169 ))
170}