Skip to main content

solverforge_config/
move_selector.rs

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// Move selector configuration.
32#[derive(Debug, Clone, Deserialize, Serialize)]
33#[serde(tag = "type", rename_all = "snake_case")]
34pub enum MoveSelectorConfig {
35    // Change move selector (scalar variables).
36    ChangeMoveSelector(ChangeMoveConfig),
37
38    // Swap move selector (scalar variables).
39    SwapMoveSelector(SwapMoveConfig),
40
41    // Nearby change move selector (scalar variables).
42    NearbyChangeMoveSelector(NearbyChangeMoveConfig),
43
44    // Nearby swap move selector (scalar variables).
45    NearbySwapMoveSelector(NearbySwapMoveConfig),
46
47    // Pillar change move selector (scalar variables).
48    PillarChangeMoveSelector(PillarChangeMoveConfig),
49
50    // Pillar swap move selector (scalar variables).
51    PillarSwapMoveSelector(PillarSwapMoveConfig),
52
53    // Ruin-and-recreate move selector (scalar variables).
54    RuinRecreateMoveSelector(RuinRecreateMoveSelectorConfig),
55
56    // Atomic grouped scalar move selector.
57    GroupedScalarMoveSelector(GroupedScalarMoveSelectorConfig),
58
59    // List change move selector — relocates single elements within/between routes.
60    ListChangeMoveSelector(ListChangeMoveConfig),
61
62    // Nearby list change move selector — distance-pruned element relocation.
63    NearbyListChangeMoveSelector(NearbyListChangeMoveConfig),
64
65    // List swap move selector — swaps single elements within/between routes.
66    ListSwapMoveSelector(ListSwapMoveConfig),
67
68    // List permute move selector — permutes contiguous windows within routes.
69    ListPermuteMoveSelector(ListPermuteMoveConfig),
70
71    // List precedence move selector — prioritizes critical list arcs in precedence makespan models.
72    ListPrecedenceMoveSelector(ListPrecedenceMoveConfig),
73
74    // Nearby list swap move selector — distance-pruned element swap.
75    NearbyListSwapMoveSelector(NearbyListSwapMoveConfig),
76
77    // Sublist change move selector (Or-opt) — relocates contiguous segments.
78    SublistChangeMoveSelector(SublistChangeMoveConfig),
79
80    // Sublist swap move selector — swaps contiguous segments between routes.
81    SublistSwapMoveSelector(SublistSwapMoveConfig),
82
83    // List reverse move selector (2-opt) — reverses segments within a route.
84    ListReverseMoveSelector(ListReverseMoveConfig),
85
86    // K-opt move selector — generalised route reconnection.
87    KOptMoveSelector(KOptMoveSelectorConfig),
88
89    // List ruin move selector (LNS) — removes elements for reinsertion.
90    ListRuinMoveSelector(ListRuinMoveSelectorConfig),
91
92    // Neighborhood that limits the number of yielded candidates from a child selector while
93    // preserving selector order.
94    LimitedNeighborhood(LimitedNeighborhoodConfig),
95
96    // Union of multiple selectors.
97    UnionMoveSelector(UnionMoveSelectorConfig),
98
99    // Cartesian product of selectors. Evaluates the right child on the left preview state,
100    // composes tabu ids in selector order, and rejects left children that require full score
101    // evaluation during preview.
102    CartesianProductMoveSelector(CartesianProductConfig),
103
104    // Conflict-directed scalar repair selector.
105    ConflictRepairMoveSelector(ConflictRepairMoveSelectorConfig),
106
107    // Conflict-directed compound scalar repair selector with framework-enforced hard improvement.
108    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// Change move configuration.
172#[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// Swap move configuration.
184#[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// Nearby change move configuration.
195#[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// Nearby swap move configuration.
221#[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// Pillar change move configuration.
245#[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// Pillar swap move configuration.
259#[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// Ruin-and-recreate move configuration.
300#[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// Configuration for `GroupedScalarMoveSelector`.
331#[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// Configuration for `ListChangeMoveSelector`.
345#[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// Configuration for `NearbyListChangeMoveSelector`.
356#[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    // Maximum nearby destination positions to consider per source element.
363    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// Configuration for `ListSwapMoveSelector`.
381#[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// Configuration for `ListPermuteMoveSelector`.
392#[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    // Minimum window size (inclusive). Default: 2.
399    pub min_window_size: usize,
400    // Maximum window size (inclusive). Default: 5.
401    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// Configuration for `ListPrecedenceMoveSelector`.
419#[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// Configuration for `NearbyListSwapMoveSelector`.
430#[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    // Maximum nearby swap partners to consider per source element.
437    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// Configuration for `SublistChangeMoveSelector` (Or-opt).
455#[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    // Minimum segment size (inclusive). Default: 1.
462    pub min_sublist_size: usize,
463    // Maximum segment size (inclusive). Default: 3.
464    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// Configuration for `SublistSwapMoveSelector`.
483#[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    // Minimum segment size (inclusive). Default: 1.
490    pub min_sublist_size: usize,
491    // Maximum segment size (inclusive). Default: 3.
492    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// Configuration for `ListReverseMoveSelector` (2-opt).
511#[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// Configuration for `KOptMoveSelector`.
522#[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    // K value (number of cuts). Default: 3.
529    pub k: usize,
530    // Minimum segment length between cuts. Default: 1.
531    pub min_segment_len: usize,
532    // Maximum nearby positions to consider per cut. Default: 0 (full enumeration).
533    // When > 0, uses distance-pruned NearbyKOptMoveSelector instead of full KOptMoveSelector.
534    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// Configuration for `ListRuinMoveSelector` (LNS).
553#[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    // Minimum number of elements to ruin per move. Default: 2.
560    pub min_ruin_count: usize,
561    // Maximum number of elements to ruin per move. Default: 5.
562    pub max_ruin_count: usize,
563    // Number of ruin moves to generate per step. Default: 10.
564    pub moves_per_step: Option<usize>,
565    // Optional maximum source list length eligible for this selector.
566    pub max_source_list_len: Option<usize>,
567    // Whether recreate should skip currently empty destination lists.
568    #[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// Configuration for `LimitedNeighborhood`.
590#[derive(Debug, Clone, Deserialize, Serialize)]
591#[serde(rename_all = "snake_case")]
592pub struct LimitedNeighborhoodConfig {
593    // Maximum number of moves yielded from the child selector.
594    pub selected_count_limit: usize,
595    // Child selector to wrap.
596    pub selector: Box<MoveSelectorConfig>,
597}
598
599// Union move selector configuration.
600#[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    // Child selectors.
610    pub selectors: Vec<MoveSelectorConfig>,
611}
612
613// Cartesian product move selector configuration.
614#[derive(Debug, Clone, Default, Deserialize, Serialize)]
615#[serde(rename_all = "snake_case")]
616pub struct CartesianProductConfig {
617    // When true, search phases reject composed candidates unless the hard score improves.
618    #[serde(default)]
619    pub require_hard_improvement: bool,
620
621    // Child selectors.
622    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}