Skip to main content

solverforge_solver/heuristic/selector/
dynamic_scalar_change.rs

1use std::fmt::{self, Debug};
2use std::marker::PhantomData;
3
4use solverforge_core::domain::{DynamicScalarVariableSlot, PlanningSolution};
5use solverforge_scoring::Director;
6
7use crate::heuristic::r#move::{DynamicScalarChangeMove, MoveArena};
8
9use super::move_selector::{
10    CandidateId, CandidateStore, MoveCandidateRef, MoveCursor, MoveSelector, MoveStreamContext,
11};
12
13pub struct DynamicScalarChangeMoveSelector<S> {
14    slot: DynamicScalarVariableSlot<S>,
15    value_candidate_limit: Option<usize>,
16}
17
18struct DynamicScalarEntityValues {
19    entity_index: usize,
20    values: Vec<usize>,
21    current_assigned: bool,
22}
23
24pub struct DynamicScalarChangeMoveCursor<S>
25where
26    S: PlanningSolution,
27{
28    store: CandidateStore<S, DynamicScalarChangeMove<S>>,
29    entity_values: Vec<DynamicScalarEntityValues>,
30    entity_offset: usize,
31    value_offset: usize,
32    slot: DynamicScalarVariableSlot<S>,
33    _phantom: PhantomData<fn() -> S>,
34}
35
36impl<S> DynamicScalarChangeMoveCursor<S>
37where
38    S: PlanningSolution,
39{
40    fn new(
41        slot: DynamicScalarVariableSlot<S>,
42        entity_values: Vec<DynamicScalarEntityValues>,
43    ) -> Self {
44        Self {
45            store: CandidateStore::new(),
46            entity_values,
47            entity_offset: 0,
48            value_offset: 0,
49            slot,
50            _phantom: PhantomData,
51        }
52    }
53}
54
55impl<S> MoveCursor<S, DynamicScalarChangeMove<S>> for DynamicScalarChangeMoveCursor<S>
56where
57    S: PlanningSolution,
58{
59    fn next_candidate(&mut self) -> Option<CandidateId> {
60        while self.entity_offset < self.entity_values.len() {
61            let entity_values = &self.entity_values[self.entity_offset];
62            if self.value_offset < entity_values.values.len() {
63                let value = entity_values.values[self.value_offset];
64                self.value_offset += 1;
65                return Some(self.store.push(DynamicScalarChangeMove::new(
66                    self.slot.clone(),
67                    entity_values.entity_index,
68                    Some(value),
69                )));
70            }
71
72            let to_none_offset = entity_values.values.len();
73            if self.slot.allows_unassigned
74                && entity_values.current_assigned
75                && self.value_offset == to_none_offset
76            {
77                self.value_offset += 1;
78                return Some(self.store.push(DynamicScalarChangeMove::new(
79                    self.slot.clone(),
80                    entity_values.entity_index,
81                    None,
82                )));
83            }
84
85            self.entity_offset += 1;
86            self.value_offset = 0;
87        }
88
89        None
90    }
91
92    fn candidate(
93        &self,
94        id: CandidateId,
95    ) -> Option<MoveCandidateRef<'_, S, DynamicScalarChangeMove<S>>> {
96        self.store.candidate(id)
97    }
98
99    fn take_candidate(&mut self, id: CandidateId) -> DynamicScalarChangeMove<S> {
100        self.store.take_candidate(id)
101    }
102}
103
104impl<S> Iterator for DynamicScalarChangeMoveCursor<S>
105where
106    S: PlanningSolution,
107{
108    type Item = DynamicScalarChangeMove<S>;
109
110    fn next(&mut self) -> Option<Self::Item> {
111        let id = self.next_candidate()?;
112        Some(self.take_candidate(id))
113    }
114}
115
116impl<S> DynamicScalarChangeMoveSelector<S> {
117    pub fn new(slot: DynamicScalarVariableSlot<S>, value_candidate_limit: Option<usize>) -> Self {
118        Self {
119            slot,
120            value_candidate_limit,
121        }
122    }
123}
124
125impl<S> Debug for DynamicScalarChangeMoveSelector<S> {
126    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127        f.debug_struct("DynamicScalarChangeMoveSelector")
128            .field("slot", &self.slot)
129            .field("value_candidate_limit", &self.value_candidate_limit)
130            .finish()
131    }
132}
133
134impl<S> MoveSelector<S, DynamicScalarChangeMove<S>> for DynamicScalarChangeMoveSelector<S>
135where
136    S: PlanningSolution,
137{
138    type Cursor<'a>
139        = DynamicScalarChangeMoveCursor<S>
140    where
141        Self: 'a;
142
143    fn open_cursor<'a, D: Director<S>>(&'a self, score_director: &D) -> Self::Cursor<'a> {
144        self.open_cursor_with_context(score_director, MoveStreamContext::default())
145    }
146
147    fn open_cursor_with_context<'a, D: Director<S>>(
148        &'a self,
149        score_director: &D,
150        context: MoveStreamContext,
151    ) -> Self::Cursor<'a> {
152        let solution = score_director.working_solution();
153        let value_limit = self.value_candidate_limit.unwrap_or(usize::MAX);
154        let mut entity_values = (0..self.slot.entity_count(solution))
155            .map(|entity_index| {
156                let current_assigned = self.slot.current_value(solution, entity_index).is_some();
157                let mut values = self
158                    .slot
159                    .candidate_values(solution, entity_index)
160                    .iter()
161                    .copied()
162                    .take(value_limit)
163                    .collect::<Vec<_>>();
164                let start = context.start_offset(
165                    values.len(),
166                    0xD94E_5CA1_0000_0000
167                        ^ entity_index as u64
168                        ^ ((self.slot.descriptor_index() as u64) << 32)
169                        ^ self.slot.variable.0 as u64,
170                );
171                values.rotate_left(start);
172                DynamicScalarEntityValues {
173                    entity_index,
174                    values,
175                    current_assigned,
176                }
177            })
178            .collect::<Vec<_>>();
179        let entity_start = context.start_offset(
180            entity_values.len(),
181            0xD94E_5CA1_0000_0001
182                ^ ((self.slot.descriptor_index() as u64) << 32)
183                ^ self.slot.variable.0 as u64,
184        );
185        entity_values.rotate_left(entity_start);
186        DynamicScalarChangeMoveCursor::new(self.slot.clone(), entity_values)
187    }
188
189    fn size<D: Director<S>>(&self, score_director: &D) -> usize {
190        let solution = score_director.working_solution();
191        let value_limit = self.value_candidate_limit.unwrap_or(usize::MAX);
192        (0..self.slot.entity_count(solution))
193            .map(|entity_index| {
194                let candidate_count = self
195                    .slot
196                    .candidate_values(solution, entity_index)
197                    .iter()
198                    .take(value_limit)
199                    .count();
200                let unassigned_count = usize::from(
201                    self.slot.allows_unassigned
202                        && self.slot.current_value(solution, entity_index).is_some(),
203                );
204                candidate_count + unassigned_count
205            })
206            .sum()
207    }
208
209    fn append_moves<D: Director<S>>(
210        &self,
211        score_director: &D,
212        arena: &mut MoveArena<DynamicScalarChangeMove<S>>,
213    ) {
214        let mut cursor = self.open_cursor(score_director);
215        while let Some(id) = cursor.next_candidate() {
216            arena.push(cursor.take_candidate(id));
217        }
218    }
219}