Skip to main content

solverforge_solver/runtime/
phases.rs

1use 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}