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