pub(crate) use super::*;
#[test]
fn test_ica_basic() {
let data = Matrix::from_vec(
10,
2,
vec![
1.0, 2.0, 2.0, 1.0, 3.0, 4.0, 4.0, 3.0, 5.0, 6.0, 1.5, 2.5, 2.5, 1.5, 3.5, 4.5, 4.5,
3.5, 5.5, 6.5,
],
)
.expect("Valid matrix");
let mut ica = ICA::new(2);
let result = ica.fit(&data);
assert!(result.is_ok(), "ICA should fit");
let sources = ica.transform(&data).expect("Should transform");
assert_eq!(sources.n_rows(), 10);
assert_eq!(sources.n_cols(), 2);
}
#[test]
fn test_ica_invalid_n_components() {
let data = Matrix::from_vec(5, 2, vec![1.0, 2.0, 2.0, 3.0, 3.0, 4.0, 4.0, 5.0, 5.0, 6.0])
.expect("Valid matrix");
let mut ica = ICA::new(3); let result = ica.fit(&data);
assert!(result.is_err());
}
#[test]
fn test_ica_transform_not_fitted() {
let ica = ICA::new(2);
let data = Matrix::from_vec(3, 2, vec![1.0, 2.0, 2.0, 3.0, 3.0, 4.0]).expect("Valid matrix");
let result = ica.transform(&data);
assert!(result.is_err());
}
#[test]
fn test_ica_dimension_mismatch() {
let data = Matrix::from_vec(
5,
3,
vec![
1.0, 2.0, 3.0, 2.0, 3.0, 4.0, 3.0, 4.0, 5.0, 4.0, 5.0, 6.0, 5.0, 6.0, 7.0,
],
)
.expect("Valid matrix");
let mut ica = ICA::new(2);
ica.fit(&data).expect("Should fit");
let wrong_data =
Matrix::from_vec(3, 2, vec![1.0, 2.0, 2.0, 3.0, 3.0, 4.0]).expect("Valid matrix");
let result = ica.transform(&wrong_data);
assert!(result.is_err());
}
#[test]
fn test_ica_with_options() {
let data = Matrix::from_vec(
8,
2,
vec![
1.0, 2.0, 2.0, 3.0, 3.0, 5.0, 4.0, 6.0, 5.0, 4.0, 6.0, 5.0, 7.0, 7.0, 8.0, 9.0,
],
)
.expect("Valid matrix");
let mut ica = ICA::new(2).with_max_iter(100).with_tolerance(1e-5);
let result = ica.fit(&data);
assert!(result.is_ok());
}
#[test]
fn test_center_data() {
let data = Matrix::from_vec(3, 2, vec![1.0, 2.0, 2.0, 4.0, 3.0, 6.0]).expect("Valid matrix");
let (centered, mean) = ICA::center_data(&data).expect("Should center");
assert_eq!(mean.len(), 2);
assert!((mean[0] - 2.0).abs() < 1e-6); assert!((mean[1] - 4.0).abs() < 1e-6);
let mut col0_sum = 0.0;
let mut col1_sum = 0.0;
for i in 0..3 {
col0_sum += centered.get(i, 0);
col1_sum += centered.get(i, 1);
}
assert!(col0_sum.abs() < 1e-6);
assert!(col1_sum.abs() < 1e-6);
}
#[test]
fn test_power_iteration() {
let matrix = Matrix::from_vec(2, 2, vec![3.0, 1.0, 1.0, 3.0]).expect("Valid matrix");
let (eigenvalue, eigenvector) = ICA::power_iteration(&matrix, 100).expect("Should converge");
assert!((eigenvalue - 4.0).abs() < 0.1, "Eigenvalue should be ~4.0");
let norm: f32 = eigenvector
.as_slice()
.iter()
.map(|x| x * x)
.sum::<f32>()
.sqrt();
assert!((norm - 1.0).abs() < 1e-6);
}
#[test]
fn test_ica_with_random_state() {
let data = Matrix::from_vec(
8,
2,
vec![
1.0, 2.0, 2.0, 3.0, 3.0, 5.0, 4.0, 6.0, 5.0, 4.0, 6.0, 5.0, 7.0, 7.0, 8.0, 9.0,
],
)
.expect("Valid matrix");
let mut ica = ICA::new(2).with_random_state(42);
let result = ica.fit(&data);
assert!(result.is_ok());
}
#[test]
fn test_ica_empty_data() {
let data = Matrix::from_vec(0, 2, vec![]).expect("Valid empty matrix");
let mut ica = ICA::new(2);
let result = ica.fit(&data);
assert!(result.is_err());
}
#[test]
fn test_ica_empty_features() {
let data = Matrix::from_vec(5, 0, vec![]).expect("Valid empty matrix");
let mut ica = ICA::new(1);
let result = ica.fit(&data);
assert!(result.is_err());
}
#[test]
fn test_ica_single_component() {
let data = Matrix::from_vec(
6,
3,
vec![
1.0, 2.0, 3.0, 2.0, 4.0, 6.0, 3.0, 6.0, 9.0, 4.0, 8.0, 12.0, 5.0, 10.0, 15.0, 6.0,
12.0, 18.0,
],
)
.expect("Valid matrix");
let mut ica = ICA::new(1);
let result = ica.fit(&data);
assert!(result.is_ok());
let sources = ica.transform(&data).expect("Should transform");
assert_eq!(sources.n_cols(), 1);
}
#[test]
fn test_ica_whitening() {
let data = Matrix::from_vec(
10,
2,
vec![
1.0, 2.0, 2.0, 1.0, 3.0, 4.0, 4.0, 3.0, 5.0, 6.0, 1.5, 2.5, 2.5, 1.5, 3.5, 4.5, 4.5,
3.5, 5.5, 6.5,
],
)
.expect("Valid matrix");
let (centered, _mean) = ICA::center_data(&data).expect("Should center");
let (whitened, _whitening_matrix) = ICA::whiten_data(¢ered, 2).expect("Should whiten");
assert_eq!(whitened.n_rows(), 10);
assert_eq!(whitened.n_cols(), 2);
let n = whitened.n_rows();
let mut cov = [[0.0_f32; 2]; 2];
for r in 0..2 {
for c in 0..2 {
let mut sum = 0.0_f32;
for i in 0..n {
sum += whitened.get(i, r) * whitened.get(i, c);
}
cov[r][c] = sum / n as f32;
}
}
assert!(
(cov[0][0] - 1.0).abs() < 0.05,
"whitened var[0] must be ~1, got {}",
cov[0][0]
);
assert!(
(cov[1][1] - 1.0).abs() < 0.05,
"whitened var[1] must be ~1, got {}",
cov[1][1]
);
assert!(
cov[0][1].abs() < 0.05,
"whitened cross-cov must be ~0, got {}",
cov[0][1]
);
}
#[test]
fn test_ica_whitening_covariance_is_identity() {
let n = 200usize;
let mut state: u64 = 42;
let mut next = || -> f32 {
state = state.wrapping_mul(1_103_515_245).wrapping_add(12_345) & 0x7fff_ffff;
(state % 1000) as f32 / 1000.0
};
let mut data = Vec::with_capacity(n * 2);
for _ in 0..n {
let a = next();
let b = next();
let f0 = 2.0 * a + b;
let f1 = a + 3.0 * b;
data.push(f0);
data.push(f1);
}
let x = Matrix::from_vec(n, 2, data).expect("Valid matrix");
let (centered, _mean) = ICA::center_data(&x).expect("Should center");
let (whitened, _w) = ICA::whiten_data(¢ered, 2).expect("Should whiten");
assert_eq!(whitened.n_rows(), n);
assert_eq!(whitened.n_cols(), 2);
let mut cov = [[0.0_f32; 2]; 2];
for r in 0..2 {
for c in 0..2 {
let mut sum = 0.0_f32;
for i in 0..n {
sum += whitened.get(i, r) * whitened.get(i, c);
}
cov[r][c] = sum / n as f32;
}
}
assert!(
(cov[0][0] - 1.0).abs() < 0.05,
"Cov(X_white)[0][0] must be ~1 (unit variance), got {}",
cov[0][0]
);
assert!(
(cov[1][1] - 1.0).abs() < 0.05,
"Cov(X_white)[1][1] must be ~1 (unit variance), got {}",
cov[1][1]
);
assert!(
cov[0][1].abs() < 0.05,
"Cov(X_white)[0][1] must be ~0 (decorrelated), got {}",
cov[0][1]
);
}
#[test]
fn test_ica_eigen_decomposition() {
let matrix = Matrix::from_vec(3, 3, vec![4.0, 1.0, 1.0, 1.0, 3.0, 1.0, 1.0, 1.0, 2.0])
.expect("Valid matrix");
let (eigenvalues, eigenvectors) =
ICA::eigen_decomposition(&matrix, 2).expect("Should decompose");
assert_eq!(eigenvalues.len(), 2);
assert_eq!(eigenvectors.n_rows(), 3);
assert_eq!(eigenvectors.n_cols(), 2);
}
#[test]
fn test_ica_eigen_decomposition_non_square() {
let matrix = Matrix::from_vec(2, 3, vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]).expect("Valid matrix");
let result = ICA::eigen_decomposition(&matrix, 2);
assert!(result.is_err());
}
#[test]
fn test_ica_clone() {
let ica = ICA::new(3)
.with_max_iter(100)
.with_tolerance(1e-5)
.with_random_state(42);
let cloned = ica.clone();
assert_eq!(format!("{:?}", ica), format!("{:?}", cloned));
}
#[test]
fn test_ica_debug() {
let ica = ICA::new(2);
let debug_str = format!("{:?}", ica);
assert!(debug_str.contains("ICA"));
assert!(debug_str.contains("n_components"));
}
#[test]
fn test_ica_fit_then_transform_new_data() {
let training_data = Matrix::from_vec(
10,
2,
vec![
1.0, 2.0, 2.0, 1.0, 3.0, 4.0, 4.0, 3.0, 5.0, 6.0, 1.5, 2.5, 2.5, 1.5, 3.5, 4.5, 4.5,
3.5, 5.5, 6.5,
],
)
.expect("Valid matrix");
let mut ica = ICA::new(2);
ica.fit(&training_data).expect("Should fit");
let new_data = Matrix::from_vec(5, 2, vec![2.0, 3.0, 3.0, 2.0, 4.0, 5.0, 5.0, 4.0, 6.0, 7.0])
.expect("Valid matrix");
let transformed = ica.transform(&new_data).expect("Should transform");
assert_eq!(transformed.n_rows(), 5);
assert_eq!(transformed.n_cols(), 2);
}
#[test]
fn test_ica_3d_data() {
let data = Matrix::from_vec(
12,
3,
vec![
1.0, 5.0, 2.0, 4.0, 2.0, 6.0, 3.0, 7.0, 1.0, 6.0, 3.0, 4.0, 2.0, 8.0, 5.0, 5.0, 1.0, 3.0, 1.5, 6.0,
2.5, 4.5, 2.5, 5.5, 3.5, 6.5, 1.5, 5.5, 4.5, 4.5, 2.5, 7.5, 6.5, 6.5, 1.5, 3.5,
],
)
.expect("Valid matrix");
let mut ica = ICA::new(2); let result = ica.fit(&data);
assert!(result.is_ok());
let sources = ica.transform(&data).expect("Should transform");
assert_eq!(sources.n_rows(), 12);
assert_eq!(sources.n_cols(), 2);
}
#[test]
fn test_ica_strict_tolerance() {
let data = Matrix::from_vec(
8,
2,
vec![
1.0, 2.0, 2.0, 3.0, 3.0, 5.0, 4.0, 6.0, 5.0, 4.0, 6.0, 5.0, 7.0, 7.0, 8.0, 9.0,
],
)
.expect("Valid matrix");
let mut ica = ICA::new(2).with_tolerance(1e-8).with_max_iter(500);
let result = ica.fit(&data);
assert!(result.is_ok());
}