solverforge_solver/manager/phase_factory/
k_opt.rs1use std::fmt::Debug;
8use std::marker::PhantomData;
9
10use solverforge_core::domain::PlanningSolution;
11use solverforge_scoring::{Director, RecordingDirector};
12
13use crate::heuristic::r#move::MoveArena;
14use crate::heuristic::selector::k_opt::{KOptConfig, KOptMoveSelector};
15use crate::phase::control::{
16 append_interruptibly, settle_search_interrupt, should_interrupt_evaluation, StepInterrupt,
17};
18use crate::phase::Phase;
19use crate::scope::{PhaseScope, SolverScope, StepScope};
20
21use super::super::PhaseFactory;
22
23pub struct KOptPhaseBuilder<S, V, DM, ESF>
84where
85 S: PlanningSolution,
86 V: Clone + Send + Sync + Debug + 'static,
87{
88 list_len: fn(&S, usize) -> usize,
89 sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
90 sublist_insert: fn(&mut S, usize, usize, Vec<V>),
91 variable_name: &'static str,
92 descriptor_index: usize,
93 k: usize,
94 step_limit: Option<u64>,
95 _marker: PhantomData<(fn() -> S, fn() -> V, fn() -> DM, fn() -> ESF)>,
96}
97
98impl<S, V, DM, ESF> KOptPhaseBuilder<S, V, DM, ESF>
99where
100 S: PlanningSolution,
101 V: Clone + Send + Sync + Debug + 'static,
102{
103 pub fn new(
108 _distance_meter: DM,
109 _entity_selector_factory: ESF,
110 list_len: fn(&S, usize) -> usize,
111 sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
112 sublist_insert: fn(&mut S, usize, usize, Vec<V>),
113 variable_name: &'static str,
114 descriptor_index: usize,
115 ) -> Self {
116 Self {
117 list_len,
118 sublist_remove,
119 sublist_insert,
120 variable_name,
121 descriptor_index,
122 k: 3, step_limit: Some(1000),
124 _marker: PhantomData,
125 }
126 }
127
128 pub fn with_k(mut self, k: usize) -> Self {
129 assert!((2..=5).contains(&k), "k must be between 2 and 5");
130 self.k = k;
131 self
132 }
133
134 pub fn with_step_limit(mut self, limit: u64) -> Self {
135 self.step_limit = Some(limit);
136 self
137 }
138
139 pub fn without_step_limit(mut self) -> Self {
141 self.step_limit = None;
142 self
143 }
144}
145
146impl<S, V, DM, ESF> Debug for KOptPhaseBuilder<S, V, DM, ESF>
147where
148 S: PlanningSolution,
149 V: Clone + Send + Sync + Debug + 'static,
150{
151 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
152 f.debug_struct("KOptPhaseBuilder")
153 .field("k", &self.k)
154 .field("variable_name", &self.variable_name)
155 .field("descriptor_index", &self.descriptor_index)
156 .field("step_limit", &self.step_limit)
157 .finish()
158 }
159}
160
161pub struct KOptPhase<S, V>
165where
166 S: PlanningSolution,
167 V: Clone + Send + Sync + Debug + 'static,
168{
169 config: KOptConfig,
170 list_len: fn(&S, usize) -> usize,
171 sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
172 sublist_insert: fn(&mut S, usize, usize, Vec<V>),
173 variable_name: &'static str,
174 descriptor_index: usize,
175 step_limit: Option<u64>,
176 _marker: PhantomData<(fn() -> S, fn() -> V)>,
177}
178
179impl<S, V> Debug for KOptPhase<S, V>
180where
181 S: PlanningSolution,
182 V: Clone + Send + Sync + Debug + 'static,
183{
184 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
185 f.debug_struct("KOptPhase")
186 .field("k", &self.config.k)
187 .field("variable_name", &self.variable_name)
188 .finish()
189 }
190}
191
192impl<S, V, D> Phase<S, D> for KOptPhase<S, V>
193where
194 S: PlanningSolution,
195 V: Clone + Send + Sync + Debug + 'static,
196 D: Director<S>,
197{
198 fn solve(&mut self, solver_scope: &mut SolverScope<S, D>) {
199 use crate::heuristic::r#move::Move;
200 use crate::heuristic::selector::entity::FromSolutionEntitySelector;
201 use crate::heuristic::selector::move_selector::MoveSelector;
202
203 let mut phase_scope = PhaseScope::new(solver_scope, 0);
204
205 let mut last_step_score = phase_scope.calculate_score();
207
208 let entity_selector = FromSolutionEntitySelector::new(self.descriptor_index);
210 let move_selector = KOptMoveSelector::<S, V, _>::new(
211 entity_selector,
212 self.config.clone(),
213 self.list_len,
214 self.sublist_remove,
215 self.sublist_insert,
216 self.variable_name,
217 self.descriptor_index,
218 );
219
220 let step_limit = self.step_limit.unwrap_or(u64::MAX);
221 let mut steps = 0u64;
222 let mut arena = MoveArena::new();
223
224 while steps < step_limit && !phase_scope.solver_scope_mut().should_terminate() {
225 let mut step_scope = StepScope::new(&mut phase_scope);
226
227 arena.reset();
228 let generation_interrupted = {
229 let iter = move_selector.iter_moves(step_scope.score_director());
230 append_interruptibly(&step_scope, &mut arena, iter)
231 };
232 if generation_interrupted {
233 match settle_search_interrupt(&mut step_scope) {
234 StepInterrupt::Restart => continue,
235 StepInterrupt::TerminatePhase => break,
236 }
237 }
238
239 let mut best_move_idx = None;
240 let mut best_score = None;
241 let mut interrupted_step = false;
242
243 for idx in 0..arena.len() {
245 if should_interrupt_evaluation(&step_scope, idx) {
246 interrupted_step = true;
247 break;
248 }
249
250 let mv = arena.get(idx).unwrap();
251 if !mv.is_doable(step_scope.score_director()) {
252 continue;
253 }
254
255 {
257 let mut recording = RecordingDirector::new(step_scope.score_director_mut());
258 mv.do_move(&mut recording);
259 let move_score = recording.calculate_score();
260
261 if move_score > last_step_score
263 && best_score.as_ref().is_none_or(|b| move_score > *b)
264 {
265 best_score = Some(move_score);
266 best_move_idx = Some(idx);
267 }
268
269 recording.undo_changes();
271 }
272 }
273
274 if interrupted_step {
275 match settle_search_interrupt(&mut step_scope) {
276 StepInterrupt::Restart => continue,
277 StepInterrupt::TerminatePhase => break,
278 }
279 }
280
281 if let (Some(idx), Some(score)) = (best_move_idx, best_score) {
283 let selected_move = arena.take(idx);
284 selected_move.do_move(step_scope.score_director_mut());
285 step_scope.set_step_score(score);
286 last_step_score = score;
287 step_scope.phase_scope_mut().update_best_solution();
288 } else {
289 break;
291 }
292
293 step_scope.complete();
294 steps += 1;
295 }
296
297 if phase_scope.solver_scope().best_solution().is_none() {
299 phase_scope.update_best_solution();
300 }
301 }
302
303 fn phase_type_name(&self) -> &'static str {
304 "KOpt"
305 }
306}
307
308impl<S, V, DM, ESF, D> PhaseFactory<S, D> for KOptPhaseBuilder<S, V, DM, ESF>
309where
310 S: PlanningSolution,
311 V: Clone + Send + Sync + Debug + 'static,
312 DM: Send + Sync,
313 ESF: Send + Sync,
314 D: Director<S>,
315{
316 type Phase = KOptPhase<S, V>;
317
318 fn create(&self) -> Self::Phase {
319 KOptPhase {
320 config: KOptConfig::new(self.k),
321 list_len: self.list_len,
322 sublist_remove: self.sublist_remove,
323 sublist_insert: self.sublist_insert,
324 variable_name: self.variable_name,
325 descriptor_index: self.descriptor_index,
326 step_limit: self.step_limit,
327 _marker: PhantomData,
328 }
329 }
330}