1use std::fmt::Debug;
4
5use solverforge_config::MoveSelectorConfig;
6use solverforge_core::domain::PlanningSolution;
7use solverforge_scoring::Director;
8
9use crate::heuristic::r#move::ListMoveImpl;
10use crate::heuristic::selector::decorator::VecUnionSelector;
11use crate::heuristic::selector::k_opt::KOptConfig;
12use crate::heuristic::selector::nearby_list_change::CrossEntityDistanceMeter;
13use crate::heuristic::selector::typed_move_selector::{
14 ListMoveKOptSelector, ListMoveListChangeSelector, ListMoveListRuinSelector,
15 ListMoveNearbyKOptSelector, MoveSelector,
16};
17
18use super::context::IntraDistanceAdapter;
19use crate::heuristic::selector::{
20 FromSolutionEntitySelector, ListMoveListReverseSelector, ListMoveListSwapSelector,
21 ListMoveNearbyListChangeSelector, ListMoveNearbyListSwapSelector,
22 ListMoveSubListChangeSelector, ListMoveSubListSwapSelector,
23};
24
25use super::context::ListContext;
26
27pub enum ListLeafSelector<S, V, DM, IDM>
33where
34 S: PlanningSolution,
35 V: Clone + PartialEq + Send + Sync + Debug + 'static,
36 DM: CrossEntityDistanceMeter<S>,
37 IDM: CrossEntityDistanceMeter<S>,
38{
39 NearbyListChange(ListMoveNearbyListChangeSelector<S, V, DM, FromSolutionEntitySelector>),
41 NearbyListSwap(ListMoveNearbyListSwapSelector<S, V, IDM, FromSolutionEntitySelector>),
43 ListReverse(ListMoveListReverseSelector<S, V, FromSolutionEntitySelector>),
45 SubListChange(ListMoveSubListChangeSelector<S, V, FromSolutionEntitySelector>),
47 KOpt(ListMoveKOptSelector<S, V, FromSolutionEntitySelector>),
49 NearbyKOpt(
51 ListMoveNearbyKOptSelector<S, V, IntraDistanceAdapter<IDM>, FromSolutionEntitySelector>,
52 ),
53 ListRuin(ListMoveListRuinSelector<S, V>),
55 ListChange(ListMoveListChangeSelector<S, V, FromSolutionEntitySelector>),
57 ListSwap(ListMoveListSwapSelector<S, V, FromSolutionEntitySelector>),
59 SubListSwap(ListMoveSubListSwapSelector<S, V, FromSolutionEntitySelector>),
61}
62
63impl<S, V, DM, IDM> Debug for ListLeafSelector<S, V, DM, IDM>
64where
65 S: PlanningSolution,
66 V: Clone + PartialEq + Send + Sync + Debug + 'static,
67 DM: CrossEntityDistanceMeter<S>,
68 IDM: CrossEntityDistanceMeter<S>,
69{
70 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71 match self {
72 Self::NearbyListChange(s) => write!(f, "ListLeafSelector::NearbyListChange({s:?})"),
73 Self::NearbyListSwap(s) => write!(f, "ListLeafSelector::NearbyListSwap({s:?})"),
74 Self::ListReverse(s) => write!(f, "ListLeafSelector::ListReverse({s:?})"),
75 Self::SubListChange(s) => write!(f, "ListLeafSelector::SubListChange({s:?})"),
76 Self::KOpt(s) => write!(f, "ListLeafSelector::KOpt({s:?})"),
77 Self::NearbyKOpt(s) => write!(f, "ListLeafSelector::NearbyKOpt({s:?})"),
78 Self::ListRuin(s) => write!(f, "ListLeafSelector::ListRuin({s:?})"),
79 Self::ListChange(s) => write!(f, "ListLeafSelector::ListChange({s:?})"),
80 Self::ListSwap(s) => write!(f, "ListLeafSelector::ListSwap({s:?})"),
81 Self::SubListSwap(s) => write!(f, "ListLeafSelector::SubListSwap({s:?})"),
82 }
83 }
84}
85
86impl<S, V, DM, IDM> MoveSelector<S, ListMoveImpl<S, V>> for ListLeafSelector<S, V, DM, IDM>
87where
88 S: PlanningSolution,
89 V: Clone + PartialEq + Send + Sync + Debug + 'static,
90 DM: CrossEntityDistanceMeter<S>,
91 IDM: CrossEntityDistanceMeter<S> + 'static,
92{
93 fn iter_moves<'a, D: Director<S>>(
94 &'a self,
95 score_director: &'a D,
96 ) -> impl Iterator<Item = ListMoveImpl<S, V>> + 'a {
97 let moves: Vec<ListMoveImpl<S, V>> = match self {
98 Self::NearbyListChange(s) => s.iter_moves(score_director).collect(),
99 Self::NearbyListSwap(s) => s.iter_moves(score_director).collect(),
100 Self::ListReverse(s) => s.iter_moves(score_director).collect(),
101 Self::SubListChange(s) => s.iter_moves(score_director).collect(),
102 Self::KOpt(s) => s.iter_moves(score_director).collect(),
103 Self::NearbyKOpt(s) => s.iter_moves(score_director).collect(),
104 Self::ListRuin(s) => s.iter_moves(score_director).collect(),
105 Self::ListChange(s) => s.iter_moves(score_director).collect(),
106 Self::ListSwap(s) => s.iter_moves(score_director).collect(),
107 Self::SubListSwap(s) => s.iter_moves(score_director).collect(),
108 };
109 moves.into_iter()
110 }
111
112 fn size<D: Director<S>>(&self, score_director: &D) -> usize {
113 match self {
114 Self::NearbyListChange(s) => s.size(score_director),
115 Self::NearbyListSwap(s) => s.size(score_director),
116 Self::ListReverse(s) => s.size(score_director),
117 Self::SubListChange(s) => s.size(score_director),
118 Self::KOpt(s) => s.size(score_director),
119 Self::NearbyKOpt(s) => s.size(score_director),
120 Self::ListRuin(s) => s.size(score_director),
121 Self::ListChange(s) => s.size(score_director),
122 Self::ListSwap(s) => s.size(score_director),
123 Self::SubListSwap(s) => s.size(score_director),
124 }
125 }
126}
127
128pub struct ListMoveSelectorBuilder;
130
131impl ListMoveSelectorBuilder {
132 pub fn build<S, V, DM, IDM>(
136 config: Option<&MoveSelectorConfig>,
137 ctx: &ListContext<S, V, DM, IDM>,
138 ) -> VecUnionSelector<S, ListMoveImpl<S, V>, ListLeafSelector<S, V, DM, IDM>>
139 where
140 S: PlanningSolution,
141 V: Clone + PartialEq + Send + Sync + Debug + 'static,
142 DM: CrossEntityDistanceMeter<S> + Clone,
143 IDM: CrossEntityDistanceMeter<S> + Clone,
144 {
145 let mut leaves: Vec<ListLeafSelector<S, V, DM, IDM>> = Vec::new();
146 match config {
147 None => {
148 Self::push_nearby_change(&mut leaves, ctx, 20);
149 Self::push_nearby_swap(&mut leaves, ctx, 20);
150 Self::push_list_reverse(&mut leaves, ctx);
151 }
152 Some(cfg) => Self::collect_leaves(cfg, ctx, &mut leaves),
153 }
154 VecUnionSelector::new(leaves)
155 }
156
157 fn collect_leaves<S, V, DM, IDM>(
158 config: &MoveSelectorConfig,
159 ctx: &ListContext<S, V, DM, IDM>,
160 out: &mut Vec<ListLeafSelector<S, V, DM, IDM>>,
161 ) where
162 S: PlanningSolution,
163 V: Clone + PartialEq + Send + Sync + Debug + 'static,
164 DM: CrossEntityDistanceMeter<S> + Clone,
165 IDM: CrossEntityDistanceMeter<S> + Clone,
166 {
167 match config {
168 MoveSelectorConfig::NearbyListChangeMoveSelector(c) => {
169 Self::push_nearby_change(out, ctx, c.max_nearby);
170 }
171 MoveSelectorConfig::NearbyListSwapMoveSelector(c) => {
172 Self::push_nearby_swap(out, ctx, c.max_nearby);
173 }
174 MoveSelectorConfig::ListReverseMoveSelector(_) => {
175 Self::push_list_reverse(out, ctx);
176 }
177 MoveSelectorConfig::SubListChangeMoveSelector(c) => {
178 Self::push_sublist_change(out, ctx, c.min_sublist_size, c.max_sublist_size);
179 }
180 MoveSelectorConfig::SubListSwapMoveSelector(c) => {
181 Self::push_sublist_swap(out, ctx, c.min_sublist_size, c.max_sublist_size);
182 }
183 MoveSelectorConfig::KOptMoveSelector(c) => {
184 Self::push_kopt(out, ctx, c.k, c.min_segment_len, c.max_nearby);
185 }
186 MoveSelectorConfig::ListRuinMoveSelector(c) => {
187 Self::push_list_ruin(out, ctx, c.min_ruin_count, c.max_ruin_count);
188 }
189 MoveSelectorConfig::ListChangeMoveSelector(_) => {
190 Self::push_list_change(out, ctx);
191 }
192 MoveSelectorConfig::ListSwapMoveSelector(_) => {
193 Self::push_list_swap(out, ctx);
194 }
195 MoveSelectorConfig::UnionMoveSelector(u) => {
196 for child in &u.selectors {
197 Self::collect_leaves(child, ctx, out);
198 }
199 }
200 _ => {}
202 }
203 }
204
205 fn push_nearby_change<S, V, DM, IDM>(
206 out: &mut Vec<ListLeafSelector<S, V, DM, IDM>>,
207 ctx: &ListContext<S, V, DM, IDM>,
208 max_nearby: usize,
209 ) where
210 S: PlanningSolution,
211 V: Clone + PartialEq + Send + Sync + Debug + 'static,
212 DM: CrossEntityDistanceMeter<S> + Clone,
213 IDM: CrossEntityDistanceMeter<S> + Clone,
214 {
215 use crate::heuristic::selector::nearby_list_change::NearbyListChangeMoveSelector;
216
217 let inner = NearbyListChangeMoveSelector::new(
218 FromSolutionEntitySelector::new(ctx.descriptor_index),
219 ctx.cross_distance_meter.clone(),
220 max_nearby,
221 ctx.list_len,
222 ctx.list_remove,
223 ctx.list_insert,
224 ctx.variable_name,
225 ctx.descriptor_index,
226 );
227 out.push(ListLeafSelector::NearbyListChange(
228 ListMoveNearbyListChangeSelector::new(inner),
229 ));
230 }
231
232 fn push_nearby_swap<S, V, DM, IDM>(
233 out: &mut Vec<ListLeafSelector<S, V, DM, IDM>>,
234 ctx: &ListContext<S, V, DM, IDM>,
235 max_nearby: usize,
236 ) where
237 S: PlanningSolution,
238 V: Clone + PartialEq + Send + Sync + Debug + 'static,
239 DM: CrossEntityDistanceMeter<S> + Clone,
240 IDM: CrossEntityDistanceMeter<S> + Clone,
241 {
242 use crate::heuristic::selector::nearby_list_swap::NearbyListSwapMoveSelector;
243
244 let inner = NearbyListSwapMoveSelector::new(
245 FromSolutionEntitySelector::new(ctx.descriptor_index),
246 ctx.intra_distance_meter.clone(),
247 max_nearby,
248 ctx.list_len,
249 ctx.list_get,
250 ctx.list_set,
251 ctx.variable_name,
252 ctx.descriptor_index,
253 );
254 out.push(ListLeafSelector::NearbyListSwap(
255 ListMoveNearbyListSwapSelector::new(inner),
256 ));
257 }
258
259 fn push_list_reverse<S, V, DM, IDM>(
260 out: &mut Vec<ListLeafSelector<S, V, DM, IDM>>,
261 ctx: &ListContext<S, V, DM, IDM>,
262 ) where
263 S: PlanningSolution,
264 V: Clone + PartialEq + Send + Sync + Debug + 'static,
265 DM: CrossEntityDistanceMeter<S> + Clone,
266 IDM: CrossEntityDistanceMeter<S> + Clone,
267 {
268 use crate::heuristic::selector::list_reverse::ListReverseMoveSelector;
269
270 let inner = ListReverseMoveSelector::new(
271 FromSolutionEntitySelector::new(ctx.descriptor_index),
272 ctx.list_len,
273 ctx.list_reverse,
274 ctx.variable_name,
275 ctx.descriptor_index,
276 );
277 out.push(ListLeafSelector::ListReverse(
278 ListMoveListReverseSelector::new(inner),
279 ));
280 }
281
282 fn push_sublist_change<S, V, DM, IDM>(
283 out: &mut Vec<ListLeafSelector<S, V, DM, IDM>>,
284 ctx: &ListContext<S, V, DM, IDM>,
285 min_sublist_size: usize,
286 max_sublist_size: usize,
287 ) where
288 S: PlanningSolution,
289 V: Clone + PartialEq + Send + Sync + Debug + 'static,
290 DM: CrossEntityDistanceMeter<S> + Clone,
291 IDM: CrossEntityDistanceMeter<S> + Clone,
292 {
293 use crate::heuristic::selector::sublist_change::SubListChangeMoveSelector;
294
295 let inner = SubListChangeMoveSelector::new(
296 FromSolutionEntitySelector::new(ctx.descriptor_index),
297 min_sublist_size,
298 max_sublist_size,
299 ctx.list_len,
300 ctx.sublist_remove,
301 ctx.sublist_insert,
302 ctx.variable_name,
303 ctx.descriptor_index,
304 );
305 out.push(ListLeafSelector::SubListChange(
306 ListMoveSubListChangeSelector::new(inner),
307 ));
308 }
309
310 fn push_sublist_swap<S, V, DM, IDM>(
311 out: &mut Vec<ListLeafSelector<S, V, DM, IDM>>,
312 ctx: &ListContext<S, V, DM, IDM>,
313 min_sublist_size: usize,
314 max_sublist_size: usize,
315 ) where
316 S: PlanningSolution,
317 V: Clone + PartialEq + Send + Sync + Debug + 'static,
318 DM: CrossEntityDistanceMeter<S> + Clone,
319 IDM: CrossEntityDistanceMeter<S> + Clone,
320 {
321 use crate::heuristic::selector::sublist_swap::SubListSwapMoveSelector;
322
323 let inner = SubListSwapMoveSelector::new(
324 FromSolutionEntitySelector::new(ctx.descriptor_index),
325 min_sublist_size,
326 max_sublist_size,
327 ctx.list_len,
328 ctx.sublist_remove,
329 ctx.sublist_insert,
330 ctx.variable_name,
331 ctx.descriptor_index,
332 );
333 out.push(ListLeafSelector::SubListSwap(
334 ListMoveSubListSwapSelector::new(inner),
335 ));
336 }
337
338 fn push_kopt<S, V, DM, IDM>(
339 out: &mut Vec<ListLeafSelector<S, V, DM, IDM>>,
340 ctx: &ListContext<S, V, DM, IDM>,
341 k: usize,
342 min_segment_len: usize,
343 max_nearby: usize,
344 ) where
345 S: PlanningSolution,
346 V: Clone + PartialEq + Send + Sync + Debug + 'static,
347 DM: CrossEntityDistanceMeter<S> + Clone,
348 IDM: CrossEntityDistanceMeter<S> + Clone,
349 {
350 use crate::heuristic::selector::k_opt::{KOptMoveSelector, NearbyKOptMoveSelector};
351
352 let config = KOptConfig::new(k.clamp(2, 5)).with_min_segment_len(min_segment_len);
353 if max_nearby > 0 {
354 let adapter = IntraDistanceAdapter(ctx.intra_distance_meter.clone());
355 let inner = NearbyKOptMoveSelector::new(
356 FromSolutionEntitySelector::new(ctx.descriptor_index),
357 adapter,
358 max_nearby,
359 config,
360 ctx.list_len,
361 ctx.sublist_remove,
362 ctx.sublist_insert,
363 ctx.variable_name,
364 ctx.descriptor_index,
365 );
366 out.push(ListLeafSelector::NearbyKOpt(
367 ListMoveNearbyKOptSelector::new(inner),
368 ));
369 } else {
370 let inner = KOptMoveSelector::new(
371 FromSolutionEntitySelector::new(ctx.descriptor_index),
372 config,
373 ctx.list_len,
374 ctx.sublist_remove,
375 ctx.sublist_insert,
376 ctx.variable_name,
377 ctx.descriptor_index,
378 );
379 out.push(ListLeafSelector::KOpt(ListMoveKOptSelector::new(inner)));
380 }
381 }
382
383 fn push_list_ruin<S, V, DM, IDM>(
384 out: &mut Vec<ListLeafSelector<S, V, DM, IDM>>,
385 ctx: &ListContext<S, V, DM, IDM>,
386 min_ruin_count: usize,
387 max_ruin_count: usize,
388 ) where
389 S: PlanningSolution,
390 V: Clone + PartialEq + Send + Sync + Debug + 'static,
391 DM: CrossEntityDistanceMeter<S> + Clone,
392 IDM: CrossEntityDistanceMeter<S> + Clone,
393 {
394 use crate::heuristic::selector::list_ruin::ListRuinMoveSelector;
395
396 let inner = ListRuinMoveSelector::new(
397 min_ruin_count.max(1),
398 max_ruin_count.max(1),
399 ctx.entity_count,
400 ctx.list_len,
401 ctx.ruin_remove,
402 ctx.ruin_insert,
403 ctx.variable_name,
404 ctx.descriptor_index,
405 );
406 out.push(ListLeafSelector::ListRuin(ListMoveListRuinSelector::new(
407 inner,
408 )));
409 }
410
411 fn push_list_change<S, V, DM, IDM>(
412 out: &mut Vec<ListLeafSelector<S, V, DM, IDM>>,
413 ctx: &ListContext<S, V, DM, IDM>,
414 ) where
415 S: PlanningSolution,
416 V: Clone + PartialEq + Send + Sync + Debug + 'static,
417 DM: CrossEntityDistanceMeter<S> + Clone,
418 IDM: CrossEntityDistanceMeter<S> + Clone,
419 {
420 use crate::heuristic::selector::list_change::ListChangeMoveSelector;
421
422 let inner = ListChangeMoveSelector::new(
423 FromSolutionEntitySelector::new(ctx.descriptor_index),
424 ctx.list_len,
425 ctx.list_remove,
426 ctx.list_insert,
427 ctx.variable_name,
428 ctx.descriptor_index,
429 );
430 out.push(ListLeafSelector::ListChange(
431 ListMoveListChangeSelector::new(inner),
432 ));
433 }
434
435 fn push_list_swap<S, V, DM, IDM>(
436 out: &mut Vec<ListLeafSelector<S, V, DM, IDM>>,
437 ctx: &ListContext<S, V, DM, IDM>,
438 ) where
439 S: PlanningSolution,
440 V: Clone + PartialEq + Send + Sync + Debug + 'static,
441 DM: CrossEntityDistanceMeter<S> + Clone,
442 IDM: CrossEntityDistanceMeter<S> + Clone,
443 {
444 use crate::heuristic::selector::list_swap::ListSwapMoveSelector;
445
446 let inner = ListSwapMoveSelector::new(
447 FromSolutionEntitySelector::new(ctx.descriptor_index),
448 ctx.list_len,
449 ctx.list_get,
450 ctx.list_set,
451 ctx.variable_name,
452 ctx.descriptor_index,
453 );
454 out.push(ListLeafSelector::ListSwap(ListMoveListSwapSelector::new(
455 inner,
456 )));
457 }
458}