blr-active 0.1.0

Active learning orchestration for Bayesian Linear Regression with Automatic Relevance Determination
Documentation
/// Integration tests for Algorithm 2: Variance-Maximizing Acquisition Function.
use blr_active::active_learning::acquisition::recommend_next_samples;

#[test]
fn test_acquisition_top_k_are_highest_variance() {
    // Grid with clear variance ordering: peaks at x=7
    let grid: Vec<f64> = (0..10).map(|i| i as f64).collect();
    let stds: Vec<f64> = grid
        .iter()
        .map(|&x| -(x - 7.0).powi(2) / 10.0 + 1.0)
        .collect();

    let recs = recommend_next_samples(&grid, &stds, &[], 3, 0.0);
    assert_eq!(recs.len(), 3);

    // First recommendation should be at x=7 (peak)
    assert!(
        (recs[0].input_value - 7.0).abs() < 0.5,
        "peak should be first: {:?}",
        recs[0]
    );
    // All recommendations in descending std order
    for w in recs.windows(2) {
        assert!(
            w[0].expected_std >= w[1].expected_std,
            "recommendations not sorted: {} vs {}",
            w[0].expected_std,
            w[1].expected_std
        );
    }
}

#[test]
fn test_exclusion_radius_no_near_existing() {
    let grid = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
    let stds = vec![0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.05];
    let existing = vec![1.0]; // exclude within 1.5
    let recs = recommend_next_samples(&grid, &stds, &existing, 5, 1.5);
    for r in &recs {
        for s in &existing {
            assert!(
                (r.input_value - s).abs() > 1.5,
                "recommended {:.1} is too close to existing {:.1}",
                r.input_value,
                s
            );
        }
    }
}

#[test]
fn test_ranking_one_indexed() {
    let grid = vec![1.0, 2.0, 3.0];
    let stds = vec![0.5, 0.9, 0.3];
    let recs = recommend_next_samples(&grid, &stds, &[], 3, 0.0);
    for (i, r) in recs.iter().enumerate() {
        assert_eq!(r.rank, i + 1);
    }
}

#[test]
fn test_fewer_than_k_available() {
    let grid = vec![1.0, 2.0];
    let stds = vec![0.5, 0.9];
    // Request 5 but only 2 available
    let recs = recommend_next_samples(&grid, &stds, &[], 5, 0.0);
    assert_eq!(recs.len(), 2, "should return all 2 available");
}

#[test]
fn test_integration_with_algorithm1_output() {
    // Use variance module to generate stds, then pass to acquisition
    use blr_active::active_learning::variance::posterior_std_grid;
    let d = 2usize;
    let sigma_cov = vec![0.5, 0.0, 0.0, 0.1];
    let feature_fn = |x: f64| vec![1.0, x];
    let (grid, stds) = posterior_std_grid(1.0, &sigma_cov, d, 0.0, 10.0, 50, &feature_fn);
    let recs = recommend_next_samples(&grid, &stds, &[5.0], 3, 0.5);
    // Should return some recommendations without panic
    assert!(!recs.is_empty() || grid.is_empty());
    for r in &recs {
        assert!(r.expected_std >= 0.0);
        assert!(r.input_value.is_finite());
    }
}