solverforge_solver/heuristic/selector/move_selector/
iter.rs1use solverforge_config::SelectionOrder;
2
3pub struct MoveSelectorIter<'a, S, M, C>
4where
5 S: PlanningSolution,
6 M: Move<S>,
7 C: MoveCursor<S, M>,
8{
9 cursor: C,
10 solution: &'a S,
11 _phantom: PhantomData<fn() -> M>,
12}
13
14impl<'a, S, M, C> MoveSelectorIter<'a, S, M, C>
15where
16 S: PlanningSolution,
17 M: Move<S>,
18 C: MoveCursor<S, M>,
19{
20 fn new(cursor: C, solution: &'a S) -> Self {
21 Self {
22 cursor,
23 solution,
24 _phantom: PhantomData,
25 }
26 }
27}
28
29#[derive(Clone, Copy, Debug, PartialEq, Eq)]
30pub struct MoveStreamContext {
31 step_index: u64,
32 step_seed: u64,
33 accepted_count_limit: Option<usize>,
34 selection_order: SelectionOrder,
35}
36
37impl MoveStreamContext {
38 pub const fn new(
39 step_index: u64,
40 step_seed: u64,
41 accepted_count_limit: Option<usize>,
42 ) -> Self {
43 Self {
44 step_index,
45 step_seed,
46 accepted_count_limit,
47 selection_order: SelectionOrder::Original,
48 }
49 }
50
51 pub const fn with_selection_order(mut self, selection_order: SelectionOrder) -> Self {
52 self.selection_order = selection_order;
53 self
54 }
55
56 pub const fn selection_order(self) -> SelectionOrder {
57 self.selection_order
58 }
59
60 pub const fn step_index(self) -> u64 {
61 self.step_index
62 }
63
64 pub const fn step_seed(self) -> u64 {
65 self.step_seed
66 }
67
68 pub const fn accepted_count_limit(self) -> Option<usize> {
69 self.accepted_count_limit
70 }
71
72 pub fn start_offset(self, len: usize, salt: u64) -> usize {
73 if len <= 1 {
74 return 0;
75 }
76 if self.is_canonical() {
77 return 0;
78 }
79 (self.mixed_seed(salt) as usize) % len
80 }
81
82 pub fn stride(self, len: usize, salt: u64) -> usize {
83 if len <= 1 {
84 return 1;
85 }
86 if self.is_canonical() {
87 return 1;
88 }
89 let mut stride = (self.mixed_seed(salt) as usize % (len - 1)) + 1;
90 while gcd(stride, len) != 1 {
91 stride = if stride == len - 1 { 1 } else { stride + 1 };
92 }
93 stride
94 }
95
96 pub fn offset_seed(self, salt: u64) -> usize {
97 if self.is_canonical() {
98 return 0;
99 }
100 self.mixed_seed(salt) as usize
101 }
102
103 pub(crate) fn random_index(self, len: usize, salt: u64) -> usize {
104 if len <= 1 {
105 return 0;
106 }
107 (self.mixed_seed(salt) as usize) % len
108 }
109
110 pub(crate) fn random_stride(self, len: usize, salt: u64) -> usize {
111 if len <= 1 {
112 return 1;
113 }
114 let mut stride = (self.mixed_seed(salt) as usize % (len - 1)) + 1;
115 while gcd(stride, len) != 1 {
116 stride = if stride == len - 1 { 1 } else { stride + 1 };
117 }
118 stride
119 }
120
121 pub(crate) fn random_seed(self, salt: u64) -> u64 {
122 self.mixed_seed(salt)
123 }
124
125 pub(crate) fn selection_index(self, offset: usize, len: usize, salt: u64) -> usize {
126 debug_assert!(offset < len);
127 match self.selection_order {
128 SelectionOrder::Original | SelectionOrder::Sorted | SelectionOrder::Probabilistic => {
129 offset
130 }
131 SelectionOrder::Random => self.random_index(
132 len,
133 salt ^ (offset as u64).wrapping_mul(0xD1B5_4A32_D192_ED03),
134 ),
135 SelectionOrder::Shuffled => {
136 let start = self.random_index(len, salt);
137 let stride = self.random_stride(len, salt ^ 0xA24B_AED4_963E_E407);
138 (start + offset * stride) % len
139 }
140 }
141 }
142
143 pub(crate) fn selection_index_without_replacement(
147 self,
148 offset: usize,
149 len: usize,
150 salt: u64,
151 ) -> usize {
152 debug_assert!(offset < len);
153 match self.selection_order {
154 SelectionOrder::Original | SelectionOrder::Sorted | SelectionOrder::Probabilistic => {
155 offset
156 }
157 SelectionOrder::Random | SelectionOrder::Shuffled => {
158 let start = self.random_index(len, salt);
159 let stride = self.random_stride(len, salt ^ 0xA24B_AED4_963E_E407);
160 (start + offset * stride) % len
161 }
162 }
163 }
164
165 pub(crate) fn apply_selection_order<T: Clone>(self, values: &mut [T], salt: u64) {
166 if self.is_canonical() {
167 return;
168 }
169 let canonical = values.to_vec();
170 for (offset, value) in values.iter_mut().enumerate() {
171 *value = canonical[self.selection_index(offset, canonical.len(), salt)].clone();
172 }
173 }
174
175 pub(crate) fn apply_selection_order_usize(self, values: &mut [usize], salt: u64) {
180 if self.is_canonical() {
181 return;
182 }
183 let len = values.len();
184 if len <= 1 {
185 return;
186 }
187 if !matches!(
188 self.selection_order,
189 SelectionOrder::Shuffled
190 ) {
191 let canonical = values.to_vec();
194 for (offset, value) in values.iter_mut().enumerate() {
195 *value = canonical[self.selection_index(offset, len, salt)];
196 }
197 return;
198 }
199 let mut inverse = vec![0usize; len];
202 for offset in 0..len {
203 inverse[self.selection_index(offset, len, salt)] = offset;
204 }
205 let mut visited = vec![false; len];
206 for start in 0..len {
207 if visited[start] {
208 continue;
209 }
210 let mut i = start;
211 let mut carry = values[start];
212 loop {
213 visited[i] = true;
214 let destination = inverse[i];
215 let taken = std::mem::replace(&mut values[destination], carry);
216 carry = taken;
217 i = destination;
218 if visited[i] {
219 break;
220 }
221 }
222 }
223 }
224
225 pub(crate) fn apply_selection_order_without_replacement<T: Clone>(
226 self,
227 values: &mut [T],
228 salt: u64,
229 ) {
230 if self.is_canonical() {
231 return;
232 }
233 let canonical = values.to_vec();
234 for (offset, value) in values.iter_mut().enumerate() {
235 *value = canonical
236 [self.selection_index_without_replacement(offset, canonical.len(), salt)]
237 .clone();
238 }
239 }
240
241 pub(crate) const fn is_canonical(self) -> bool {
242 matches!(
243 self.selection_order,
244 SelectionOrder::Original | SelectionOrder::Sorted | SelectionOrder::Probabilistic
245 )
246 }
247
248 fn mixed_seed(self, salt: u64) -> u64 {
249 splitmix64(self.step_seed ^ self.step_index.wrapping_mul(0x9E37_79B9_7F4A_7C15) ^ salt)
250 }
251}
252
253impl Default for MoveStreamContext {
254 fn default() -> Self {
255 Self::new(0, 0, None)
256 }
257}
258
259fn splitmix64(mut value: u64) -> u64 {
260 value = value.wrapping_add(0x9E37_79B9_7F4A_7C15);
261 value = (value ^ (value >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
262 value = (value ^ (value >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
263 value ^ (value >> 31)
264}
265
266fn gcd(mut left: usize, mut right: usize) -> usize {
267 while right != 0 {
268 let remainder = left % right;
269 left = right;
270 right = remainder;
271 }
272 left
273}
274
275impl<'a, S, M, C> Iterator for MoveSelectorIter<'a, S, M, C>
276where
277 S: PlanningSolution,
278 M: Move<S>,
279 C: MoveCursor<S, M>,
280{
281 type Item = M;
282
283 #[inline(always)]
284 fn next(&mut self) -> Option<Self::Item> {
285 self.cursor.next_owned_candidate(self.solution)
286 }
287}
288
289pub trait MoveSelector<S: PlanningSolution, M: Move<S>>: Send + Debug {
292 type Cursor<'a>: MoveCursor<S, M> + 'a
293 where
294 Self: 'a;
295
296 fn open_cursor<'a, D: Director<S>>(&'a self, score_director: &D) -> Self::Cursor<'a>;
297
298 fn open_cursor_with_context<'a, D: Director<S>>(
299 &'a self,
300 score_director: &D,
301 _context: MoveStreamContext,
302 ) -> Self::Cursor<'a> {
303 self.open_cursor(score_director)
304 }
305
306 fn validate_cursor<D: Director<S>>(&self, _score_director: &D) {}
311
312 fn iter_moves<'a, 'd, D: Director<S>>(
313 &'a self,
314 score_director: &'d D,
315 ) -> MoveSelectorIter<'d, S, M, Self::Cursor<'a>> {
316 MoveSelectorIter::new(
317 self.open_cursor(score_director),
318 score_director.working_solution(),
319 )
320 }
321
322 fn size<D: Director<S>>(&self, score_director: &D) -> usize;
323
324 fn append_moves<D: Director<S>>(&self, score_director: &D, arena: &mut MoveArena<M>) {
325 let mut cursor = self.open_cursor(score_director);
326 let solution = score_director.working_solution();
327 for id in collect_cursor_indices::<S, M, _>(&mut cursor, solution) {
328 arena.push(cursor.take_candidate(id));
329 }
330 }
331
332 fn is_never_ending(&self) -> bool {
333 false
334 }
335}