Skip to main content

solverforge_solver/heuristic/selector/
selection_order.rs

1/* Selection order configuration for selectors.
2
3Defines the order in which elements are selected from a selector.
4*/
5
6/* Defines the order in which elements are selected from a selector.
7
8This enum controls how entities, values, or moves are ordered when
9iterating through a selector.
10*/
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
12pub enum SelectionOrder {
13    /* Inherit the selection order from the parent configuration.
14
15    If the parent is cached, defaults to `Original`.
16    If there is no parent, defaults to `Random`.
17    */
18    #[default]
19    Inherit,
20
21    /* Select elements in their original order.
22
23    Elements are returned in the order they appear in the underlying
24    collection. This is deterministic and reproducible.
25    */
26    Original,
27
28    /* Select elements in random order without shuffling.
29
30    Elements are selected randomly from the pool on each call to next().
31    The same element may be selected multiple times.
32    This scales well because it does not require caching.
33    */
34    Random,
35
36    /* Select elements in random order by shuffling.
37
38    Elements are shuffled when a selection iterator is created.
39    Each element will be selected exactly once (if all elements are consumed).
40    Requires caching (at least step-level).
41    */
42    Shuffled,
43
44    /* Select elements in sorted order.
45
46    Elements are sorted according to a sorter before iteration.
47    Each element will be selected exactly once (if all elements are consumed).
48    Requires caching (at least step-level).
49    */
50    Sorted,
51
52    /* Select elements based on probability weights.
53
54    Elements with higher probability have a greater chance of being selected.
55    The same element may be selected multiple times.
56    Requires caching (at least step-level).
57    */
58    Probabilistic,
59}
60
61impl SelectionOrder {
62    /// Resolves the selection order by inheriting from a parent if necessary.
63    ///
64    /// # Arguments
65    ///
66    /// * `inherited` - The selection order to inherit from if this is `Inherit`
67    ///
68    /// # Returns
69    ///
70    /// The resolved selection order (never `Inherit`)
71    pub fn resolve(self, inherited: SelectionOrder) -> SelectionOrder {
72        match self {
73            SelectionOrder::Inherit => {
74                if inherited == SelectionOrder::Inherit {
75                    SelectionOrder::Random
76                } else {
77                    inherited
78                }
79            }
80            other => other,
81        }
82    }
83
84    /// Returns `true` if this selection order implies random selection.
85    ///
86    /// This is used to determine whether a selector should use random iteration
87    /// or deterministic iteration.
88    pub fn is_random(&self) -> bool {
89        matches!(
90            self,
91            SelectionOrder::Random | SelectionOrder::Shuffled | SelectionOrder::Probabilistic
92        )
93    }
94
95    /// Returns `true` if this selection order requires caching.
96    ///
97    /// Some selection orders need to collect all elements before iteration
98    /// can begin (e.g., Shuffled, Sorted, Probabilistic).
99    pub fn requires_caching(&self) -> bool {
100        matches!(
101            self,
102            SelectionOrder::Shuffled | SelectionOrder::Sorted | SelectionOrder::Probabilistic
103        )
104    }
105
106    /// Converts from a boolean random selection flag.
107    ///
108    /// # Arguments
109    ///
110    /// * `random` - `true` for `Random`, `false` for `Original`
111    pub fn from_random_selection(random: bool) -> Self {
112        if random {
113            SelectionOrder::Random
114        } else {
115            SelectionOrder::Original
116        }
117    }
118
119    /// Converts to a boolean random selection flag.
120    ///
121    /// # Panics
122    ///
123    /// Panics if this is not `Random` or `Original`.
124    pub fn to_random_selection(&self) -> bool {
125        match self {
126            SelectionOrder::Random => true,
127            SelectionOrder::Original => false,
128            _ => panic!(
129                "Selection order {:?} cannot be converted to a random selection boolean",
130                self
131            ),
132        }
133    }
134}
135
136#[cfg(test)]
137mod tests {
138    use super::*;
139
140    #[test]
141    fn test_resolve_inherit_from_random() {
142        let order = SelectionOrder::Inherit;
143        assert_eq!(
144            order.resolve(SelectionOrder::Random),
145            SelectionOrder::Random
146        );
147    }
148
149    #[test]
150    fn test_resolve_inherit_from_original() {
151        let order = SelectionOrder::Inherit;
152        assert_eq!(
153            order.resolve(SelectionOrder::Original),
154            SelectionOrder::Original
155        );
156    }
157
158    #[test]
159    fn test_resolve_inherit_from_inherit() {
160        let order = SelectionOrder::Inherit;
161        // When inheriting from Inherit, default to Random
162        assert_eq!(
163            order.resolve(SelectionOrder::Inherit),
164            SelectionOrder::Random
165        );
166    }
167
168    #[test]
169    fn test_resolve_non_inherit() {
170        let order = SelectionOrder::Original;
171        // Non-inherit should not change
172        assert_eq!(
173            order.resolve(SelectionOrder::Random),
174            SelectionOrder::Original
175        );
176    }
177
178    #[test]
179    fn test_is_random() {
180        assert!(SelectionOrder::Random.is_random());
181        assert!(SelectionOrder::Shuffled.is_random());
182        assert!(SelectionOrder::Probabilistic.is_random());
183
184        assert!(!SelectionOrder::Original.is_random());
185        assert!(!SelectionOrder::Sorted.is_random());
186        assert!(!SelectionOrder::Inherit.is_random());
187    }
188
189    #[test]
190    fn test_requires_caching() {
191        assert!(SelectionOrder::Shuffled.requires_caching());
192        assert!(SelectionOrder::Sorted.requires_caching());
193        assert!(SelectionOrder::Probabilistic.requires_caching());
194
195        assert!(!SelectionOrder::Original.requires_caching());
196        assert!(!SelectionOrder::Random.requires_caching());
197        assert!(!SelectionOrder::Inherit.requires_caching());
198    }
199
200    #[test]
201    fn test_from_random_selection() {
202        assert_eq!(
203            SelectionOrder::from_random_selection(true),
204            SelectionOrder::Random
205        );
206        assert_eq!(
207            SelectionOrder::from_random_selection(false),
208            SelectionOrder::Original
209        );
210    }
211
212    #[test]
213    fn test_to_random_selection() {
214        assert!(SelectionOrder::Random.to_random_selection());
215        assert!(!SelectionOrder::Original.to_random_selection());
216    }
217
218    #[test]
219    #[should_panic(expected = "cannot be converted")]
220    fn test_to_random_selection_panics_on_shuffled() {
221        SelectionOrder::Shuffled.to_random_selection();
222    }
223
224    #[test]
225    fn test_default() {
226        assert_eq!(SelectionOrder::default(), SelectionOrder::Inherit);
227    }
228}