solverforge_solver/heuristic/move/
pillar_swap.rs1use std::fmt::Debug;
13
14use smallvec::SmallVec;
15use solverforge_core::domain::PlanningSolution;
16use solverforge_scoring::Director;
17
18use super::metadata::{
19 append_canonical_usize_slice_pair, encode_option_debug, encode_usize, scoped_move_identity,
20 MoveTabuScope, ScopedEntityTabuToken, TABU_OP_PILLAR_SWAP,
21};
22use super::{Move, MoveAffectedEntity, MoveTabuSignature};
23
24pub struct PillarSwapMove<S, V> {
33 left_indices: Vec<usize>,
34 right_indices: Vec<usize>,
35 descriptor_index: usize,
36 variable_name: &'static str,
37 getter: fn(&S, usize, usize) -> Option<V>,
39 setter: fn(&mut S, usize, usize, Option<V>),
41 variable_index: usize,
42}
43
44impl<S, V: Debug> Debug for PillarSwapMove<S, V> {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 f.debug_struct("PillarSwapMove")
47 .field("left_indices", &self.left_indices)
48 .field("right_indices", &self.right_indices)
49 .field("descriptor_index", &self.descriptor_index)
50 .field("variable_index", &self.variable_index)
51 .field("variable_name", &self.variable_name)
52 .finish()
53 }
54}
55
56impl<S, V> PillarSwapMove<S, V> {
57 pub fn new(
67 left_indices: Vec<usize>,
68 right_indices: Vec<usize>,
69 getter: fn(&S, usize, usize) -> Option<V>,
70 setter: fn(&mut S, usize, usize, Option<V>),
71 variable_index: usize,
72 variable_name: &'static str,
73 descriptor_index: usize,
74 ) -> Self {
75 Self {
76 left_indices,
77 right_indices,
78 descriptor_index,
79 variable_name,
80 getter,
81 setter,
82 variable_index,
83 }
84 }
85
86 pub fn left_indices(&self) -> &[usize] {
87 &self.left_indices
88 }
89
90 pub fn right_indices(&self) -> &[usize] {
91 &self.right_indices
92 }
93}
94
95impl<S, V> Move<S> for PillarSwapMove<S, V>
96where
97 S: PlanningSolution,
98 V: Clone + PartialEq + Send + Sync + Debug + 'static,
99{
100 type Undo = (Vec<(usize, Option<V>)>, Vec<(usize, Option<V>)>);
101
102 fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
103 if self.left_indices.is_empty() || self.right_indices.is_empty() {
104 return false;
105 }
106
107 let count = score_director.entity_count(self.descriptor_index);
108 let max = count.unwrap_or(0);
109
110 for &idx in self.left_indices.iter().chain(&self.right_indices) {
112 if idx >= max {
113 return false;
114 }
115 }
116
117 let left_val = self
119 .left_indices
120 .first()
121 .map(|&idx| (self.getter)(score_director.working_solution(), idx, self.variable_index));
122 let right_val = self
123 .right_indices
124 .first()
125 .map(|&idx| (self.getter)(score_director.working_solution(), idx, self.variable_index));
126
127 left_val != right_val
128 }
129
130 fn do_move<D: Director<S>>(&self, score_director: &mut D) -> Self::Undo {
131 let left_old: Vec<(usize, Option<V>)> = self
133 .left_indices
134 .iter()
135 .map(|&idx| {
136 (
137 idx,
138 (self.getter)(score_director.working_solution(), idx, self.variable_index),
139 )
140 })
141 .collect();
142 let right_old: Vec<(usize, Option<V>)> = self
143 .right_indices
144 .iter()
145 .map(|&idx| {
146 (
147 idx,
148 (self.getter)(score_director.working_solution(), idx, self.variable_index),
149 )
150 })
151 .collect();
152
153 let left_value = left_old.first().and_then(|(_, v)| v.clone());
155 let right_value = right_old.first().and_then(|(_, v)| v.clone());
156
157 for &idx in self.left_indices.iter().chain(&self.right_indices) {
159 score_director.before_variable_changed(self.descriptor_index, idx);
160 }
161
162 for &idx in &self.left_indices {
164 (self.setter)(
165 score_director.working_solution_mut(),
166 idx,
167 self.variable_index,
168 right_value.clone(),
169 );
170 }
171 for &idx in &self.right_indices {
173 (self.setter)(
174 score_director.working_solution_mut(),
175 idx,
176 self.variable_index,
177 left_value.clone(),
178 );
179 }
180
181 for &idx in self.left_indices.iter().chain(&self.right_indices) {
183 score_director.after_variable_changed(self.descriptor_index, idx);
184 }
185
186 (left_old, right_old)
187 }
188
189 fn undo_move<D: Director<S>>(&self, score_director: &mut D, undo: Self::Undo) {
190 for (idx, _) in undo.0.iter().chain(&undo.1) {
191 score_director.before_variable_changed(self.descriptor_index, *idx);
192 }
193 for (idx, old_value) in undo.0.into_iter().chain(undo.1) {
194 (self.setter)(
195 score_director.working_solution_mut(),
196 idx,
197 self.variable_index,
198 old_value,
199 );
200 }
201 for &idx in self.left_indices.iter().chain(&self.right_indices) {
202 score_director.after_variable_changed(self.descriptor_index, idx);
203 }
204 }
205
206 fn descriptor_index(&self) -> usize {
207 self.descriptor_index
208 }
209
210 fn entity_indices(&self) -> &[usize] {
211 &self.left_indices
213 }
214
215 fn variable_name(&self) -> &str {
216 self.variable_name
217 }
218
219 fn for_each_affected_entity<W: FnMut(MoveAffectedEntity<'_>)>(&self, visitor: &mut W) {
223 for &entity_index in self.left_indices.iter().chain(&self.right_indices) {
224 visitor(MoveAffectedEntity {
225 descriptor_index: self.descriptor_index,
226 entity_index,
227 variable_name: self.variable_name,
228 });
229 }
230 }
231
232 fn tabu_signature<D: Director<S>>(&self, score_director: &D) -> MoveTabuSignature {
233 let left_value = self.left_indices.first().and_then(|&idx| {
234 (self.getter)(score_director.working_solution(), idx, self.variable_index)
235 });
236 let right_value = self.right_indices.first().and_then(|&idx| {
237 (self.getter)(score_director.working_solution(), idx, self.variable_index)
238 });
239 let left_id = encode_option_debug(left_value.as_ref());
240 let right_id = encode_option_debug(right_value.as_ref());
241 let scope = MoveTabuScope::new(self.descriptor_index, self.variable_name);
242 let mut entity_ids: SmallVec<[u64; 2]> = self
243 .left_indices
244 .iter()
245 .chain(&self.right_indices)
246 .map(|&idx| encode_usize(idx))
247 .collect();
248 entity_ids.sort_unstable();
249 entity_ids.dedup();
250 let entity_tokens: SmallVec<[ScopedEntityTabuToken; 2]> = entity_ids
251 .iter()
252 .copied()
253 .map(|entity_id| scope.entity_token(entity_id))
254 .collect();
255
256 let mut move_id = scoped_move_identity(scope, TABU_OP_PILLAR_SWAP, std::iter::empty());
257 append_canonical_usize_slice_pair(&mut move_id, &self.left_indices, &self.right_indices);
258
259 MoveTabuSignature::new_self_undoing(scope, move_id)
260 .with_entity_tokens(entity_tokens)
261 .with_destination_value_tokens([
262 scope.value_token(right_id),
263 scope.value_token(left_id),
264 ])
265 }
266}