#[cfg(test)]
mod metrics_edge_cases {
use crate::metrics::{
accuracy::Accuracy,
auc::AUC,
f1::F1,
mean_absolute_error::MeanAbsoluteError,
mean_squared_error::MeanSquaredError,
precision::Precision,
r2::R2,
recall::Recall,
Metrics,
};
fn assert_close(a: f64, b: f64, tol: f64, label: &str) {
assert!((a - b).abs() < tol, "{label}: expected {b}, got {a} (tol {tol})");
}
#[test]
fn accuracy_perfect_score() {
let y: Vec<i32> = vec![0, 1, 2, 1, 0];
let score = Accuracy::<i32>::new().get_score(&y, &y);
assert_close(score, 1.0, 1e-10, "accuracy perfect");
}
#[test]
fn accuracy_worst_score_binary() {
let y_true: Vec<i32> = vec![0, 0, 0, 0];
let y_pred: Vec<i32> = vec![1, 1, 1, 1];
let score = Accuracy::<i32>::new().get_score(&y_true, &y_pred);
assert_close(score, 0.0, 1e-10, "accuracy worst");
}
#[test]
fn accuracy_single_sample_correct() {
let y_true: Vec<i32> = vec![1];
let y_pred: Vec<i32> = vec![1];
assert_close(Accuracy::<i32>::new().get_score(&y_true, &y_pred), 1.0, 1e-10, "single correct");
}
#[test]
fn accuracy_single_sample_wrong() {
let y_true: Vec<i32> = vec![1];
let y_pred: Vec<i32> = vec![0];
assert_close(Accuracy::<i32>::new().get_score(&y_true, &y_pred), 0.0, 1e-10, "single wrong");
}
#[test]
fn precision_perfect() {
let y: Vec<i32> = vec![0, 1, 1, 0, 1];
let score = Precision::<i32>::new().get_score(&y, &y);
assert_close(score, 1.0, 1e-10, "precision perfect");
}
#[test]
fn precision_all_false_positives() {
let y_true: Vec<i32> = vec![0, 0, 0, 0];
let y_pred: Vec<i32> = vec![1, 1, 1, 1];
let score = Precision::<i32>::new().get_score(&y_true, &y_pred);
assert_close(score, 0.0, 1e-10, "precision all FP");
}
#[test]
fn recall_perfect() {
let y: Vec<i32> = vec![1, 0, 1, 1, 0];
let score = Recall::<i32>::new().get_score(&y, &y);
assert_close(score, 1.0, 1e-10, "recall perfect");
}
#[test]
fn recall_all_false_negatives() {
let y_true: Vec<i32> = vec![1, 1, 1, 1];
let y_pred: Vec<i32> = vec![0, 0, 0, 0];
let score = Recall::<i32>::new().get_score(&y_true, &y_pred);
assert_close(score, 0.0, 1e-10, "recall all FN");
}
#[test]
fn f1_perfect() {
let y: Vec<i32> = vec![0, 1, 1, 0, 1];
let score = F1::<i32>::new().get_score(&y, &y);
assert_close(score, 1.0, 1e-10, "f1 perfect");
}
#[test]
fn f1_all_wrong() {
let y_true: Vec<i32> = vec![1, 1, 1, 1];
let y_pred: Vec<i32> = vec![0, 0, 0, 0];
let score = F1::<i32>::new().get_score(&y_true, &y_pred);
assert_close(score, 0.0, 1e-10, "f1 worst");
}
#[test]
fn f1_known_answer() {
let y_true: Vec<i32> = vec![1, 1, 0, 1];
let y_pred: Vec<i32> = vec![1, 1, 1, 0];
let score = F1::<i32>::new().get_score(&y_true, &y_pred);
assert_close(score, 2.0 / 3.0, 1e-6, "f1 known");
}
#[test]
fn auc_perfect() {
let y_true: Vec<f64> = vec![0.0, 0.0, 1.0, 1.0];
let y_score: Vec<f64> = vec![0.1, 0.2, 0.8, 0.9];
let score = AUC::<f64>::new().get_score(&y_true, &y_score);
assert_close(score, 1.0, 1e-8, "auc perfect");
}
#[test]
fn auc_worst() {
let y_true: Vec<f64> = vec![1.0, 1.0, 0.0, 0.0];
let y_score: Vec<f64> = vec![0.1, 0.2, 0.8, 0.9];
let score = AUC::<f64>::new().get_score(&y_true, &y_score);
assert_close(score, 0.0, 1e-8, "auc worst");
}
#[test]
fn auc_random_is_half() {
let y_true: Vec<f64> = vec![0.0, 1.0, 0.0, 1.0];
let y_score: Vec<f64> = vec![0.5, 0.5, 0.5, 0.5]; let score = AUC::<f64>::new().get_score(&y_true, &y_score);
assert!(score >= 0.0 && score <= 1.0, "auc tied scores out of range: {score}");
}
#[test]
fn r2_perfect() {
let y: Vec<f64> = vec![1.0, 2.0, 3.0, 4.0];
let score = R2::<f64>::new().get_score(&y, &y);
assert_close(score, 1.0, 1e-10, "r2 perfect");
}
#[test]
fn r2_predicting_mean() {
let y_true: Vec<f64> = vec![1.0, 2.0, 3.0, 4.0]; let y_pred: Vec<f64> = vec![2.5, 2.5, 2.5, 2.5];
let score = R2::<f64>::new().get_score(&y_true, &y_pred);
assert_close(score, 0.0, 1e-10, "r2 mean baseline");
}
#[test]
fn r2_known_answer_negative() {
let y_true: Vec<f64> = vec![1.0, 2.0, 3.0];
let y_pred: Vec<f64> = vec![2.0, 2.0, 2.0];
let score = R2::<f64>::new().get_score(&y_true, &y_pred);
assert_close(score, 0.0, 1e-10, "r2 predict mean");
}
#[test]
fn mae_perfect() {
let y: Vec<f64> = vec![1.0, 2.0, 3.0];
let score = MeanAbsoluteError::<f64>::new().get_score(&y, &y);
assert_close(score, 0.0, 1e-10, "mae perfect");
}
#[test]
fn mae_known_answer() {
let y_true: Vec<f64> = vec![1.0, 2.0, 3.0];
let y_pred: Vec<f64> = vec![3.0, 2.0, 1.0];
let score = MeanAbsoluteError::<f64>::new().get_score(&y_true, &y_pred);
assert_close(score, 4.0 / 3.0, 1e-10, "mae known");
}
#[test]
fn mae_single_sample() {
let y_true: Vec<f64> = vec![5.0];
let y_pred: Vec<f64> = vec![2.0];
let score = MeanAbsoluteError::<f64>::new().get_score(&y_true, &y_pred);
assert_close(score, 3.0, 1e-10, "mae single");
}
#[test]
fn mse_perfect() {
let y: Vec<f64> = vec![1.0, 2.0, 3.0];
let score = MeanSquaredError::<f64>::new().get_score(&y, &y);
assert_close(score, 0.0, 1e-10, "mse perfect");
}
#[test]
fn mse_known_answer() {
let y_true: Vec<f64> = vec![1.0, 2.0, 3.0];
let y_pred: Vec<f64> = vec![2.0, 4.0, 6.0];
let score = MeanSquaredError::<f64>::new().get_score(&y_true, &y_pred);
assert_close(score, 14.0 / 3.0, 1e-10, "mse known");
}
#[test]
fn mse_single_sample() {
let y_true: Vec<f64> = vec![0.0];
let y_pred: Vec<f64> = vec![3.0];
let score = MeanSquaredError::<f64>::new().get_score(&y_true, &y_pred);
assert_close(score, 9.0, 1e-10, "mse single");
}
#[test]
fn cluster_hcv_single_cluster_no_panic() {
use crate::metrics::cluster_hcv::{
homogeneity_score, completeness_score, v_measure_score,
};
let labels_true: Vec<u32> = vec![0, 1, 2, 0, 1];
let labels_pred: Vec<u32> = vec![0, 0, 0, 0, 0]; let h = homogeneity_score(&labels_true, &labels_pred);
let c = completeness_score(&labels_true, &labels_pred);
let v = v_measure_score(&labels_true, &labels_pred);
assert!(h >= 0.0 && h <= 1.0, "h={h}");
assert_close(c, 1.0, 1e-6, "completeness single cluster");
assert!(v >= 0.0 && v <= 1.0, "v={v}");
}
#[test]
fn cluster_hcv_perfect_clustering() {
use crate::metrics::cluster_hcv::{
homogeneity_score, completeness_score, v_measure_score,
};
let labels: Vec<u32> = vec![0, 0, 1, 1, 2, 2];
let h = homogeneity_score(&labels, &labels);
let c = completeness_score(&labels, &labels);
let v = v_measure_score(&labels, &labels);
assert_close(h, 1.0, 1e-6, "h perfect");
assert_close(c, 1.0, 1e-6, "c perfect");
assert_close(v, 1.0, 1e-6, "v perfect");
}
#[test]
fn cluster_hcv_all_same_true_labels() {
use crate::metrics::cluster_hcv::completeness_score;
let labels_true: Vec<u32> = vec![0, 0, 0, 0];
let labels_pred: Vec<u32> = vec![0, 1, 0, 1];
let c = completeness_score(&labels_true, &labels_pred);
assert!(c >= 0.0 && c <= 1.0, "completeness={c}");
}
}