use ferrolearn_core::{Fit, Predict};
use ferrolearn_tree::{
BaggingClassifier, BaggingRegressor, DecisionTreeClassifier, DecisionTreeRegressor,
};
use ndarray::{Array1, Array2, array};
fn x_nan() -> Array2<f64> {
let mut x = Array2::from_shape_vec(
(6, 2),
vec![1.0, 2.0, 2.0, 3.0, 3.0, 3.0, 5.0, 6.0, 6.0, 7.0, 7.0, 8.0],
)
.unwrap();
x[[0, 0]] = f64::NAN;
x
}
fn yc() -> Array1<usize> {
array![0, 0, 0, 1, 1, 1]
}
fn yr() -> Array1<f64> {
array![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
}
#[test]
fn divergence_decision_tree_classifier_nan_stack_overflow() {
let fitted = DecisionTreeClassifier::<f64>::new()
.fit(&x_nan(), &yc())
.expect("fit must accept NaN (sklearn force_all_finite=False), not abort");
assert_eq!(fitted.predict(&x_nan()).unwrap(), array![0, 0, 0, 1, 1, 1]);
}
#[test]
fn divergence_decision_tree_regressor_nan_stack_overflow() {
let fitted = DecisionTreeRegressor::<f64>::new()
.fit(&x_nan(), &yr())
.expect("fit must accept NaN, not abort");
let preds = fitted.predict(&x_nan()).unwrap();
for (p, e) in preds.iter().zip([1.0, 2.0, 3.0, 4.0, 5.0, 6.0]) {
assert!((p - e).abs() < 1e-12, "{p} != {e}");
}
}
#[test]
fn divergence_bagging_classifier_nan_stack_overflow() {
let fitted = BaggingClassifier::<f64>::new()
.with_n_estimators(3)
.with_random_state(0)
.fit(&x_nan(), &yc())
.expect("fit must accept NaN (DecisionTree base inherits the fix), not abort");
let preds = fitted.predict(&x_nan()).unwrap();
assert_eq!(preds.len(), 6);
}
#[test]
fn divergence_bagging_regressor_nan_stack_overflow() {
let fitted = BaggingRegressor::<f64>::new()
.with_n_estimators(3)
.with_random_state(0)
.fit(&x_nan(), &yr())
.expect("fit must accept NaN, not abort");
let preds = fitted.predict(&x_nan()).unwrap();
assert_eq!(preds.len(), 6);
}