use std::collections::HashMap;
use super::{ExperimentRecord, MetricRecord, RunRecord};
#[derive(Debug, Default)]
pub struct ExperimentStore {
experiments: HashMap<String, ExperimentRecord>,
runs: HashMap<String, RunRecord>,
metrics: Vec<MetricRecord>,
}
impl ExperimentStore {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.experiments.is_empty() && self.runs.is_empty() && self.metrics.is_empty()
}
#[must_use]
pub fn experiment_count(&self) -> usize {
self.experiments.len()
}
#[must_use]
pub fn run_count(&self) -> usize {
self.runs.len()
}
#[must_use]
pub fn metric_count(&self) -> usize {
self.metrics.len()
}
pub fn add_experiment(&mut self, experiment: ExperimentRecord) {
self.experiments.insert(experiment.experiment_id().to_string(), experiment);
}
#[must_use]
pub fn get_experiment(&self, experiment_id: &str) -> Option<&ExperimentRecord> {
self.experiments.get(experiment_id)
}
pub fn add_run(&mut self, run: RunRecord) {
self.runs.insert(run.run_id().to_string(), run);
}
#[must_use]
pub fn get_run(&self, run_id: &str) -> Option<&RunRecord> {
self.runs.get(run_id)
}
#[must_use]
pub fn get_runs_for_experiment(&self, experiment_id: &str) -> Vec<&RunRecord> {
self.runs.values().filter(|run| run.experiment_id() == experiment_id).collect()
}
pub fn add_metric(&mut self, metric: MetricRecord) {
self.metrics.push(metric);
}
#[must_use]
pub fn get_metrics_for_run(&self, run_id: &str, key: &str) -> Vec<MetricRecord> {
let mut metrics: Vec<MetricRecord> = self
.metrics
.iter()
.filter(|m| m.run_id() == run_id && m.key() == key)
.cloned()
.collect();
metrics.sort_by_key(MetricRecord::step);
metrics
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_store_default() {
let store = ExperimentStore::new();
assert!(store.is_empty());
assert_eq!(store.experiment_count(), 0);
assert_eq!(store.run_count(), 0);
assert_eq!(store.metric_count(), 0);
}
#[test]
fn test_store_add_and_get() {
let mut store = ExperimentStore::new();
let experiment = ExperimentRecord::new("exp-1", "Test");
store.add_experiment(experiment);
let run = RunRecord::new("run-1", "exp-1");
store.add_run(run);
let metric = MetricRecord::new("run-1", "loss", 0, 0.5);
store.add_metric(metric);
assert!(!store.is_empty());
assert!(store.get_experiment("exp-1").is_some());
assert!(store.get_run("run-1").is_some());
}
#[test]
fn test_get_metrics_for_run_ordering() {
let mut store = ExperimentStore::new();
store.add_metric(MetricRecord::new("run-1", "loss", 2, 0.2));
store.add_metric(MetricRecord::new("run-1", "loss", 0, 0.0));
store.add_metric(MetricRecord::new("run-1", "loss", 1, 0.1));
let metrics = store.get_metrics_for_run("run-1", "loss");
assert_eq!(metrics.len(), 3);
assert_eq!(metrics[0].step(), 0);
assert_eq!(metrics[1].step(), 1);
assert_eq!(metrics[2].step(), 2);
}
}