#[cfg(feature = "accelerate")]
extern crate accelerate_src;
use ndarray::{Array1, Array2};
use ndarray_rand::rand_distr::Uniform;
use ndarray_rand::RandomExt;
use ngboost_rs::ngboost::{NGBClassifier, NGBRegressor};
use tempfile::tempdir;
fn generate_regression_data(n_samples: usize, n_features: usize) -> (Array2<f64>, Array1<f64>) {
let x = Array2::random((n_samples, n_features), Uniform::new(0.0, 1.0).unwrap());
let y = x.column(0).mapv(|v| v * 2.0 + 1.0)
+ x.column(1).mapv(|v| v * 0.5)
+ Array1::random(n_samples, Uniform::new(-0.1, 0.1).unwrap());
(x, y.mapv(|v| v.abs() + 0.1))
}
fn generate_classification_data(n_samples: usize, n_features: usize) -> (Array2<f64>, Array1<f64>) {
let x = Array2::random((n_samples, n_features), Uniform::new(0.0, 1.0).unwrap());
let linear = x.column(0).mapv(|v| v * 2.0) - x.column(1).mapv(|v| v * 1.5);
let y = linear.mapv(|v| if v > 0.5 { 1.0 } else { 0.0 });
(x, y)
}
fn arrays_approx_equal(a: &Array1<f64>, b: &Array1<f64>, tol: f64) -> bool {
if a.len() != b.len() {
return false;
}
a.iter().zip(b.iter()).all(|(x, y)| (x - y).abs() < tol)
}
#[test]
fn test_regressor_save_load() {
let (x, y) = generate_regression_data(100, 5);
let mut model = NGBRegressor::new(50, 0.1);
model.fit(&x, &y).expect("Fit should succeed");
let preds_before = model.predict(&x);
let dir = tempdir().expect("Failed to create temp dir");
let path = dir.path().join("model.bin");
let path_str = path.to_str().unwrap();
model.save_model(path_str).expect("Save should succeed");
assert!(path.exists(), "Model file should exist");
let loaded_model = NGBRegressor::load_model(path_str).expect("Load should succeed");
let preds_after = loaded_model.predict(&x);
assert!(
arrays_approx_equal(&preds_before, &preds_after, 1e-10),
"Predictions should match after load"
);
dir.close().expect("Failed to close temp dir");
}
#[test]
fn test_classifier_save_load() {
let (x, y) = generate_classification_data(100, 5);
let mut model = NGBClassifier::new(30, 0.1);
model.fit(&x, &y).expect("Fit should succeed");
let preds_before = model.predict(&x);
let proba_before = model.predict_proba(&x);
let dir = tempdir().expect("Failed to create temp dir");
let path = dir.path().join("classifier.bin");
let path_str = path.to_str().unwrap();
model.save_model(path_str).expect("Save should succeed");
let loaded_model = NGBClassifier::load_model(path_str).expect("Load should succeed");
let preds_after = loaded_model.predict(&x);
let proba_after = loaded_model.predict_proba(&x);
assert!(
arrays_approx_equal(&preds_before, &preds_after, 1e-10),
"Class predictions should match after load"
);
for i in 0..proba_before.nrows() {
for j in 0..proba_before.ncols() {
let diff = (proba_before[[i, j]] - proba_after[[i, j]]).abs();
assert!(diff < 1e-10, "Probabilities should match after load");
}
}
dir.close().expect("Failed to close temp dir");
}
#[test]
fn test_regressor_save_load_with_options() {
let (x, y) = generate_regression_data(100, 5);
let mut model = NGBRegressor::with_options(
100, 0.05, true, 0.8, 0.9, false, 50.0, 1e-5, None, 0.15, false,
);
model.fit(&x, &y).expect("Fit should succeed");
let preds_before = model.predict(&x);
let dir = tempdir().expect("Failed to create temp dir");
let path = dir.path().join("model_options.bin");
let path_str = path.to_str().unwrap();
model.save_model(path_str).expect("Save should succeed");
let loaded_model = NGBRegressor::load_model(path_str).expect("Load should succeed");
let preds_after = loaded_model.predict(&x);
assert!(
arrays_approx_equal(&preds_before, &preds_after, 1e-10),
"Predictions should match for model with options"
);
dir.close().expect("Failed to close temp dir");
}
#[test]
fn test_save_load_new_data() {
let (x_train, y_train) = generate_regression_data(100, 5);
let (x_test, _) = generate_regression_data(20, 5);
let mut model = NGBRegressor::new(50, 0.1);
model.fit(&x_train, &y_train).expect("Fit should succeed");
let preds_before = model.predict(&x_test);
let dir = tempdir().expect("Failed to create temp dir");
let path = dir.path().join("model.bin");
let path_str = path.to_str().unwrap();
model.save_model(path_str).expect("Save should succeed");
let loaded_model = NGBRegressor::load_model(path_str).expect("Load should succeed");
let preds_after = loaded_model.predict(&x_test);
assert!(
arrays_approx_equal(&preds_before, &preds_after, 1e-10),
"Test predictions should match after load"
);
dir.close().expect("Failed to close temp dir");
}
#[test]
fn test_load_nonexistent_file() {
let result = NGBRegressor::load_model("/nonexistent/path/model.bin");
assert!(result.is_err(), "Loading nonexistent file should fail");
}
#[test]
fn test_multiple_save_load_cycles() {
let (x, y) = generate_regression_data(80, 4);
let mut model = NGBRegressor::new(30, 0.1);
model.fit(&x, &y).expect("Fit should succeed");
let original_preds = model.predict(&x);
let dir = tempdir().expect("Failed to create temp dir");
for i in 0..3 {
let path = dir.path().join(format!("model_{}.bin", i));
let path_str = path.to_str().unwrap();
model.save_model(path_str).expect("Save should succeed");
let loaded = NGBRegressor::load_model(path_str).expect("Load should succeed");
let preds = loaded.predict(&x);
assert!(
arrays_approx_equal(&original_preds, &preds, 1e-10),
"Predictions should remain consistent through save/load cycles"
);
}
dir.close().expect("Failed to close temp dir");
}
#[test]
fn test_save_creates_file() {
let (x, y) = generate_regression_data(50, 3);
let mut model = NGBRegressor::new(20, 0.1);
model.fit(&x, &y).expect("Fit should succeed");
let dir = tempdir().expect("Failed to create temp dir");
let path = dir.path().join("test_model.bin");
let path_str = path.to_str().unwrap();
assert!(!path.exists());
model.save_model(path_str).expect("Save should succeed");
assert!(path.exists());
let metadata = std::fs::metadata(&path).expect("Should get metadata");
assert!(metadata.len() > 0, "File should not be empty");
dir.close().expect("Failed to close temp dir");
}