zernel/experiments/
compare.rs1use super::store::Experiment;
4use std::collections::BTreeSet;
5
6pub fn compare(a: &Experiment, b: &Experiment) -> String {
8 let mut out = String::new();
9
10 out.push_str(&format!("Comparing: {} vs {}\n", a.id, b.id));
11 out.push_str(&format!(" {} vs {}\n\n", a.name, b.name));
12
13 let all_keys: BTreeSet<_> = a
15 .hyperparams
16 .keys()
17 .chain(b.hyperparams.keys())
18 .cloned()
19 .collect();
20
21 if !all_keys.is_empty() {
22 out.push_str("Hyperparameters:\n");
23 for key in &all_keys {
24 let va = a
25 .hyperparams
26 .get(key)
27 .map(|v| v.to_string())
28 .unwrap_or_default();
29 let vb = b
30 .hyperparams
31 .get(key)
32 .map(|v| v.to_string())
33 .unwrap_or_default();
34 if va != vb {
35 out.push_str(&format!(" {key}: {va} -> {vb}\n"));
36 }
37 }
38 out.push('\n');
39 }
40
41 let all_metrics: BTreeSet<_> = a.metrics.keys().chain(b.metrics.keys()).cloned().collect();
43
44 if !all_metrics.is_empty() {
45 out.push_str("Metrics:\n");
46 for key in &all_metrics {
47 let va = a.metrics.get(key).copied();
48 let vb = b.metrics.get(key).copied();
49 match (va, vb) {
50 (Some(a_val), Some(b_val)) => {
51 let pct = if a_val != 0.0 {
52 ((b_val - a_val) / a_val) * 100.0
53 } else {
54 0.0
55 };
56 let arrow = if pct > 0.0 { "+" } else { "" };
57 out.push_str(&format!(
58 " {key}: {a_val:.4} -> {b_val:.4} ({arrow}{pct:.1}%)\n"
59 ));
60 }
61 (Some(v), None) => out.push_str(&format!(" {key}: {v:.4} -> (missing)\n")),
62 (None, Some(v)) => out.push_str(&format!(" {key}: (missing) -> {v:.4}\n")),
63 (None, None) => {}
64 }
65 }
66 }
67
68 out
69}