solverforge_solver/heuristic/selector/
selection_order.rs

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