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