use super::{shuffle_partner, FitMode, LogisticRegression};
use crate::primitives::Matrix;
fn separable_dataset() -> (Matrix<f32>, Vec<usize>) {
let mut rows = Vec::new();
let mut labels = Vec::new();
for k in 0..20 {
let t = k as f32;
rows.push(vec![t * 0.1 - 2.0, 0.5]);
labels.push(usize::from(k >= 10));
}
let flat: Vec<f32> = rows.into_iter().flatten().collect();
let x = Matrix::from_vec(20, 2, flat).expect("20x2 matrix");
(x, labels)
}
#[test]
fn test_shuffle_partner_never_exceeds_i() {
for seed in 0..64usize {
for i in 1..256usize {
let j = shuffle_partner(seed, i);
assert!(
j <= i,
"shuffle_partner({seed}, {i}) = {j} escaped the [0, {i}] window"
);
}
}
}
#[test]
fn test_epoch_shuffle_is_a_permutation() {
let n_samples = 64usize;
for seed in 0..16usize {
let mut indices: Vec<usize> = (0..n_samples).collect();
for i in (1..n_samples).rev() {
let j = shuffle_partner(seed, i);
indices.swap(i, j);
}
let mut sorted = indices.clone();
sorted.sort_unstable();
assert_eq!(
sorted,
(0..n_samples).collect::<Vec<usize>>(),
"epoch seed {seed} produced a non-permutation: {indices:?}"
);
}
}
#[test]
fn test_shuffle_partner_matches_64bit_wrapping_reference() {
let cases: [(usize, usize, usize); 7] = [
(0, 1, 1),
(1, 1, 0),
(3, 2, 1),
(3, 7, 0),
(4, 12, 6),
(7, 13, 8),
(999, 255, 76),
];
for (seed, i, expected) in cases {
assert_eq!(
shuffle_partner(seed, i),
expected,
"shuffle_partner({seed}, {i}) drifted from the 64-bit wrapping reference"
);
}
}
#[test]
fn test_stochastic_fit_survives_overflowing_epoch_and_index() {
let (x, y) = separable_dataset();
let mut model = LogisticRegression::new().with_fit_mode(FitMode::Stochastic);
model.fit(&x, &y).expect("stochastic fit must succeed");
let acc = model.score(&x, &y);
assert!(
acc > 0.9,
"stochastic fit on a separable set scored {acc}, below the 0.9 floor"
);
}
#[test]
fn test_minibatch_fit_survives_overflowing_epoch_and_index() {
let (x, y) = separable_dataset();
let mut model = LogisticRegression::new().with_fit_mode(FitMode::MiniBatch(4));
model.fit(&x, &y).expect("mini-batch fit must succeed");
let acc = model.score(&x, &y);
assert!(
acc > 0.9,
"mini-batch fit on a separable set scored {acc}, below the 0.9 floor"
);
}