solverforge_solver/heuristic/selector/
entity.rs

1//! Entity selectors for iterating over planning entities
2
3use std::fmt::Debug;
4
5use solverforge_core::domain::PlanningSolution;
6use solverforge_scoring::ScoreDirector;
7
8/// A reference to an entity within a solution.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10pub struct EntityReference {
11    /// Index of the entity descriptor.
12    pub descriptor_index: usize,
13    /// Index of the entity within its collection.
14    pub entity_index: usize,
15}
16
17impl EntityReference {
18    /// Creates a new entity reference.
19    pub fn new(descriptor_index: usize, entity_index: usize) -> Self {
20        Self {
21            descriptor_index,
22            entity_index,
23        }
24    }
25}
26
27/// Trait for selecting entities from a planning solution.
28///
29/// Entity selectors provide an iteration order over the entities that
30/// the solver will consider for moves.
31///
32/// # Type Parameters
33/// * `S` - The planning solution type
34pub trait EntitySelector<S: PlanningSolution>: Send + Debug {
35    /// Returns an iterator over entity references.
36    ///
37    /// The iterator yields `EntityReference` values that identify entities
38    /// within the solution.
39    fn iter<'a, D: ScoreDirector<S>>(
40        &'a self,
41        score_director: &'a D,
42    ) -> Box<dyn Iterator<Item = EntityReference> + 'a>;
43
44    /// Returns the approximate number of entities.
45    fn size<D: ScoreDirector<S>>(&self, score_director: &D) -> usize;
46
47    /// Returns true if this selector may return the same entity multiple times.
48    fn is_never_ending(&self) -> bool {
49        false
50    }
51}
52
53/// An entity selector that iterates over all entities from the solution.
54#[derive(Debug, Clone)]
55pub struct FromSolutionEntitySelector {
56    /// The descriptor index to select from.
57    descriptor_index: usize,
58    /// Whether to skip pinned entities.
59    skip_pinned: bool,
60}
61
62impl FromSolutionEntitySelector {
63    /// Creates a new entity selector for the given descriptor index.
64    pub fn new(descriptor_index: usize) -> Self {
65        Self {
66            descriptor_index,
67            skip_pinned: false,
68        }
69    }
70
71    /// Creates an entity selector that skips pinned entities.
72    pub fn with_skip_pinned(mut self, skip: bool) -> Self {
73        self.skip_pinned = skip;
74        self
75    }
76}
77
78impl<S: PlanningSolution> EntitySelector<S> for FromSolutionEntitySelector {
79    fn iter<'a, D: ScoreDirector<S>>(
80        &'a self,
81        score_director: &'a D,
82    ) -> Box<dyn Iterator<Item = EntityReference> + 'a> {
83        let count = score_director
84            .entity_count(self.descriptor_index)
85            .unwrap_or(0);
86
87        let desc_idx = self.descriptor_index;
88
89        Box::new((0..count).map(move |i| EntityReference::new(desc_idx, i)))
90    }
91
92    fn size<D: ScoreDirector<S>>(&self, score_director: &D) -> usize {
93        score_director
94            .entity_count(self.descriptor_index)
95            .unwrap_or(0)
96    }
97}
98
99/// An entity selector that iterates over all entities from all descriptors.
100#[derive(Debug, Clone, Default)]
101pub struct AllEntitiesSelector;
102
103impl AllEntitiesSelector {
104    /// Creates a new selector for all entities.
105    pub fn new() -> Self {
106        Self
107    }
108}
109
110impl<S: PlanningSolution> EntitySelector<S> for AllEntitiesSelector {
111    fn iter<'a, D: ScoreDirector<S>>(
112        &'a self,
113        score_director: &'a D,
114    ) -> Box<dyn Iterator<Item = EntityReference> + 'a> {
115        let desc = score_director.solution_descriptor();
116        let descriptor_count = desc.entity_descriptors.len();
117
118        let mut refs = Vec::new();
119        for desc_idx in 0..descriptor_count {
120            let count = score_director.entity_count(desc_idx).unwrap_or(0);
121            for entity_idx in 0..count {
122                refs.push(EntityReference::new(desc_idx, entity_idx));
123            }
124        }
125
126        Box::new(refs.into_iter())
127    }
128
129    fn size<D: ScoreDirector<S>>(&self, score_director: &D) -> usize {
130        score_director.total_entity_count().unwrap_or(0)
131    }
132}
133
134#[cfg(test)]
135mod tests {
136    use super::*;
137    use solverforge_core::domain::{EntityDescriptor, SolutionDescriptor, TypedEntityExtractor};
138    use solverforge_core::score::SimpleScore;
139    use solverforge_scoring::SimpleScoreDirector;
140    use std::any::TypeId;
141
142    #[derive(Clone, Debug)]
143    struct Queen {
144        id: i64,
145    }
146
147    #[derive(Clone, Debug)]
148    struct NQueensSolution {
149        queens: Vec<Queen>,
150        score: Option<SimpleScore>,
151    }
152
153    impl PlanningSolution for NQueensSolution {
154        type Score = SimpleScore;
155
156        fn score(&self) -> Option<Self::Score> {
157            self.score
158        }
159
160        fn set_score(&mut self, score: Option<Self::Score>) {
161            self.score = score;
162        }
163    }
164
165    fn get_queens(s: &NQueensSolution) -> &Vec<Queen> {
166        &s.queens
167    }
168
169    fn get_queens_mut(s: &mut NQueensSolution) -> &mut Vec<Queen> {
170        &mut s.queens
171    }
172
173    fn create_test_director(
174        n: usize,
175    ) -> SimpleScoreDirector<NQueensSolution, impl Fn(&NQueensSolution) -> SimpleScore> {
176        let queens: Vec<_> = (0..n).map(|i| Queen { id: i as i64 }).collect();
177
178        let solution = NQueensSolution {
179            queens,
180            score: None,
181        };
182
183        let extractor = Box::new(TypedEntityExtractor::new(
184            "Queen",
185            "queens",
186            get_queens,
187            get_queens_mut,
188        ));
189        let entity_desc = EntityDescriptor::new("Queen", TypeId::of::<Queen>(), "queens")
190            .with_extractor(extractor);
191
192        let descriptor =
193            SolutionDescriptor::new("NQueensSolution", TypeId::of::<NQueensSolution>())
194                .with_entity(entity_desc);
195
196        SimpleScoreDirector::with_calculator(solution, descriptor, |_| SimpleScore::of(0))
197    }
198
199    #[test]
200    fn test_from_solution_entity_selector() {
201        let director = create_test_director(4);
202
203        // Verify entity IDs match indices
204        let solution = director.working_solution();
205        for (i, queen) in solution.queens.iter().enumerate() {
206            assert_eq!(queen.id, i as i64);
207        }
208
209        let selector = FromSolutionEntitySelector::new(0);
210
211        let refs: Vec<_> = selector.iter(&director).collect();
212        assert_eq!(refs.len(), 4);
213        assert_eq!(refs[0], EntityReference::new(0, 0));
214        assert_eq!(refs[1], EntityReference::new(0, 1));
215        assert_eq!(refs[2], EntityReference::new(0, 2));
216        assert_eq!(refs[3], EntityReference::new(0, 3));
217
218        assert_eq!(selector.size(&director), 4);
219    }
220
221    #[test]
222    fn test_all_entities_selector() {
223        let director = create_test_director(3);
224
225        let selector = AllEntitiesSelector::new();
226
227        let refs: Vec<_> = selector.iter(&director).collect();
228        assert_eq!(refs.len(), 3);
229
230        assert_eq!(selector.size(&director), 3);
231    }
232}