solverforge_solver/heuristic/move/
swap.rs1use std::fmt::Debug;
13
14use solverforge_core::domain::PlanningSolution;
15use solverforge_scoring::Director;
16
17use crate::stats::CandidateTraceIdentity;
18
19use super::metadata::{
20 encode_option_debug, encode_usize, ordered_coordinate_pair, scoped_move_identity,
21 MoveTabuScope, TABU_OP_SWAP,
22};
23use super::{Move, MoveTabuSignature};
24
25pub struct SwapMove<S, V> {
57 left_entity_index: usize,
58 right_entity_index: usize,
59 getter: fn(&S, usize, usize) -> Option<V>,
61 setter: fn(&mut S, usize, usize, Option<V>),
63 variable_index: usize,
64 variable_name: &'static str,
65 descriptor_index: usize,
66 indices: [usize; 2],
68}
69
70impl<S, V> Clone for SwapMove<S, V> {
71 fn clone(&self) -> Self {
72 *self
73 }
74}
75
76impl<S, V> Copy for SwapMove<S, V> {}
77
78impl<S, V: Debug> Debug for SwapMove<S, V> {
79 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80 f.debug_struct("SwapMove")
81 .field("left_entity_index", &self.left_entity_index)
82 .field("right_entity_index", &self.right_entity_index)
83 .field("descriptor_index", &self.descriptor_index)
84 .field("variable_index", &self.variable_index)
85 .field("variable_name", &self.variable_name)
86 .finish()
87 }
88}
89
90impl<S, V> SwapMove<S, V> {
91 pub fn new(
101 left_entity_index: usize,
102 right_entity_index: usize,
103 getter: fn(&S, usize, usize) -> Option<V>,
104 setter: fn(&mut S, usize, usize, Option<V>),
105 variable_index: usize,
106 variable_name: &'static str,
107 descriptor_index: usize,
108 ) -> Self {
109 Self {
110 left_entity_index,
111 right_entity_index,
112 getter,
113 setter,
114 variable_index,
115 variable_name,
116 descriptor_index,
117 indices: [left_entity_index, right_entity_index],
118 }
119 }
120
121 pub fn left_entity_index(&self) -> usize {
122 self.left_entity_index
123 }
124
125 pub fn right_entity_index(&self) -> usize {
126 self.right_entity_index
127 }
128}
129
130impl<S, V> Move<S> for SwapMove<S, V>
131where
132 S: PlanningSolution,
133 V: Clone + PartialEq + Send + Sync + Debug + 'static,
134{
135 type Undo = (Option<V>, Option<V>);
136
137 fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
138 if self.left_entity_index == self.right_entity_index {
140 return false;
141 }
142
143 let left_val = (self.getter)(
145 score_director.working_solution(),
146 self.left_entity_index,
147 self.variable_index,
148 );
149 let right_val = (self.getter)(
150 score_director.working_solution(),
151 self.right_entity_index,
152 self.variable_index,
153 );
154
155 left_val != right_val
157 }
158
159 fn do_move<D: Director<S>>(&self, score_director: &mut D) -> Self::Undo {
160 let left_value = (self.getter)(
162 score_director.working_solution(),
163 self.left_entity_index,
164 self.variable_index,
165 );
166 let right_value = (self.getter)(
167 score_director.working_solution(),
168 self.right_entity_index,
169 self.variable_index,
170 );
171
172 score_director.before_variable_changed(self.descriptor_index, self.left_entity_index);
174 score_director.before_variable_changed(self.descriptor_index, self.right_entity_index);
175
176 (self.setter)(
178 score_director.working_solution_mut(),
179 self.left_entity_index,
180 self.variable_index,
181 right_value.clone(),
182 );
183 (self.setter)(
184 score_director.working_solution_mut(),
185 self.right_entity_index,
186 self.variable_index,
187 left_value.clone(),
188 );
189
190 score_director.after_variable_changed(self.descriptor_index, self.left_entity_index);
192 score_director.after_variable_changed(self.descriptor_index, self.right_entity_index);
193
194 (left_value, right_value)
195 }
196
197 fn undo_move<D: Director<S>>(&self, score_director: &mut D, undo: Self::Undo) {
198 score_director.before_variable_changed(self.descriptor_index, self.left_entity_index);
199 score_director.before_variable_changed(self.descriptor_index, self.right_entity_index);
200 (self.setter)(
201 score_director.working_solution_mut(),
202 self.left_entity_index,
203 self.variable_index,
204 undo.0,
205 );
206 (self.setter)(
207 score_director.working_solution_mut(),
208 self.right_entity_index,
209 self.variable_index,
210 undo.1,
211 );
212 score_director.after_variable_changed(self.descriptor_index, self.left_entity_index);
213 score_director.after_variable_changed(self.descriptor_index, self.right_entity_index);
214 }
215
216 fn descriptor_index(&self) -> usize {
217 self.descriptor_index
218 }
219
220 fn entity_indices(&self) -> &[usize] {
221 &self.indices
222 }
223
224 fn variable_name(&self) -> &str {
225 self.variable_name
226 }
227
228 fn tabu_signature<D: Director<S>>(&self, score_director: &D) -> MoveTabuSignature {
229 let left_val = (self.getter)(
230 score_director.working_solution(),
231 self.left_entity_index,
232 self.variable_index,
233 );
234 let right_val = (self.getter)(
235 score_director.working_solution(),
236 self.right_entity_index,
237 self.variable_index,
238 );
239 let left_id = encode_option_debug(left_val.as_ref());
240 let right_id = encode_option_debug(right_val.as_ref());
241 let left_entity_id = encode_usize(self.left_entity_index);
242 let right_entity_id = encode_usize(self.right_entity_index);
243 let scope = MoveTabuScope::new(self.descriptor_index, self.variable_name);
244 let entity_pair = ordered_coordinate_pair((left_entity_id, 0), (right_entity_id, 0));
245 let move_id = scoped_move_identity(
246 scope,
247 TABU_OP_SWAP,
248 entity_pair.into_iter().map(|(entity_id, _)| entity_id),
249 );
250
251 MoveTabuSignature::new(scope, move_id.clone(), move_id)
252 .with_entity_tokens([
253 scope.entity_token(left_entity_id),
254 scope.entity_token(right_entity_id),
255 ])
256 .with_destination_value_tokens([
257 scope.value_token(right_id),
258 scope.value_token(left_id),
259 ])
260 }
261
262 fn candidate_trace_identity(&self) -> Option<CandidateTraceIdentity> {
263 Some(CandidateTraceIdentity::logical_move(
264 self.descriptor_index,
265 self.variable_name,
266 "scalar_swap",
267 [self.left_entity_index, self.right_entity_index],
268 ))
269 }
270}