#[test]
fn test_isolation_forest_single_feature() {
let data = Matrix::from_vec(5, 1, vec![1.0, 1.1, 1.2, 1.0, 100.0])
.expect("Matrix creation should succeed");
let mut iforest = IsolationForest::new()
.with_n_estimators(50)
.with_contamination(0.2)
.with_random_state(42);
iforest.fit(&data).expect("Fit should succeed");
let predictions = iforest.predict(&data);
assert_eq!(predictions.len(), 5);
let scores = iforest.score_samples(&data);
assert!(scores[4] < scores[0]);
}
#[test]
fn test_isolation_forest_max_samples_larger_than_data() {
let data = Matrix::from_vec(4, 2, vec![1.0, 1.0, 2.0, 2.0, 3.0, 3.0, 4.0, 4.0])
.expect("Matrix creation should succeed");
let mut iforest = IsolationForest::new()
.with_max_samples(1000)
.with_n_estimators(10)
.with_random_state(42);
iforest.fit(&data).expect("Fit should succeed");
assert!(iforest.is_fitted());
}
#[test]
fn test_isolation_forest_contamination_zero() {
let data = Matrix::from_vec(
6,
2,
vec![
2.0, 2.0, 2.1, 2.0, 1.9, 2.1, 2.0, 1.9, 10.0, 10.0, -10.0, -10.0,
],
)
.expect("Matrix creation should succeed");
let mut iforest = IsolationForest::new()
.with_contamination(0.0)
.with_random_state(42);
iforest.fit(&data).expect("Fit should succeed");
let predictions = iforest.predict(&data);
assert_eq!(predictions.len(), 6);
}
#[test]
fn test_isolation_forest_refit_clears_trees() {
let data = Matrix::from_vec(
6,
2,
vec![
2.0, 2.0, 2.1, 2.0, 1.9, 2.1, 2.0, 1.9, 10.0, 10.0, -10.0, -10.0,
],
)
.expect("Matrix creation should succeed");
let mut iforest = IsolationForest::new()
.with_n_estimators(10)
.with_random_state(42);
iforest.fit(&data).expect("First fit should succeed");
let scores1 = iforest.score_samples(&data);
iforest.fit(&data).expect("Second fit should succeed");
let scores2 = iforest.score_samples(&data);
assert_eq!(scores1, scores2);
}
#[test]
fn test_isolation_forest_many_features() {
let data = Matrix::from_vec(
5,
5,
vec![
1.0, 2.0, 3.0, 4.0, 5.0, 1.1, 2.1, 3.1, 4.1, 5.1, 1.0, 2.0, 3.0, 4.0, 5.0, 0.9, 1.9,
2.9, 3.9, 4.9, 50.0, 50.0, 50.0, 50.0, 50.0, ],
)
.expect("Matrix creation should succeed");
let mut iforest = IsolationForest::new()
.with_n_estimators(50)
.with_contamination(0.2)
.with_random_state(42);
iforest.fit(&data).expect("Fit should succeed");
let scores = iforest.score_samples(&data);
assert_eq!(scores.len(), 5);
let min_idx = scores
.iter()
.enumerate()
.min_by(|(_, a), (_, b)| a.partial_cmp(b).expect("Valid floats"))
.map(|(i, _)| i)
.expect("Non-empty scores");
assert_eq!(min_idx, 4);
}
#[test]
fn test_lof_new() {
let lof = LocalOutlierFactor::new();
assert!(!lof.is_fitted());
}
#[test]
fn test_lof_fit_basic() {
let data = Matrix::from_vec(
10,
2,
vec![
2.0, 2.0, 2.1, 2.0, 1.9, 2.1, 2.0, 1.9, 2.1, 2.1, 1.8, 2.0, 2.2, 2.0, 2.0, 2.2, 1.9,
1.9, 2.1, 1.8,
],
)
.expect("Matrix creation should succeed");
let mut lof = LocalOutlierFactor::new().with_n_neighbors(5);
lof.fit(&data).expect("LOF fit should succeed");
assert!(lof.is_fitted());
}
#[test]
fn test_lof_predict_anomalies() {
let data = Matrix::from_vec(
10,
2,
vec![
2.0, 2.0, 2.1, 2.0, 1.9, 2.1, 2.0, 1.9, 2.1, 2.1, 1.8, 2.0, 2.2, 2.0, 2.0, 2.2, 10.0,
10.0, -10.0, -10.0, ],
)
.expect("Matrix creation should succeed");
let mut lof = LocalOutlierFactor::new()
.with_n_neighbors(5)
.with_contamination(0.2);
lof.fit(&data).expect("LOF fit should succeed");
let predictions = lof.predict(&data);
assert_eq!(predictions.len(), 10);
for &pred in &predictions {
assert!(pred == 1 || pred == -1);
}
let n_anomalies = predictions.iter().filter(|&&p| p == -1).count();
assert!((1..=3).contains(&n_anomalies));
}
#[test]
fn test_lof_score_samples() {
let data = Matrix::from_vec(
6,
2,
vec![
2.0, 2.0, 2.1, 2.0, 1.9, 2.1, 2.0, 1.9, 10.0, 10.0, -10.0, -10.0, ],
)
.expect("Matrix creation should succeed");
let mut lof = LocalOutlierFactor::new().with_n_neighbors(3);
lof.fit(&data).expect("LOF fit should succeed");
let scores = lof.score_samples(&data);
assert_eq!(scores.len(), 6);
let normal_avg = (scores[0] + scores[1] + scores[2] + scores[3]) / 4.0;
let outlier_avg = (scores[4] + scores[5]) / 2.0;
assert!(outlier_avg > normal_avg);
}
#[test]
fn test_lof_negative_outlier_factor() {
let data = Matrix::from_vec(
6,
2,
vec![
2.0, 2.0, 2.1, 2.0, 1.9, 2.1, 2.0, 1.9, 10.0, 10.0, -10.0, -10.0,
],
)
.expect("Matrix creation should succeed");
let mut lof = LocalOutlierFactor::new().with_n_neighbors(3);
lof.fit(&data).expect("LOF fit should succeed");
let nof = lof.negative_outlier_factor();
assert_eq!(nof.len(), 6);
let scores = lof.score_samples(&data);
for i in 0..6 {
assert!(nof[i] < 0.0 || scores[i] < 1.0);
}
}
#[test]
fn test_lof_contamination() {
let data = Matrix::from_vec(
10,
2,
vec![
2.0, 2.0, 2.1, 2.0, 1.9, 2.1, 2.0, 1.9, 2.1, 2.1, 1.8, 2.0, 2.2, 2.0, 2.0, 2.2, 10.0,
10.0, -10.0, -10.0,
],
)
.expect("Matrix creation should succeed");
let mut lof_low = LocalOutlierFactor::new()
.with_contamination(0.1)
.with_n_neighbors(5);
lof_low.fit(&data).expect("LOF fit should succeed");
let pred_low = lof_low.predict(&data);
let anomalies_low = pred_low.iter().filter(|&&p| p == -1).count();
let mut lof_high = LocalOutlierFactor::new()
.with_contamination(0.3)
.with_n_neighbors(5);
lof_high.fit(&data).expect("LOF fit should succeed");
let pred_high = lof_high.predict(&data);
let anomalies_high = pred_high.iter().filter(|&&p| p == -1).count();
assert!(anomalies_high >= anomalies_low);
}
#[test]
fn test_lof_n_neighbors() {
let data = Matrix::from_vec(
8,
2,
vec![
2.0, 2.0, 2.1, 2.0, 1.9, 2.1, 2.0, 1.9, 2.1, 2.1, 1.8, 2.0, 10.0, 10.0, -10.0, -10.0,
],
)
.expect("Matrix creation should succeed");
let mut lof_few = LocalOutlierFactor::new().with_n_neighbors(3);
lof_few.fit(&data).expect("LOF fit should succeed");
let scores_few = lof_few.score_samples(&data);
let mut lof_many = LocalOutlierFactor::new().with_n_neighbors(5);
lof_many.fit(&data).expect("LOF fit should succeed");
let scores_many = lof_many.score_samples(&data);
assert_eq!(scores_few.len(), 8);
assert_eq!(scores_many.len(), 8);
let diff_exists = scores_few
.iter()
.zip(scores_many.iter())
.any(|(a, b)| (a - b).abs() > 0.01);
assert!(diff_exists);
}
#[test]
fn test_lof_varying_density_clusters() {
let data = Matrix::from_vec(
9,
2,
vec![
0.0, 0.0, 0.1, 0.1, -0.1, -0.1, 0.0, 0.1, 10.0, 10.0, 12.0, 12.0, 11.0, 9.0, 5.0, 5.0, 5.5, 5.5,
],
)
.expect("Matrix creation should succeed");
let mut lof = LocalOutlierFactor::new()
.with_n_neighbors(3)
.with_contamination(0.2);
lof.fit(&data).expect("LOF fit should succeed");
let scores = lof.score_samples(&data);
let predictions = lof.predict(&data);
assert!(scores[7] > 1.0 || scores[8] > 1.0);
let n_anomalies = predictions.iter().filter(|&&p| p == -1).count();
assert!(n_anomalies >= 1);
}
#[test]
fn test_lof_lof_score_interpretation() {
let data = Matrix::from_vec(
5,
2,
vec![
2.0, 2.0, 2.1, 2.0, 1.9, 2.1, 2.0, 1.9, 10.0, 10.0, ],
)
.expect("Matrix creation should succeed");
let mut lof = LocalOutlierFactor::new().with_n_neighbors(3);
lof.fit(&data).expect("LOF fit should succeed");
let scores = lof.score_samples(&data);
let normal_scores = &scores[0..4];
let outlier_score = scores[4];
for &score in normal_scores {
assert!((0.5..2.0).contains(&score));
}
assert!(outlier_score > 1.5);
}
#[test]
fn test_lof_all_normal() {
let data = Matrix::from_vec(
6,
2,
vec![2.0, 2.0, 2.1, 2.0, 1.9, 2.1, 2.0, 1.9, 2.1, 2.1, 1.8, 2.0],
)
.expect("Matrix creation should succeed");
let mut lof = LocalOutlierFactor::new()
.with_contamination(0.1)
.with_n_neighbors(3);
lof.fit(&data).expect("LOF fit should succeed");
let predictions = lof.predict(&data);
let scores = lof.score_samples(&data);
for &score in &scores {
assert!((0.5..1.5).contains(&score));
}
let n_normal = predictions.iter().filter(|&&p| p == 1).count();
assert!(n_normal >= 5);
}
#[test]
fn test_lof_score_samples_finite() {
let data = Matrix::from_vec(4, 2, vec![2.0, 2.0, 2.1, 2.0, 1.9, 2.1, 10.0, 10.0])
.expect("Matrix creation should succeed");
let mut lof = LocalOutlierFactor::new().with_n_neighbors(2);
lof.fit(&data).expect("LOF fit should succeed");
let scores = lof.score_samples(&data);
for &score in &scores {
assert!(score.is_finite());
assert!(score > 0.0); }
}
#[test]
fn test_lof_multidimensional() {
let data = Matrix::from_vec(
6,
3,
vec![
1.0, 2.0, 3.0, 1.1, 2.1, 3.1, 1.0, 2.0, 3.0, 0.9, 1.9, 2.9, 10.0, 10.0, 10.0, -10.0,
-10.0, -10.0,
],
)
.expect("Matrix creation should succeed");
let mut lof = LocalOutlierFactor::new()
.with_contamination(0.3)
.with_n_neighbors(3);
lof.fit(&data).expect("LOF fit should succeed");
let predictions = lof.predict(&data);
let scores = lof.score_samples(&data);
assert_eq!(predictions.len(), 6);
assert_eq!(scores.len(), 6);
}
#[test]
#[should_panic(expected = "Model not fitted")]
fn test_lof_predict_before_fit() {
let data =
Matrix::from_vec(2, 2, vec![1.0, 1.0, 2.0, 2.0]).expect("Matrix creation should succeed");
let lof = LocalOutlierFactor::new();
let _ = lof.predict(&data); }
#[test]
#[should_panic(expected = "Model not fitted")]
fn test_lof_score_samples_before_fit() {
let data =
Matrix::from_vec(2, 2, vec![1.0, 1.0, 2.0, 2.0]).expect("Matrix creation should succeed");
let lof = LocalOutlierFactor::new();
let _ = lof.score_samples(&data); }
#[test]
#[should_panic(expected = "Model not fitted")]
fn test_lof_negative_outlier_factor_before_fit() {
let lof = LocalOutlierFactor::new();
let _ = lof.negative_outlier_factor(); }