use aprender::active_learning::{
EntropySampling, MarginSampling, QueryByCommittee, QueryStrategy, UncertaintySampling,
};
use aprender::primitives::Vector;
use proptest::prelude::*;
fn make_prob_vec(rng: &mut u64, n_classes: usize) -> Vector<f32> {
let mut raw = Vec::with_capacity(n_classes);
for _ in 0..n_classes {
*rng = rng.wrapping_mul(6_364_136_223_846_793_005).wrapping_add(1);
raw.push((*rng >> 33) as f32 / u32::MAX as f32 + 0.001); }
let sum: f32 = raw.iter().sum();
let normalized: Vec<f32> = raw.iter().map(|x| x / sum).collect();
Vector::from_vec(normalized)
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(256))]
#[test]
fn prop_uncertainty_score_bounded(
n_samples in 1usize..10,
n_classes in 2usize..6,
seed in 0u64..10000,
) {
let mut rng = seed;
let predictions: Vec<Vector<f32>> = (0..n_samples)
.map(|_| make_prob_vec(&mut rng, n_classes))
.collect();
let strategy = UncertaintySampling::new();
let scores = strategy.score(&predictions);
prop_assert_eq!(scores.len(), predictions.len());
for (i, &s) in scores.iter().enumerate() {
prop_assert!(
s >= 0.0 && s <= 1.0,
"FALSIFY-AL-001: uncertainty[{}] = {}, expected in [0, 1]",
i, s
);
}
}
#[test]
fn prop_margin_score_bounded(
n_samples in 1usize..10,
n_classes in 2usize..6,
seed in 0u64..10000,
) {
let mut rng = seed;
let predictions: Vec<Vector<f32>> = (0..n_samples)
.map(|_| make_prob_vec(&mut rng, n_classes))
.collect();
let strategy = MarginSampling::new();
let scores = strategy.score(&predictions);
prop_assert_eq!(scores.len(), predictions.len());
for (i, &s) in scores.iter().enumerate() {
prop_assert!(
s >= 0.0 && s <= 1.0,
"FALSIFY-AL-002: margin[{}] = {}, expected in [0, 1]",
i, s
);
}
}
#[test]
fn prop_entropy_score_non_negative(
n_samples in 1usize..10,
n_classes in 2usize..6,
seed in 0u64..10000,
) {
let mut rng = seed;
let predictions: Vec<Vector<f32>> = (0..n_samples)
.map(|_| make_prob_vec(&mut rng, n_classes))
.collect();
let strategy = EntropySampling::new();
let scores = strategy.score(&predictions);
prop_assert_eq!(scores.len(), predictions.len());
for (i, &s) in scores.iter().enumerate() {
prop_assert!(
s >= 0.0,
"FALSIFY-AL-003: entropy[{}] = {}, expected >= 0",
i, s
);
prop_assert!(
s.is_finite(),
"FALSIFY-AL-003: entropy[{}] = {}, expected finite",
i, s
);
}
}
#[test]
fn prop_qbc_score_non_negative(
n_members in 2usize..5,
n_samples in 2usize..8,
n_classes in 2usize..5,
seed in 0u64..10000,
) {
let mut rng = seed;
let committee_preds: Vec<Vec<Vector<f32>>> = (0..n_members)
.map(|_| {
(0..n_samples)
.map(|_| make_prob_vec(&mut rng, n_classes))
.collect()
})
.collect();
let qbc = QueryByCommittee::new(n_members);
let scores = qbc.score_committee(&committee_preds);
prop_assert_eq!(scores.len(), n_samples);
for (i, &s) in scores.iter().enumerate() {
prop_assert!(
s >= 0.0,
"FALSIFY-AL-004: qbc[{}] = {}, expected >= 0",
i, s
);
prop_assert!(
s.is_finite(),
"FALSIFY-AL-004: qbc[{}] = {}, expected finite",
i, s
);
}
}
}