use super::*;
use scirs2_core::ndarray::{array, Array1, Array2};
mod ridge_regression_struct_tests {
use super::*;
fn make_dataset() -> (Array2<f64>, Array1<f64>) {
let x = Array2::from_shape_vec(
(6, 2),
vec![1.0, 0.0, 1.0, 1.0, 1.0, 2.0, 1.0, 3.0, 1.0, 4.0, 1.0, 5.0],
)
.expect("shape ok");
let y = array![1.0_f64, 3.0, 5.0, 7.0, 9.0, 11.0];
(x, y)
}
#[test]
fn test_ridge_regression_is_pub() {
let _ = RidgeRegression::new(1.0);
}
#[test]
fn test_ridge_regression_fit() {
let (x, y) = make_dataset();
let mut model = RidgeRegression::new(0.5);
let result = model.fit(&x.view(), &y.view());
assert!(
result.is_ok(),
"Ridge fit should succeed: {:?}",
result.err()
);
}
#[test]
fn test_ridge_regression_predict_shape() {
let (x, y) = make_dataset();
let mut model = RidgeRegression::new(1.0);
let fitted = model.fit(&x.view(), &y.view()).expect("fit ok");
let preds = fitted.predict(&x.view()).expect("predict ok");
assert_eq!(preds.len(), x.nrows());
}
#[test]
fn test_ridge_regression_low_alpha() {
let (x, y) = make_dataset();
let mut model = RidgeRegression::new(1e-8);
let fitted = model.fit(&x.view(), &y.view()).expect("fit ok");
let preds = fitted.predict(&x.view()).expect("predict ok");
for (p, t) in preds.iter().zip(y.iter()) {
assert!((p - t).abs() < 0.5, "pred={p} target={t}");
}
}
}
mod f_p_value_fix_tests {
use super::*;
use approx::assert_relative_eq;
fn fixture_x() -> Array2<f64> {
let x1 = [
1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0,
17.0, 18.0, 19.0, 20.0,
];
let x2 = [
5.0, 3.0, 8.0, 2.0, 9.0, 4.0, 7.0, 1.0, 6.0, 10.0, 2.0, 8.0, 3.0, 9.0, 1.0, 7.0, 4.0,
10.0, 5.0, 6.0,
];
let n = x1.len();
let mut x = Array2::<f64>::zeros((n, 2));
for i in 0..n {
x[[i, 0]] = x1[i];
x[[i, 1]] = x2[i];
}
x
}
fn fixture_y_strong() -> Array1<f64> {
array![
-2.2, 3.3, -0.9, 10.6, 3.7, 14.05, 12.35, 24.75, 19.9, 17.12, 31.95, 26.18, 36.28,
30.58, 45.2, 39.64, 46.92, 41.22, 51.32, 53.1
]
}
fn fixture_y_noise() -> Array1<f64> {
array![
3.0, 7.0, 2.0, 9.0, 4.0, 8.0, 1.0, 6.0, 5.0, 10.0, 2.5, 7.5, 3.5, 9.5, 1.5, 6.5, 4.5,
10.5, 5.5, 8.5
]
}
#[test]
fn test_ridge_regression_f_p_value_strong_signal_matches_scipy() {
let x = fixture_x();
let y = fixture_y_strong();
let result = ridge_regression(
&x.view(),
&y.view(),
Some(0.0),
None,
None,
None,
None,
None,
)
.expect("ridge regression should succeed");
assert_relative_eq!(result.f_statistic, 97408.17758838173, max_relative = 1e-4);
assert!(
result.f_p_value < 1e-12,
"expected ~0 (perfect fit), got {}",
result.f_p_value
);
assert!(result.f_p_value >= 0.0);
}
#[test]
fn test_ridge_regression_f_p_value_weak_signal_matches_scipy() {
let x = fixture_x();
let y = fixture_y_noise();
let result = ridge_regression(
&x.view(),
&y.view(),
Some(0.0),
None,
None,
None,
None,
None,
)
.expect("ridge regression should succeed");
assert_relative_eq!(result.f_statistic, 1.6747197597833423, max_relative = 1e-4);
assert_relative_eq!(
result.f_p_value,
0.21683030932143513,
max_relative = 1e-3,
epsilon = 1e-6
);
assert!(
result.f_p_value > 0.05,
"expected a large, non-significant p-value, got {}",
result.f_p_value
);
}
#[test]
fn test_lasso_regression_f_p_value_distinguishes_signal_from_noise() {
let x = fixture_x();
let strong = lasso_regression(
&x.view(),
&fixture_y_strong().view(),
Some(0.01),
None,
None,
None,
None,
None,
)
.expect("lasso regression should succeed");
let noise = lasso_regression(
&x.view(),
&fixture_y_noise().view(),
Some(0.01),
None,
None,
None,
None,
None,
)
.expect("lasso regression should succeed");
assert!((0.0..=1.0).contains(&strong.f_p_value));
assert!((0.0..=1.0).contains(&noise.f_p_value));
assert!(
strong.f_p_value < 0.01,
"strong-signal fit should be highly significant, got {}",
strong.f_p_value
);
assert!(
noise.f_p_value > 0.05,
"weak-signal fit should not look significant, got {}",
noise.f_p_value
);
}
#[test]
fn test_elastic_net_f_p_value_distinguishes_signal_from_noise() {
let x = fixture_x();
let strong = elastic_net(
&x.view(),
&fixture_y_strong().view(),
Some(0.01),
Some(0.5),
None,
None,
None,
None,
None,
)
.expect("elastic net should succeed");
let noise = elastic_net(
&x.view(),
&fixture_y_noise().view(),
Some(0.01),
Some(0.5),
None,
None,
None,
None,
None,
)
.expect("elastic net should succeed");
assert!((0.0..=1.0).contains(&strong.f_p_value));
assert!((0.0..=1.0).contains(&noise.f_p_value));
assert!(
strong.f_p_value < 0.01,
"strong-signal fit should be highly significant, got {}",
strong.f_p_value
);
assert!(
noise.f_p_value > 0.05,
"weak-signal fit should not look significant, got {}",
noise.f_p_value
);
}
#[test]
fn test_group_lasso_f_p_value_distinguishes_signal_from_noise() {
let x = fixture_x();
let groups = [0usize, 1usize];
let strong = group_lasso(
&x.view(),
&fixture_y_strong().view(),
&groups,
Some(0.01),
None,
None,
None,
None,
None,
)
.expect("group lasso should succeed");
let noise = group_lasso(
&x.view(),
&fixture_y_noise().view(),
&groups,
Some(0.01),
None,
None,
None,
None,
None,
)
.expect("group lasso should succeed");
assert!((0.0..=1.0).contains(&strong.f_p_value));
assert!((0.0..=1.0).contains(&noise.f_p_value));
assert!(
strong.f_p_value < 0.01,
"strong-signal fit should be highly significant, got {}",
strong.f_p_value
);
assert!(
noise.f_p_value > 0.05,
"weak-signal fit should not look significant, got {}",
noise.f_p_value
);
}
}
mod t_p_value_fix_tests {
use super::*;
fn fixture_x() -> Array2<f64> {
let x1 = [
1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0,
17.0, 18.0, 19.0, 20.0,
];
let x2 = [
5.0, 3.0, 8.0, 2.0, 9.0, 4.0, 7.0, 1.0, 6.0, 10.0, 2.0, 8.0, 3.0, 9.0, 1.0, 7.0, 4.0,
10.0, 5.0, 6.0,
];
let n = x1.len();
let mut x = Array2::<f64>::zeros((n, 2));
for i in 0..n {
x[[i, 0]] = x1[i];
x[[i, 1]] = x2[i];
}
x
}
fn fixture_y_strong() -> Array1<f64> {
array![
-2.2, 3.3, -0.9, 10.6, 3.7, 14.05, 12.35, 24.75, 19.9, 17.12, 31.95, 26.18, 36.28,
30.58, 45.2, 39.64, 46.92, 41.22, 51.32, 53.1
]
}
#[test]
fn test_ridge_regression_p_values_in_range_and_reflect_signal() {
let x = fixture_x();
let strong = ridge_regression(
&x.view(),
&fixture_y_strong().view(),
Some(0.0),
None,
None,
None,
None,
None,
)
.expect("ridge regression should succeed");
let noise = ridge_regression(
&x.view(),
&array![
3.0, 7.0, 2.0, 9.0, 4.0, 8.0, 1.0, 6.0, 5.0, 10.0, 2.5, 7.5, 3.5, 9.5, 1.5, 6.5,
4.5, 10.5, 5.5, 8.5
]
.view(),
Some(0.0),
None,
None,
None,
None,
None,
)
.expect("ridge regression should succeed");
for &p in strong.p_values.iter() {
assert!(
(0.0..=1.0).contains(&p),
"strong-signal p-value out of range: {p}"
);
}
for &p in noise.p_values.iter() {
assert!(
(0.0..=1.0).contains(&p),
"noise-only p-value out of range: {p}"
);
}
assert!(
strong.p_values.iter().all(|&p| p < 0.01),
"expected all strong-signal coefficients to look significant, got {:?}",
strong.p_values
);
}
}