use ferrolearn_core::traits::{Fit, Transform};
use ferrolearn_tree::RandomTreesEmbedding;
use ndarray::Array2;
fn make_data() -> Array2<f64> {
let mut data = Vec::with_capacity(30 * 4);
let mut state: u64 = 0x243F_6A88_85A3_08D3;
for _ in 0..(30 * 4) {
state ^= state >> 12;
state ^= state << 25;
state ^= state >> 27;
let z = state.wrapping_mul(0x2545_F491_4F6C_DD1D);
data.push((z >> 11) as f64 / (1u64 << 53) as f64 * 10.0);
}
Array2::from_shape_vec((30, 4), data).unwrap()
}
#[test]
fn divergence_n_estimators_default_is_100() {
const SK_N_ESTIMATORS: usize = 100;
let model = RandomTreesEmbedding::<f64>::new();
assert_eq!(
model.n_estimators, SK_N_ESTIMATORS,
"RandomTreesEmbedding::new() sets n_estimators = {}; sklearn default is \
100 (_forest.py:2820, live get_params)",
model.n_estimators
);
}
#[test]
fn defaults_match_sklearn() {
const SK_MAX_DEPTH: usize = 5; const SK_MIN_SAMPLES_SPLIT: usize = 2;
let model = RandomTreesEmbedding::<f64>::new();
assert_eq!(model.max_depth, Some(SK_MAX_DEPTH));
assert_eq!(model.min_samples_split, SK_MIN_SAMPLES_SPLIT);
assert!(model.random_state.is_none()); }
#[test]
fn one_hot_row_sum_equals_n_estimators_and_binary() {
const K: usize = 7;
let x = make_data();
let model = RandomTreesEmbedding::<f64>::new()
.with_n_estimators(K)
.with_max_depth(Some(3))
.with_random_state(42);
let fitted = model.fit(&x, &()).unwrap();
let embedded = fitted.transform(&x).unwrap();
assert_eq!(embedded.nrows(), x.nrows());
for i in 0..embedded.nrows() {
let mut row_sum = 0.0;
for &v in embedded.row(i).iter() {
assert!(
(v - 0.0).abs() < 1e-12 || (v - 1.0).abs() < 1e-12,
"entry [{i}] = {v} not in {{0,1}} (sklearn one-hot of apply is binary)"
);
row_sum += v;
}
assert!(
(row_sum - K as f64).abs() < 1e-12,
"row {i} sums to {row_sum}, expected exactly {K} (one active leaf per \
tree, sklearn _forest.py:2982)"
);
}
}
#[test]
fn ncols_equals_n_output_features() {
let x = make_data();
let model = RandomTreesEmbedding::<f64>::new()
.with_n_estimators(6)
.with_max_depth(Some(3))
.with_random_state(7);
let fitted = model.fit(&x, &()).unwrap();
let embedded = fitted.transform(&x).unwrap();
assert_eq!(embedded.ncols(), fitted.n_output_features());
assert!(fitted.n_output_features() >= fitted.n_estimators());
}
#[test]
fn random_state_reproducible() {
let x = make_data();
let model = RandomTreesEmbedding::<f64>::new()
.with_n_estimators(6)
.with_max_depth(Some(3))
.with_random_state(42);
let f1 = model.fit(&x, &()).unwrap();
let e1 = f1.transform(&x).unwrap();
let f2 = model.fit(&x, &()).unwrap();
let e2 = f2.transform(&x).unwrap();
assert_eq!(e1, e2, "same seed must produce identical embedding");
}