solverforge_solver/heuristic/move/
ruin.rs1use std::borrow::Cow;
13use std::fmt::Debug;
14
15use smallvec::SmallVec;
16use solverforge_core::domain::PlanningSolution;
17use solverforge_scoring::Director;
18
19use super::metadata::{
20 encode_option_debug, encode_usize, hash_str, MoveTabuScope, ScopedEntityTabuToken,
21};
22use super::{Move, MoveTabuSignature};
23
24pub struct RuinMove<S, V> {
67 entity_indices: SmallVec<[usize; 8]>,
69 getter: fn(&S, usize, usize) -> Option<V>,
70 setter: fn(&mut S, usize, usize, Option<V>),
72 variable_index: usize,
73 variable_name: &'static str,
74 descriptor_index: usize,
75}
76
77impl<S, V: Debug> Debug for RuinMove<S, V> {
78 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 f.debug_struct("RuinMove")
80 .field("entities", &self.entity_indices.as_slice())
81 .field("variable_index", &self.variable_index)
82 .field("variable_name", &self.variable_name)
83 .finish()
84 }
85}
86
87impl<S, V> RuinMove<S, V> {
88 pub fn new(
97 entity_indices: &[usize],
98 getter: fn(&S, usize, usize) -> Option<V>,
99 setter: fn(&mut S, usize, usize, Option<V>),
100 variable_index: usize,
101 variable_name: &'static str,
102 descriptor_index: usize,
103 ) -> Self {
104 Self::from_indices(
105 SmallVec::from_slice(entity_indices),
106 getter,
107 setter,
108 variable_index,
109 variable_name,
110 descriptor_index,
111 )
112 }
113
114 pub(crate) fn from_indices(
115 entity_indices: SmallVec<[usize; 8]>,
116 getter: fn(&S, usize, usize) -> Option<V>,
117 setter: fn(&mut S, usize, usize, Option<V>),
118 variable_index: usize,
119 variable_name: &'static str,
120 descriptor_index: usize,
121 ) -> Self {
122 Self {
123 entity_indices,
124 getter,
125 setter,
126 variable_index,
127 variable_name,
128 descriptor_index,
129 }
130 }
131
132 pub fn entity_indices_slice(&self) -> &[usize] {
133 &self.entity_indices
134 }
135
136 pub fn ruin_count(&self) -> usize {
137 self.entity_indices.len()
138 }
139}
140
141impl<S, V> Move<S> for RuinMove<S, V>
142where
143 S: PlanningSolution,
144 V: Clone + Send + Sync + Debug + 'static,
145{
146 type Undo = SmallVec<[(usize, Option<V>); 8]>;
147
148 fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
149 let solution = score_director.working_solution();
151 self.entity_indices
152 .iter()
153 .any(|&idx| (self.getter)(solution, idx, self.variable_index).is_some())
154 }
155
156 fn do_move<D: Director<S>>(&self, score_director: &mut D) -> Self::Undo {
157 let getter = self.getter;
158 let setter = self.setter;
159 let descriptor = self.descriptor_index;
160 let variable_index = self.variable_index;
161
162 let old_values: SmallVec<[(usize, Option<V>); 8]> = self
164 .entity_indices
165 .iter()
166 .map(|&idx| {
167 let old = getter(score_director.working_solution(), idx, variable_index);
168 (idx, old)
169 })
170 .collect();
171
172 for &idx in &self.entity_indices {
174 score_director.before_variable_changed(descriptor, idx);
175 setter(
176 score_director.working_solution_mut(),
177 idx,
178 variable_index,
179 None,
180 );
181 score_director.after_variable_changed(descriptor, idx);
182 }
183
184 old_values
185 }
186
187 fn undo_move<D: Director<S>>(&self, score_director: &mut D, undo: Self::Undo) {
188 for (idx, _) in &undo {
189 score_director.before_variable_changed(self.descriptor_index, *idx);
190 }
191 for (idx, old_value) in undo {
192 (self.setter)(
193 score_director.working_solution_mut(),
194 idx,
195 self.variable_index,
196 old_value,
197 );
198 }
199 for &idx in &self.entity_indices {
200 score_director.after_variable_changed(self.descriptor_index, idx);
201 }
202 }
203
204 fn descriptor_index(&self) -> usize {
205 self.descriptor_index
206 }
207
208 fn entity_indices(&self) -> &[usize] {
209 &self.entity_indices
210 }
211
212 fn variable_name(&self) -> &str {
213 self.variable_name
214 }
215
216 fn tabu_signature<D: Director<S>>(&self, score_director: &D) -> Cow<'_, MoveTabuSignature> {
217 let scope = MoveTabuScope::new(self.descriptor_index, self.variable_name);
218 let entity_ids: SmallVec<[u64; 2]> = self
219 .entity_indices
220 .iter()
221 .map(|&idx| encode_usize(idx))
222 .collect();
223 let entity_tokens: SmallVec<[ScopedEntityTabuToken; 2]> = entity_ids
224 .iter()
225 .copied()
226 .map(|entity_id| scope.entity_token(entity_id))
227 .collect();
228 let mut value_ids: SmallVec<[u64; 2]> = SmallVec::new();
229 for &idx in &self.entity_indices {
230 let value = (self.getter)(score_director.working_solution(), idx, self.variable_index);
231 value_ids.push(encode_option_debug(value.as_ref()));
232 }
233 let variable_id = hash_str(self.variable_name);
234 let mut move_id = SmallVec::<[u64; 8]>::from_slice(&[
235 encode_usize(self.descriptor_index),
236 variable_id,
237 encode_usize(self.entity_indices.len()),
238 ]);
239 move_id.extend(entity_ids.iter().copied());
240 move_id.extend(value_ids.iter().copied());
241
242 Cow::Owned(
243 MoveTabuSignature::new_self_undoing(scope, move_id).with_entity_tokens(entity_tokens),
244 )
245 }
246}