solverforge_solver/phase/vnd/
phase.rs1use std::fmt::Debug;
4use std::marker::PhantomData;
5
6use solverforge_core::domain::PlanningSolution;
7use solverforge_scoring::{Director, RecordingDirector};
8
9use crate::heuristic::r#move::{Move, MoveArena};
10use crate::heuristic::selector::MoveSelector;
11use crate::phase::control::{
12 append_interruptibly, settle_search_interrupt, should_interrupt_evaluation, StepInterrupt,
13};
14use crate::phase::Phase;
15use crate::scope::ProgressCallback;
16use crate::scope::{PhaseScope, SolverScope, StepScope};
17
18pub struct VndPhase<T, M>(pub T, PhantomData<fn() -> M>);
67
68impl<T: Debug, M> Debug for VndPhase<T, M> {
69 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70 f.debug_tuple("VndPhase").field(&self.0).finish()
71 }
72}
73
74impl<T, M> VndPhase<T, M> {
75 pub fn new(neighborhoods: T) -> Self {
76 Self(neighborhoods, PhantomData)
77 }
78}
79
80macro_rules! impl_vnd_phase {
82 ($idx:tt: $MS:ident) => {
84 impl<S, D, BestCb, M, $MS> Phase<S, D, BestCb> for VndPhase<($MS,), M>
85 where
86 S: PlanningSolution,
87 D: Director<S>,
88 BestCb: ProgressCallback<S>,
89 M: Move<S>,
90 $MS: MoveSelector<S, M>,
91 {
92 fn solve(&mut self, solver_scope: &mut SolverScope<S, D, BestCb>) {
93 let mut arena = MoveArena::<M>::new();
94 let mut phase_scope = PhaseScope::new(solver_scope, 0);
95 let mut current_score = phase_scope.calculate_score();
96
97 loop {
98 let mut step_scope = StepScope::new(&mut phase_scope);
99 arena.reset();
100 let generation_interrupted = {
101 let iter = (self.0).$idx.iter_moves(step_scope.score_director());
102 append_interruptibly(&step_scope, &mut arena, iter)
103 };
104 if generation_interrupted {
105 match settle_search_interrupt(&mut step_scope) {
106 StepInterrupt::Restart => continue,
107 StepInterrupt::TerminatePhase => break,
108 }
109 }
110
111 match find_best_improving_move_index(&arena, &mut step_scope, ¤t_score) {
112 MoveSearchResult::Found(selected_index, selected_score) => {
113 let selected_move = arena.take(selected_index);
114 selected_move.do_move(step_scope.score_director_mut());
115 step_scope.set_step_score(selected_score);
116 current_score = selected_score;
117 step_scope.phase_scope_mut().update_best_solution();
118 step_scope.complete();
119 }
120 MoveSearchResult::NotFound => {
121 step_scope.complete();
122 break;
123 }
124 MoveSearchResult::Interrupted => match settle_search_interrupt(&mut step_scope) {
125 StepInterrupt::Restart => continue,
126 StepInterrupt::TerminatePhase => break,
127 },
128 }
129 }
130 }
131
132 fn phase_type_name(&self) -> &'static str {
133 "VariableNeighborhoodDescent"
134 }
135 }
136 };
137
138 ($($idx:tt: $MS:ident),+) => {
140 impl<S, D, BestCb, M, $($MS),+> Phase<S, D, BestCb> for VndPhase<($($MS,)+), M>
141 where
142 S: PlanningSolution,
143 D: Director<S>,
144 BestCb: ProgressCallback<S>,
145 M: Move<S>,
146 $($MS: MoveSelector<S, M>,)+
147 {
148 fn solve(&mut self, solver_scope: &mut SolverScope<S, D, BestCb>) {
149 const COUNT: usize = impl_vnd_phase!(@count $($idx),+);
150 let mut arena = MoveArena::<M>::new();
151 let mut phase_scope = PhaseScope::new(solver_scope, 0);
152 let mut current_score = phase_scope.calculate_score();
153 let mut k = 0usize;
154
155 while k < COUNT {
156 let mut step_scope = StepScope::new(&mut phase_scope);
157 arena.reset();
158
159 match k {
161 $($idx => {
162 let generation_interrupted = {
163 let iter = (self.0).$idx.iter_moves(step_scope.score_director());
164 append_interruptibly(&step_scope, &mut arena, iter)
165 };
166 if generation_interrupted {
167 match settle_search_interrupt(&mut step_scope) {
168 StepInterrupt::Restart => continue,
169 StepInterrupt::TerminatePhase => break,
170 }
171 }
172 },)+
173 _ => {}
174 }
175
176 match find_best_improving_move_index(&arena, &mut step_scope, ¤t_score) {
177 MoveSearchResult::Found(selected_index, selected_score) => {
178 let selected_move = arena.take(selected_index);
179 selected_move.do_move(step_scope.score_director_mut());
180 step_scope.set_step_score(selected_score);
181 current_score = selected_score;
182 step_scope.phase_scope_mut().update_best_solution();
183 step_scope.complete();
184 k = 0; }
186 MoveSearchResult::NotFound => {
187 step_scope.complete();
188 k += 1; }
190 MoveSearchResult::Interrupted => match settle_search_interrupt(&mut step_scope) {
191 StepInterrupt::Restart => continue,
192 StepInterrupt::TerminatePhase => break,
193 },
194 }
195 }
196 }
197
198 fn phase_type_name(&self) -> &'static str {
199 "VariableNeighborhoodDescent"
200 }
201 }
202 };
203
204 (@count $($idx:tt),+) => {
206 0 $(+ { let _ = $idx; 1 })+
207 };
208}
209
210enum MoveSearchResult<Sc> {
215 Found(usize, Sc),
216 NotFound,
217 Interrupted,
218}
219
220fn find_best_improving_move_index<S, D, BestCb, M>(
221 arena: &MoveArena<M>,
222 step_scope: &mut StepScope<'_, '_, '_, S, D, BestCb>,
223 current_score: &S::Score,
224) -> MoveSearchResult<S::Score>
225where
226 S: PlanningSolution,
227 D: Director<S>,
228 BestCb: ProgressCallback<S>,
229 M: Move<S>,
230{
231 let mut best: Option<(usize, S::Score)> = None;
232
233 for i in 0..arena.len() {
234 if should_interrupt_evaluation(step_scope, i) {
235 return MoveSearchResult::Interrupted;
236 }
237
238 let m = arena.get(i).unwrap();
239
240 if !m.is_doable(step_scope.score_director()) {
241 continue;
242 }
243
244 let mut recording = RecordingDirector::new(step_scope.score_director_mut());
245 m.do_move(&mut recording);
246 let move_score = recording.calculate_score();
247 recording.undo_changes();
248
249 if move_score > *current_score {
250 match &best {
251 Some((_, best_score)) if move_score > *best_score => {
252 best = Some((i, move_score));
253 }
254 None => {
255 best = Some((i, move_score));
256 }
257 _ => {}
258 }
259 }
260 }
261
262 match best {
263 Some((index, score)) => MoveSearchResult::Found(index, score),
264 None => MoveSearchResult::NotFound,
265 }
266}
267
268impl_vnd_phase!(0: MS0);
269impl_vnd_phase!(0: MS0, 1: MS1);
270impl_vnd_phase!(0: MS0, 1: MS1, 2: MS2);
271impl_vnd_phase!(0: MS0, 1: MS1, 2: MS2, 3: MS3);
272impl_vnd_phase!(0: MS0, 1: MS1, 2: MS2, 3: MS3, 4: MS4);
273impl_vnd_phase!(0: MS0, 1: MS1, 2: MS2, 3: MS3, 4: MS4, 5: MS5);
274impl_vnd_phase!(0: MS0, 1: MS1, 2: MS2, 3: MS3, 4: MS4, 5: MS5, 6: MS6);
275impl_vnd_phase!(0: MS0, 1: MS1, 2: MS2, 3: MS3, 4: MS4, 5: MS5, 6: MS6, 7: MS7);
276
277#[cfg(test)]
278#[path = "phase_tests.rs"]
279mod tests;