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, MoveArena};
10use crate::heuristic::selector::decorator::VecUnionSelector;
11use crate::heuristic::selector::k_opt::KOptConfig;
12use crate::heuristic::selector::move_selector::{
13 ListMoveKOptSelector, ListMoveListChangeSelector, ListMoveListRuinSelector,
14 ListMoveNearbyKOptSelector, MoveSelector,
15};
16use crate::heuristic::selector::nearby_list_change::CrossEntityDistanceMeter;
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 enum ListLeafIter<A, B, C, DIter, E, F, G, H, I, J> {
98 NearbyListChange(A),
99 NearbyListSwap(B),
100 ListReverse(C),
101 SubListChange(DIter),
102 KOpt(E),
103 NearbyKOpt(F),
104 ListRuin(G),
105 ListChange(H),
106 ListSwap(I),
107 SubListSwap(J),
108 }
109
110 impl<T, A, B, C, DIter, E, F, G, H, I, J> Iterator
111 for ListLeafIter<A, B, C, DIter, E, F, G, H, I, J>
112 where
113 A: Iterator<Item = T>,
114 B: Iterator<Item = T>,
115 C: Iterator<Item = T>,
116 DIter: Iterator<Item = T>,
117 E: Iterator<Item = T>,
118 F: Iterator<Item = T>,
119 G: Iterator<Item = T>,
120 H: Iterator<Item = T>,
121 I: Iterator<Item = T>,
122 J: Iterator<Item = T>,
123 {
124 type Item = T;
125
126 fn next(&mut self) -> Option<Self::Item> {
127 match self {
128 Self::NearbyListChange(iter) => iter.next(),
129 Self::NearbyListSwap(iter) => iter.next(),
130 Self::ListReverse(iter) => iter.next(),
131 Self::SubListChange(iter) => iter.next(),
132 Self::KOpt(iter) => iter.next(),
133 Self::NearbyKOpt(iter) => iter.next(),
134 Self::ListRuin(iter) => iter.next(),
135 Self::ListChange(iter) => iter.next(),
136 Self::ListSwap(iter) => iter.next(),
137 Self::SubListSwap(iter) => iter.next(),
138 }
139 }
140 }
141
142 match self {
143 Self::NearbyListChange(s) => {
144 ListLeafIter::NearbyListChange(s.iter_moves(score_director))
145 }
146 Self::NearbyListSwap(s) => ListLeafIter::NearbyListSwap(s.iter_moves(score_director)),
147 Self::ListReverse(s) => ListLeafIter::ListReverse(s.iter_moves(score_director)),
148 Self::SubListChange(s) => ListLeafIter::SubListChange(s.iter_moves(score_director)),
149 Self::KOpt(s) => ListLeafIter::KOpt(s.iter_moves(score_director)),
150 Self::NearbyKOpt(s) => ListLeafIter::NearbyKOpt(s.iter_moves(score_director)),
151 Self::ListRuin(s) => ListLeafIter::ListRuin(s.iter_moves(score_director)),
152 Self::ListChange(s) => ListLeafIter::ListChange(s.iter_moves(score_director)),
153 Self::ListSwap(s) => ListLeafIter::ListSwap(s.iter_moves(score_director)),
154 Self::SubListSwap(s) => ListLeafIter::SubListSwap(s.iter_moves(score_director)),
155 }
156 }
157
158 fn size<D: Director<S>>(&self, score_director: &D) -> usize {
159 match self {
160 Self::NearbyListChange(s) => s.size(score_director),
161 Self::NearbyListSwap(s) => s.size(score_director),
162 Self::ListReverse(s) => s.size(score_director),
163 Self::SubListChange(s) => s.size(score_director),
164 Self::KOpt(s) => s.size(score_director),
165 Self::NearbyKOpt(s) => s.size(score_director),
166 Self::ListRuin(s) => s.size(score_director),
167 Self::ListChange(s) => s.size(score_director),
168 Self::ListSwap(s) => s.size(score_director),
169 Self::SubListSwap(s) => s.size(score_director),
170 }
171 }
172
173 fn append_moves<D: Director<S>>(
174 &self,
175 score_director: &D,
176 arena: &mut MoveArena<ListMoveImpl<S, V>>,
177 ) {
178 match self {
179 Self::NearbyListChange(s) => arena.extend(s.iter_moves(score_director)),
180 Self::NearbyListSwap(s) => arena.extend(s.iter_moves(score_director)),
181 Self::ListReverse(s) => arena.extend(s.iter_moves(score_director)),
182 Self::SubListChange(s) => arena.extend(s.iter_moves(score_director)),
183 Self::KOpt(s) => arena.extend(s.iter_moves(score_director)),
184 Self::NearbyKOpt(s) => arena.extend(s.iter_moves(score_director)),
185 Self::ListRuin(s) => arena.extend(s.iter_moves(score_director)),
186 Self::ListChange(s) => arena.extend(s.iter_moves(score_director)),
187 Self::ListSwap(s) => arena.extend(s.iter_moves(score_director)),
188 Self::SubListSwap(s) => arena.extend(s.iter_moves(score_director)),
189 }
190 }
191}
192
193pub struct ListMoveSelectorBuilder;
195
196impl ListMoveSelectorBuilder {
197 pub fn build<S, V, DM, IDM>(
201 config: Option<&MoveSelectorConfig>,
202 ctx: &ListContext<S, V, DM, IDM>,
203 ) -> VecUnionSelector<S, ListMoveImpl<S, V>, ListLeafSelector<S, V, DM, IDM>>
204 where
205 S: PlanningSolution,
206 V: Clone + PartialEq + Send + Sync + Debug + 'static,
207 DM: CrossEntityDistanceMeter<S> + Clone,
208 IDM: CrossEntityDistanceMeter<S> + Clone,
209 {
210 let mut leaves: Vec<ListLeafSelector<S, V, DM, IDM>> = Vec::new();
211 match config {
212 None => {
213 Self::push_nearby_change(&mut leaves, ctx, 20);
214 Self::push_nearby_swap(&mut leaves, ctx, 20);
215 Self::push_list_reverse(&mut leaves, ctx);
216 }
217 Some(cfg) => Self::collect_leaves(cfg, ctx, &mut leaves),
218 }
219 assert!(
220 !leaves.is_empty(),
221 "stock move selector configuration produced no list neighborhoods"
222 );
223 VecUnionSelector::new(leaves)
224 }
225
226 fn collect_leaves<S, V, DM, IDM>(
227 config: &MoveSelectorConfig,
228 ctx: &ListContext<S, V, DM, IDM>,
229 out: &mut Vec<ListLeafSelector<S, V, DM, IDM>>,
230 ) where
231 S: PlanningSolution,
232 V: Clone + PartialEq + Send + Sync + Debug + 'static,
233 DM: CrossEntityDistanceMeter<S> + Clone,
234 IDM: CrossEntityDistanceMeter<S> + Clone,
235 {
236 match config {
237 MoveSelectorConfig::NearbyListChangeMoveSelector(c) => {
238 Self::push_nearby_change(out, ctx, c.max_nearby);
239 }
240 MoveSelectorConfig::NearbyListSwapMoveSelector(c) => {
241 Self::push_nearby_swap(out, ctx, c.max_nearby);
242 }
243 MoveSelectorConfig::ListReverseMoveSelector(_) => {
244 Self::push_list_reverse(out, ctx);
245 }
246 MoveSelectorConfig::SubListChangeMoveSelector(c) => {
247 Self::push_sublist_change(out, ctx, c.min_sublist_size, c.max_sublist_size);
248 }
249 MoveSelectorConfig::SubListSwapMoveSelector(c) => {
250 Self::push_sublist_swap(out, ctx, c.min_sublist_size, c.max_sublist_size);
251 }
252 MoveSelectorConfig::KOptMoveSelector(c) => {
253 Self::push_kopt(out, ctx, c.k, c.min_segment_len, c.max_nearby);
254 }
255 MoveSelectorConfig::ListRuinMoveSelector(c) => {
256 Self::push_list_ruin(out, ctx, c.min_ruin_count, c.max_ruin_count);
257 }
258 MoveSelectorConfig::ListChangeMoveSelector(_) => {
259 Self::push_list_change(out, ctx);
260 }
261 MoveSelectorConfig::ListSwapMoveSelector(_) => {
262 Self::push_list_swap(out, ctx);
263 }
264 MoveSelectorConfig::UnionMoveSelector(u) => {
265 for child in &u.selectors {
266 Self::collect_leaves(child, ctx, out);
267 }
268 }
269 MoveSelectorConfig::ChangeMoveSelector(_) | MoveSelectorConfig::SwapMoveSelector(_) => {
270 panic!("standard move selector configured against a list-variable stock context");
271 }
272 MoveSelectorConfig::CartesianProductMoveSelector(_) => {
273 panic!("cartesian_product move selectors are not supported in stock solving");
274 }
275 }
276 }
277
278 fn push_nearby_change<S, V, DM, IDM>(
279 out: &mut Vec<ListLeafSelector<S, V, DM, IDM>>,
280 ctx: &ListContext<S, V, DM, IDM>,
281 max_nearby: usize,
282 ) where
283 S: PlanningSolution,
284 V: Clone + PartialEq + Send + Sync + Debug + 'static,
285 DM: CrossEntityDistanceMeter<S> + Clone,
286 IDM: CrossEntityDistanceMeter<S> + Clone,
287 {
288 use crate::heuristic::selector::nearby_list_change::NearbyListChangeMoveSelector;
289
290 let inner = NearbyListChangeMoveSelector::new(
291 FromSolutionEntitySelector::new(ctx.descriptor_index),
292 ctx.cross_distance_meter.clone(),
293 max_nearby,
294 ctx.list_len,
295 ctx.list_remove,
296 ctx.list_insert,
297 ctx.variable_name,
298 ctx.descriptor_index,
299 );
300 out.push(ListLeafSelector::NearbyListChange(
301 ListMoveNearbyListChangeSelector::new(inner),
302 ));
303 }
304
305 fn push_nearby_swap<S, V, DM, IDM>(
306 out: &mut Vec<ListLeafSelector<S, V, DM, IDM>>,
307 ctx: &ListContext<S, V, DM, IDM>,
308 max_nearby: usize,
309 ) where
310 S: PlanningSolution,
311 V: Clone + PartialEq + Send + Sync + Debug + 'static,
312 DM: CrossEntityDistanceMeter<S> + Clone,
313 IDM: CrossEntityDistanceMeter<S> + Clone,
314 {
315 use crate::heuristic::selector::nearby_list_swap::NearbyListSwapMoveSelector;
316
317 let inner = NearbyListSwapMoveSelector::new(
318 FromSolutionEntitySelector::new(ctx.descriptor_index),
319 ctx.intra_distance_meter.clone(),
320 max_nearby,
321 ctx.list_len,
322 ctx.list_get,
323 ctx.list_set,
324 ctx.variable_name,
325 ctx.descriptor_index,
326 );
327 out.push(ListLeafSelector::NearbyListSwap(
328 ListMoveNearbyListSwapSelector::new(inner),
329 ));
330 }
331
332 fn push_list_reverse<S, V, DM, IDM>(
333 out: &mut Vec<ListLeafSelector<S, V, DM, IDM>>,
334 ctx: &ListContext<S, V, DM, IDM>,
335 ) where
336 S: PlanningSolution,
337 V: Clone + PartialEq + Send + Sync + Debug + 'static,
338 DM: CrossEntityDistanceMeter<S> + Clone,
339 IDM: CrossEntityDistanceMeter<S> + Clone,
340 {
341 use crate::heuristic::selector::list_reverse::ListReverseMoveSelector;
342
343 let inner = ListReverseMoveSelector::new(
344 FromSolutionEntitySelector::new(ctx.descriptor_index),
345 ctx.list_len,
346 ctx.list_reverse,
347 ctx.variable_name,
348 ctx.descriptor_index,
349 );
350 out.push(ListLeafSelector::ListReverse(
351 ListMoveListReverseSelector::new(inner),
352 ));
353 }
354
355 fn push_sublist_change<S, V, DM, IDM>(
356 out: &mut Vec<ListLeafSelector<S, V, DM, IDM>>,
357 ctx: &ListContext<S, V, DM, IDM>,
358 min_sublist_size: usize,
359 max_sublist_size: usize,
360 ) where
361 S: PlanningSolution,
362 V: Clone + PartialEq + Send + Sync + Debug + 'static,
363 DM: CrossEntityDistanceMeter<S> + Clone,
364 IDM: CrossEntityDistanceMeter<S> + Clone,
365 {
366 use crate::heuristic::selector::sublist_change::SubListChangeMoveSelector;
367
368 let inner = SubListChangeMoveSelector::new(
369 FromSolutionEntitySelector::new(ctx.descriptor_index),
370 min_sublist_size,
371 max_sublist_size,
372 ctx.list_len,
373 ctx.sublist_remove,
374 ctx.sublist_insert,
375 ctx.variable_name,
376 ctx.descriptor_index,
377 );
378 out.push(ListLeafSelector::SubListChange(
379 ListMoveSubListChangeSelector::new(inner),
380 ));
381 }
382
383 fn push_sublist_swap<S, V, DM, IDM>(
384 out: &mut Vec<ListLeafSelector<S, V, DM, IDM>>,
385 ctx: &ListContext<S, V, DM, IDM>,
386 min_sublist_size: usize,
387 max_sublist_size: 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::sublist_swap::SubListSwapMoveSelector;
395
396 let inner = SubListSwapMoveSelector::new(
397 FromSolutionEntitySelector::new(ctx.descriptor_index),
398 min_sublist_size,
399 max_sublist_size,
400 ctx.list_len,
401 ctx.sublist_remove,
402 ctx.sublist_insert,
403 ctx.variable_name,
404 ctx.descriptor_index,
405 );
406 out.push(ListLeafSelector::SubListSwap(
407 ListMoveSubListSwapSelector::new(inner),
408 ));
409 }
410
411 fn push_kopt<S, V, DM, IDM>(
412 out: &mut Vec<ListLeafSelector<S, V, DM, IDM>>,
413 ctx: &ListContext<S, V, DM, IDM>,
414 k: usize,
415 min_segment_len: usize,
416 max_nearby: usize,
417 ) where
418 S: PlanningSolution,
419 V: Clone + PartialEq + Send + Sync + Debug + 'static,
420 DM: CrossEntityDistanceMeter<S> + Clone,
421 IDM: CrossEntityDistanceMeter<S> + Clone,
422 {
423 use crate::heuristic::selector::k_opt::{KOptMoveSelector, NearbyKOptMoveSelector};
424
425 let config = KOptConfig::new(k.clamp(2, 5)).with_min_segment_len(min_segment_len);
426 if max_nearby > 0 {
427 let adapter = IntraDistanceAdapter(ctx.intra_distance_meter.clone());
428 let inner = NearbyKOptMoveSelector::new(
429 FromSolutionEntitySelector::new(ctx.descriptor_index),
430 adapter,
431 max_nearby,
432 config,
433 ctx.list_len,
434 ctx.sublist_remove,
435 ctx.sublist_insert,
436 ctx.variable_name,
437 ctx.descriptor_index,
438 );
439 out.push(ListLeafSelector::NearbyKOpt(
440 ListMoveNearbyKOptSelector::new(inner),
441 ));
442 } else {
443 let inner = KOptMoveSelector::new(
444 FromSolutionEntitySelector::new(ctx.descriptor_index),
445 config,
446 ctx.list_len,
447 ctx.sublist_remove,
448 ctx.sublist_insert,
449 ctx.variable_name,
450 ctx.descriptor_index,
451 );
452 out.push(ListLeafSelector::KOpt(ListMoveKOptSelector::new(inner)));
453 }
454 }
455
456 fn push_list_ruin<S, V, DM, IDM>(
457 out: &mut Vec<ListLeafSelector<S, V, DM, IDM>>,
458 ctx: &ListContext<S, V, DM, IDM>,
459 min_ruin_count: usize,
460 max_ruin_count: usize,
461 ) where
462 S: PlanningSolution,
463 V: Clone + PartialEq + Send + Sync + Debug + 'static,
464 DM: CrossEntityDistanceMeter<S> + Clone,
465 IDM: CrossEntityDistanceMeter<S> + Clone,
466 {
467 use crate::heuristic::selector::list_ruin::ListRuinMoveSelector;
468
469 let inner = ListRuinMoveSelector::new(
470 min_ruin_count.max(1),
471 max_ruin_count.max(1),
472 ctx.entity_count,
473 ctx.list_len,
474 ctx.ruin_remove,
475 ctx.ruin_insert,
476 ctx.variable_name,
477 ctx.descriptor_index,
478 );
479 out.push(ListLeafSelector::ListRuin(ListMoveListRuinSelector::new(
480 inner,
481 )));
482 }
483
484 fn push_list_change<S, V, DM, IDM>(
485 out: &mut Vec<ListLeafSelector<S, V, DM, IDM>>,
486 ctx: &ListContext<S, V, DM, IDM>,
487 ) where
488 S: PlanningSolution,
489 V: Clone + PartialEq + Send + Sync + Debug + 'static,
490 DM: CrossEntityDistanceMeter<S> + Clone,
491 IDM: CrossEntityDistanceMeter<S> + Clone,
492 {
493 use crate::heuristic::selector::list_change::ListChangeMoveSelector;
494
495 let inner = ListChangeMoveSelector::new(
496 FromSolutionEntitySelector::new(ctx.descriptor_index),
497 ctx.list_len,
498 ctx.list_remove,
499 ctx.list_insert,
500 ctx.variable_name,
501 ctx.descriptor_index,
502 );
503 out.push(ListLeafSelector::ListChange(
504 ListMoveListChangeSelector::new(inner),
505 ));
506 }
507
508 fn push_list_swap<S, V, DM, IDM>(
509 out: &mut Vec<ListLeafSelector<S, V, DM, IDM>>,
510 ctx: &ListContext<S, V, DM, IDM>,
511 ) where
512 S: PlanningSolution,
513 V: Clone + PartialEq + Send + Sync + Debug + 'static,
514 DM: CrossEntityDistanceMeter<S> + Clone,
515 IDM: CrossEntityDistanceMeter<S> + Clone,
516 {
517 use crate::heuristic::selector::list_swap::ListSwapMoveSelector;
518
519 let inner = ListSwapMoveSelector::new(
520 FromSolutionEntitySelector::new(ctx.descriptor_index),
521 ctx.list_len,
522 ctx.list_get,
523 ctx.list_set,
524 ctx.variable_name,
525 ctx.descriptor_index,
526 );
527 out.push(ListLeafSelector::ListSwap(ListMoveListSwapSelector::new(
528 inner,
529 )));
530 }
531}