solverforge_solver/heuristic/selector/
ruin.rs1use std::cell::RefCell;
74use std::fmt::Debug;
75use std::marker::PhantomData;
76
77use rand::rngs::SmallRng;
78use rand::{RngExt, SeedableRng};
79use smallvec::SmallVec;
80use solverforge_core::domain::PlanningSolution;
81use solverforge_scoring::Director;
82
83use crate::heuristic::r#move::RuinMove;
84
85use super::move_selector::{
86 CandidateId, CandidateStore, MoveCandidateRef, MoveCursor, MoveSelector,
87};
88
89pub struct RuinVariableAccess<S, V> {
90 entity_count: fn(&S) -> usize,
92 getter: fn(&S, usize, usize) -> Option<V>,
94 setter: fn(&mut S, usize, usize, Option<V>),
96 variable_index: usize,
97 variable_name: &'static str,
99 descriptor_index: usize,
101 _phantom: PhantomData<fn() -> V>,
102}
103
104impl<S, V> Clone for RuinVariableAccess<S, V> {
105 fn clone(&self) -> Self {
106 *self
107 }
108}
109
110impl<S, V> Copy for RuinVariableAccess<S, V> {}
111
112impl<S, V> RuinVariableAccess<S, V> {
113 pub fn new(
115 entity_count: fn(&S) -> usize,
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_count,
124 getter,
125 setter,
126 variable_index,
127 variable_name,
128 descriptor_index,
129 _phantom: PhantomData,
130 }
131 }
132}
133
134pub struct RuinMoveSelector<S, V> {
150 min_ruin_count: usize,
152 max_ruin_count: usize,
154 rng: RefCell<SmallRng>,
156 access: RuinVariableAccess<S, V>,
157 moves_per_step: usize,
159}
160
161pub struct RuinMoveCursor<S, V>
162where
163 S: PlanningSolution,
164 V: Clone + Send + Sync + Debug + 'static,
165{
166 store: CandidateStore<S, RuinMove<S, V>>,
167 subsets: std::vec::IntoIter<SmallVec<[usize; 8]>>,
168 access: RuinVariableAccess<S, V>,
169}
170
171impl<S, V> RuinMoveCursor<S, V>
172where
173 S: PlanningSolution,
174 V: Clone + Send + Sync + Debug + 'static,
175{
176 pub(crate) fn next_subset(&mut self) -> Option<SmallVec<[usize; 8]>> {
177 self.subsets.next()
178 }
179
180 fn next_move(&mut self) -> Option<RuinMove<S, V>> {
181 let indices = self.next_subset()?;
182 Some(RuinMove::from_indices(
183 indices,
184 self.access.getter,
185 self.access.setter,
186 self.access.variable_index,
187 self.access.variable_name,
188 self.access.descriptor_index,
189 ))
190 }
191}
192
193impl<S, V> MoveCursor<S, RuinMove<S, V>> for RuinMoveCursor<S, V>
194where
195 S: PlanningSolution,
196 V: Clone + Send + Sync + Debug + 'static,
197{
198 fn next_candidate(&mut self) -> Option<CandidateId> {
199 let mov = self.next_move()?;
200 Some(self.store.push(mov))
201 }
202
203 fn candidate(&self, id: CandidateId) -> Option<MoveCandidateRef<'_, S, RuinMove<S, V>>> {
204 self.store.candidate(id)
205 }
206
207 fn take_candidate(&mut self, id: CandidateId) -> RuinMove<S, V> {
208 self.store.take_candidate(id)
209 }
210
211 fn next_owned_candidate(&mut self) -> Option<RuinMove<S, V>> {
212 self.next_move()
213 }
214
215 #[inline(always)]
216 fn next_owned_candidate_matching(
217 &mut self,
218 predicate: for<'a> fn(MoveCandidateRef<'a, S, RuinMove<S, V>>) -> bool,
219 ) -> Option<RuinMove<S, V>> {
220 loop {
221 let mov = self.next_move()?;
222 if predicate(MoveCandidateRef::Borrowed(&mov)) {
223 return Some(mov);
224 }
225 }
226 }
227
228 fn release_candidate(&mut self, id: CandidateId) -> bool {
229 self.store.release_candidate(id)
230 }
231}
232
233impl<S, V> Iterator for RuinMoveCursor<S, V>
234where
235 S: PlanningSolution,
236 V: Clone + Send + Sync + Debug + 'static,
237{
238 type Item = RuinMove<S, V>;
239
240 fn next(&mut self) -> Option<Self::Item> {
241 self.next_owned_candidate()
242 }
243}
244
245unsafe impl<S, V> Send for RuinMoveSelector<S, V> {}
248
249impl<S, V: Debug> Debug for RuinMoveSelector<S, V> {
250 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
251 f.debug_struct("RuinMoveSelector")
252 .field("min_ruin_count", &self.min_ruin_count)
253 .field("max_ruin_count", &self.max_ruin_count)
254 .field("moves_per_step", &self.moves_per_step)
255 .field("variable_name", &self.access.variable_name)
256 .field("descriptor_index", &self.access.descriptor_index)
257 .finish()
258 }
259}
260
261impl<S, V> RuinMoveSelector<S, V> {
262 pub fn new(
272 min_ruin_count: usize,
273 max_ruin_count: usize,
274 access: RuinVariableAccess<S, V>,
275 ) -> Self {
276 assert!(min_ruin_count >= 1, "min_ruin_count must be at least 1");
277 assert!(
278 max_ruin_count >= min_ruin_count,
279 "max_ruin_count must be >= min_ruin_count"
280 );
281
282 Self {
283 min_ruin_count,
284 max_ruin_count,
285 rng: RefCell::new(SmallRng::from_rng(&mut rand::rng())),
286 access,
287 moves_per_step: 10, }
289 }
290
291 pub fn with_moves_per_step(mut self, count: usize) -> Self {
295 self.moves_per_step = count;
296 self
297 }
298
299 pub fn with_seed(mut self, seed: u64) -> Self {
300 self.rng = RefCell::new(SmallRng::seed_from_u64(seed));
301 self
302 }
303}
304
305impl<S, V> MoveSelector<S, RuinMove<S, V>> for RuinMoveSelector<S, V>
306where
307 S: PlanningSolution,
308 V: Clone + Send + Sync + Debug + 'static,
309{
310 type Cursor<'a>
311 = RuinMoveCursor<S, V>
312 where
313 Self: 'a;
314
315 fn open_cursor<'a, D: Director<S>>(&'a self, score_director: &D) -> Self::Cursor<'a> {
316 let access = self.access;
317 let total_entities = (access.entity_count)(score_director.working_solution());
318
319 let min = self.min_ruin_count.min(total_entities);
320 let max = self.max_ruin_count.min(total_entities);
321 let moves_count = self.moves_per_step;
322
323 let mut rng = self.rng.borrow_mut();
325 let mut permutation: Vec<usize> = (0..total_entities).collect();
326 let subsets: Vec<SmallVec<[usize; 8]>> = (0..moves_count)
327 .map(|_| {
328 if total_entities == 0 {
329 return SmallVec::new();
330 }
331 for (index, entity) in permutation.iter_mut().enumerate() {
332 *entity = index;
333 }
334 let ruin_count = if min == max {
335 min
336 } else {
337 rng.random_range(min..=max)
338 };
339 for i in 0..ruin_count {
340 let j = rng.random_range(i..total_entities);
341 permutation.swap(i, j);
342 }
343 permutation[..ruin_count].iter().copied().collect()
344 })
345 .collect();
346
347 RuinMoveCursor {
348 store: CandidateStore::new(),
349 subsets: subsets.into_iter(),
350 access,
351 }
352 }
353
354 fn size<D: Director<S>>(&self, score_director: &D) -> usize {
355 let total = (self.access.entity_count)(score_director.working_solution());
356 if total == 0 {
357 return 0;
358 }
359 self.moves_per_step
361 }
362
363 fn is_never_ending(&self) -> bool {
364 false
366 }
367}