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}