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, 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: Clone> Clone for PillarSwapMove<S, V> {
45 fn clone(&self) -> Self {
46 Self {
47 left_indices: self.left_indices.clone(),
48 right_indices: self.right_indices.clone(),
49 descriptor_index: self.descriptor_index,
50 variable_name: self.variable_name,
51 getter: self.getter,
52 setter: self.setter,
53 variable_index: self.variable_index,
54 }
55 }
56}
57
58impl<S, V: Debug> Debug for PillarSwapMove<S, V> {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 f.debug_struct("PillarSwapMove")
61 .field("left_indices", &self.left_indices)
62 .field("right_indices", &self.right_indices)
63 .field("descriptor_index", &self.descriptor_index)
64 .field("variable_index", &self.variable_index)
65 .field("variable_name", &self.variable_name)
66 .finish()
67 }
68}
69
70impl<S, V> PillarSwapMove<S, V> {
71 pub fn new(
81 left_indices: Vec<usize>,
82 right_indices: Vec<usize>,
83 getter: fn(&S, usize, usize) -> Option<V>,
84 setter: fn(&mut S, usize, usize, Option<V>),
85 variable_index: usize,
86 variable_name: &'static str,
87 descriptor_index: usize,
88 ) -> Self {
89 Self {
90 left_indices,
91 right_indices,
92 descriptor_index,
93 variable_name,
94 getter,
95 setter,
96 variable_index,
97 }
98 }
99
100 pub fn left_indices(&self) -> &[usize] {
101 &self.left_indices
102 }
103
104 pub fn right_indices(&self) -> &[usize] {
105 &self.right_indices
106 }
107}
108
109impl<S, V> Move<S> for PillarSwapMove<S, V>
110where
111 S: PlanningSolution,
112 V: Clone + PartialEq + Send + Sync + Debug + 'static,
113{
114 type Undo = (Vec<(usize, Option<V>)>, Vec<(usize, Option<V>)>);
115
116 fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
117 if self.left_indices.is_empty() || self.right_indices.is_empty() {
118 return false;
119 }
120
121 let count = score_director.entity_count(self.descriptor_index);
122 let max = count.unwrap_or(0);
123
124 for &idx in self.left_indices.iter().chain(&self.right_indices) {
126 if idx >= max {
127 return false;
128 }
129 }
130
131 let left_val = self
133 .left_indices
134 .first()
135 .map(|&idx| (self.getter)(score_director.working_solution(), idx, self.variable_index));
136 let right_val = self
137 .right_indices
138 .first()
139 .map(|&idx| (self.getter)(score_director.working_solution(), idx, self.variable_index));
140
141 left_val != right_val
142 }
143
144 fn do_move<D: Director<S>>(&self, score_director: &mut D) -> Self::Undo {
145 let left_old: Vec<(usize, Option<V>)> = self
147 .left_indices
148 .iter()
149 .map(|&idx| {
150 (
151 idx,
152 (self.getter)(score_director.working_solution(), idx, self.variable_index),
153 )
154 })
155 .collect();
156 let right_old: Vec<(usize, Option<V>)> = self
157 .right_indices
158 .iter()
159 .map(|&idx| {
160 (
161 idx,
162 (self.getter)(score_director.working_solution(), idx, self.variable_index),
163 )
164 })
165 .collect();
166
167 let left_value = left_old.first().and_then(|(_, v)| v.clone());
169 let right_value = right_old.first().and_then(|(_, v)| v.clone());
170
171 for &idx in self.left_indices.iter().chain(&self.right_indices) {
173 score_director.before_variable_changed(self.descriptor_index, idx);
174 }
175
176 for &idx in &self.left_indices {
178 (self.setter)(
179 score_director.working_solution_mut(),
180 idx,
181 self.variable_index,
182 right_value.clone(),
183 );
184 }
185 for &idx in &self.right_indices {
187 (self.setter)(
188 score_director.working_solution_mut(),
189 idx,
190 self.variable_index,
191 left_value.clone(),
192 );
193 }
194
195 for &idx in self.left_indices.iter().chain(&self.right_indices) {
197 score_director.after_variable_changed(self.descriptor_index, idx);
198 }
199
200 (left_old, right_old)
201 }
202
203 fn undo_move<D: Director<S>>(&self, score_director: &mut D, undo: Self::Undo) {
204 for (idx, _) in undo.0.iter().chain(&undo.1) {
205 score_director.before_variable_changed(self.descriptor_index, *idx);
206 }
207 for (idx, old_value) in undo.0.into_iter().chain(undo.1) {
208 (self.setter)(
209 score_director.working_solution_mut(),
210 idx,
211 self.variable_index,
212 old_value,
213 );
214 }
215 for &idx in self.left_indices.iter().chain(&self.right_indices) {
216 score_director.after_variable_changed(self.descriptor_index, idx);
217 }
218 }
219
220 fn descriptor_index(&self) -> usize {
221 self.descriptor_index
222 }
223
224 fn entity_indices(&self) -> &[usize] {
225 &self.left_indices
227 }
228
229 fn variable_name(&self) -> &str {
230 self.variable_name
231 }
232
233 fn tabu_signature<D: Director<S>>(&self, score_director: &D) -> MoveTabuSignature {
234 let left_value = self.left_indices.first().and_then(|&idx| {
235 (self.getter)(score_director.working_solution(), idx, self.variable_index)
236 });
237 let right_value = self.right_indices.first().and_then(|&idx| {
238 (self.getter)(score_director.working_solution(), idx, self.variable_index)
239 });
240 let left_id = encode_option_debug(left_value.as_ref());
241 let right_id = encode_option_debug(right_value.as_ref());
242 let scope = MoveTabuScope::new(self.descriptor_index, self.variable_name);
243 let mut entity_ids: SmallVec<[u64; 2]> = self
244 .left_indices
245 .iter()
246 .chain(&self.right_indices)
247 .map(|&idx| encode_usize(idx))
248 .collect();
249 entity_ids.sort_unstable();
250 entity_ids.dedup();
251 let entity_tokens: SmallVec<[ScopedEntityTabuToken; 2]> = entity_ids
252 .iter()
253 .copied()
254 .map(|entity_id| scope.entity_token(entity_id))
255 .collect();
256
257 let mut move_id = scoped_move_identity(scope, TABU_OP_PILLAR_SWAP, std::iter::empty());
258 append_canonical_usize_slice_pair(&mut move_id, &self.left_indices, &self.right_indices);
259
260 MoveTabuSignature::new(scope, move_id.clone(), move_id)
261 .with_entity_tokens(entity_tokens)
262 .with_destination_value_tokens([
263 scope.value_token(right_id),
264 scope.value_token(left_id),
265 ])
266 }
267}