radiate_core/stats/
set.rs

1use 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    // --- Default accessors ---
172    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}