1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
4#[serde(rename_all = "snake_case")]
5pub struct VariableTargetConfig {
6 pub entity_class: Option<String>,
7 pub variable_name: Option<String>,
8}
9
10#[derive(Debug, Clone, Copy, Default, Deserialize, Serialize, PartialEq, Eq, Hash)]
11#[serde(rename_all = "snake_case")]
12pub enum SelectionOrder {
13 #[default]
14 Original,
15 Random,
16 Shuffled,
17 Sorted,
18 Probabilistic,
19}
20
21impl SelectionOrder {
22 pub const fn is_random(self) -> bool {
23 matches!(self, Self::Random | Self::Shuffled | Self::Probabilistic)
24 }
25
26 pub const fn requires_complete_stream(self) -> bool {
27 matches!(self, Self::Sorted | Self::Probabilistic)
28 }
29}
30
31#[derive(Debug, Clone, Deserialize, Serialize)]
33#[serde(tag = "type", rename_all = "snake_case")]
34pub enum MoveSelectorConfig {
35 ChangeMoveSelector(ChangeMoveConfig),
37
38 SwapMoveSelector(SwapMoveConfig),
40
41 NearbyChangeMoveSelector(NearbyChangeMoveConfig),
43
44 NearbySwapMoveSelector(NearbySwapMoveConfig),
46
47 PillarChangeMoveSelector(PillarChangeMoveConfig),
49
50 PillarSwapMoveSelector(PillarSwapMoveConfig),
52
53 RuinRecreateMoveSelector(RuinRecreateMoveSelectorConfig),
55
56 GroupedScalarMoveSelector(GroupedScalarMoveSelectorConfig),
58
59 ListChangeMoveSelector(ListChangeMoveConfig),
61
62 NearbyListChangeMoveSelector(NearbyListChangeMoveConfig),
64
65 ListSwapMoveSelector(ListSwapMoveConfig),
67
68 ListPermuteMoveSelector(ListPermuteMoveConfig),
70
71 ListPrecedenceMoveSelector(ListPrecedenceMoveConfig),
73
74 NearbyListSwapMoveSelector(NearbyListSwapMoveConfig),
76
77 SublistChangeMoveSelector(SublistChangeMoveConfig),
79
80 SublistSwapMoveSelector(SublistSwapMoveConfig),
82
83 ListReverseMoveSelector(ListReverseMoveConfig),
85
86 KOptMoveSelector(KOptMoveSelectorConfig),
88
89 ListRuinMoveSelector(ListRuinMoveSelectorConfig),
91
92 LimitedNeighborhood(LimitedNeighborhoodConfig),
95
96 UnionMoveSelector(UnionMoveSelectorConfig),
98
99 CartesianProductMoveSelector(CartesianProductConfig),
103
104 ConflictRepairMoveSelector(ConflictRepairMoveSelectorConfig),
106
107 CompoundConflictRepairMoveSelector(CompoundConflictRepairMoveSelectorConfig),
109}
110
111impl MoveSelectorConfig {
112 pub const fn selection_order(&self) -> Option<SelectionOrder> {
113 match self {
114 Self::ChangeMoveSelector(config) => config.selection_order,
115 Self::SwapMoveSelector(config) => config.selection_order,
116 Self::NearbyChangeMoveSelector(config) => config.selection_order,
117 Self::NearbySwapMoveSelector(config) => config.selection_order,
118 Self::PillarChangeMoveSelector(config) => config.selection_order,
119 Self::PillarSwapMoveSelector(config) => config.selection_order,
120 Self::RuinRecreateMoveSelector(config) => config.selection_order,
121 Self::GroupedScalarMoveSelector(config) => config.selection_order,
122 Self::ListChangeMoveSelector(config) => config.selection_order,
123 Self::NearbyListChangeMoveSelector(config) => config.selection_order,
124 Self::ListSwapMoveSelector(config) => config.selection_order,
125 Self::ListPermuteMoveSelector(config) => config.selection_order,
126 Self::ListPrecedenceMoveSelector(config) => config.selection_order,
127 Self::NearbyListSwapMoveSelector(config) => config.selection_order,
128 Self::SublistChangeMoveSelector(config) => config.selection_order,
129 Self::SublistSwapMoveSelector(config) => config.selection_order,
130 Self::ListReverseMoveSelector(config) => config.selection_order,
131 Self::KOptMoveSelector(config) => config.selection_order,
132 Self::ListRuinMoveSelector(config) => config.selection_order,
133 Self::ConflictRepairMoveSelector(config) => config.selection_order,
134 Self::CompoundConflictRepairMoveSelector(config) => config.selection_order,
135 Self::LimitedNeighborhood(_)
136 | Self::UnionMoveSelector(_)
137 | Self::CartesianProductMoveSelector(_) => None,
138 }
139 }
140
141 pub fn selection_metric(&self) -> Option<&str> {
142 match self {
143 Self::ChangeMoveSelector(config) => config.selection_metric.as_deref(),
144 Self::SwapMoveSelector(config) => config.selection_metric.as_deref(),
145 Self::NearbyChangeMoveSelector(config) => config.selection_metric.as_deref(),
146 Self::NearbySwapMoveSelector(config) => config.selection_metric.as_deref(),
147 Self::PillarChangeMoveSelector(config) => config.selection_metric.as_deref(),
148 Self::PillarSwapMoveSelector(config) => config.selection_metric.as_deref(),
149 Self::RuinRecreateMoveSelector(config) => config.selection_metric.as_deref(),
150 Self::GroupedScalarMoveSelector(config) => config.selection_metric.as_deref(),
151 Self::ListChangeMoveSelector(config) => config.selection_metric.as_deref(),
152 Self::NearbyListChangeMoveSelector(config) => config.selection_metric.as_deref(),
153 Self::ListSwapMoveSelector(config) => config.selection_metric.as_deref(),
154 Self::ListPermuteMoveSelector(config) => config.selection_metric.as_deref(),
155 Self::ListPrecedenceMoveSelector(config) => config.selection_metric.as_deref(),
156 Self::NearbyListSwapMoveSelector(config) => config.selection_metric.as_deref(),
157 Self::SublistChangeMoveSelector(config) => config.selection_metric.as_deref(),
158 Self::SublistSwapMoveSelector(config) => config.selection_metric.as_deref(),
159 Self::ListReverseMoveSelector(config) => config.selection_metric.as_deref(),
160 Self::KOptMoveSelector(config) => config.selection_metric.as_deref(),
161 Self::ListRuinMoveSelector(config) => config.selection_metric.as_deref(),
162 Self::ConflictRepairMoveSelector(config) => config.selection_metric.as_deref(),
163 Self::CompoundConflictRepairMoveSelector(config) => config.selection_metric.as_deref(),
164 Self::LimitedNeighborhood(_)
165 | Self::UnionMoveSelector(_)
166 | Self::CartesianProductMoveSelector(_) => None,
167 }
168 }
169}
170
171#[derive(Debug, Clone, Default, Deserialize, Serialize)]
173#[serde(rename_all = "snake_case")]
174pub struct ChangeMoveConfig {
175 #[serde(default)]
176 pub selection_order: Option<SelectionOrder>,
177 pub selection_metric: Option<String>,
178 pub value_candidate_limit: Option<usize>,
179 #[serde(flatten)]
180 pub target: VariableTargetConfig,
181}
182
183#[derive(Debug, Clone, Default, Deserialize, Serialize)]
185#[serde(rename_all = "snake_case")]
186pub struct SwapMoveConfig {
187 #[serde(default)]
188 pub selection_order: Option<SelectionOrder>,
189 pub selection_metric: Option<String>,
190 #[serde(flatten)]
191 pub target: VariableTargetConfig,
192}
193
194#[derive(Debug, Clone, Deserialize, Serialize)]
196#[serde(rename_all = "snake_case")]
197pub struct NearbyChangeMoveConfig {
198 #[serde(default)]
199 pub selection_order: Option<SelectionOrder>,
200 pub selection_metric: Option<String>,
201 pub max_nearby: usize,
202 pub value_candidate_limit: Option<usize>,
203 #[serde(flatten)]
204 pub target: VariableTargetConfig,
205}
206
207impl Default for NearbyChangeMoveConfig {
208 fn default() -> Self {
209 let (max_nearby, target) = default_nearby_target_config();
210 Self {
211 selection_order: None,
212 selection_metric: None,
213 max_nearby,
214 value_candidate_limit: None,
215 target,
216 }
217 }
218}
219
220#[derive(Debug, Clone, Deserialize, Serialize)]
222#[serde(rename_all = "snake_case")]
223pub struct NearbySwapMoveConfig {
224 #[serde(default)]
225 pub selection_order: Option<SelectionOrder>,
226 pub selection_metric: Option<String>,
227 pub max_nearby: usize,
228 #[serde(flatten)]
229 pub target: VariableTargetConfig,
230}
231
232impl Default for NearbySwapMoveConfig {
233 fn default() -> Self {
234 let (max_nearby, target) = default_nearby_target_config();
235 Self {
236 selection_order: None,
237 selection_metric: None,
238 max_nearby,
239 target,
240 }
241 }
242}
243
244#[derive(Debug, Clone, Default, Deserialize, Serialize)]
246#[serde(rename_all = "snake_case")]
247pub struct PillarChangeMoveConfig {
248 #[serde(default)]
249 pub selection_order: Option<SelectionOrder>,
250 pub selection_metric: Option<String>,
251 pub minimum_sub_pillar_size: usize,
252 pub maximum_sub_pillar_size: usize,
253 pub value_candidate_limit: Option<usize>,
254 #[serde(flatten)]
255 pub target: VariableTargetConfig,
256}
257
258#[derive(Debug, Clone, Default, Deserialize, Serialize)]
260#[serde(rename_all = "snake_case")]
261pub struct PillarSwapMoveConfig {
262 #[serde(default)]
263 pub selection_order: Option<SelectionOrder>,
264 pub selection_metric: Option<String>,
265 pub minimum_sub_pillar_size: usize,
266 pub maximum_sub_pillar_size: usize,
267 #[serde(flatten)]
268 pub target: VariableTargetConfig,
269}
270
271#[derive(Debug, Clone, Copy, Default, Deserialize, Serialize, PartialEq, Eq)]
272#[serde(rename_all = "snake_case")]
273pub enum RecreateHeuristicType {
274 #[default]
275 FirstFit,
276 CheapestInsertion,
277}
278
279#[derive(Debug, Clone, Copy, Default, Deserialize, Serialize, PartialEq, Eq)]
280#[serde(rename_all = "snake_case")]
281pub enum UnionSelectionOrder {
282 Sequential,
283 RoundRobin,
284 RotatingRoundRobin,
285 Random,
286 #[default]
287 StratifiedRandom,
288}
289
290#[derive(Debug, Clone, Copy, Default, Deserialize, Serialize, PartialEq, Eq)]
291#[serde(rename_all = "snake_case")]
292pub enum UnionWeighting {
293 #[default]
294 Equal,
295 Fixed,
296 CandidateCount,
297}
298
299#[derive(Debug, Clone, Deserialize, Serialize)]
301#[serde(rename_all = "snake_case")]
302pub struct RuinRecreateMoveSelectorConfig {
303 #[serde(default)]
304 pub selection_order: Option<SelectionOrder>,
305 pub selection_metric: Option<String>,
306 pub min_ruin_count: usize,
307 pub max_ruin_count: usize,
308 pub moves_per_step: Option<usize>,
309 pub value_candidate_limit: Option<usize>,
310 pub recreate_heuristic_type: RecreateHeuristicType,
311 #[serde(flatten)]
312 pub target: VariableTargetConfig,
313}
314
315impl Default for RuinRecreateMoveSelectorConfig {
316 fn default() -> Self {
317 Self {
318 selection_order: None,
319 selection_metric: None,
320 min_ruin_count: 2,
321 max_ruin_count: 5,
322 moves_per_step: None,
323 value_candidate_limit: None,
324 recreate_heuristic_type: RecreateHeuristicType::FirstFit,
325 target: VariableTargetConfig::default(),
326 }
327 }
328}
329
330#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
332#[serde(rename_all = "snake_case")]
333pub struct GroupedScalarMoveSelectorConfig {
334 #[serde(default)]
335 pub selection_order: Option<SelectionOrder>,
336 pub selection_metric: Option<String>,
337 pub group_name: String,
338 pub value_candidate_limit: Option<usize>,
339 pub max_moves_per_step: Option<usize>,
340 #[serde(default)]
341 pub require_hard_improvement: bool,
342}
343
344#[derive(Debug, Clone, Default, Deserialize, Serialize)]
346#[serde(rename_all = "snake_case")]
347pub struct ListChangeMoveConfig {
348 #[serde(default)]
349 pub selection_order: Option<SelectionOrder>,
350 pub selection_metric: Option<String>,
351 #[serde(flatten)]
352 pub target: VariableTargetConfig,
353}
354
355#[derive(Debug, Clone, Deserialize, Serialize)]
357#[serde(rename_all = "snake_case")]
358pub struct NearbyListChangeMoveConfig {
359 #[serde(default)]
360 pub selection_order: Option<SelectionOrder>,
361 pub selection_metric: Option<String>,
362 pub max_nearby: usize,
364 #[serde(flatten)]
365 pub target: VariableTargetConfig,
366}
367
368impl Default for NearbyListChangeMoveConfig {
369 fn default() -> Self {
370 let (max_nearby, target) = default_nearby_target_config();
371 Self {
372 selection_order: None,
373 selection_metric: None,
374 max_nearby,
375 target,
376 }
377 }
378}
379
380#[derive(Debug, Clone, Default, Deserialize, Serialize)]
382#[serde(rename_all = "snake_case")]
383pub struct ListSwapMoveConfig {
384 #[serde(default)]
385 pub selection_order: Option<SelectionOrder>,
386 pub selection_metric: Option<String>,
387 #[serde(flatten)]
388 pub target: VariableTargetConfig,
389}
390
391#[derive(Debug, Clone, Deserialize, Serialize)]
393#[serde(rename_all = "snake_case")]
394pub struct ListPermuteMoveConfig {
395 #[serde(default)]
396 pub selection_order: Option<SelectionOrder>,
397 pub selection_metric: Option<String>,
398 pub min_window_size: usize,
400 pub max_window_size: usize,
402 #[serde(flatten)]
403 pub target: VariableTargetConfig,
404}
405
406impl Default for ListPermuteMoveConfig {
407 fn default() -> Self {
408 Self {
409 selection_order: None,
410 selection_metric: None,
411 min_window_size: 2,
412 max_window_size: 5,
413 target: VariableTargetConfig::default(),
414 }
415 }
416}
417
418#[derive(Debug, Clone, Default, Deserialize, Serialize)]
420#[serde(rename_all = "snake_case")]
421pub struct ListPrecedenceMoveConfig {
422 #[serde(default)]
423 pub selection_order: Option<SelectionOrder>,
424 pub selection_metric: Option<String>,
425 #[serde(flatten)]
426 pub target: VariableTargetConfig,
427}
428
429#[derive(Debug, Clone, Deserialize, Serialize)]
431#[serde(rename_all = "snake_case")]
432pub struct NearbyListSwapMoveConfig {
433 #[serde(default)]
434 pub selection_order: Option<SelectionOrder>,
435 pub selection_metric: Option<String>,
436 pub max_nearby: usize,
438 #[serde(flatten)]
439 pub target: VariableTargetConfig,
440}
441
442impl Default for NearbyListSwapMoveConfig {
443 fn default() -> Self {
444 let (max_nearby, target) = default_nearby_target_config();
445 Self {
446 selection_order: None,
447 selection_metric: None,
448 max_nearby,
449 target,
450 }
451 }
452}
453
454#[derive(Debug, Clone, Deserialize, Serialize)]
456#[serde(rename_all = "snake_case")]
457pub struct SublistChangeMoveConfig {
458 #[serde(default)]
459 pub selection_order: Option<SelectionOrder>,
460 pub selection_metric: Option<String>,
461 pub min_sublist_size: usize,
463 pub max_sublist_size: usize,
465 #[serde(flatten)]
466 pub target: VariableTargetConfig,
467}
468
469impl Default for SublistChangeMoveConfig {
470 fn default() -> Self {
471 let (min_sublist_size, max_sublist_size, target) = default_sublist_target_config();
472 Self {
473 selection_order: None,
474 selection_metric: None,
475 min_sublist_size,
476 max_sublist_size,
477 target,
478 }
479 }
480}
481
482#[derive(Debug, Clone, Deserialize, Serialize)]
484#[serde(rename_all = "snake_case")]
485pub struct SublistSwapMoveConfig {
486 #[serde(default)]
487 pub selection_order: Option<SelectionOrder>,
488 pub selection_metric: Option<String>,
489 pub min_sublist_size: usize,
491 pub max_sublist_size: usize,
493 #[serde(flatten)]
494 pub target: VariableTargetConfig,
495}
496
497impl Default for SublistSwapMoveConfig {
498 fn default() -> Self {
499 let (min_sublist_size, max_sublist_size, target) = default_sublist_target_config();
500 Self {
501 selection_order: None,
502 selection_metric: None,
503 min_sublist_size,
504 max_sublist_size,
505 target,
506 }
507 }
508}
509
510#[derive(Debug, Clone, Default, Deserialize, Serialize)]
512#[serde(rename_all = "snake_case")]
513pub struct ListReverseMoveConfig {
514 #[serde(default)]
515 pub selection_order: Option<SelectionOrder>,
516 pub selection_metric: Option<String>,
517 #[serde(flatten)]
518 pub target: VariableTargetConfig,
519}
520
521#[derive(Debug, Clone, Deserialize, Serialize)]
523#[serde(rename_all = "snake_case")]
524pub struct KOptMoveSelectorConfig {
525 #[serde(default)]
526 pub selection_order: Option<SelectionOrder>,
527 pub selection_metric: Option<String>,
528 pub k: usize,
530 pub min_segment_len: usize,
532 pub max_nearby: usize,
535 #[serde(flatten)]
536 pub target: VariableTargetConfig,
537}
538
539impl Default for KOptMoveSelectorConfig {
540 fn default() -> Self {
541 Self {
542 selection_order: None,
543 selection_metric: None,
544 k: 3,
545 min_segment_len: 1,
546 max_nearby: 0,
547 target: VariableTargetConfig::default(),
548 }
549 }
550}
551
552#[derive(Debug, Clone, Deserialize, Serialize)]
554#[serde(rename_all = "snake_case")]
555pub struct ListRuinMoveSelectorConfig {
556 #[serde(default)]
557 pub selection_order: Option<SelectionOrder>,
558 pub selection_metric: Option<String>,
559 pub min_ruin_count: usize,
561 pub max_ruin_count: usize,
563 pub moves_per_step: Option<usize>,
565 pub max_source_list_len: Option<usize>,
567 #[serde(default)]
569 pub skip_empty_destinations: bool,
570 #[serde(flatten)]
571 pub target: VariableTargetConfig,
572}
573
574impl Default for ListRuinMoveSelectorConfig {
575 fn default() -> Self {
576 Self {
577 selection_order: None,
578 selection_metric: None,
579 min_ruin_count: 2,
580 max_ruin_count: 5,
581 moves_per_step: None,
582 max_source_list_len: None,
583 skip_empty_destinations: false,
584 target: VariableTargetConfig::default(),
585 }
586 }
587}
588
589#[derive(Debug, Clone, Deserialize, Serialize)]
591#[serde(rename_all = "snake_case")]
592pub struct LimitedNeighborhoodConfig {
593 pub selected_count_limit: usize,
595 pub selector: Box<MoveSelectorConfig>,
597}
598
599#[derive(Debug, Clone, Default, Deserialize, Serialize)]
601#[serde(rename_all = "snake_case")]
602pub struct UnionMoveSelectorConfig {
603 #[serde(default)]
604 pub selection_order: UnionSelectionOrder,
605 #[serde(default)]
606 pub weighting: UnionWeighting,
607 #[serde(default)]
608 pub weights: Vec<u64>,
609 pub selectors: Vec<MoveSelectorConfig>,
611}
612
613#[derive(Debug, Clone, Default, Deserialize, Serialize)]
615#[serde(rename_all = "snake_case")]
616pub struct CartesianProductConfig {
617 #[serde(default)]
619 pub require_hard_improvement: bool,
620
621 pub selectors: Vec<MoveSelectorConfig>,
623}
624
625#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
626#[serde(rename_all = "snake_case")]
627pub struct ConflictRepairMoveSelectorConfig {
628 #[serde(default)]
629 pub selection_order: Option<SelectionOrder>,
630 pub selection_metric: Option<String>,
631 pub constraints: Vec<String>,
632 #[serde(default = "default_conflict_repair_max_matches")]
633 pub max_matches_per_step: usize,
634 #[serde(default = "default_conflict_repair_max_repairs")]
635 pub max_repairs_per_match: usize,
636 #[serde(default = "default_conflict_repair_max_moves")]
637 pub max_moves_per_step: usize,
638 #[serde(default)]
639 pub require_hard_improvement: bool,
640 #[serde(default)]
641 pub include_soft_matches: bool,
642}
643
644impl Default for ConflictRepairMoveSelectorConfig {
645 fn default() -> Self {
646 Self {
647 selection_order: None,
648 selection_metric: None,
649 constraints: Vec::new(),
650 max_matches_per_step: default_conflict_repair_max_matches(),
651 max_repairs_per_match: default_conflict_repair_max_repairs(),
652 max_moves_per_step: default_conflict_repair_max_moves(),
653 require_hard_improvement: false,
654 include_soft_matches: false,
655 }
656 }
657}
658
659#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
660#[serde(rename_all = "snake_case")]
661pub struct CompoundConflictRepairMoveSelectorConfig {
662 #[serde(default)]
663 pub selection_order: Option<SelectionOrder>,
664 pub selection_metric: Option<String>,
665 pub constraints: Vec<String>,
666 #[serde(default = "default_conflict_repair_max_matches")]
667 pub max_matches_per_step: usize,
668 #[serde(default = "default_conflict_repair_max_repairs")]
669 pub max_repairs_per_match: usize,
670 #[serde(default = "default_conflict_repair_max_moves")]
671 pub max_moves_per_step: usize,
672 #[serde(default = "default_require_hard_improvement")]
673 pub require_hard_improvement: bool,
674 #[serde(default)]
675 pub include_soft_matches: bool,
676}
677
678impl Default for CompoundConflictRepairMoveSelectorConfig {
679 fn default() -> Self {
680 Self {
681 selection_order: None,
682 selection_metric: None,
683 constraints: Vec::new(),
684 max_matches_per_step: default_conflict_repair_max_matches(),
685 max_repairs_per_match: default_conflict_repair_max_repairs(),
686 max_moves_per_step: default_conflict_repair_max_moves(),
687 require_hard_improvement: default_require_hard_improvement(),
688 include_soft_matches: false,
689 }
690 }
691}
692
693fn default_conflict_repair_max_matches() -> usize {
694 16
695}
696
697fn default_conflict_repair_max_repairs() -> usize {
698 32
699}
700
701fn default_conflict_repair_max_moves() -> usize {
702 256
703}
704
705fn default_require_hard_improvement() -> bool {
706 true
707}
708
709fn default_nearby_target_config() -> (usize, VariableTargetConfig) {
710 (10, VariableTargetConfig::default())
711}
712
713fn default_sublist_target_config() -> (usize, usize, VariableTargetConfig) {
714 (1, 3, VariableTargetConfig::default())
715}