baselines 0.1.0

Baseline correction algorithms for signals, spectra, and images
Documentation
use baselines::two_d::spline::{
    Irsqr2DParams, MixtureModel2DParams, PsplineAirPls2DParams, PsplineArPls2DParams,
    PsplineAsls2DParams, PsplineBrPls2DParams, PsplineIarPls2DParams, PsplineIasls2DParams,
    PsplineLsrPls2DParams, PsplinePsalsa2DParams, Spline2DParams, Spline2DWorkspace, irsqr,
    mixture_model, pspline_airpls, pspline_arpls, pspline_asls, pspline_asls_into, pspline_brpls,
    pspline_iarpls, pspline_iasls, pspline_lsrpls, pspline_psalsa,
};
use baselines::{BaselineError, MatrixView, MatrixViewMut};

#[test]
fn two_d_spline_methods_preserve_constant_surfaces() {
    let data = vec![2.5; 30];
    let input = MatrixView::row_major(&data, 5, 6).unwrap();

    for fit in [
        pspline_asls(input, PsplineAsls2DParams::default()).unwrap(),
        pspline_iasls(input, PsplineIasls2DParams::default()).unwrap(),
        pspline_airpls(input, PsplineAirPls2DParams::default()).unwrap(),
        pspline_arpls(input, PsplineArPls2DParams::default()).unwrap(),
        pspline_iarpls(input, PsplineIarPls2DParams::default()).unwrap(),
        pspline_psalsa(
            input,
            PsplinePsalsa2DParams {
                k: Some(1.0),
                ..PsplinePsalsa2DParams::default()
            },
        )
        .unwrap(),
        pspline_brpls(input, PsplineBrPls2DParams::default()).unwrap(),
        pspline_lsrpls(input, PsplineLsrPls2DParams::default()).unwrap(),
        irsqr(input, Irsqr2DParams::default()).unwrap(),
        mixture_model(input, MixtureModel2DParams::default()).unwrap(),
    ] {
        assert_eq!(fit.shape(), (5, 6));
        assert!(fit.baseline.iter().all(|value| (*value - 2.5).abs() < 1e-6));
    }
}

#[test]
fn two_d_spline_into_reuses_workspace_and_output_buffer() {
    let rows = 5;
    let cols = 6;
    let data = (0..rows)
        .flat_map(|row| (0..cols).map(move |col| 1.0 + row as f64 * 0.1 + col as f64 * 0.05))
        .collect::<Vec<_>>();
    let input = MatrixView::row_major(&data, rows, cols).unwrap();
    let mut output = vec![0.0; data.len()];
    let output_view = MatrixViewMut::row_major(&mut output, rows, cols).unwrap();
    let mut workspace = Spline2DWorkspace::new(0, 0);

    let report = pspline_asls_into(
        input,
        PsplineAsls2DParams::default(),
        output_view,
        &mut workspace,
    )
    .unwrap();

    assert!(report.iterations > 0);
    assert!(output.iter().all(|value| value.is_finite()));

    let mut second_output = vec![0.0; data.len()];
    let second_output_view = MatrixViewMut::row_major(&mut second_output, rows, cols).unwrap();
    let second_report = pspline_asls_into(
        input,
        PsplineAsls2DParams::default(),
        second_output_view,
        &mut workspace,
    )
    .unwrap();

    assert!(second_report.iterations > 0);
    assert_eq!(output, second_output);
}

#[test]
fn two_d_spline_rejects_invalid_parameters_and_shapes() {
    let data = vec![1.0; 30];
    let input = MatrixView::row_major(&data, 5, 6).unwrap();

    let error = pspline_asls(
        input,
        PsplineAsls2DParams {
            p: 0.0,
            ..PsplineAsls2DParams::default()
        },
    )
    .unwrap_err();
    assert!(matches!(error, BaselineError::InvalidParameter { .. }));

    let error = pspline_arpls(
        input,
        PsplineArPls2DParams {
            spline: Spline2DParams {
                lambda: 0.0,
                ..Spline2DParams::default()
            },
        },
    )
    .unwrap_err();
    assert!(matches!(error, BaselineError::InvalidParameter { .. }));

    let error = irsqr(
        input,
        Irsqr2DParams {
            quantile: 1.0,
            ..Irsqr2DParams::default()
        },
    )
    .unwrap_err();
    assert!(matches!(error, BaselineError::InvalidParameter { .. }));

    let small = vec![1.0; 12];
    let small_input = MatrixView::row_major(&small, 3, 4).unwrap();
    let error = pspline_asls(small_input, PsplineAsls2DParams::default()).unwrap_err();
    assert!(matches!(error, BaselineError::TooShort { .. }));

    let mut output = vec![0.0; data.len()];
    let output = MatrixViewMut::row_major(&mut output, 6, 5).unwrap();
    let mut workspace = Spline2DWorkspace::new(5, 6);
    let error = pspline_asls_into(
        input,
        PsplineAsls2DParams::default(),
        output,
        &mut workspace,
    )
    .unwrap_err();
    assert!(matches!(error, BaselineError::LengthMismatch { .. }));
}