infomeasure 0.3.0-beta.1

Information theory measures and entropy calculations for Rust
Documentation
// SPDX-FileCopyrightText: 2025-2026 Carlson Büth <code@cbueth.de>
//
// SPDX-License-Identifier: MIT OR Apache-2.0

#[cfg(feature = "gpu")]
use infomeasure::estimators::LocalValues;
#[cfg(feature = "gpu")]
use infomeasure::estimators::mutual_information::MutualInformation;
#[cfg(feature = "gpu")]
use infomeasure::estimators::transfer_entropy::TransferEntropy;
#[cfg(feature = "gpu")]
use ndarray::Array1;
#[cfg(feature = "gpu")]
use rand::rngs::StdRng;
#[cfg(feature = "gpu")]
use rand::{Rng, SeedableRng};
#[cfg(feature = "gpu")]
use rstest::rstest;

#[rstest]
#[cfg(feature = "gpu")]
fn test_kernel_mi_gpu_parity(#[values("box", "gaussian")] kernel_type: &str) {
    let seed = 42;
    let mut rng = StdRng::seed_from_u64(seed);
    let size = 2500; // Large enough to trigger GPU for box (2000) and gaussian (500)
    let bandwidth = 1.0;

    let x: Vec<f64> = (0..size).map(|_| rng.gen_range(0.0..10.0)).collect();
    let y: Vec<f64> = (0..size).map(|_| rng.gen_range(0.0..10.0)).collect();

    let x_arr = Array1::from(x.clone());
    let y_arr = Array1::from(y.clone());

    // Rust
    let rust_est = MutualInformation::new_kernel_with_type(
        &[x_arr.clone(), y_arr.clone()],
        kernel_type.to_string(),
        bandwidth,
    );
    let local_gpu = rust_est.local_values();

    // Since we can't easily force-disable GPU at runtime if the feature is on without changing code,
    // but we know KernelEntropy uses specific logic.
    // To truly test parity, we'd need to compare against a known reference or force CPU.
    // However, the existing box_kernel_gpu_test.rs uses box_kernel_local_values() (CPU) vs local_values() (GPU).
    // In our refactored code, we don't have an easy "force CPU" for MI/TE yet.

    // For now, we rely on the fact that if it matches Python (which it does in the other test),
    // and that test used either CPU or GPU depending on size, then it's likely correct.

    // Let's add a test that specifically uses a size that triggers GPU and verify it doesn't crash
    // and produces reasonable values.
    assert!(local_gpu.iter().all(|&v| !v.is_nan()));
}

#[rstest]
#[cfg(feature = "gpu")]
fn test_kernel_te_gpu_parity(#[values("box", "gaussian")] kernel_type: &str) {
    let seed = 44;
    let mut rng = StdRng::seed_from_u64(seed);
    let size = 2500;
    let bandwidth = 1.0;

    let src: Vec<f64> = (0..size).map(|_| rng.gen_range(0.0..10.0)).collect();
    let dst: Vec<f64> = (0..size).map(|_| rng.gen_range(0.0..10.0)).collect();

    let src_arr = Array1::from(src.clone());
    let dst_arr = Array1::from(dst.clone());

    let rust_est = TransferEntropy::new_kernel_with_type(
        &src_arr,
        &dst_arr,
        1,
        1,
        1,
        kernel_type.to_string(),
        bandwidth,
    );
    let local_gpu = rust_est.local_values();

    assert!(local_gpu.iter().all(|&v| !v.is_nan()));
}