Skip to main content

solverforge_solver/heuristic/move/
list_union.rs

1/* ListMoveUnion - a monomorphized union of all list-variable move types.
2
3This allows local search to combine all list move types in a single arena
4without trait-object dispatch.
5*/
6
7use std::fmt::Debug;
8
9use solverforge_core::domain::PlanningSolution;
10use solverforge_scoring::Director;
11
12use crate::stats::CandidateTraceIdentity;
13
14use super::{
15    KOptMove, ListChangeMove, ListMultiSwapMove, ListPermuteMove, ListReverseMove, ListRuinMove,
16    ListSwapMove, Move, MoveTabuSignature, SublistChangeMove, SublistSwapMove,
17};
18
19/// A monomorphized union of all list-variable move types.
20///
21/// Implements `Move<S>` by delegating to the inner variant.
22/// Enables combining `ListChangeMoveSelector`, `ListSwapMoveSelector`,
23/// `ListPermuteMoveSelector`, `SublistChangeMoveSelector`, `SublistSwapMoveSelector`,
24/// `ListReverseMoveSelector`, `KOptMoveSelector`, and
25/// `ListRuinMoveSelector` without type erasure.
26///
27/// # Example
28///
29/// ```
30/// use solverforge_solver::heuristic::r#move::ListMoveUnion;
31/// ```
32#[allow(clippy::large_enum_variant)]
33pub enum ListMoveUnion<S, V> {
34    ListChange(ListChangeMove<S, V>),
35    ListSwap(ListSwapMove<S, V>),
36    ListMultiSwap(ListMultiSwapMove<S, V>),
37    ListPermute(ListPermuteMove<S, V>),
38    SublistChange(SublistChangeMove<S, V>),
39    SublistSwap(SublistSwapMove<S, V>),
40    ListReverse(ListReverseMove<S, V>),
41    KOpt(KOptMove<S, V>),
42    ListRuin(ListRuinMove<S, V>),
43}
44
45pub enum ListMoveUnionUndo<S, V>
46where
47    S: PlanningSolution,
48    V: Clone + PartialEq + Send + Sync + Debug + 'static,
49{
50    ListChange(<ListChangeMove<S, V> as Move<S>>::Undo),
51    ListSwap(<ListSwapMove<S, V> as Move<S>>::Undo),
52    ListMultiSwap(<ListMultiSwapMove<S, V> as Move<S>>::Undo),
53    ListPermute(<ListPermuteMove<S, V> as Move<S>>::Undo),
54    SublistChange(<SublistChangeMove<S, V> as Move<S>>::Undo),
55    SublistSwap(<SublistSwapMove<S, V> as Move<S>>::Undo),
56    ListReverse(<ListReverseMove<S, V> as Move<S>>::Undo),
57    KOpt(<KOptMove<S, V> as Move<S>>::Undo),
58    ListRuin(<ListRuinMove<S, V> as Move<S>>::Undo),
59}
60
61impl<S, V> Debug for ListMoveUnion<S, V>
62where
63    S: PlanningSolution,
64    V: Clone + PartialEq + Send + Sync + Debug + 'static,
65{
66    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67        match self {
68            Self::ListChange(m) => m.fmt(f),
69            Self::ListSwap(m) => m.fmt(f),
70            Self::ListMultiSwap(m) => m.fmt(f),
71            Self::ListPermute(m) => m.fmt(f),
72            Self::SublistChange(m) => m.fmt(f),
73            Self::SublistSwap(m) => m.fmt(f),
74            Self::ListReverse(m) => m.fmt(f),
75            Self::KOpt(m) => m.fmt(f),
76            Self::ListRuin(m) => m.fmt(f),
77        }
78    }
79}
80
81impl<S, V> Move<S> for ListMoveUnion<S, V>
82where
83    S: PlanningSolution,
84    V: Clone + PartialEq + Send + Sync + Debug + 'static,
85{
86    type Undo = ListMoveUnionUndo<S, V>;
87
88    fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
89        match self {
90            Self::ListChange(m) => m.is_doable(score_director),
91            Self::ListSwap(m) => m.is_doable(score_director),
92            Self::ListMultiSwap(m) => m.is_doable(score_director),
93            Self::ListPermute(m) => m.is_doable(score_director),
94            Self::SublistChange(m) => m.is_doable(score_director),
95            Self::SublistSwap(m) => m.is_doable(score_director),
96            Self::ListReverse(m) => m.is_doable(score_director),
97            Self::KOpt(m) => m.is_doable(score_director),
98            Self::ListRuin(m) => m.is_doable(score_director),
99        }
100    }
101
102    fn do_move<D: Director<S>>(&self, score_director: &mut D) -> Self::Undo {
103        match self {
104            Self::ListChange(m) => {
105                m.do_move(score_director);
106                ListMoveUnionUndo::ListChange(())
107            }
108            Self::ListSwap(m) => {
109                m.do_move(score_director);
110                ListMoveUnionUndo::ListSwap(())
111            }
112            Self::ListMultiSwap(m) => {
113                m.do_move(score_director);
114                ListMoveUnionUndo::ListMultiSwap(())
115            }
116            Self::ListPermute(m) => ListMoveUnionUndo::ListPermute(m.do_move(score_director)),
117            Self::SublistChange(m) => {
118                m.do_move(score_director);
119                ListMoveUnionUndo::SublistChange(())
120            }
121            Self::SublistSwap(m) => {
122                m.do_move(score_director);
123                ListMoveUnionUndo::SublistSwap(())
124            }
125            Self::ListReverse(m) => {
126                m.do_move(score_director);
127                ListMoveUnionUndo::ListReverse(())
128            }
129            Self::KOpt(m) => ListMoveUnionUndo::KOpt(m.do_move(score_director)),
130            Self::ListRuin(m) => ListMoveUnionUndo::ListRuin(m.do_move(score_director)),
131        }
132    }
133
134    fn do_move_with_scratch<D: Director<S>>(
135        &self,
136        score_director: &mut D,
137        journal_pool: &mut Vec<Self::Undo>,
138    ) -> Self::Undo {
139        match self {
140            Self::KOpt(m) => {
141                // The outer pool stores union undos; a K-opt-shaped entry
142                // carries an inner journal whose inlined capacity this arm
143                // reuses.
144                let mut journal = journal_pool
145                    .iter()
146                    .rposition(|undo| matches!(undo, ListMoveUnionUndo::KOpt(_)))
147                    .map(|index| match journal_pool.swap_remove(index) {
148                        ListMoveUnionUndo::KOpt(journal) => journal,
149                        _ => unreachable!("rposition matched the KOpt shape"),
150                    })
151                    .unwrap_or_default();
152                m.do_move_into_journal(score_director, &mut journal);
153                ListMoveUnionUndo::KOpt(journal)
154            }
155            other => other.do_move(score_director),
156        }
157    }
158
159    fn undo_move_with_scratch<D: Director<S>>(
160        &self,
161        score_director: &mut D,
162        undo: Self::Undo,
163        journal_pool: &mut Vec<Self::Undo>,
164    ) {
165        match (self, undo) {
166            (Self::KOpt(m), ListMoveUnionUndo::KOpt(undo)) => {
167                m.undo_move(score_director, undo);
168                journal_pool.push(ListMoveUnionUndo::KOpt(
169                    <KOptMove<S, V> as Move<S>>::Undo::new(),
170                ));
171            }
172            (other, undo) => other.undo_move(score_director, undo),
173        }
174    }
175
176    fn undo_move<D: Director<S>>(&self, score_director: &mut D, undo: Self::Undo) {
177        match (self, undo) {
178            (Self::ListChange(m), ListMoveUnionUndo::ListChange(undo)) => {
179                m.undo_move(score_director, undo)
180            }
181            (Self::ListSwap(m), ListMoveUnionUndo::ListSwap(undo)) => {
182                m.undo_move(score_director, undo)
183            }
184            (Self::ListMultiSwap(m), ListMoveUnionUndo::ListMultiSwap(undo)) => {
185                m.undo_move(score_director, undo)
186            }
187            (Self::ListPermute(m), ListMoveUnionUndo::ListPermute(undo)) => {
188                m.undo_move(score_director, undo)
189            }
190            (Self::SublistChange(m), ListMoveUnionUndo::SublistChange(undo)) => {
191                m.undo_move(score_director, undo)
192            }
193            (Self::SublistSwap(m), ListMoveUnionUndo::SublistSwap(undo)) => {
194                m.undo_move(score_director, undo)
195            }
196            (Self::ListReverse(m), ListMoveUnionUndo::ListReverse(undo)) => {
197                m.undo_move(score_director, undo)
198            }
199            (Self::KOpt(m), ListMoveUnionUndo::KOpt(undo)) => m.undo_move(score_director, undo),
200            (Self::ListRuin(m), ListMoveUnionUndo::ListRuin(undo)) => {
201                m.undo_move(score_director, undo)
202            }
203            _ => panic!("list move undo shape must match move shape"),
204        }
205    }
206
207    fn descriptor_index(&self) -> usize {
208        match self {
209            Self::ListChange(m) => m.descriptor_index(),
210            Self::ListSwap(m) => m.descriptor_index(),
211            Self::ListMultiSwap(m) => m.descriptor_index(),
212            Self::ListPermute(m) => m.descriptor_index(),
213            Self::SublistChange(m) => m.descriptor_index(),
214            Self::SublistSwap(m) => m.descriptor_index(),
215            Self::ListReverse(m) => m.descriptor_index(),
216            Self::KOpt(m) => m.descriptor_index(),
217            Self::ListRuin(m) => m.descriptor_index(),
218        }
219    }
220
221    fn entity_indices(&self) -> &[usize] {
222        match self {
223            Self::ListChange(m) => m.entity_indices(),
224            Self::ListSwap(m) => m.entity_indices(),
225            Self::ListMultiSwap(m) => m.entity_indices(),
226            Self::ListPermute(m) => m.entity_indices(),
227            Self::SublistChange(m) => m.entity_indices(),
228            Self::SublistSwap(m) => m.entity_indices(),
229            Self::ListReverse(m) => m.entity_indices(),
230            Self::KOpt(m) => m.entity_indices(),
231            Self::ListRuin(m) => m.entity_indices(),
232        }
233    }
234
235    fn variable_name(&self) -> &str {
236        match self {
237            Self::ListChange(m) => m.variable_name(),
238            Self::ListSwap(m) => m.variable_name(),
239            Self::ListMultiSwap(m) => m.variable_name(),
240            Self::ListPermute(m) => m.variable_name(),
241            Self::SublistChange(m) => m.variable_name(),
242            Self::SublistSwap(m) => m.variable_name(),
243            Self::ListReverse(m) => m.variable_name(),
244            Self::KOpt(m) => m.variable_name(),
245            Self::ListRuin(m) => m.variable_name(),
246        }
247    }
248
249    fn telemetry_label(&self) -> &'static str {
250        match self {
251            Self::ListChange(m) => m.telemetry_label(),
252            Self::ListSwap(m) => m.telemetry_label(),
253            Self::ListMultiSwap(m) => m.telemetry_label(),
254            Self::ListPermute(m) => m.telemetry_label(),
255            Self::SublistChange(m) => m.telemetry_label(),
256            Self::SublistSwap(m) => m.telemetry_label(),
257            Self::ListReverse(m) => m.telemetry_label(),
258            Self::KOpt(m) => m.telemetry_label(),
259            Self::ListRuin(m) => m.telemetry_label(),
260        }
261    }
262
263    fn requires_hard_improvement(&self) -> bool {
264        match self {
265            Self::ListChange(m) => m.requires_hard_improvement(),
266            Self::ListSwap(m) => m.requires_hard_improvement(),
267            Self::ListMultiSwap(m) => m.requires_hard_improvement(),
268            Self::ListPermute(m) => m.requires_hard_improvement(),
269            Self::SublistChange(m) => m.requires_hard_improvement(),
270            Self::SublistSwap(m) => m.requires_hard_improvement(),
271            Self::ListReverse(m) => m.requires_hard_improvement(),
272            Self::KOpt(m) => m.requires_hard_improvement(),
273            Self::ListRuin(m) => m.requires_hard_improvement(),
274        }
275    }
276
277    fn requires_score_improvement(&self) -> bool {
278        match self {
279            Self::ListChange(m) => m.requires_score_improvement(),
280            Self::ListSwap(m) => m.requires_score_improvement(),
281            Self::ListMultiSwap(m) => m.requires_score_improvement(),
282            Self::ListPermute(m) => m.requires_score_improvement(),
283            Self::SublistChange(m) => m.requires_score_improvement(),
284            Self::SublistSwap(m) => m.requires_score_improvement(),
285            Self::ListReverse(m) => m.requires_score_improvement(),
286            Self::KOpt(m) => m.requires_score_improvement(),
287            Self::ListRuin(m) => m.requires_score_improvement(),
288        }
289    }
290
291    fn borrowed_tabu_signature(&self) -> Option<&MoveTabuSignature> {
292        match self {
293            Self::ListChange(m) => m.borrowed_tabu_signature(),
294            Self::ListSwap(m) => m.borrowed_tabu_signature(),
295            Self::ListMultiSwap(m) => m.borrowed_tabu_signature(),
296            Self::ListPermute(m) => m.borrowed_tabu_signature(),
297            Self::SublistChange(m) => m.borrowed_tabu_signature(),
298            Self::SublistSwap(m) => m.borrowed_tabu_signature(),
299            Self::ListReverse(m) => m.borrowed_tabu_signature(),
300            Self::KOpt(m) => m.borrowed_tabu_signature(),
301            Self::ListRuin(m) => m.borrowed_tabu_signature(),
302        }
303    }
304
305    fn tabu_signature<D: Director<S>>(&self, score_director: &D) -> MoveTabuSignature {
306        match self {
307            Self::ListChange(m) => m.tabu_signature(score_director),
308            Self::ListSwap(m) => m.tabu_signature(score_director),
309            Self::ListMultiSwap(m) => m.tabu_signature(score_director),
310            Self::ListPermute(m) => m.tabu_signature(score_director),
311            Self::SublistChange(m) => m.tabu_signature(score_director),
312            Self::SublistSwap(m) => m.tabu_signature(score_director),
313            Self::ListReverse(m) => m.tabu_signature(score_director),
314            Self::KOpt(m) => m.tabu_signature(score_director),
315            Self::ListRuin(m) => m.tabu_signature(score_director),
316        }
317    }
318
319    fn candidate_trace_identity(&self) -> Option<CandidateTraceIdentity> {
320        match self {
321            Self::ListChange(m) => m.candidate_trace_identity(),
322            Self::ListSwap(m) => m.candidate_trace_identity(),
323            Self::ListMultiSwap(m) => m.candidate_trace_identity(),
324            Self::ListPermute(m) => m.candidate_trace_identity(),
325            Self::SublistChange(m) => m.candidate_trace_identity(),
326            Self::SublistSwap(m) => m.candidate_trace_identity(),
327            Self::ListReverse(m) => m.candidate_trace_identity(),
328            Self::KOpt(m) => m.candidate_trace_identity(),
329            Self::ListRuin(m) => m.candidate_trace_identity(),
330        }
331    }
332}