use super::*;
use crate::primitives::Matrix;
#[test]
fn falsify_if_001_scores_bounded() {
let data = Matrix::from_vec(
8,
2,
vec![
1.0, 1.0, 1.1, 1.0, 1.0, 1.1, 0.9, 0.9, 1.1, 1.1, 1.0, 0.9, 0.9, 1.1, 1.0, 1.0,
],
)
.expect("valid matrix");
let mut iforest = IsolationForest::new()
.with_n_estimators(50)
.with_random_state(42);
iforest.fit(&data).expect("fit succeeds");
let scores = iforest.score_samples(&data);
for (i, &score) in scores.iter().enumerate() {
assert!(
(-1.0..=0.0).contains(&score),
"FALSIFIED IF-001: score[{i}]={score}, expected in [-1,0]"
);
}
}
#[test]
fn falsify_if_002_predictions_binary() {
let data = Matrix::from_vec(
8,
2,
vec![
1.0, 1.0, 1.1, 1.0, 1.0, 1.1, 0.9, 0.9, 1.1, 1.1, 1.0, 0.9, 0.9, 1.1, 1.0, 1.0,
],
)
.expect("valid matrix");
let mut iforest = IsolationForest::new()
.with_n_estimators(50)
.with_random_state(42)
.with_contamination(0.1);
iforest.fit(&data).expect("fit succeeds");
let preds = iforest.predict(&data);
for (i, &p) in preds.iter().enumerate() {
assert!(
p == 1 || p == -1,
"FALSIFIED IF-002: prediction[{i}]={p}, expected 1 or -1"
);
}
}
#[test]
fn falsify_if_003_predictions_length() {
let data = Matrix::from_vec(
10,
2,
vec![
1.0, 1.0, 1.1, 1.0, 1.0, 1.1, 0.9, 0.9, 1.2, 1.0, 1.1, 1.1, 1.0, 0.9, 0.9, 1.1, 1.0,
1.0, 0.8, 1.2,
],
)
.expect("valid matrix");
let mut iforest = IsolationForest::new()
.with_n_estimators(50)
.with_random_state(42);
iforest.fit(&data).expect("fit succeeds");
let preds = iforest.predict(&data);
assert_eq!(
preds.len(),
10,
"FALSIFIED IF-003: predictions len={}, expected 10",
preds.len()
);
}
#[test]
fn falsify_if_004_batch_equals_per_row_scores() {
let data = Matrix::from_vec(
8,
2,
vec![
1.0, 1.0, 1.1, 1.0, 1.0, 1.1, 0.9, 0.9, 1.1, 1.1, 5.0, 5.0, 0.9, 1.1, -4.0, 4.0,
],
)
.expect("valid matrix");
let mut iforest = IsolationForest::new()
.with_n_estimators(64)
.with_random_state(7);
iforest.fit(&data).expect("fit succeeds");
let batch = iforest.score_samples(&data);
let (n, d) = data.shape();
for i in 0..n {
let row_vals: Vec<f32> = (0..d).map(|j| data.get(i, j)).collect();
let row = Matrix::from_vec(1, d, row_vals).expect("valid row");
let single = iforest.score_samples(&row);
assert_eq!(single.len(), 1);
assert_eq!(
single[0].to_bits(),
batch[i].to_bits(),
"FALSIFIED IF-004: batched score[{i}]={} != per-row score={} (buffer reuse leaked state)",
batch[i],
single[0]
);
}
}
#[test]
fn falsify_if_005_score_samples_deterministic() {
let data = Matrix::from_vec(
6,
3,
vec![
1.0, 2.0, 3.0, 1.1, 2.1, 2.9, 0.9, 1.9, 3.1, 10.0, -5.0, 0.0, 1.0, 2.0, 3.2, -8.0, 7.0,
1.0,
],
)
.expect("valid matrix");
let mut iforest = IsolationForest::new()
.with_n_estimators(40)
.with_random_state(99);
iforest.fit(&data).expect("fit succeeds");
let a = iforest.score_samples(&data);
let b = iforest.score_samples(&data);
for (i, (&x, &y)) in a.iter().zip(b.iter()).enumerate() {
assert_eq!(
x.to_bits(),
y.to_bits(),
"FALSIFIED IF-005: score[{i}] not deterministic ({x} vs {y})"
);
}
}
mod iforest_proptest_falsify {
use super::*;
use proptest::prelude::*;
proptest! {
#![proptest_config(ProptestConfig::with_cases(10))]
#[test]
fn falsify_if_001_prop_scores_bounded(
n in 8..=20usize,
seed in 0..200u32,
) {
let data: Vec<f32> = (0..n * 2)
.map(|i| ((i as f32 + seed as f32) * 0.37).sin() * 10.0)
.collect();
let matrix = Matrix::from_vec(n, 2, data).expect("valid");
let mut iforest = IsolationForest::new()
.with_n_estimators(50)
.with_random_state(seed as u64);
iforest.fit(&matrix).expect("fit");
let scores = iforest.score_samples(&matrix);
for (i, &score) in scores.iter().enumerate() {
prop_assert!(
(-1.0..=0.0).contains(&score),
"FALSIFIED IF-001-prop: score[{}]={} not in [-1,0]",
i, score
);
}
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(10))]
#[test]
fn falsify_if_003_prop_predictions_length(
n in 8..=20usize,
seed in 0..200u32,
) {
let data: Vec<f32> = (0..n * 2)
.map(|i| ((i as f32 + seed as f32) * 0.37).sin() * 10.0)
.collect();
let matrix = Matrix::from_vec(n, 2, data).expect("valid");
let mut iforest = IsolationForest::new()
.with_n_estimators(50)
.with_random_state(seed as u64);
iforest.fit(&matrix).expect("fit");
let preds = iforest.predict(&matrix);
prop_assert_eq!(
preds.len(),
n,
"FALSIFIED IF-003-prop: predictions len {} != {}",
preds.len(), n
);
}
}
}