solverforge_solver/heuristic/selector/
sublist_change.rs1use std::fmt::Debug;
68use std::marker::PhantomData;
69
70use solverforge_core::domain::PlanningSolution;
71use solverforge_scoring::Director;
72
73use crate::heuristic::r#move::{ListMoveImpl, SubListChangeMove};
74
75use super::entity::EntitySelector;
76use super::list_support::collect_selected_entities;
77use super::move_selector::MoveSelector;
78use super::sublist_support::count_sublist_change_moves_for_len;
79
80pub struct SubListChangeMoveSelector<S, V, ES> {
91 entity_selector: ES,
92 min_sublist_size: usize,
94 max_sublist_size: usize,
96 list_len: fn(&S, usize) -> usize,
97 sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
98 sublist_insert: fn(&mut S, usize, usize, Vec<V>),
99 variable_name: &'static str,
100 descriptor_index: usize,
101 _phantom: PhantomData<(fn() -> S, fn() -> V)>,
102}
103
104impl<S, V: Debug, ES: Debug> Debug for SubListChangeMoveSelector<S, V, ES> {
105 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106 f.debug_struct("SubListChangeMoveSelector")
107 .field("entity_selector", &self.entity_selector)
108 .field("min_sublist_size", &self.min_sublist_size)
109 .field("max_sublist_size", &self.max_sublist_size)
110 .field("variable_name", &self.variable_name)
111 .field("descriptor_index", &self.descriptor_index)
112 .finish()
113 }
114}
115
116impl<S, V, ES> SubListChangeMoveSelector<S, V, ES> {
117 #[allow(clippy::too_many_arguments)]
133 pub fn new(
134 entity_selector: ES,
135 min_sublist_size: usize,
136 max_sublist_size: usize,
137 list_len: fn(&S, usize) -> usize,
138 sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
139 sublist_insert: fn(&mut S, usize, usize, Vec<V>),
140 variable_name: &'static str,
141 descriptor_index: usize,
142 ) -> Self {
143 assert!(min_sublist_size >= 1, "min_sublist_size must be at least 1");
144 assert!(
145 max_sublist_size >= min_sublist_size,
146 "max_sublist_size must be >= min_sublist_size"
147 );
148 Self {
149 entity_selector,
150 min_sublist_size,
151 max_sublist_size,
152 list_len,
153 sublist_remove,
154 sublist_insert,
155 variable_name,
156 descriptor_index,
157 _phantom: PhantomData,
158 }
159 }
160}
161
162impl<S, V, ES> MoveSelector<S, SubListChangeMove<S, V>> for SubListChangeMoveSelector<S, V, ES>
163where
164 S: PlanningSolution,
165 V: Clone + PartialEq + Send + Sync + Debug + 'static,
166 ES: EntitySelector<S>,
167{
168 fn open_cursor<'a, D: Director<S>>(
169 &'a self,
170 score_director: &D,
171 ) -> impl Iterator<Item = SubListChangeMove<S, V>> + 'a {
172 let list_len = self.list_len;
173 let sublist_remove = self.sublist_remove;
174 let sublist_insert = self.sublist_insert;
175 let variable_name = self.variable_name;
176 let descriptor_index = self.descriptor_index;
177 let min_seg = self.min_sublist_size;
178 let max_seg = self.max_sublist_size;
179
180 let selected = collect_selected_entities(&self.entity_selector, score_director, list_len);
181 let entities = selected.entities;
182 let route_lens = selected.route_lens;
183 let mut moves = Vec::new();
184
185 for (src_idx, &src_entity) in entities.iter().enumerate() {
186 let src_len = route_lens[src_idx];
187
188 for seg_start in 0..src_len {
189 for seg_size in min_seg..=max_seg {
190 let seg_end = seg_start + seg_size;
191 if seg_end > src_len {
192 break;
193 }
194
195 let post_removal_len = src_len - seg_size;
196 for dst_pos in 0..=post_removal_len {
197 if dst_pos == seg_start {
198 continue;
199 }
200 moves.push(SubListChangeMove::new(
201 src_entity,
202 seg_start,
203 seg_end,
204 src_entity,
205 dst_pos,
206 list_len,
207 sublist_remove,
208 sublist_insert,
209 variable_name,
210 descriptor_index,
211 ));
212 }
213
214 for (dst_idx, &dst_entity) in entities.iter().enumerate() {
215 if dst_idx == src_idx {
216 continue;
217 }
218 let dst_len = route_lens[dst_idx];
219 for dst_pos in 0..=dst_len {
220 moves.push(SubListChangeMove::new(
221 src_entity,
222 seg_start,
223 seg_end,
224 dst_entity,
225 dst_pos,
226 list_len,
227 sublist_remove,
228 sublist_insert,
229 variable_name,
230 descriptor_index,
231 ));
232 }
233 }
234 }
235 }
236 }
237
238 moves.into_iter()
239 }
240
241 fn size<D: Director<S>>(&self, score_director: &D) -> usize {
242 let selected =
243 collect_selected_entities(&self.entity_selector, score_director, self.list_len);
244 let total_elements = selected.route_lens.iter().sum::<usize>();
245 let entity_count = selected.entities.len();
246
247 selected
248 .route_lens
249 .iter()
250 .map(|&route_len| {
251 let inter_destinations =
252 total_elements.saturating_sub(route_len) + entity_count.saturating_sub(1);
253 count_sublist_change_moves_for_len(
254 route_len,
255 inter_destinations,
256 self.min_sublist_size,
257 self.max_sublist_size,
258 )
259 })
260 .sum()
261 }
262}
263
264pub struct ListMoveSubListChangeSelector<S, V, ES> {
266 inner: SubListChangeMoveSelector<S, V, ES>,
267}
268
269impl<S, V: Debug, ES: Debug> Debug for ListMoveSubListChangeSelector<S, V, ES> {
270 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
271 f.debug_struct("ListMoveSubListChangeSelector")
272 .field("inner", &self.inner)
273 .finish()
274 }
275}
276
277impl<S, V, ES> ListMoveSubListChangeSelector<S, V, ES> {
278 pub fn new(inner: SubListChangeMoveSelector<S, V, ES>) -> Self {
280 Self { inner }
281 }
282}
283
284impl<S, V, ES> MoveSelector<S, ListMoveImpl<S, V>> for ListMoveSubListChangeSelector<S, V, ES>
285where
286 S: PlanningSolution,
287 V: Clone + PartialEq + Send + Sync + Debug + 'static,
288 ES: EntitySelector<S>,
289{
290 fn open_cursor<'a, D: Director<S>>(
291 &'a self,
292 score_director: &D,
293 ) -> impl Iterator<Item = ListMoveImpl<S, V>> + 'a {
294 self.inner
295 .open_cursor(score_director)
296 .map(ListMoveImpl::SubListChange)
297 }
298
299 fn size<D: Director<S>>(&self, score_director: &D) -> usize {
300 self.inner.size(score_director)
301 }
302}