Skip to main content

solverforge_solver/heuristic/selector/move_selector/
change.rs

1/// A change move selector that generates `ChangeMove` instances.
2pub struct ChangeMoveSelector<S, V, ES, VS> {
3    entity_selector: ES,
4    value_selector: VS,
5    getter: fn(&S, usize, usize) -> Option<V>,
6    setter: fn(&mut S, usize, usize, Option<V>),
7    descriptor_index: usize,
8    variable_index: usize,
9    variable_name: &'static str,
10    allows_unassigned: bool,
11    _phantom: PhantomData<(fn() -> S, fn() -> V)>,
12}
13
14struct ChangeEntityValues<V> {
15    entity_ref: super::entity::EntityReference,
16    values: Vec<V>,
17    /// Value count before any drain: the `None` (unassigned) offer lands after
18    /// every value slot, so the count must survive draining.
19    total_values: usize,
20    current_assigned: bool,
21}
22
23pub struct ChangeMoveCursor<S, V>
24where
25    S: PlanningSolution,
26    V: Clone + PartialEq + Send + Sync + Debug + 'static,
27{
28    store: CandidateStore<S, ChangeMove<S, V>>,
29    entity_values: Vec<ChangeEntityValues<V>>,
30    entity_offset: usize,
31    value_offset: usize,
32    getter: fn(&S, usize, usize) -> Option<V>,
33    setter: fn(&mut S, usize, usize, Option<V>),
34    descriptor_index: usize,
35    variable_index: usize,
36    variable_name: &'static str,
37    allows_unassigned: bool,
38}
39
40impl<S, V> ChangeMoveCursor<S, V>
41where
42    S: PlanningSolution,
43    V: Clone + PartialEq + Send + Sync + Debug + 'static,
44{
45    fn new(
46        entity_values: Vec<ChangeEntityValues<V>>,
47        getter: fn(&S, usize, usize) -> Option<V>,
48        setter: fn(&mut S, usize, usize, Option<V>),
49        descriptor_index: usize,
50        variable_index: usize,
51        variable_name: &'static str,
52        allows_unassigned: bool,
53    ) -> Self {
54        Self {
55            store: CandidateStore::new(),
56            entity_values,
57            entity_offset: 0,
58            value_offset: 0,
59            getter,
60            setter,
61            descriptor_index,
62            variable_index,
63            variable_name,
64            allows_unassigned,
65        }
66    }
67
68    #[inline(always)]
69    fn next_move(&mut self) -> Option<ChangeMove<S, V>> {
70        while self.entity_offset < self.entity_values.len() {
71            if self.value_offset < self.entity_values[self.entity_offset].total_values {
72                let entity_values = &mut self.entity_values[self.entity_offset];
73                let value = entity_values
74                    .values
75                    .drain(..1)
76                    .next()
77                    .expect("value slot below total_values must exist before its drain");
78                self.value_offset += 1;
79                return Some(ChangeMove::new(
80                    entity_values.entity_ref.entity_index,
81                    Some(value),
82                    self.getter,
83                    self.setter,
84                    self.variable_index,
85                    self.variable_name,
86                    self.descriptor_index,
87                ));
88            }
89
90            let current = &self.entity_values[self.entity_offset];
91            let to_none_offset = current.total_values;
92            if self.allows_unassigned
93                && current.current_assigned
94                && self.value_offset == to_none_offset
95            {
96                self.value_offset += 1;
97                return Some(ChangeMove::new(
98                    current.entity_ref.entity_index,
99                    None,
100                    self.getter,
101                    self.setter,
102                    self.variable_index,
103                    self.variable_name,
104                    self.descriptor_index,
105                ));
106            }
107
108            self.entity_offset += 1;
109            self.value_offset = 0;
110        }
111        None
112    }
113}
114
115impl<S, V> MoveCursor<S, ChangeMove<S, V>> for ChangeMoveCursor<S, V>
116where
117    S: PlanningSolution,
118    V: Clone + PartialEq + Send + Sync + Debug + 'static,
119{
120    fn next_candidate(&mut self, _solution: &S) -> Option<CandidateId> {
121        let mov = self.next_move()?;
122        Some(self.store.push(mov))
123    }
124
125    fn candidate(
126        &self,
127        id: CandidateId,
128    ) -> Option<MoveCandidateRef<'_, S, ChangeMove<S, V>>> {
129        self.store.candidate(id)
130    }
131
132    fn take_candidate(&mut self, id: CandidateId) -> ChangeMove<S, V> {
133        self.store.take_candidate(id)
134    }
135
136    #[inline(always)]
137    fn next_owned_candidate(&mut self, _solution: &S) -> Option<ChangeMove<S, V>> {
138        self.next_move()
139    }
140
141    #[inline(always)]
142    fn next_owned_candidate_inspected<T, F>(&mut self,
143        _solution: &S, mut inspect: F) -> Option<(ChangeMove<S, V>, T)>
144    where
145        F: for<'a> FnMut(MoveCandidateRef<'a, S, ChangeMove<S, V>>) -> Option<T>,
146    {
147        loop {
148            let mov = self.next_move()?;
149            if let Some(metadata) = inspect(MoveCandidateRef::Borrowed(&mov)) {
150                return Some((mov, metadata));
151            }
152        }
153    }
154
155    fn release_candidate(&mut self, id: CandidateId) -> bool {
156        self.store.release_candidate(id)
157    }
158}
159
160
161impl<S, V: Debug, ES: Debug, VS: Debug> Debug for ChangeMoveSelector<S, V, ES, VS> {
162    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163        f.debug_struct("ChangeMoveSelector")
164            .field("entity_selector", &self.entity_selector)
165            .field("value_selector", &self.value_selector)
166            .field("descriptor_index", &self.descriptor_index)
167            .field("variable_index", &self.variable_index)
168            .field("variable_name", &self.variable_name)
169            .field("allows_unassigned", &self.allows_unassigned)
170            .finish()
171    }
172}
173
174impl<S: PlanningSolution, V: Clone, ES, VS> ChangeMoveSelector<S, V, ES, VS> {
175    pub fn new(
176        entity_selector: ES,
177        value_selector: VS,
178        getter: fn(&S, usize, usize) -> Option<V>,
179        setter: fn(&mut S, usize, usize, Option<V>),
180        descriptor_index: usize,
181        variable_index: usize,
182        variable_name: &'static str,
183    ) -> Self {
184        Self {
185            entity_selector,
186            value_selector,
187            getter,
188            setter,
189            descriptor_index,
190            variable_index,
191            variable_name,
192            allows_unassigned: false,
193            _phantom: PhantomData,
194        }
195    }
196
197    pub fn with_allows_unassigned(mut self, allows_unassigned: bool) -> Self {
198        self.allows_unassigned = allows_unassigned;
199        self
200    }
201}
202
203impl<S: PlanningSolution, V: Clone + Send + Sync + Debug + 'static>
204    ChangeMoveSelector<S, V, FromSolutionEntitySelector, StaticValueSelector<S, V>>
205{
206    pub fn simple(
207        getter: fn(&S, usize, usize) -> Option<V>,
208        setter: fn(&mut S, usize, usize, Option<V>),
209        descriptor_index: usize,
210        variable_index: usize,
211        variable_name: &'static str,
212        values: Vec<V>,
213    ) -> Self {
214        Self {
215            entity_selector: FromSolutionEntitySelector::new(descriptor_index),
216            value_selector: StaticValueSelector::new(values),
217            getter,
218            setter,
219            descriptor_index,
220            variable_index,
221            variable_name,
222            allows_unassigned: false,
223            _phantom: PhantomData,
224        }
225    }
226}
227
228impl<S, V, ES, VS> MoveSelector<S, ChangeMove<S, V>> for ChangeMoveSelector<S, V, ES, VS>
229where
230    S: PlanningSolution,
231    V: Clone + PartialEq + Send + Sync + Debug + 'static,
232    ES: EntitySelector<S>,
233    VS: ValueSelector<S, V>,
234{
235    type Cursor<'a>
236        = ChangeMoveCursor<S, V>
237    where
238        Self: 'a;
239
240    fn open_cursor<'a, D: Director<S>>(&'a self, score_director: &D) -> Self::Cursor<'a> {
241        self.open_cursor_with_context(score_director, MoveStreamContext::default())
242    }
243
244    fn open_cursor_with_context<'a, D: Director<S>>(
245        &'a self,
246        score_director: &D,
247        context: MoveStreamContext,
248    ) -> Self::Cursor<'a> {
249        let solution = score_director.working_solution();
250        let canonical_entities = self.entity_selector.iter(score_director).collect::<Vec<_>>();
251        let entity_count = canonical_entities.len();
252        let entity_salt = 0xC4A4_6E00_0000_0001
253            ^ ((self.descriptor_index as u64) << 32)
254            ^ self.variable_index as u64;
255        let entity_values = (0..entity_count)
256            .map(|entity_offset| {
257                let entity_ref = canonical_entities
258                    [context.selection_index(entity_offset, entity_count, entity_salt)];
259                let current_assigned = (self.getter)(
260                    solution,
261                    entity_ref.entity_index,
262                    self.variable_index,
263                )
264                .is_some();
265                let canonical_values = self
266                    .value_selector
267                    .iter(
268                        score_director,
269                        entity_ref.descriptor_index,
270                        entity_ref.entity_index,
271                    )
272                    .collect::<Vec<_>>();
273                let value_count = canonical_values.len();
274                let value_salt = 0xC4A4_6E00_0000_0000
275                    ^ entity_ref.entity_index as u64
276                    ^ ((self.descriptor_index as u64) << 32)
277                    ^ self.variable_index as u64;
278                let values = if context.is_canonical() {
279                    canonical_values
280                } else {
281                    (0..value_count)
282                        .map(|value_offset| {
283                            canonical_values
284                                [context.selection_index(value_offset, value_count, value_salt)]
285                            .clone()
286                        })
287                        .collect()
288                };
289                ChangeEntityValues {
290                    entity_ref,
291                    total_values: value_count,
292                    values,
293                    current_assigned,
294                }
295            })
296            .collect();
297        ChangeMoveCursor::new(
298            entity_values,
299            self.getter,
300            self.setter,
301            self.descriptor_index,
302            self.variable_index,
303            self.variable_name,
304            self.allows_unassigned,
305        )
306    }
307
308    fn size<D: Director<S>>(&self, score_director: &D) -> usize {
309        self.entity_selector
310            .iter(score_director)
311            .map(|entity_ref| {
312                self.value_selector.size(
313                    score_director,
314                    entity_ref.descriptor_index,
315                    entity_ref.entity_index,
316                ) + usize::from(
317                    self.allows_unassigned
318                        && (self.getter)(
319                            score_director.working_solution(),
320                            entity_ref.entity_index,
321                            self.variable_index,
322                        )
323                        .is_some(),
324                )
325            })
326            .sum()
327    }
328}