use super::*;
#[test]
fn response_curve_of_monotone_sweep_is_straight() {
let sweep = scalar_sweep(&[0.0, 1.0, 2.0, 3.0]);
let r = ResponseCurve::measure(&sweep).unwrap();
assert!((r.net_response() - 3.0).abs() < 1e-6);
assert!((r.monotonicity() - 1.0).abs() < 1e-6);
assert_eq!(r.step_count(), 4);
}
#[test]
fn response_curve_penalizes_backtracking() {
let sweep = scalar_sweep(&[0.0, 2.0, 1.0]);
let r = ResponseCurve::measure(&sweep).unwrap();
assert!((r.net_response() - 1.0).abs() < 1e-6);
assert!((r.monotonicity() - 1.0 / 3.0).abs() < 1e-6);
}
#[test]
fn response_curve_of_flat_sweep_is_unresponsive() {
let sweep = scalar_sweep(&[2.0, 2.0, 2.0]);
let r = ResponseCurve::measure(&sweep).unwrap();
assert_eq!(r.net_response(), 0.0);
assert_eq!(r.monotonicity(), 0.0);
}
#[test]
fn response_curve_averages_seeds_per_step() {
let sweep = vec![
vec![latent([1, 1], vec![-0.5]), latent([1, 1], vec![0.5])],
vec![latent([1, 1], vec![0.5]), latent([1, 1], vec![1.5])],
vec![latent([1, 1], vec![1.5]), latent([1, 1], vec![2.5])],
];
let r = ResponseCurve::measure(&sweep).unwrap();
assert!((r.net_response() - 2.0).abs() < 1e-6);
assert!((r.monotonicity() - 1.0).abs() < 1e-6);
}
#[test]
fn response_curve_is_straight_in_multiple_dimensions() {
let sweep = vec![
vec![latent([2, 1], vec![0.0, 0.0])],
vec![latent([2, 1], vec![1.0, 1.0])],
vec![latent([2, 1], vec![2.0, 2.0])],
];
let r = ResponseCurve::measure(&sweep).unwrap();
assert!((r.net_response() - (8.0f32).sqrt()).abs() < 1e-6);
assert!((r.monotonicity() - 1.0).abs() < 1e-6);
}
#[test]
fn response_curve_rejects_too_few_steps() {
assert!(matches!(
ResponseCurve::measure(&scalar_sweep(&[1.0])),
Err(Error::Validation(_))
));
assert!(matches!(
ResponseCurve::measure(&[]),
Err(Error::Validation(_))
));
}
#[test]
fn response_curve_rejects_empty_step_and_shape_mismatch() {
let with_empty = vec![vec![latent([1, 1], vec![0.0])], vec![]];
assert!(matches!(
ResponseCurve::measure(&with_empty),
Err(Error::Validation(_))
));
let mismatch = vec![
vec![latent([1, 2], vec![0.0, 1.0])],
vec![latent([2, 1], vec![0.0, 1.0])],
];
assert!(matches!(
ResponseCurve::measure(&mismatch),
Err(Error::Validation(_))
));
}
#[test]
fn sweep_response_reports_controllable_signature() {
let generator = MockGenerator {
cond_weight: 2.0,
seed_weight: 1.0,
};
let r = sweep_response(
&generator,
&[cond(0.0), cond(1.0), cond(2.0), cond(3.0)],
&[Seed(1), Seed(2)],
¶ms(),
)
.unwrap();
assert!((r.net_response() - 6.0).abs() < 1e-4);
assert!((r.monotonicity() - 1.0).abs() < 1e-4);
assert_eq!(r.step_count(), 4);
}
#[test]
fn sweep_response_flags_condition_blind_generator() {
let generator = MockGenerator {
cond_weight: 0.0,
seed_weight: 1.0,
};
let r = sweep_response(
&generator,
&[cond(0.0), cond(5.0), cond(9.0)],
&[Seed(3), Seed(4)],
¶ms(),
)
.unwrap();
assert_eq!(r.net_response(), 0.0);
assert_eq!(r.monotonicity(), 0.0);
}
#[test]
fn sweep_response_propagates_and_validates() {
let ok = MockGenerator {
cond_weight: 1.0,
seed_weight: 0.0,
};
assert!(matches!(
sweep_response(&ok, &[cond(0.0)], &[Seed(1)], ¶ms()),
Err(Error::Validation(_))
));
assert!(matches!(
sweep_response(&ok, &[cond(0.0), cond(1.0)], &[], ¶ms()),
Err(Error::Validation(_))
));
assert!(matches!(
sweep_response(
&FailingGenerator,
&[cond(0.0), cond(1.0)],
&[Seed(1)],
¶ms()
),
Err(Error::Validation(_))
));
}