#[allow(non_snake_case)]
#[cfg(test)]
mod it_tests {
use scirs2_core::ndarray::{Array1, Array2};
use crate::information_theory::utils::{
compute_mutual_information, compute_mutual_information_knn, estimate_entropy_knn,
};
fn make_linear_dataset(n: usize) -> (Array2<f64>, Array1<f64>) {
let data: Vec<f64> = (0..n)
.flat_map(|i| {
let v = i as f64;
vec![v, v * 0.5 + 1.0] })
.collect();
let X = Array2::from_shape_vec((n, 2), data).expect("shape is correct");
let y = Array1::from_shape_vec(n, (0..n).map(|i| i as f64).collect())
.expect("shape is correct");
(X, y)
}
fn make_independent_dataset(n: usize) -> (Array2<f64>, Array2<f64>) {
let x_data: Vec<f64> = (0..n).map(|i| i as f64).collect();
let X = Array2::from_shape_vec((n, 1), x_data).expect("shape is correct");
let y_data: Vec<f64> = (0..n)
.map(|i| {
let half = n / 2;
let idx = if i < half { i * 2 } else { (i - half) * 2 + 1 };
idx.min(n - 1) as f64
})
.collect();
let Y = Array2::from_shape_vec((n, 1), y_data).expect("shape is correct");
(X, Y)
}
#[test]
fn test_basic_functionality() {
let _x = Array2::from_shape_vec((10, 4), (0..40).map(|i| i as f64).collect())
.expect("operation should succeed");
let _y = Array1::from_shape_vec(10, (0..10).map(|i| i as f64).collect())
.expect("operation should succeed");
}
#[test]
fn test_entropy_knn_positive_for_spread_data() {
let n = 20_usize;
let data: Vec<f64> = (0..n).map(|i| i as f64).collect();
let X = Array2::from_shape_vec((n, 1), data).expect("shape is correct");
let h = estimate_entropy_knn(&X, 3).expect("entropy computation should succeed");
assert!(
h > 0.0,
"entropy of spread data should be positive, got {h}"
);
}
#[test]
fn test_entropy_knn_less_for_constant_data() {
let n = 10_usize;
let const_data: Vec<f64> = std::iter::repeat_n(5.0_f64, n).collect();
let X_const = Array2::from_shape_vec((n, 1), const_data).expect("shape is correct");
let spread_data: Vec<f64> = (0..n).map(|i| i as f64 * 10.0).collect();
let X_spread = Array2::from_shape_vec((n, 1), spread_data).expect("shape is correct");
let h_const =
estimate_entropy_knn(&X_const, 3).expect("entropy computation should succeed");
let h_spread =
estimate_entropy_knn(&X_spread, 3).expect("entropy computation should succeed");
assert!(
h_const < h_spread,
"constant data entropy ({h_const}) should be less than spread data entropy ({h_spread})"
);
}
#[test]
fn test_mutual_information_self_vs_independent() {
let n = 30_usize;
let data: Vec<f64> = (0..n).map(|i| i as f64).collect();
let X = Array2::from_shape_vec((n, 1), data).expect("shape is correct");
let (_, Y_indep) = make_independent_dataset(n);
let mi_self = compute_mutual_information_knn(&X, &X, 3)
.expect("mutual information computation should succeed");
let mi_indep = compute_mutual_information_knn(&X, &Y_indep, 3)
.expect("mutual information computation should succeed");
assert!(
mi_self > mi_indep - 1.0,
"MI(X,X)={mi_self} should be substantially larger than MI(X,Y_indep)={mi_indep}"
);
}
#[test]
fn test_mutual_information_correlated() {
let n = 25_usize;
let (X, y) = make_linear_dataset(n);
let mi = compute_mutual_information(&X, &y)
.expect("mutual information computation should succeed");
assert!(
mi > 0.0,
"MI between correlated X and y should be positive, got {mi}"
);
}
#[test]
fn test_mutual_information_knn_mismatch_error() {
let X = Array2::from_shape_vec((5, 1), vec![1.0, 2.0, 3.0, 4.0, 5.0])
.expect("shape is correct");
let Y = Array2::from_shape_vec((6, 1), vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0])
.expect("shape is correct");
let result = compute_mutual_information_knn(&X, &Y, 2);
assert!(
result.is_err(),
"mismatched sample counts should return an error"
);
}
#[test]
fn test_entropy_knn_invalid_k_error() {
let X = Array2::from_shape_vec((5, 1), vec![1.0, 2.0, 3.0, 4.0, 5.0])
.expect("shape is correct");
let result = estimate_entropy_knn(&X, 5);
assert!(result.is_err(), "k >= n_samples should return an error");
}
}