solverforge_solver/heuristic/move/
sublist_swap.rs1use std::fmt::Debug;
12use std::marker::PhantomData;
13
14use solverforge_core::domain::PlanningSolution;
15use solverforge_scoring::Director;
16
17use super::list_kernel::{
18 sublist_swap_candidate_trace_identity, sublist_swap_do_move, sublist_swap_is_doable,
19 sublist_swap_tabu_signature, sublist_swap_undo_move, StaticListWindowAccess,
20};
21use super::segment_layout::SegmentSwapCoords;
22use super::{Move, MoveTabuSignature};
23
24pub struct SublistSwapMove<S, V> {
83 first_entity_index: usize,
85 first_start: usize,
87 first_end: usize,
89 second_entity_index: usize,
91 second_start: usize,
93 second_end: usize,
95 list_len: fn(&S, usize) -> usize,
96 list_get: fn(&S, usize, usize) -> Option<V>,
97 sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
99 sublist_insert: fn(&mut S, usize, usize, Vec<V>),
101 variable_name: &'static str,
102 descriptor_index: usize,
103 indices: [usize; 2],
105 _phantom: PhantomData<fn() -> V>,
106}
107
108impl<S, V> Clone for SublistSwapMove<S, V> {
109 fn clone(&self) -> Self {
110 *self
111 }
112}
113
114impl<S, V> Copy for SublistSwapMove<S, V> {}
115
116impl<S, V: Debug> Debug for SublistSwapMove<S, V> {
117 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118 f.debug_struct("SublistSwapMove")
119 .field("first_entity", &self.first_entity_index)
120 .field("first_range", &(self.first_start..self.first_end))
121 .field("second_entity", &self.second_entity_index)
122 .field("second_range", &(self.second_start..self.second_end))
123 .field("variable_name", &self.variable_name)
124 .finish()
125 }
126}
127
128impl<S, V> SublistSwapMove<S, V> {
129 #[allow(clippy::too_many_arguments)]
145 pub fn new(
146 first_entity_index: usize,
147 first_start: usize,
148 first_end: usize,
149 second_entity_index: usize,
150 second_start: usize,
151 second_end: usize,
152 list_len: fn(&S, usize) -> usize,
153 list_get: fn(&S, usize, usize) -> Option<V>,
154 sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
155 sublist_insert: fn(&mut S, usize, usize, Vec<V>),
156 variable_name: &'static str,
157 descriptor_index: usize,
158 ) -> Self {
159 Self {
160 first_entity_index,
161 first_start,
162 first_end,
163 second_entity_index,
164 second_start,
165 second_end,
166 list_len,
167 list_get,
168 sublist_remove,
169 sublist_insert,
170 variable_name,
171 descriptor_index,
172 indices: [first_entity_index, second_entity_index],
173 _phantom: PhantomData,
174 }
175 }
176
177 pub fn first_entity_index(&self) -> usize {
178 self.first_entity_index
179 }
180
181 pub fn first_start(&self) -> usize {
182 self.first_start
183 }
184
185 pub fn first_end(&self) -> usize {
186 self.first_end
187 }
188
189 pub fn first_len(&self) -> usize {
190 self.first_end.saturating_sub(self.first_start)
191 }
192
193 pub fn second_entity_index(&self) -> usize {
194 self.second_entity_index
195 }
196
197 pub fn second_start(&self) -> usize {
198 self.second_start
199 }
200
201 pub fn second_end(&self) -> usize {
202 self.second_end
203 }
204
205 pub fn second_len(&self) -> usize {
206 self.second_end.saturating_sub(self.second_start)
207 }
208
209 pub fn is_intra_list(&self) -> bool {
210 self.first_entity_index == self.second_entity_index
211 }
212
213 fn access(&self) -> StaticListWindowAccess<S, V> {
214 StaticListWindowAccess {
215 list_len: self.list_len,
216 list_get: self.list_get,
217 sublist_remove: self.sublist_remove,
218 sublist_insert: self.sublist_insert,
219 variable_name: self.variable_name,
220 descriptor_index: self.descriptor_index,
221 }
222 }
223
224 fn coordinates(&self) -> SegmentSwapCoords {
225 SegmentSwapCoords::new(
226 self.first_entity_index,
227 self.first_start,
228 self.first_end,
229 self.second_entity_index,
230 self.second_start,
231 self.second_end,
232 )
233 }
234}
235
236impl<S, V> Move<S> for SublistSwapMove<S, V>
237where
238 S: PlanningSolution,
239 V: Clone + Send + Sync + Debug + 'static,
240{
241 type Undo = ();
242
243 fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
244 sublist_swap_is_doable(&self.access(), self.coordinates(), score_director)
245 }
246
247 fn do_move<D: Director<S>>(&self, score_director: &mut D) -> Self::Undo {
248 sublist_swap_do_move(&self.access(), self.coordinates(), score_director);
249 }
250
251 fn undo_move<D: Director<S>>(&self, score_director: &mut D, (): Self::Undo) {
252 sublist_swap_undo_move(&self.access(), self.coordinates(), score_director);
253 }
254
255 fn descriptor_index(&self) -> usize {
256 self.descriptor_index
257 }
258
259 fn entity_indices(&self) -> &[usize] {
260 if self.is_intra_list() {
261 &self.indices[0..1]
262 } else {
263 &self.indices
264 }
265 }
266
267 fn variable_name(&self) -> &str {
268 self.variable_name
269 }
270
271 fn telemetry_label(&self) -> &'static str {
272 "sublist_swap"
273 }
274
275 fn tabu_signature<D: Director<S>>(&self, score_director: &D) -> MoveTabuSignature {
276 sublist_swap_tabu_signature(&self.access(), self.coordinates(), score_director)
277 }
278
279 fn candidate_trace_identity(&self) -> Option<crate::stats::CandidateTraceIdentity> {
280 Some(sublist_swap_candidate_trace_identity(
281 &self.access(),
282 self.coordinates(),
283 ))
284 }
285}