use aprender::bayesian::{BayesianLinearRegression, BetaBinomial};
use aprender::primitives::{Matrix, Vector};
use proptest::prelude::*;
fn blr_training_strategy() -> impl Strategy<Value = (Matrix<f32>, Vector<f32>, usize)> {
(10usize..=20, 2usize..=4)
.prop_flat_map(|(n, d)| {
let features = proptest::collection::vec(-5.0f32..5.0, n * d);
let targets = proptest::collection::vec(-10.0f32..10.0, n);
let d_val = Just(d);
let n_val = Just(n);
(features, targets, n_val, d_val)
})
.prop_map(|(features, targets, n, d)| {
let x = Matrix::from_vec(n, d, features).expect("valid matrix dimensions");
let y = Vector::from_vec(targets);
(x, y, d)
})
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(256))]
#[test]
fn prop_posterior_parameters_positive(
successes in 0u32..100,
trials_extra in 0u32..100,
) {
let trials = successes + trials_extra; let mut prior = BetaBinomial::uniform(); prior.update(successes, trials);
prop_assert!(
prior.alpha() > 0.0,
"alpha = {}, expected > 0 after update({}, {})",
prior.alpha(), successes, trials
);
prop_assert!(
prior.beta() > 0.0,
"beta = {}, expected > 0 after update({}, {})",
prior.beta(), successes, trials
);
}
#[test]
fn prop_posterior_mean_bounded(
successes in 0u32..100,
trials_extra in 0u32..100,
) {
let trials = successes + trials_extra;
let mut prior = BetaBinomial::uniform();
prior.update(successes, trials);
let mean = prior.posterior_mean();
prop_assert!(
(0.0..=1.0).contains(&mean),
"posterior_mean = {}, expected in [0, 1] after update({}, {})",
mean, successes, trials
);
prop_assert!(
mean.is_finite(),
"posterior_mean = {}, expected finite after update({}, {})",
mean, successes, trials
);
}
#[test]
fn prop_blr_predictions_finite(
(x_train, y_train, d) in blr_training_strategy(),
) {
let mut model = BayesianLinearRegression::new(d);
model.fit(&x_train, &y_train).expect("fit succeeds");
let predictions = model.predict(&x_train).expect("predict succeeds");
for i in 0..predictions.len() {
prop_assert!(
predictions[i].is_finite(),
"prediction[{}] = {}, expected finite value",
i, predictions[i]
);
}
}
#[test]
fn prop_blr_predictions_deterministic(
(x_train, y_train, d) in blr_training_strategy(),
) {
let mut model = BayesianLinearRegression::new(d);
model.fit(&x_train, &y_train).expect("fit succeeds");
let pred1 = model.predict(&x_train).expect("predict succeeds (1st)");
let pred2 = model.predict(&x_train).expect("predict succeeds (2nd)");
prop_assert!(
pred1.len() == pred2.len(),
"prediction lengths differ: {} vs {}",
pred1.len(), pred2.len()
);
for i in 0..pred1.len() {
let diff = (pred1[i] - pred2[i]).abs();
prop_assert!(
diff < 1e-6,
"non-deterministic at [{}]: first={}, second={}, diff={}",
i, pred1[i], pred2[i], diff
);
}
}
}