solverforge_solver/heuristic/move/
list_ruin.rs1use std::fmt::Debug;
14use std::marker::PhantomData;
15
16use smallvec::{smallvec, SmallVec};
17use solverforge_core::domain::PlanningSolution;
18use solverforge_scoring::Director;
19
20use super::metadata::{
21 encode_option_debug, encode_usize, hash_str, MoveTabuScope, ScopedValueTabuToken,
22};
23use super::{Move, MoveTabuSignature};
24
25pub struct ListRuinMove<S, V> {
67 entity_index: usize,
69 element_indices: SmallVec<[usize; 8]>,
71 entity_count: fn(&S) -> usize,
73 list_len: fn(&S, usize) -> usize,
74 list_get: fn(&S, usize, usize) -> Option<V>,
75 list_remove: fn(&mut S, usize, usize) -> V,
77 list_insert: fn(&mut S, usize, usize, V),
79 variable_name: &'static str,
80 descriptor_index: usize,
81 _phantom: PhantomData<fn() -> V>,
82}
83
84impl<S, V> Clone for ListRuinMove<S, V> {
85 fn clone(&self) -> Self {
86 Self {
87 entity_index: self.entity_index,
88 element_indices: self.element_indices.clone(),
89 entity_count: self.entity_count,
90 list_len: self.list_len,
91 list_get: self.list_get,
92 list_remove: self.list_remove,
93 list_insert: self.list_insert,
94 variable_name: self.variable_name,
95 descriptor_index: self.descriptor_index,
96 _phantom: PhantomData,
97 }
98 }
99}
100
101impl<S, V: Debug> Debug for ListRuinMove<S, V> {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 f.debug_struct("ListRuinMove")
104 .field("entity", &self.entity_index)
105 .field("elements", &self.element_indices.as_slice())
106 .field("variable_name", &self.variable_name)
107 .finish()
108 }
109}
110
111impl<S, V> ListRuinMove<S, V> {
112 #[allow(clippy::too_many_arguments)]
125 pub fn new(
126 entity_index: usize,
127 element_indices: &[usize],
128 entity_count: fn(&S) -> usize,
129 list_len: fn(&S, usize) -> usize,
130 list_get: fn(&S, usize, usize) -> Option<V>,
131 list_remove: fn(&mut S, usize, usize) -> V,
132 list_insert: fn(&mut S, usize, usize, V),
133 variable_name: &'static str,
134 descriptor_index: usize,
135 ) -> Self {
136 let mut indices: SmallVec<[usize; 8]> = SmallVec::from_slice(element_indices);
137 indices.sort_unstable();
138 Self {
139 entity_index,
140 element_indices: indices,
141 entity_count,
142 list_len,
143 list_get,
144 list_remove,
145 list_insert,
146 variable_name,
147 descriptor_index,
148 _phantom: PhantomData,
149 }
150 }
151
152 pub fn entity_index(&self) -> usize {
153 self.entity_index
154 }
155
156 pub fn element_indices(&self) -> &[usize] {
157 &self.element_indices
158 }
159
160 pub fn ruin_count(&self) -> usize {
161 self.element_indices.len()
162 }
163}
164
165pub(crate) fn final_positions_after_insertions(
166 placements: &SmallVec<[(usize, usize); 8]>,
167) -> SmallVec<[usize; 8]> {
168 let mut current_positions: SmallVec<[usize; 8]> = SmallVec::with_capacity(placements.len());
169
170 for i in 0..placements.len() {
171 let (entity_i, insert_pos_i) = placements[i];
172
173 for j in 0..i {
174 let (entity_j, _) = placements[j];
175 if entity_j == entity_i && current_positions[j] >= insert_pos_i {
176 current_positions[j] += 1;
177 }
178 }
179
180 current_positions.push(insert_pos_i);
181 }
182
183 current_positions
184}
185
186impl<S, V> Move<S> for ListRuinMove<S, V>
187where
188 S: PlanningSolution,
189 V: Clone + Send + Sync + Debug + 'static,
190{
191 fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
192 if self.element_indices.is_empty() {
193 return false;
194 }
195 let solution = score_director.working_solution();
196 let len = (self.list_len)(solution, self.entity_index);
197 self.element_indices.iter().all(|&idx| idx < len)
198 }
199
200 fn do_move<D: Director<S>>(&self, score_director: &mut D) {
201 let list_remove = self.list_remove;
202 let list_insert = self.list_insert;
203 let list_len = self.list_len;
204 let entity_count = self.entity_count;
205 let src = self.entity_index;
206 let descriptor = self.descriptor_index;
207
208 score_director.before_variable_changed(descriptor, src);
210 let mut removed: SmallVec<[V; 8]> = SmallVec::new();
211 for &idx in self.element_indices.iter().rev() {
212 let value = list_remove(score_director.working_solution_mut(), src, idx);
213 removed.push(value);
214 }
215 removed.reverse();
217 score_director.after_variable_changed(descriptor, src);
218
219 let mut placements: SmallVec<[(usize, usize); 8]> = SmallVec::new();
222
223 let n_entities = entity_count(score_director.working_solution());
224
225 for elem in removed.iter() {
226 let mut best_score: Option<S::Score> = None;
227 let mut best_entity = src;
228 let mut best_pos = list_len(score_director.working_solution(), src);
229
230 for e in 0..n_entities {
231 let len = list_len(score_director.working_solution(), e);
232 for pos in 0..=len {
233 score_director.before_variable_changed(descriptor, e);
234 list_insert(score_director.working_solution_mut(), e, pos, elem.clone());
235 score_director.after_variable_changed(descriptor, e);
236
237 let candidate_score = score_director.calculate_score();
238 if best_score.is_none_or(|b| candidate_score > b) {
239 best_score = Some(candidate_score);
240 best_entity = e;
241 best_pos = pos;
242 }
243
244 score_director.before_variable_changed(descriptor, e);
245 list_remove(score_director.working_solution_mut(), e, pos);
246 score_director.after_variable_changed(descriptor, e);
247 }
248 }
249
250 score_director.before_variable_changed(descriptor, best_entity);
252 list_insert(
253 score_director.working_solution_mut(),
254 best_entity,
255 best_pos,
256 elem.clone(),
257 );
258 score_director.after_variable_changed(descriptor, best_entity);
259
260 placements.push((best_entity, best_pos));
263 }
264
265 let orig_entity = src;
282 let orig_indices: SmallVec<[usize; 8]> = self.element_indices.clone();
283
284 score_director.register_undo(Box::new(move |s: &mut S| {
285 let n = placements.len();
286 let mut current_pos = final_positions_after_insertions(&placements);
287
288 let mut vals: SmallVec<[V; 8]> = SmallVec::with_capacity(n);
294 for i in (0..n).rev() {
295 let (e_i, _) = placements[i];
296 let actual_pos = current_pos[i];
297 vals.push(list_remove(s, e_i, actual_pos));
298
299 for j in 0..i {
300 let (e_j, _) = placements[j];
301 if e_j == e_i && current_pos[j] > actual_pos {
302 current_pos[j] -= 1;
303 }
304 }
305 }
306 vals.reverse();
308
309 for (&idx, val) in orig_indices.iter().zip(vals) {
322 list_insert(s, orig_entity, idx, val);
323 }
324 }));
325 }
326
327 fn descriptor_index(&self) -> usize {
328 self.descriptor_index
329 }
330
331 fn entity_indices(&self) -> &[usize] {
332 std::slice::from_ref(&self.entity_index)
333 }
334
335 fn variable_name(&self) -> &str {
336 self.variable_name
337 }
338
339 fn tabu_signature<D: Director<S>>(&self, score_director: &D) -> MoveTabuSignature {
340 let mut value_ids: SmallVec<[u64; 2]> = SmallVec::new();
341 for &idx in &self.element_indices {
342 let value = (self.list_get)(score_director.working_solution(), self.entity_index, idx);
343 value_ids.push(encode_option_debug(value.as_ref()));
344 }
345 let entity_id = encode_usize(self.entity_index);
346 let variable_id = hash_str(self.variable_name);
347 let scope = MoveTabuScope::new(self.descriptor_index, self.variable_name);
348 let destination_value_tokens: SmallVec<[ScopedValueTabuToken; 2]> = value_ids
349 .iter()
350 .copied()
351 .map(|value_id| scope.value_token(value_id))
352 .collect();
353 let mut move_id = smallvec![
354 encode_usize(self.descriptor_index),
355 variable_id,
356 entity_id,
357 encode_usize(self.element_indices.len())
358 ];
359 move_id.extend(self.element_indices.iter().map(|&idx| encode_usize(idx)));
360 move_id.extend(value_ids.iter().copied());
361
362 MoveTabuSignature::new(scope, move_id.clone(), move_id)
363 .with_entity_tokens([scope.entity_token(entity_id)])
364 .with_destination_value_tokens(destination_value_tokens)
365 }
366}