use super::{lower_bound_outward_active_count, LOWER_BOUND_SEPARATION_ACTIVE_MIN};
use ndarray::{array, Array1};
const GRAD_THRESHOLD: f64 = 1.0e-3;
fn bounds(lower: Array1<f64>, upper: Array1<f64>) -> (Array1<f64>, Array1<f64>) {
(lower, upper)
}
#[test]
fn no_bounds_counts_nothing() {
let x = array![0.0, 0.0];
let g = array![5.0, 5.0];
assert_eq!(
lower_bound_outward_active_count(&x, &g, None, GRAD_THRESHOLD),
0,
"with no bounds no axis can be bound-active",
);
}
#[test]
fn positive_gradient_at_lower_bound_is_counted() {
let lower = array![0.0];
let upper = array![10.0];
let x = array![0.0]; let g = array![1.0]; let n = lower_bound_outward_active_count(&x, &g, Some(&bounds(lower, upper)), GRAD_THRESHOLD);
assert_eq!(
n, 1,
"a strong POSITIVE gradient at the lower bound is the outward pull and must be counted",
);
}
#[test]
fn negative_gradient_at_lower_bound_is_not_counted() {
let lower = array![0.0];
let upper = array![10.0];
let x = array![0.0]; let g = array![-1.0]; let n = lower_bound_outward_active_count(&x, &g, Some(&bounds(lower, upper)), GRAD_THRESHOLD);
assert_eq!(
n, 0,
"a strong NEGATIVE gradient at the lower bound is feasible descent, not outward",
);
}
#[test]
fn interior_axis_is_never_counted() {
let lower = array![0.0];
let upper = array![10.0];
let x = array![5.0]; let g = array![100.0]; let n = lower_bound_outward_active_count(&x, &g, Some(&bounds(lower, upper)), GRAD_THRESHOLD);
assert_eq!(n, 0, "an interior (non-bound) axis must never be counted");
}
#[test]
fn weak_outward_gradient_below_floor_is_not_counted() {
let lower = array![0.0];
let upper = array![10.0];
let x = array![0.0];
let g = array![1.0e-6]; let n = lower_bound_outward_active_count(&x, &g, Some(&bounds(lower, upper)), GRAD_THRESHOLD);
assert_eq!(n, 0, "a sub-floor outward gradient must not be counted");
}
#[test]
fn separation_active_min_threshold_semantics() {
let lower = array![0.0, 0.0, 0.0, 0.0];
let upper = array![10.0, 10.0, 10.0, 10.0];
let x = array![0.0, 0.0, 0.0, 5.0];
let g = array![1.0, 2.0, -3.0, 4.0];
let n = lower_bound_outward_active_count(
&x,
&g,
Some(&bounds(lower.clone(), upper.clone())),
GRAD_THRESHOLD,
);
assert_eq!(
n, 2,
"only the two outward lower-bound axes count (idx 0,1); feasible-descent and interior excluded",
);
assert!(
n >= LOWER_BOUND_SEPARATION_ACTIVE_MIN,
"two outward axes must meet the separation-active activation threshold",
);
let g_one = array![1.0, -2.0, -3.0, 4.0];
let n_one =
lower_bound_outward_active_count(&x, &g_one, Some(&bounds(lower, upper)), GRAD_THRESHOLD);
assert_eq!(n_one, 1, "only one axis is outward-railed here");
assert!(
n_one < LOWER_BOUND_SEPARATION_ACTIVE_MIN,
"a single outward axis must stay below the separation-active threshold",
);
}