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