helena 0.1.0

Core types and component interfaces for helena, a latent data-to-waveform generation platform.
Documentation
use super::*;

#[test]
fn rejects_non_finite_location_and_non_positive_scale() {
    for bad_location in [f32::NAN, f32::INFINITY, f32::NEG_INFINITY] {
        assert!(matches!(
            LogitNormalDensity::new(bad_location, 1.0),
            Err(Error::Validation(_))
        ));
    }
    for bad_scale in [0.0, -1.0, f32::NAN, f32::INFINITY] {
        assert!(matches!(
            LogitNormalDensity::new(0.0, bad_scale),
            Err(Error::Validation(_))
        ));
    }
    let density = LogitNormalDensity::new(-0.5, 0.8).unwrap();
    assert_eq!((density.location(), density.scale()), (-0.5, 0.8));
}

#[test]
fn median_draw_lands_at_the_logistic_of_location() {
    // The median draw (n = 0) lands at logistic(location); centered => t = 0.5.
    let centered = LogitNormalDensity::new(LogitNormalDensity::DEFAULT_LOCATION, 1.0).unwrap();
    assert!((centered.sample(0.0) - 0.5).abs() < 1e-6);
    // A positive location shifts the median above 0.5, a negative one below.
    assert!(LogitNormalDensity::new(2.0, 1.0).unwrap().sample(0.0) > 0.5);
    assert!(LogitNormalDensity::new(-2.0, 1.0).unwrap().sample(0.0) < 0.5);
}

#[test]
fn deserialization_validates_and_rejects_unknown_fields() {
    // A valid object round-trips through the validating `try_from`.
    let density: LogitNormalDensity =
        serde_json::from_str(r#"{"location":-0.5,"scale":0.8}"#).unwrap();
    assert_eq!((density.location(), density.scale()), (-0.5, 0.8));
    // A non-positive scale is rejected on deserialize, not only via `new`.
    assert!(serde_json::from_str::<LogitNormalDensity>(r#"{"location":0.0,"scale":0.0}"#).is_err());
    // A typo'd extra field fails closed rather than being silently ignored.
    assert!(
        serde_json::from_str::<LogitNormalDensity>(r#"{"location":0.0,"scale":1.0,"scle":0.2}"#)
            .is_err()
    );
}

#[test]
fn sample_is_monotone_and_saturates_into_the_unit_interval() {
    let density = LogitNormalDensity::new(0.3, 1.5).unwrap();
    // Increasing the standard-normal draw increases the time (order-preserving).
    assert!(density.sample(-3.0) < density.sample(0.0));
    assert!(density.sample(0.0) < density.sample(3.0));
    // Extreme draws saturate into [0, 1] rather than diverging, so every draw is
    // a valid schedule time.
    for n in [-1e3, 1e3, -50.0, 50.0] {
        let t = density.sample(n);
        assert!((0.0..=1.0).contains(&t), "t={t} out of range for n={n}");
    }
}