use crate::dict_learning;
use scirs2_core::ndarray::{array, Array2};
#[test]
fn test_dictionary_learning() {
let X = array![[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]];
let dict_learner = dict_learning::DictionaryLearning::new()
.n_components(2)
.alpha(0.1)
.max_iter(10);
let fitted = dict_learner
.fit(&X.view(), &())
.expect("operation should succeed");
let codes = fitted
.transform(&X.view())
.expect("operation should succeed");
assert_eq!(codes.dim(), (3, 2));
let dictionary = fitted.get_dictionary();
assert_eq!(dictionary.dim(), (2, 3));
}
#[test]
fn test_mini_batch_dictionary_learning() {
let X = array![
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
[7.0, 8.0, 9.0],
[2.0, 3.0, 4.0]
];
let mini_batch_dict = dict_learning::MiniBatchDictionaryLearning::new()
.n_components(2)
.alpha(0.1)
.batch_size(2)
.max_iter(10);
let fitted = mini_batch_dict
.fit(&X.view(), &())
.expect("operation should succeed");
let codes = fitted
.transform(&X.view())
.expect("operation should succeed");
assert_eq!(codes.dim(), (4, 2));
let dictionary = fitted.get_dictionary();
assert_eq!(dictionary.dim(), (2, 3)); }
#[test]
fn test_nmf() {
let X = array![
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
[7.0, 8.0, 9.0],
[2.0, 3.0, 4.0]
];
let nmf = dict_learning::NMF::new()
.n_components(2)
.max_iter(50)
.solver("cd".to_string());
let fitted = nmf.fit(&X.view(), &()).expect("operation should succeed");
let W = fitted
.transform(&X.view())
.expect("operation should succeed");
assert_eq!(W.dim(), (4, 2));
for &val in W.iter() {
assert!(val >= 0.0);
}
let H = fitted.components();
assert_eq!(H.dim(), (2, 3));
for &val in H.iter() {
assert!(val >= 0.0);
}
let reconstruction = fitted
.inverse_transform(&W.view())
.expect("operation should succeed");
assert_eq!(reconstruction.dim(), X.dim());
let nmf_mu = dict_learning::NMF::new()
.n_components(2)
.max_iter(20)
.solver("mu".to_string());
let fitted_mu = nmf_mu
.fit(&X.view(), &())
.expect("operation should succeed");
let W_mu = fitted_mu
.transform(&X.view())
.expect("operation should succeed");
assert_eq!(W_mu.dim(), (4, 2));
}
#[test]
fn test_nmf_with_negative_input() {
let X = array![[-1.0, 2.0, 3.0], [4.0, 5.0, 6.0]];
let nmf = dict_learning::NMF::new().n_components(2);
let result = nmf.fit(&X.view(), &());
assert!(result.is_err());
}
#[test]
fn test_ica() {
let X = array![
[1.0, 2.0, 3.0],
[2.0, 4.0, 6.0],
[3.0, 6.0, 9.0],
[1.5, 3.0, 4.5],
[2.5, 5.0, 7.5]
];
let ica = dict_learning::ICA::new()
.n_components(2)
.max_iter(50)
.tol(1e-3);
let fitted = ica.fit(&X.view(), &()).expect("operation should succeed");
let sources = fitted
.transform(&X.view())
.expect("operation should succeed");
assert_eq!(sources.dim(), (5, 2));
let components = fitted.components();
assert_eq!(components.dim(), (2, 3));
let ica_exp = dict_learning::ICA::new()
.n_components(2)
.fun("exp".to_string())
.max_iter(20);
let fitted_exp = ica_exp
.fit(&X.view(), &())
.expect("operation should succeed");
let sources_exp = fitted_exp
.transform(&X.view())
.expect("operation should succeed");
assert_eq!(sources_exp.dim(), (5, 2));
let ica_cube = dict_learning::ICA::new()
.n_components(2)
.fun("cube".to_string())
.max_iter(20);
let fitted_cube = ica_cube
.fit(&X.view(), &())
.expect("operation should succeed");
let sources_cube = fitted_cube
.transform(&X.view())
.expect("operation should succeed");
assert_eq!(sources_cube.dim(), (5, 2));
let ica_parallel = dict_learning::ICA::new()
.n_components(2)
.algorithm("parallel".to_string())
.max_iter(20);
let fitted_parallel = ica_parallel
.fit(&X.view(), &())
.expect("operation should succeed");
let sources_parallel = fitted_parallel
.transform(&X.view())
.expect("operation should succeed");
assert_eq!(sources_parallel.dim(), (5, 2));
}
#[test]
fn test_ica_error_cases() {
let X = array![[1.0, 2.0, 3.0]];
let ica = dict_learning::ICA::new();
let result = ica.fit(&X.view(), &());
assert!(result.is_err());
let X_small = array![[1.0, 2.0], [3.0, 4.0]];
let ica_large = dict_learning::ICA::new().n_components(5); let result = ica_large.fit(&X_small.view(), &());
assert!(result.is_err());
let ica_invalid = dict_learning::ICA::new().fun("invalid".to_string());
let result = ica_invalid.fit(&X_small.view(), &());
assert!(result.is_err()); }
#[test]
fn test_pca() {
let X = array![
[1.0, 1.0, 0.0],
[2.0, 2.0, 0.0],
[3.0, 3.0, 0.0],
[4.0, 4.0, 0.0],
[5.0, 5.0, 0.0]
];
let pca = dict_learning::PCA::new().n_components(2).whiten(false);
let fitted = pca.fit(&X.view(), &()).expect("operation should succeed");
let transformed = fitted
.transform(&X.view())
.expect("operation should succeed");
assert_eq!(transformed.dim(), (5, 2));
let components = fitted.components();
assert_eq!(components.dim(), (2, 3));
let explained_variance = fitted.explained_variance();
assert_eq!(explained_variance.len(), 2);
let explained_variance_ratio = fitted.explained_variance_ratio();
assert_eq!(explained_variance_ratio.len(), 2);
let reconstructed = fitted
.inverse_transform(&transformed.view())
.expect("operation should succeed");
assert_eq!(reconstructed.dim(), X.dim());
let score = fitted.score(&X.view()).expect("operation should succeed");
assert!(score.is_finite());
let pca_whitened = dict_learning::PCA::new().n_components(2).whiten(true);
let fitted_whitened = pca_whitened
.fit(&X.view(), &())
.expect("operation should succeed");
let transformed_whitened = fitted_whitened
.transform(&X.view())
.expect("operation should succeed");
assert_eq!(transformed_whitened.dim(), (5, 2));
}
#[test]
fn test_pca_error_cases() {
let X = array![[1.0, 2.0, 3.0]];
let pca = dict_learning::PCA::new();
let result = pca.fit(&X.view(), &());
assert!(result.is_err());
let X_small = array![[1.0, 2.0], [3.0, 4.0]];
let pca_large = dict_learning::PCA::new().n_components(5); let result = pca_large.fit(&X_small.view(), &());
assert!(result.is_err()); }
#[test]
fn test_factor_analysis() {
let X = array![
[1.0, 2.0, 3.0, 4.0],
[2.0, 4.0, 6.0, 8.0],
[3.0, 6.0, 9.0, 12.0],
[1.5, 3.0, 4.5, 6.0],
[2.5, 5.0, 7.5, 10.0]
];
let fa = dict_learning::FactorAnalysis::new()
.n_components(2)
.max_iter(50)
.tol(1e-4);
let fitted = fa.fit(&X.view(), &()).expect("operation should succeed");
let factors = fitted
.transform(&X.view())
.expect("operation should succeed");
assert_eq!(factors.dim(), (5, 2));
let loadings = fitted.loadings();
assert_eq!(loadings.dim(), (4, 2));
let noise_variance = fitted.noise_variance();
assert_eq!(noise_variance.len(), 4);
for &var in noise_variance.iter() {
assert!(var > 0.0);
}
let explained_variance = fitted.explained_variance();
assert_eq!(explained_variance.len(), 2);
let reconstructed_cov = fitted.get_covariance().expect("operation should succeed");
assert_eq!(reconstructed_cov.dim(), (4, 4));
let score = fitted.score(&X.view()).expect("operation should succeed");
assert!(score.is_finite());
let fa_rotated = dict_learning::FactorAnalysis::new()
.n_components(2)
.rotation("varimax")
.max_iter(20);
let fitted_rotated = fa_rotated
.fit(&X.view(), &())
.expect("operation should succeed");
let factors_rotated = fitted_rotated
.transform(&X.view())
.expect("operation should succeed");
assert_eq!(factors_rotated.dim(), (5, 2));
}
#[test]
fn test_factor_analysis_error_cases() {
let X = array![[1.0, 2.0, 3.0]];
let fa = dict_learning::FactorAnalysis::new();
let result = fa.fit(&X.view(), &());
assert!(result.is_err());
let X_small = array![[1.0, 2.0], [3.0, 4.0]];
let fa_large = dict_learning::FactorAnalysis::new().n_components(5); let result = fa_large.fit(&X_small.view(), &());
assert!(result.is_err()); }
#[test]
fn test_probabilistic_matrix_factorization() {
let X = array![
[5.0, 3.0, 0.0, 1.0],
[4.0, 0.0, 0.0, 1.0],
[1.0, 1.0, 0.0, 5.0],
[1.0, 0.0, 0.0, 4.0],
[0.0, 1.0, 5.0, 4.0]
];
let pmf = dict_learning::ProbabilisticMatrixFactorization::new()
.n_components(2)
.lambda_u(0.01)
.lambda_v(0.01)
.sigma(0.1)
.learning_rate(0.01)
.max_iter(50)
.random_state(Some(42));
let fitted = pmf.fit(&X.view(), &()).expect("operation should succeed");
let U = fitted.get_user_features();
let V = fitted.get_item_features();
assert_eq!(U.dim(), (5, 2)); assert_eq!(V.dim(), (4, 2));
let prediction = fitted.predict(0, 0).expect("operation should succeed");
assert!(prediction.is_finite());
let user_indices = vec![0, 1, 2];
let item_indices = vec![0, 1, 2];
let predictions = fitted
.predict_batch(&user_indices, &item_indices)
.expect("operation should succeed");
assert_eq!(predictions.len(), 3);
let reconstructed = fitted.reconstruct();
assert_eq!(reconstructed.dim(), X.dim());
let loss = fitted.get_training_loss();
assert!(loss.is_finite());
assert!(loss >= 0.0);
let log_prob = fitted
.log_probability(0, 0, 5.0)
.expect("operation should succeed");
assert!(log_prob.is_finite());
}
#[test]
fn test_pmf_error_cases() {
let X = array![[1.0, 2.0], [3.0, 4.0]];
let empty_X = Array2::<f64>::zeros((0, 0));
let pmf = dict_learning::ProbabilisticMatrixFactorization::new();
let result = pmf.fit(&empty_X.view(), &());
assert!(result.is_err());
let pmf_large = dict_learning::ProbabilisticMatrixFactorization::new().n_components(10); let result = pmf_large.fit(&X.view(), &());
assert!(result.is_err());
let pmf_fitted = dict_learning::ProbabilisticMatrixFactorization::new()
.n_components(2)
.max_iter(5)
.fit(&X.view(), &())
.expect("operation should succeed");
assert!(pmf_fitted.predict(10, 0).is_err()); assert!(pmf_fitted.predict(0, 10).is_err());
let user_indices = vec![0, 1];
let item_indices = vec![0]; let result = pmf_fitted.predict_batch(&user_indices, &item_indices);
assert!(result.is_err());
let wrong_X = array![[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]]; let result = pmf_fitted.transform(&wrong_X.view());
assert!(result.is_err());
}
#[test]
fn test_pmf_convergence() {
let X = array![
[5.0, 3.0, 0.0, 1.0],
[4.0, 0.0, 0.0, 1.0],
[1.0, 1.0, 0.0, 5.0],
[1.0, 0.0, 0.0, 4.0]
];
let pmf = dict_learning::ProbabilisticMatrixFactorization::new()
.n_components(2)
.lambda_u(0.01)
.lambda_v(0.01)
.sigma(1.0)
.learning_rate(0.01)
.max_iter(50)
.random_state(Some(42));
let fitted = pmf.fit(&X.view(), &()).expect("operation should succeed");
let reconstructed = fitted.reconstruct();
for &val in reconstructed.iter() {
assert!(
val.is_finite(),
"Reconstructed value should be finite, got: {}",
val
);
}
assert_eq!(reconstructed.dim(), X.dim());
let pred_00 = fitted.predict(0, 0).expect("operation should succeed"); let pred_03 = fitted.predict(0, 3).expect("operation should succeed");
assert!(pred_00.is_finite());
assert!(pred_03.is_finite());
let U = fitted.get_user_features();
let V = fitted.get_item_features();
let u_sum = U.iter().map(|x| x.abs()).sum::<f64>();
let v_sum = V.iter().map(|x| x.abs()).sum::<f64>();
assert!(
u_sum > 1e-6,
"User features should have learned non-trivial values"
);
assert!(
v_sum > 1e-6,
"Item features should have learned non-trivial values"
);
}
#[test]
fn test_pmf_sparsity_handling() {
let X = array![
[5.0, 0.0, 0.0, 1.0, 0.0],
[0.0, 3.0, 0.0, 0.0, 1.0],
[0.0, 0.0, 4.0, 2.0, 0.0],
[2.0, 0.0, 0.0, 0.0, 3.0]
];
let pmf = dict_learning::ProbabilisticMatrixFactorization::new()
.n_components(2)
.lambda_u(0.01)
.lambda_v(0.01)
.max_iter(30)
.random_state(Some(42));
let fitted = pmf.fit(&X.view(), &()).expect("operation should succeed");
let U = fitted.get_user_features();
let V = fitted.get_item_features();
assert_eq!(U.dim(), (4, 2));
assert_eq!(V.dim(), (5, 2));
for i in 0..X.nrows() {
for j in 0..X.ncols() {
let pred = fitted.predict(i, j).expect("operation should succeed");
assert!(pred.is_finite());
}
}
}
#[test]
fn test_pmf_regularization_effect() {
let X = array![
[5.0, 3.0, 0.0, 1.0],
[4.0, 0.0, 0.0, 1.0],
[1.0, 1.0, 0.0, 5.0],
[1.0, 0.0, 0.0, 4.0]
];
let pmf_low_reg = dict_learning::ProbabilisticMatrixFactorization::new()
.n_components(2)
.lambda_u(0.001)
.lambda_v(0.001)
.max_iter(20)
.random_state(Some(42));
let pmf_high_reg = dict_learning::ProbabilisticMatrixFactorization::new()
.n_components(2)
.lambda_u(1.0)
.lambda_v(1.0)
.max_iter(20)
.random_state(Some(42));
let fitted_low = pmf_low_reg
.fit(&X.view(), &())
.expect("operation should succeed");
let fitted_high = pmf_high_reg
.fit(&X.view(), &())
.expect("operation should succeed");
let U_low = fitted_low.get_user_features();
let U_high = fitted_high.get_user_features();
for &val in U_low.iter() {
assert!(val.is_finite());
}
for &val in U_high.iter() {
assert!(val.is_finite());
}
let norm_low: f64 = U_low.iter().map(|x| x * x).sum::<f64>().sqrt();
let norm_high: f64 = U_high.iter().map(|x| x * x).sum::<f64>().sqrt();
assert!(norm_low.is_finite() && norm_low > 0.0);
assert!(norm_high.is_finite() && norm_high > 0.0);
}