use approx::assert_relative_eq;
use infomeasure::estimators::entropy::{Entropy, GlobalValue, LocalValues};
use ndarray::{Array1, Array2};
use rand::rngs::StdRng;
use rand::{Rng, SeedableRng};
use validation::python;
#[test]
fn test_python_interface_works() {
let seed = 42;
let mut rng = StdRng::seed_from_u64(seed);
let size = 10;
let data: Vec<i32> = (0..size).map(|_| rng.gen_range(0..10)).collect();
let data_array = Array1::from(data.clone());
let discrete_entropy = Entropy::new_discrete(data_array);
let rust_entropy = discrete_entropy.global_value();
let python_entropy = python::calculate_entropy(&data, "discrete", &[]).unwrap();
println!("Rust entropy: {rust_entropy}");
println!("Python entropy: {python_entropy}");
assert_relative_eq!(
rust_entropy,
python_entropy,
epsilon = 1e-10,
max_relative = 1e-6
);
}
#[test]
fn test_calculate_entropy_float() {
let seed = 42;
let mut rng = StdRng::seed_from_u64(seed);
let size = 10;
let data: Vec<f64> = (0..size).map(|_| rng.gen_range(0.0..10.0)).collect();
let data_array = Array1::from(data.clone());
let bandwidth = 1.0;
let kernel_entropy = Entropy::new_kernel(data_array, bandwidth);
let rust_entropy = kernel_entropy.global_value();
let kernel_kwargs = [
("kernel".to_string(), "\"box\"".to_string()),
("bandwidth".to_string(), bandwidth.to_string()),
];
let python_entropy = python::calculate_entropy_float(&data, "kernel", &kernel_kwargs).unwrap();
println!("Rust kernel entropy: {rust_entropy}");
println!("Python kernel entropy: {python_entropy}");
assert_relative_eq!(
rust_entropy,
python_entropy,
epsilon = 1e-6,
max_relative = 1e-3
);
}
#[test]
fn test_calculate_local_entropy() {
let seed = 42;
let mut rng = StdRng::seed_from_u64(seed);
let size = 10;
let data: Vec<i32> = (0..size).map(|_| rng.gen_range(0..10)).collect();
let data_array = Array1::from(data.clone());
let discrete_entropy = Entropy::new_discrete(data_array);
let rust_local_entropy = discrete_entropy.local_values();
let python_local_entropy = python::calculate_local_entropy(&data, "discrete", &[]).unwrap();
println!("Rust local entropy: {rust_local_entropy:?}");
println!("Python local entropy: {python_local_entropy:?}");
for (rust_val, python_val) in rust_local_entropy.iter().zip(python_local_entropy.iter()) {
assert_relative_eq!(*rust_val, *python_val, epsilon = 1e-10, max_relative = 1e-6);
}
}
#[test]
fn test_calculate_local_entropy_float() {
let seed = 42;
let mut rng = StdRng::seed_from_u64(seed);
let size = 10;
let data: Vec<f64> = (0..size).map(|_| rng.gen_range(0.0..10.0)).collect();
let data_array = Array1::from(data.clone());
let bandwidth = 1.0;
let kernel_entropy = Entropy::new_kernel(data_array, bandwidth);
let rust_local_entropy = kernel_entropy.local_values();
let kernel_kwargs = [
("kernel".to_string(), "\"box\"".to_string()),
("bandwidth".to_string(), bandwidth.to_string()),
];
let python_local_entropy =
python::calculate_local_entropy_float(&data, "kernel", &kernel_kwargs).unwrap();
println!("Rust local kernel entropy: {rust_local_entropy:?}");
println!("Python local kernel entropy: {python_local_entropy:?}");
for (rust_val, python_val) in rust_local_entropy.iter().zip(python_local_entropy.iter()) {
assert_relative_eq!(*rust_val, *python_val, epsilon = 1e-6, max_relative = 1e-3);
}
}
#[test]
fn test_calculate_entropy_float_nd() {
let seed = 42;
let mut rng = StdRng::seed_from_u64(seed);
let size = 10;
let dims = 2;
let mut data = Array2::zeros((size, dims));
for i in 0..size {
for j in 0..dims {
data[[i, j]] = rng.gen_range(0.0..10.0);
}
}
let bandwidth = 1.0;
let kernel_entropy = Entropy::nd_kernel::<2>(data.clone(), bandwidth);
let rust_entropy = kernel_entropy.global_value();
let flat_data: Vec<f64> = data.iter().cloned().collect();
let kernel_kwargs = [
("kernel".to_string(), "\"box\"".to_string()),
("bandwidth".to_string(), bandwidth.to_string()),
];
let python_entropy =
python::calculate_entropy_float_nd(&flat_data, dims, "kernel", &kernel_kwargs).unwrap();
println!("Rust 2D kernel entropy: {rust_entropy}");
println!("Python 2D kernel entropy: {python_entropy}");
assert_relative_eq!(
rust_entropy,
python_entropy,
epsilon = 1e-6,
max_relative = 0.1
);
}
#[test]
fn test_calculate_local_entropy_float_nd() {
let seed = 42;
let mut rng = StdRng::seed_from_u64(seed);
let size = 10;
let dims = 2;
let mut data = Array2::zeros((size, dims));
for i in 0..size {
for j in 0..dims {
data[[i, j]] = rng.gen_range(0.0..10.0);
}
}
let bandwidth = 1.0;
let kernel_entropy = Entropy::nd_kernel::<2>(data.clone(), bandwidth);
let rust_local_entropy = kernel_entropy.local_values();
let flat_data: Vec<f64> = data.iter().cloned().collect();
let kernel_kwargs = [
("kernel".to_string(), "\"box\"".to_string()),
("bandwidth".to_string(), bandwidth.to_string()),
];
let python_local_entropy =
python::calculate_local_entropy_float_nd(&flat_data, dims, "kernel", &kernel_kwargs)
.unwrap();
println!("Rust 2D local kernel entropy: {rust_local_entropy:?}");
println!("Python 2D local kernel entropy: {python_local_entropy:?}");
let sample_size = rust_local_entropy.len().min(10);
let step = rust_local_entropy.len() / sample_size.max(1);
for i in (0..rust_local_entropy.len()).step_by(step.max(1)) {
if i < rust_local_entropy.len() && i < python_local_entropy.len() {
let rust_val = rust_local_entropy[i];
let python_val = python_local_entropy[i];
if rust_val.abs() > 1e-6 && python_val.abs() > 1e-6 {
assert_relative_eq!(rust_val, python_val, epsilon = 1e-6, max_relative = 0.3);
}
}
}
}
#[test]
fn test_benchmark_entropy() {
let seed = 42;
let mut rng = StdRng::seed_from_u64(seed);
let size = 10;
let data: Vec<i32> = (0..size).map(|_| rng.gen_range(0..10)).collect();
let num_runs = 5;
let python_time = python::benchmark_entropy(&data, num_runs).unwrap();
println!("Python benchmark time: {python_time} seconds");
assert!(python_time > 0.0, "Benchmark time should be positive");
}
#[test]
fn test_benchmark_entropy_float_nd() {
let seed = 42;
let mut rng = StdRng::seed_from_u64(seed);
let size = 10;
let dims = 2;
let flat_data: Vec<f64> = (0..size * dims).map(|_| rng.gen_range(0.0..10.0)).collect();
let num_runs = 5;
let kernel_kwargs = [
("kernel".to_string(), "\"box\"".to_string()),
("bandwidth".to_string(), "1.0".to_string()),
];
let python_time =
python::benchmark_entropy_float_nd(&flat_data, dims, "kernel", &kernel_kwargs, num_runs)
.unwrap();
println!("Python benchmark time for float_nd: {python_time} seconds");
assert!(python_time > 0.0, "Benchmark time should be positive");
}
#[test]
fn test_benchmark_entropy_generic() {
let seed = 42;
let mut rng = StdRng::seed_from_u64(seed);
let size = 10;
let data: Vec<f64> = (0..size).map(|_| rng.gen_range(0.0..10.0)).collect();
let num_runs = 5;
let kernel_kwargs = [
("kernel".to_string(), "\"box\"".to_string()),
("bandwidth".to_string(), "1.0".to_string()),
];
let python_time =
python::benchmark_entropy_generic(&data, "kernel", &kernel_kwargs, num_runs).unwrap();
println!("Python benchmark time for generic: {python_time} seconds");
assert!(python_time > 0.0, "Benchmark time should be positive");
}