Skip to main content

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    ) -> impl 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(Clone, Debug)]
55pub struct FromSolutionEntitySelector {
56    /// The descriptor index to select from.
57    descriptor_index: usize,
58}
59
60impl FromSolutionEntitySelector {
61    /// Creates a new entity selector for the given descriptor index.
62    pub fn new(descriptor_index: usize) -> Self {
63        Self { descriptor_index }
64    }
65}
66
67impl<S: PlanningSolution> EntitySelector<S> for FromSolutionEntitySelector {
68    fn iter<'a, D: ScoreDirector<S>>(
69        &'a self,
70        score_director: &'a D,
71    ) -> impl Iterator<Item = EntityReference> + 'a {
72        let count = score_director
73            .entity_count(self.descriptor_index)
74            .unwrap_or(0);
75        let desc_idx = self.descriptor_index;
76        (0..count).map(move |i| EntityReference::new(desc_idx, i))
77    }
78
79    fn size<D: ScoreDirector<S>>(&self, score_director: &D) -> usize {
80        score_director
81            .entity_count(self.descriptor_index)
82            .unwrap_or(0)
83    }
84}
85
86/// An entity selector that iterates over all entities from all descriptors.
87#[derive(Debug, Clone, Default)]
88pub struct AllEntitiesSelector;
89
90impl AllEntitiesSelector {
91    /// Creates a new selector for all entities.
92    pub fn new() -> Self {
93        Self
94    }
95}
96
97impl<S: PlanningSolution> EntitySelector<S> for AllEntitiesSelector {
98    fn iter<'a, D: ScoreDirector<S>>(
99        &'a self,
100        score_director: &'a D,
101    ) -> impl Iterator<Item = EntityReference> + 'a {
102        let desc = score_director.solution_descriptor();
103        let descriptor_count = desc.entity_descriptors.len();
104
105        let mut refs = Vec::new();
106        for desc_idx in 0..descriptor_count {
107            let count = score_director.entity_count(desc_idx).unwrap_or(0);
108            for entity_idx in 0..count {
109                refs.push(EntityReference::new(desc_idx, entity_idx));
110            }
111        }
112
113        refs.into_iter()
114    }
115
116    fn size<D: ScoreDirector<S>>(&self, score_director: &D) -> usize {
117        score_director.total_entity_count().unwrap_or(0)
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124    use crate::test_utils::create_simple_nqueens_director;
125
126    #[test]
127    fn test_from_solution_entity_selector() {
128        let director = create_simple_nqueens_director(4);
129
130        // Verify column values match indices (column is set to index in create_uninitialized_nqueens)
131        let solution = director.working_solution();
132        for (i, queen) in solution.queens.iter().enumerate() {
133            assert_eq!(queen.column, i as i64);
134        }
135
136        let selector = FromSolutionEntitySelector::new(0);
137
138        let refs: Vec<_> = selector.iter(&director).collect();
139        assert_eq!(refs.len(), 4);
140        assert_eq!(refs[0], EntityReference::new(0, 0));
141        assert_eq!(refs[1], EntityReference::new(0, 1));
142        assert_eq!(refs[2], EntityReference::new(0, 2));
143        assert_eq!(refs[3], EntityReference::new(0, 3));
144
145        assert_eq!(selector.size(&director), 4);
146    }
147
148    #[test]
149    fn test_all_entities_selector() {
150        let director = create_simple_nqueens_director(3);
151
152        let selector = AllEntitiesSelector::new();
153
154        let refs: Vec<_> = selector.iter(&director).collect();
155        assert_eq!(refs.len(), 3);
156
157        assert_eq!(selector.size(&director), 3);
158    }
159}