radiate_core/stats/
set.rs1use crate::{
2 Metric, MetricUpdate,
3 stats::{Tag, TagKind, defaults::try_add_tag_from_str, fmt},
4};
5use radiate_utils::intern;
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8use std::{
9 collections::HashMap,
10 fmt::{Debug, Display},
11};
12
13pub(super) const METRIC_SET: &str = "metric_set";
14
15pub struct MetricSetSummary {
16 pub metrics: usize,
17 pub updates: f32,
18}
19
20#[derive(Clone, Default)]
21pub struct MetricSet {
22 metrics: HashMap<&'static str, Metric>,
23 set_stats: Metric,
24}
25
26impl MetricSet {
27 pub fn new() -> Self {
28 MetricSet {
29 metrics: HashMap::new(),
30 set_stats: Metric::new(METRIC_SET),
31 }
32 }
33
34 #[inline(always)]
35 pub fn keys(&self) -> Vec<&'static str> {
36 self.metrics.keys().cloned().collect()
37 }
38
39 #[inline(always)]
40 pub fn flush_all_into(&mut self, target: &mut MetricSet) {
41 for (key, mut m) in self.metrics.drain() {
42 if let Some(target_metric) = target.metrics.get_mut(key) {
43 target_metric.update_from(m);
44 } else {
45 try_add_tag_from_str(&mut m);
46 target.metrics.insert(key, m);
47 }
48 }
49
50 target.set_stats.update_from(self.set_stats.clone());
51 self.clear();
52 }
53
54 #[inline(always)]
55 pub fn upsert<'a>(&mut self, metric: impl Into<MetricSetUpdate<'a>>) {
56 let update = metric.into();
57 match update {
58 MetricSetUpdate::Many(metrics) => {
59 for metric in metrics {
60 self.add_or_update_internal(metric);
61 }
62 }
63 MetricSetUpdate::Single(metric) => {
64 self.add_or_update_internal(metric);
65 }
66 MetricSetUpdate::NamedSingle(name, metric_update) => {
67 self.set_stats.apply_update(1);
68 if let Some(m) = self.metrics.get_mut(name) {
69 m.apply_update(metric_update);
70 return;
71 }
72
73 let new_name = radiate_utils::intern_name_as_snake_case(name);
74 if let Some(m) = self.metrics.get_mut(&new_name) {
75 m.apply_update(metric_update);
76 } else {
77 let mut metric = Metric::new(new_name);
78 metric.apply_update(metric_update);
79 self.add(metric);
80 }
81 }
82 }
83 }
84
85 fn add_or_update_internal(&mut self, metric: Metric) {
86 self.set_stats.apply_update(1);
87 if let Some(existing) = self.metrics.get_mut(metric.name()) {
88 existing.update_from(metric);
89 } else {
90 self.metrics.insert(intern!(metric.name()), metric);
91 }
92 }
93
94 pub fn iter_tagged<'a>(
95 &'a self,
96 tag: TagKind,
97 ) -> impl Iterator<Item = (&'static str, &'a Metric)> {
98 self.metrics
99 .iter()
100 .filter_map(move |(k, m)| if m.tags.has(tag) { Some((*k, m)) } else { None })
101 }
102
103 pub fn iter_stats<'a>(&'a self) -> impl Iterator<Item = &'a Metric> {
104 self.metrics.values().filter(|m| m.statistic().is_some())
105 }
106
107 pub fn iter_times<'a>(&'a self) -> impl Iterator<Item = &'a Metric> {
108 self.metrics
109 .values()
110 .filter(|m| m.time_statistic().is_some())
111 }
112
113 pub fn tags(&self) -> impl Iterator<Item = TagKind> {
114 self.metrics
115 .values()
116 .fold(Tag::empty(), |acc, m| acc.union(m.tags))
117 .into_iter()
118 }
119
120 #[inline(always)]
121 pub fn iter(&self) -> impl Iterator<Item = (&'static str, &Metric)> {
122 self.metrics.iter().map(|(name, metric)| (*name, metric))
123 }
124
125 #[inline(always)]
126 pub fn add(&mut self, metric: Metric) {
127 self.metrics.insert(intern!(metric.name()), metric);
128 }
129
130 #[inline(always)]
131 pub fn get(&self, name: &str) -> Option<&Metric> {
132 self.metrics.get(name)
133 }
134
135 #[inline(always)]
136 pub fn get_from_string(&self, name: String) -> Option<&Metric> {
137 self.metrics.get(name.as_str())
138 }
139
140 #[inline(always)]
141 pub fn clear(&mut self) {
142 for (_, m) in self.metrics.iter_mut() {
143 m.clear_values();
144 }
145
146 self.set_stats.clear_values();
147 }
148
149 #[inline(always)]
150 pub fn contains_key(&self, name: &str) -> bool {
151 self.metrics.contains_key(intern!(name))
152 }
153
154 #[inline(always)]
155 pub fn len(&self) -> usize {
156 self.metrics.len()
157 }
158
159 #[inline(always)]
160 pub fn summary(&self) -> MetricSetSummary {
161 MetricSetSummary {
162 metrics: self.metrics.len(),
163 updates: self.set_stats.statistic().map(|s| s.sum()).unwrap_or(0.0),
164 }
165 }
166
167 pub fn dashboard(&self) -> String {
168 fmt::render_full(self).unwrap_or_default()
169 }
170
171 pub fn time(&self) -> Option<&Metric> {
173 self.get(super::metric_names::TIME)
174 }
175
176 pub fn score(&self) -> Option<&Metric> {
177 self.get(super::metric_names::SCORES)
178 }
179
180 pub fn improvements(&self) -> Option<&Metric> {
181 self.get(super::metric_names::BEST_SCORE_IMPROVEMENT)
182 }
183
184 pub fn age(&self) -> Option<&Metric> {
185 self.get(super::metric_names::AGE)
186 }
187
188 pub fn replace_age(&self) -> Option<&Metric> {
189 self.get(super::metric_names::REPLACE_AGE)
190 }
191
192 pub fn replace_invalid(&self) -> Option<&Metric> {
193 self.get(super::metric_names::REPLACE_INVALID)
194 }
195
196 pub fn genome_size(&self) -> Option<&Metric> {
197 self.get(super::metric_names::GENOME_SIZE)
198 }
199
200 pub fn front_size(&self) -> Option<&Metric> {
201 self.get(super::metric_names::FRONT_SIZE)
202 }
203
204 pub fn front_comparisons(&self) -> Option<&Metric> {
205 self.get(super::metric_names::FRONT_COMPARISONS)
206 }
207
208 pub fn front_removals(&self) -> Option<&Metric> {
209 self.get(super::metric_names::FRONT_REMOVALS)
210 }
211
212 pub fn front_additions(&self) -> Option<&Metric> {
213 self.get(super::metric_names::FRONT_ADDITIONS)
214 }
215
216 pub fn front_entropy(&self) -> Option<&Metric> {
217 self.get(super::metric_names::FRONT_ENTROPY)
218 }
219
220 pub fn unique_members(&self) -> Option<&Metric> {
221 self.get(super::metric_names::UNIQUE_MEMBERS)
222 }
223
224 pub fn unique_scores(&self) -> Option<&Metric> {
225 self.get(super::metric_names::UNIQUE_SCORES)
226 }
227
228 pub fn new_children(&self) -> Option<&Metric> {
229 self.get(super::metric_names::NEW_CHILDREN)
230 }
231
232 pub fn survivor_count(&self) -> Option<&Metric> {
233 self.get(super::metric_names::SURVIVOR_COUNT)
234 }
235
236 pub fn carryover_rate(&self) -> Option<&Metric> {
237 self.get(super::metric_names::CARRYOVER_RATE)
238 }
239
240 pub fn evaluation_count(&self) -> Option<&Metric> {
241 self.get(super::metric_names::EVALUATION_COUNT)
242 }
243
244 pub fn diversity_ratio(&self) -> Option<&Metric> {
245 self.get(super::metric_names::DIVERSITY_RATIO)
246 }
247
248 pub fn score_volatility(&self) -> Option<&Metric> {
249 self.get(super::metric_names::SCORE_VOLATILITY)
250 }
251
252 pub fn species_count(&self) -> Option<&Metric> {
253 self.get(super::metric_names::SPECIES_COUNT)
254 }
255
256 pub fn species_age_fail(&self) -> Option<&Metric> {
257 self.get(super::metric_names::SPECIES_AGE_FAIL)
258 }
259
260 pub fn species_distance_dist(&self) -> Option<&Metric> {
261 self.get(super::metric_names::SPECIES_DISTANCE_DIST)
262 }
263
264 pub fn species_created(&self) -> Option<&Metric> {
265 self.get(super::metric_names::SPECIES_CREATED)
266 }
267
268 pub fn species_died(&self) -> Option<&Metric> {
269 self.get(super::metric_names::SPECIES_DIED)
270 }
271
272 pub fn species_age(&self) -> Option<&Metric> {
273 self.get(super::metric_names::SPECIES_AGE)
274 }
275}
276
277impl Display for MetricSet {
278 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
279 let summary = self.summary();
280 let out = format!(
281 "[{} metrics, {:.0} updates]",
282 summary.metrics, summary.updates
283 );
284 write!(f, "{out}\n{}", fmt::render_full(self).unwrap_or_default())?;
285 Ok(())
286 }
287}
288
289impl Debug for MetricSet {
290 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
291 write!(f, "MetricSet {{\n")?;
292 write!(f, "{}\n", fmt::render_dashboard(&self).unwrap_or_default())?;
293 write!(f, "}}")
294 }
295}
296
297#[cfg(feature = "serde")]
298impl Serialize for MetricSet {
299 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
300 where
301 S: serde::Serializer,
302 {
303 let metrics = self
304 .metrics
305 .iter()
306 .map(|(_, metric)| metric.clone())
307 .collect::<Vec<Metric>>();
308 metrics.serialize(serializer)
309 }
310}
311
312#[cfg(feature = "serde")]
313impl<'de> Deserialize<'de> for MetricSet {
314 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
315 where
316 D: serde::Deserializer<'de>,
317 {
318 let metrics = Vec::<Metric>::deserialize(deserializer)?;
319
320 let mut metric_set = MetricSet::new();
321 for metric in metrics {
322 metric_set.add(metric);
323 }
324
325 Ok(metric_set)
326 }
327}
328
329pub enum MetricSetUpdate<'a> {
330 Many(Vec<Metric>),
331 Single(Metric),
332 NamedSingle(&'static str, MetricUpdate<'a>),
333}
334
335impl From<Vec<Metric>> for MetricSetUpdate<'_> {
336 fn from(metrics: Vec<Metric>) -> Self {
337 MetricSetUpdate::Many(metrics)
338 }
339}
340
341impl From<Metric> for MetricSetUpdate<'_> {
342 fn from(metric: Metric) -> Self {
343 MetricSetUpdate::Single(metric)
344 }
345}
346
347impl<'a, U> From<(&'static str, U)> for MetricSetUpdate<'a>
348where
349 U: Into<MetricUpdate<'a>>,
350{
351 fn from((name, update): (&'static str, U)) -> Self {
352 MetricSetUpdate::NamedSingle(name, update.into())
353 }
354}