Skip to main content

solverforge_solver/heuristic/move/
list_multi_swap.rs

1//! Public static facade for canonical multi-list-swap mechanics.
2
3use std::borrow::Cow;
4use std::fmt::Debug;
5
6use smallvec::SmallVec;
7use solverforge_core::domain::PlanningSolution;
8use solverforge_scoring::Director;
9
10use super::list_kernel::{
11    multi_swap_do_move, multi_swap_entity_indices, multi_swap_is_doable, multi_swap_tabu_signature,
12    multi_swap_undo_move, MultiSwapCoordinates, StaticListSwapAccess,
13};
14use super::{Move, MoveTabuSignature};
15
16/// Multiple independent intra-list swaps, one per entity.
17///
18/// The public move keeps its historic constructor and score-improvement flag;
19/// shared list-kernel mechanics own doability, notifications, mutation, undo,
20/// and tabu identity for static and runtime carriers alike.
21pub struct ListMultiSwapMove<S, V> {
22    swaps: MultiSwapCoordinates,
23    access: StaticListSwapAccess<S, V>,
24    indices: SmallVec<[usize; 4]>,
25    require_score_improvement: bool,
26}
27
28impl<S, V: Debug> Debug for ListMultiSwapMove<S, V> {
29    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        formatter
31            .debug_struct("ListMultiSwapMove")
32            .field("swaps", &self.swaps)
33            .field("variable_name", &self.access.variable_name)
34            .field("require_score_improvement", &self.require_score_improvement)
35            .finish()
36    }
37}
38
39impl<S, V> ListMultiSwapMove<S, V> {
40    pub fn new(
41        swaps: &[(usize, usize, usize)],
42        list_len: fn(&S, usize) -> usize,
43        list_get: fn(&S, usize, usize) -> Option<V>,
44        list_set: fn(&mut S, usize, usize, V),
45        variable_name: &'static str,
46        descriptor_index: usize,
47    ) -> Self {
48        Self {
49            swaps: swaps.iter().copied().collect(),
50            access: StaticListSwapAccess {
51                list_len,
52                list_get,
53                list_set,
54                variable_name,
55                descriptor_index,
56            },
57            indices: multi_swap_entity_indices(swaps),
58            require_score_improvement: false,
59        }
60    }
61
62    pub fn with_require_score_improvement(mut self, require_score_improvement: bool) -> Self {
63        self.require_score_improvement = require_score_improvement;
64        self
65    }
66
67    pub fn swaps(&self) -> &[(usize, usize, usize)] {
68        &self.swaps
69    }
70}
71
72impl<S, V> Move<S> for ListMultiSwapMove<S, V>
73where
74    S: PlanningSolution,
75    V: Clone + PartialEq + Send + Sync + Debug + 'static,
76{
77    type Undo = ();
78
79    fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
80        multi_swap_is_doable(&self.access, &self.swaps, score_director)
81    }
82
83    fn do_move<D: Director<S>>(&self, score_director: &mut D) -> Self::Undo {
84        multi_swap_do_move(&self.access, &self.swaps, &self.indices, score_director)
85    }
86
87    fn undo_move<D: Director<S>>(&self, score_director: &mut D, (): Self::Undo) {
88        multi_swap_undo_move(&self.access, &self.swaps, &self.indices, score_director);
89    }
90
91    fn descriptor_index(&self) -> usize {
92        self.access.descriptor_index
93    }
94
95    fn entity_indices(&self) -> &[usize] {
96        &self.indices
97    }
98
99    fn variable_name(&self) -> &str {
100        self.access.variable_name
101    }
102
103    fn telemetry_label(&self) -> &'static str {
104        "list_multi_swap"
105    }
106
107    fn requires_score_improvement(&self) -> bool {
108        self.require_score_improvement
109    }
110
111    fn tabu_signature<D: Director<S>>(&self, score_director: &D) -> Cow<'_, MoveTabuSignature> {
112        Cow::Owned(multi_swap_tabu_signature(
113            &self.access,
114            &self.swaps,
115            &self.indices,
116            score_director,
117        ))
118    }
119}