Skip to main content

solverforge_solver/heuristic/selector/
value_selector.rs

1/* Value selectors for high-performance value iteration.
2
3Unlike the type-erased `ValueSelector` that yields `Arc<dyn Any>`,
4these selectors yield `V` directly with no heap allocation.
5*/
6
7use std::fmt::Debug;
8use std::marker::PhantomData;
9
10use solverforge_core::domain::PlanningSolution;
11use solverforge_scoring::Director;
12
13/// A value selector that yields values of type `V` directly.
14///
15/// Unlike `ValueSelector` which returns `Arc<dyn Any>`, this trait
16/// returns `V` inline, eliminating heap allocation per value.
17///
18/// # Type Parameters
19/// * `S` - The planning solution type
20/// * `V` - The value type
21pub trait ValueSelector<S: PlanningSolution, V>: Send + Debug {
22    // Returns an iterator over values for the given entity.
23    fn iter_typed<'a, D: Director<S>>(
24        &'a self,
25        score_director: &'a D,
26        descriptor_index: usize,
27        entity_index: usize,
28    ) -> impl Iterator<Item = V> + 'a;
29
30    fn size<D: Director<S>>(
31        &self,
32        score_director: &D,
33        descriptor_index: usize,
34        entity_index: usize,
35    ) -> usize;
36
37    // Returns true if this selector may return the same value multiple times.
38    fn is_never_ending(&self) -> bool {
39        false
40    }
41}
42
43/// A value selector with a static list of values.
44pub struct StaticValueSelector<S, V> {
45    values: Vec<V>,
46    _phantom: PhantomData<fn() -> S>,
47}
48
49impl<S, V: Clone> Clone for StaticValueSelector<S, V> {
50    fn clone(&self) -> Self {
51        Self {
52            values: self.values.clone(),
53            _phantom: PhantomData,
54        }
55    }
56}
57
58impl<S, V: Debug> Debug for StaticValueSelector<S, V> {
59    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60        f.debug_struct("StaticValueSelector")
61            .field("values", &self.values)
62            .finish()
63    }
64}
65
66impl<S, V: Clone> StaticValueSelector<S, V> {
67    pub fn new(values: Vec<V>) -> Self {
68        Self {
69            values,
70            _phantom: PhantomData,
71        }
72    }
73
74    pub fn values(&self) -> &[V] {
75        &self.values
76    }
77}
78
79impl<S, V> ValueSelector<S, V> for StaticValueSelector<S, V>
80where
81    S: PlanningSolution,
82    V: Clone + Send + Debug + 'static,
83{
84    fn iter_typed<'a, D: Director<S>>(
85        &'a self,
86        _score_director: &'a D,
87        _descriptor_index: usize,
88        _entity_index: usize,
89    ) -> impl Iterator<Item = V> + 'a {
90        self.values.iter().cloned()
91    }
92
93    fn size<D: Director<S>>(
94        &self,
95        _score_director: &D,
96        _descriptor_index: usize,
97        _entity_index: usize,
98    ) -> usize {
99        self.values.len()
100    }
101}
102
103/// A value selector that extracts values from the solution using a function pointer.
104pub struct FromSolutionValueSelector<S, V> {
105    extractor: fn(&S) -> Vec<V>,
106    _phantom: PhantomData<(fn() -> S, fn() -> V)>,
107}
108
109impl<S, V> Debug for FromSolutionValueSelector<S, V> {
110    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111        f.debug_struct("FromSolutionValueSelector").finish()
112    }
113}
114
115impl<S, V> FromSolutionValueSelector<S, V> {
116    pub fn new(extractor: fn(&S) -> Vec<V>) -> Self {
117        Self {
118            extractor,
119            _phantom: PhantomData,
120        }
121    }
122}
123
124pub struct PerEntityValueSelector<S, V> {
125    extractor: fn(&S, usize) -> Vec<V>,
126    _phantom: PhantomData<(fn() -> S, fn() -> V)>,
127}
128
129impl<S, V> Debug for PerEntityValueSelector<S, V> {
130    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131        f.debug_struct("PerEntityValueSelector").finish()
132    }
133}
134
135impl<S, V> PerEntityValueSelector<S, V> {
136    pub fn new(extractor: fn(&S, usize) -> Vec<V>) -> Self {
137        Self {
138            extractor,
139            _phantom: PhantomData,
140        }
141    }
142}
143
144impl<S, V> ValueSelector<S, V> for PerEntityValueSelector<S, V>
145where
146    S: PlanningSolution,
147    V: Clone + Send + Debug + 'static,
148{
149    fn iter_typed<'a, D: Director<S>>(
150        &'a self,
151        score_director: &'a D,
152        _descriptor_index: usize,
153        entity_index: usize,
154    ) -> impl Iterator<Item = V> + 'a {
155        (self.extractor)(score_director.working_solution(), entity_index).into_iter()
156    }
157
158    fn size<D: Director<S>>(
159        &self,
160        score_director: &D,
161        _descriptor_index: usize,
162        entity_index: usize,
163    ) -> usize {
164        (self.extractor)(score_director.working_solution(), entity_index).len()
165    }
166}
167
168impl<S, V> ValueSelector<S, V> for FromSolutionValueSelector<S, V>
169where
170    S: PlanningSolution,
171    V: Clone + Send + Debug + 'static,
172{
173    fn iter_typed<'a, D: Director<S>>(
174        &'a self,
175        score_director: &'a D,
176        _descriptor_index: usize,
177        _entity_index: usize,
178    ) -> impl Iterator<Item = V> + 'a {
179        let values = (self.extractor)(score_director.working_solution());
180        values.into_iter()
181    }
182
183    fn size<D: Director<S>>(
184        &self,
185        score_director: &D,
186        _descriptor_index: usize,
187        _entity_index: usize,
188    ) -> usize {
189        (self.extractor)(score_director.working_solution()).len()
190    }
191}
192
193/// A value selector that generates a range of usize values 0..count.
194///
195/// Uses a function pointer to get the count from the solution.
196pub struct RangeValueSelector<S> {
197    count_fn: fn(&S) -> usize,
198    _phantom: PhantomData<fn() -> S>,
199}
200
201impl<S> Debug for RangeValueSelector<S> {
202    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
203        f.debug_struct("RangeValueSelector").finish()
204    }
205}
206
207impl<S> RangeValueSelector<S> {
208    pub fn new(count_fn: fn(&S) -> usize) -> Self {
209        Self {
210            count_fn,
211            _phantom: PhantomData,
212        }
213    }
214}
215
216impl<S> ValueSelector<S, usize> for RangeValueSelector<S>
217where
218    S: PlanningSolution,
219{
220    fn iter_typed<'a, D: Director<S>>(
221        &'a self,
222        score_director: &'a D,
223        _descriptor_index: usize,
224        _entity_index: usize,
225    ) -> impl Iterator<Item = usize> + 'a {
226        let count = (self.count_fn)(score_director.working_solution());
227        0..count
228    }
229
230    fn size<D: Director<S>>(
231        &self,
232        score_director: &D,
233        _descriptor_index: usize,
234        _entity_index: usize,
235    ) -> usize {
236        (self.count_fn)(score_director.working_solution())
237    }
238}
239
240#[cfg(test)]
241#[path = "value_selector_tests.rs"]
242mod tests;