solverforge_solver/stats/
phase.rs1use std::time::{Duration, Instant};
2
3use super::{SelectorTelemetry, Throughput};
4
5#[derive(Debug)]
6pub struct PhaseStats {
7 pub phase_index: usize,
9 pub phase_type: &'static str,
11 start_time: Instant,
12 pub step_count: u64,
14 pub moves_generated: u64,
16 pub moves_evaluated: u64,
18 pub moves_accepted: u64,
20 pub moves_applied: u64,
22 pub moves_not_doable: u64,
23 pub moves_acceptor_rejected: u64,
24 pub moves_forager_ignored: u64,
25 pub moves_hard_improving: u64,
26 pub moves_hard_neutral: u64,
27 pub moves_hard_worse: u64,
28 pub conflict_repair_provider_generated: u64,
29 pub conflict_repair_duplicate_filtered: u64,
30 pub conflict_repair_illegal_filtered: u64,
31 pub conflict_repair_not_doable_filtered: u64,
32 pub conflict_repair_hard_improving: u64,
33 pub conflict_repair_exposed: u64,
34 pub score_calculations: u64,
36 pub construction_slots_assigned: u64,
37 pub construction_slots_kept: u64,
38 pub construction_slots_no_doable: u64,
39 generation_time: Duration,
40 evaluation_time: Duration,
41 selector_stats: Vec<SelectorTelemetry>,
42}
43
44impl PhaseStats {
45 pub fn new(phase_index: usize, phase_type: &'static str) -> Self {
47 Self {
48 phase_index,
49 phase_type,
50 start_time: Instant::now(),
51 step_count: 0,
52 moves_generated: 0,
53 moves_evaluated: 0,
54 moves_accepted: 0,
55 moves_applied: 0,
56 moves_not_doable: 0,
57 moves_acceptor_rejected: 0,
58 moves_forager_ignored: 0,
59 moves_hard_improving: 0,
60 moves_hard_neutral: 0,
61 moves_hard_worse: 0,
62 conflict_repair_provider_generated: 0,
63 conflict_repair_duplicate_filtered: 0,
64 conflict_repair_illegal_filtered: 0,
65 conflict_repair_not_doable_filtered: 0,
66 conflict_repair_hard_improving: 0,
67 conflict_repair_exposed: 0,
68 score_calculations: 0,
69 construction_slots_assigned: 0,
70 construction_slots_kept: 0,
71 construction_slots_no_doable: 0,
72 generation_time: Duration::default(),
73 evaluation_time: Duration::default(),
74 selector_stats: Vec::new(),
75 }
76 }
77
78 pub fn elapsed(&self) -> Duration {
79 self.start_time.elapsed()
80 }
81
82 pub fn record_step(&mut self) {
84 self.step_count += 1;
85 }
86
87 pub fn record_generated_batch(&mut self, count: u64, duration: Duration) {
89 self.moves_generated += count;
90 self.generation_time += duration;
91 }
92
93 pub fn record_selector_generated(
94 &mut self,
95 selector_index: usize,
96 count: u64,
97 duration: Duration,
98 ) {
99 self.record_generated_batch(count, duration);
100 let selector = self.selector_stats_entry(selector_index);
101 selector.moves_generated += count;
102 selector.generation_time += duration;
103 }
104
105 pub fn record_generation_time(&mut self, duration: Duration) {
107 self.generation_time += duration;
108 }
109
110 pub fn record_generated_move(&mut self, duration: Duration) {
112 self.record_generated_batch(1, duration);
113 }
114
115 pub fn record_evaluated_move(&mut self, duration: Duration) {
117 self.moves_evaluated += 1;
118 self.evaluation_time += duration;
119 }
120
121 pub fn record_selector_evaluated(&mut self, selector_index: usize, duration: Duration) {
122 self.record_evaluated_move(duration);
123 let selector = self.selector_stats_entry(selector_index);
124 selector.moves_evaluated += 1;
125 selector.evaluation_time += duration;
126 }
127
128 pub fn record_move_accepted(&mut self) {
130 self.moves_accepted += 1;
131 }
132
133 pub fn record_selector_accepted(&mut self, selector_index: usize) {
134 self.record_move_accepted();
135 self.selector_stats_entry(selector_index).moves_accepted += 1;
136 }
137
138 pub fn record_move_applied(&mut self) {
139 self.moves_applied += 1;
140 }
141
142 pub fn record_selector_applied(&mut self, selector_index: usize) {
143 self.record_move_applied();
144 self.selector_stats_entry(selector_index).moves_applied += 1;
145 }
146
147 pub fn record_move_not_doable(&mut self) {
148 self.moves_not_doable += 1;
149 }
150
151 pub fn record_selector_not_doable(&mut self, selector_index: usize) {
152 self.record_move_not_doable();
153 self.selector_stats_entry(selector_index).moves_not_doable += 1;
154 }
155
156 pub fn record_move_acceptor_rejected(&mut self) {
157 self.moves_acceptor_rejected += 1;
158 }
159
160 pub fn record_selector_acceptor_rejected(&mut self, selector_index: usize) {
161 self.record_move_acceptor_rejected();
162 self.selector_stats_entry(selector_index)
163 .moves_acceptor_rejected += 1;
164 }
165
166 pub fn record_moves_forager_ignored(&mut self, count: u64) {
167 self.moves_forager_ignored += count;
168 }
169
170 pub fn record_move_hard_improving(&mut self) {
171 self.moves_hard_improving += 1;
172 }
173
174 pub fn record_move_hard_neutral(&mut self) {
175 self.moves_hard_neutral += 1;
176 }
177
178 pub fn record_move_hard_worse(&mut self) {
179 self.moves_hard_worse += 1;
180 }
181
182 pub fn record_conflict_repair_provider_generated(&mut self, count: u64) {
183 self.conflict_repair_provider_generated += count;
184 }
185
186 pub fn record_conflict_repair_duplicate_filtered(&mut self) {
187 self.conflict_repair_duplicate_filtered += 1;
188 }
189
190 pub fn record_conflict_repair_illegal_filtered(&mut self) {
191 self.conflict_repair_illegal_filtered += 1;
192 }
193
194 pub fn record_conflict_repair_not_doable_filtered(&mut self) {
195 self.conflict_repair_not_doable_filtered += 1;
196 }
197
198 pub fn record_conflict_repair_hard_improving(&mut self) {
199 self.conflict_repair_hard_improving += 1;
200 }
201
202 pub fn record_conflict_repair_exposed(&mut self) {
203 self.conflict_repair_exposed += 1;
204 }
205
206 pub fn record_score_calculation(&mut self) {
208 self.score_calculations += 1;
209 }
210
211 pub fn record_construction_slot_assigned(&mut self) {
212 self.construction_slots_assigned += 1;
213 }
214
215 pub fn record_construction_slot_kept(&mut self) {
216 self.construction_slots_kept += 1;
217 }
218
219 pub fn record_construction_slot_no_doable(&mut self) {
220 self.construction_slots_no_doable += 1;
221 }
222
223 pub fn generated_throughput(&self) -> Throughput {
224 Throughput {
225 count: self.moves_generated,
226 elapsed: self.generation_time,
227 }
228 }
229
230 pub fn evaluated_throughput(&self) -> Throughput {
231 Throughput {
232 count: self.moves_evaluated,
233 elapsed: self.evaluation_time,
234 }
235 }
236
237 pub fn acceptance_rate(&self) -> f64 {
238 if self.moves_evaluated == 0 {
239 0.0
240 } else {
241 self.moves_accepted as f64 / self.moves_evaluated as f64
242 }
243 }
244
245 pub fn generation_time(&self) -> Duration {
246 self.generation_time
247 }
248
249 pub fn evaluation_time(&self) -> Duration {
250 self.evaluation_time
251 }
252
253 pub fn selector_telemetry(&self) -> &[SelectorTelemetry] {
254 &self.selector_stats
255 }
256
257 pub fn record_selector_generated_with_label(
258 &mut self,
259 selector_index: usize,
260 selector_label: impl Into<String>,
261 count: u64,
262 duration: Duration,
263 ) {
264 self.record_generated_batch(count, duration);
265 let selector = self.selector_stats_entry_with_label(selector_index, selector_label);
266 selector.moves_generated += count;
267 selector.generation_time += duration;
268 }
269
270 fn selector_stats_entry(&mut self, selector_index: usize) -> &mut SelectorTelemetry {
271 self.selector_stats_entry_with_label(selector_index, format!("selector-{selector_index}"))
272 }
273
274 fn selector_stats_entry_with_label(
275 &mut self,
276 selector_index: usize,
277 selector_label: impl Into<String>,
278 ) -> &mut SelectorTelemetry {
279 let selector_label = selector_label.into();
280 if let Some(position) = self
281 .selector_stats
282 .iter()
283 .position(|entry| entry.selector_index == selector_index)
284 {
285 if self.selector_stats[position]
286 .selector_label
287 .starts_with("selector-")
288 && !selector_label.starts_with("selector-")
289 {
290 self.selector_stats[position].selector_label = selector_label;
291 }
292 return &mut self.selector_stats[position];
293 }
294 self.selector_stats.push(SelectorTelemetry {
295 selector_index,
296 selector_label,
297 ..SelectorTelemetry::default()
298 });
299 self.selector_stats
300 .last_mut()
301 .expect("selector stats entry was just inserted")
302 }
303}