use crate::metrics::ranking::*;
#[test]
fn falsify_rk_001_hit_at_k_binary() {
let predictions = vec![5, 3, 1, 4, 2];
for k in 1..=5 {
let h = hit_at_k(&predictions, &3, k);
assert!(
h == 0.0 || h == 1.0,
"FALSIFIED RK-001: hit_at_k={h} for k={k}, expected 0.0 or 1.0"
);
}
}
#[test]
fn falsify_rk_002_hit_monotone_in_k() {
let predictions = vec![5, 3, 1, 4, 2];
let target = 1;
let mut prev = 0.0_f32;
for k in 1..=5 {
let h = hit_at_k(&predictions, &target, k);
assert!(
h >= prev,
"FALSIFIED RK-002: hit_at_k decreased from {prev} to {h} at k={k}"
);
prev = h;
}
}
#[test]
fn falsify_rk_003_ndcg_bounded() {
let relevance = vec![3.0, 2.0, 3.0, 0.0, 1.0, 2.0];
for k in 1..=6 {
let score = ndcg_at_k(&relevance, k);
assert!(
(0.0..=1.0001).contains(&score),
"FALSIFIED RK-003: NDCG@{k}={score}, expected in [0,1]"
);
}
}
#[test]
fn falsify_rk_004_reciprocal_rank_bounded() {
let predictions = vec![5, 3, 1, 4, 2];
let rr = reciprocal_rank(&predictions, &3);
assert!(
(0.0..=1.0).contains(&rr),
"FALSIFIED RK-004: reciprocal_rank={rr}, expected in [0,1]"
);
assert!(
(rr - 0.5).abs() < 1e-6,
"FALSIFIED RK-004: reciprocal_rank={rr}, expected 0.5"
);
}
mod rk_proptest_falsify {
use super::*;
use proptest::prelude::*;
proptest! {
#![proptest_config(ProptestConfig::with_cases(20))]
#[test]
fn falsify_rk_001_prop_hit_binary(
n in 3..=10usize,
seed in 0..500u32,
) {
let predictions: Vec<usize> = (0..n).map(|i| (i + seed as usize) % n).collect();
let target = seed as usize % n;
for k in 1..=n {
let h = hit_at_k(&predictions, &target, k);
prop_assert!(
h == 0.0 || h == 1.0,
"FALSIFIED RK-001-prop: hit_at_k={} for k={}, expected 0 or 1",
h, k
);
}
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(20))]
#[test]
fn falsify_rk_003_prop_ndcg_bounded(
n in 3..=8usize,
seed in 0..500u32,
) {
let relevance: Vec<f32> = (0..n)
.map(|i| (((i as f32 + seed as f32) * 0.37).sin().abs() * 5.0).floor())
.collect();
for k in 1..=n {
let score = ndcg_at_k(&relevance, k);
prop_assert!(
(-0.001..=1.001).contains(&score),
"FALSIFIED RK-003-prop: NDCG@{}={} not in [0,1]",
k, score
);
}
}
}
}