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::selector::k_opt::{KOptConfig, KOptMoveSelector};
14use crate::phase::Phase;
15use crate::scope::{PhaseScope, SolverScope, StepScope};
16
17use super::super::PhaseFactory;
18
19pub struct KOptPhaseBuilder<S, V, DM, ESF>
80where
81 S: PlanningSolution,
82 V: Clone + Send + Sync + Debug + 'static,
83{
84 list_len: fn(&S, usize) -> usize,
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, fn() -> DM, fn() -> ESF)>,
92}
93
94impl<S, V, DM, ESF> KOptPhaseBuilder<S, V, DM, ESF>
95where
96 S: PlanningSolution,
97 V: Clone + Send + Sync + Debug + 'static,
98{
99 pub fn new(
104 _distance_meter: DM,
105 _entity_selector_factory: ESF,
106 list_len: fn(&S, usize) -> usize,
107 sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
108 sublist_insert: fn(&mut S, usize, usize, Vec<V>),
109 variable_name: &'static str,
110 descriptor_index: usize,
111 ) -> Self {
112 Self {
113 list_len,
114 sublist_remove,
115 sublist_insert,
116 variable_name,
117 descriptor_index,
118 k: 3, step_limit: Some(1000),
120 _marker: PhantomData,
121 }
122 }
123
124 pub fn with_k(mut self, k: usize) -> Self {
125 assert!((2..=5).contains(&k), "k must be between 2 and 5");
126 self.k = k;
127 self
128 }
129
130 pub fn with_step_limit(mut self, limit: u64) -> Self {
131 self.step_limit = Some(limit);
132 self
133 }
134
135 pub fn without_step_limit(mut self) -> Self {
137 self.step_limit = None;
138 self
139 }
140}
141
142impl<S, V, DM, ESF> Debug for KOptPhaseBuilder<S, V, DM, ESF>
143where
144 S: PlanningSolution,
145 V: Clone + Send + Sync + Debug + 'static,
146{
147 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148 f.debug_struct("KOptPhaseBuilder")
149 .field("k", &self.k)
150 .field("variable_name", &self.variable_name)
151 .field("descriptor_index", &self.descriptor_index)
152 .field("step_limit", &self.step_limit)
153 .finish()
154 }
155}
156
157pub struct KOptPhase<S, V>
161where
162 S: PlanningSolution,
163 V: Clone + Send + Sync + Debug + 'static,
164{
165 config: KOptConfig,
166 list_len: fn(&S, usize) -> usize,
167 sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
168 sublist_insert: fn(&mut S, usize, usize, Vec<V>),
169 variable_name: &'static str,
170 descriptor_index: usize,
171 step_limit: Option<u64>,
172 _marker: PhantomData<(fn() -> S, fn() -> V)>,
173}
174
175impl<S, V> Debug for KOptPhase<S, V>
176where
177 S: PlanningSolution,
178 V: Clone + Send + Sync + Debug + 'static,
179{
180 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
181 f.debug_struct("KOptPhase")
182 .field("k", &self.config.k)
183 .field("variable_name", &self.variable_name)
184 .finish()
185 }
186}
187
188impl<S, V, D> Phase<S, D> for KOptPhase<S, V>
189where
190 S: PlanningSolution,
191 V: Clone + Send + Sync + Debug + 'static,
192 D: Director<S>,
193{
194 fn solve(&mut self, solver_scope: &mut SolverScope<S, D>) {
195 use crate::heuristic::r#move::Move;
196 use crate::heuristic::selector::entity::FromSolutionEntitySelector;
197 use crate::heuristic::selector::typed_move_selector::MoveSelector;
198
199 let mut phase_scope = PhaseScope::new(solver_scope, 0);
200
201 let mut last_step_score = phase_scope.calculate_score();
203
204 let entity_selector = FromSolutionEntitySelector::new(self.descriptor_index);
206 let move_selector = KOptMoveSelector::<S, V, _>::new(
207 entity_selector,
208 self.config.clone(),
209 self.list_len,
210 self.sublist_remove,
211 self.sublist_insert,
212 self.variable_name,
213 self.descriptor_index,
214 );
215
216 let step_limit = self.step_limit.unwrap_or(u64::MAX);
217 let mut steps = 0u64;
218
219 while steps < step_limit && !phase_scope.solver_scope().should_terminate() {
220 let mut step_scope = StepScope::new(&mut phase_scope);
221
222 let moves: Vec<_> = move_selector
224 .iter_moves(step_scope.score_director())
225 .collect();
226
227 let mut best_move_idx = None;
228 let mut best_score = None;
229
230 for (idx, mv) in moves.iter().enumerate() {
232 if !mv.is_doable(step_scope.score_director()) {
233 continue;
234 }
235
236 {
238 let mut recording = RecordingDirector::new(step_scope.score_director_mut());
239 mv.do_move(&mut recording);
240 let move_score = recording.calculate_score();
241
242 if move_score > last_step_score
244 && best_score.as_ref().is_none_or(|b| move_score > *b)
245 {
246 best_score = Some(move_score);
247 best_move_idx = Some(idx);
248 }
249
250 recording.undo_changes();
252 }
253 }
254
255 if let (Some(idx), Some(score)) = (best_move_idx, best_score) {
257 moves[idx].do_move(step_scope.score_director_mut());
258 step_scope.set_step_score(score);
259 last_step_score = score;
260 step_scope.phase_scope_mut().update_best_solution();
261 } else {
262 break;
264 }
265
266 step_scope.complete();
267 steps += 1;
268 }
269
270 if phase_scope.solver_scope().best_solution().is_none() {
272 phase_scope.update_best_solution();
273 }
274 }
275
276 fn phase_type_name(&self) -> &'static str {
277 "KOpt"
278 }
279}
280
281impl<S, V, DM, ESF, D> PhaseFactory<S, D> for KOptPhaseBuilder<S, V, DM, ESF>
282where
283 S: PlanningSolution,
284 V: Clone + Send + Sync + Debug + 'static,
285 DM: Send + Sync,
286 ESF: Send + Sync,
287 D: Director<S>,
288{
289 type Phase = KOptPhase<S, V>;
290
291 fn create(&self) -> Self::Phase {
292 KOptPhase {
293 config: KOptConfig::new(self.k),
294 list_len: self.list_len,
295 sublist_remove: self.sublist_remove,
296 sublist_insert: self.sublist_insert,
297 variable_name: self.variable_name,
298 descriptor_index: self.descriptor_index,
299 step_limit: self.step_limit,
300 _marker: PhantomData,
301 }
302 }
303}