adaptif 0.0.1

Adaptive filters for digital signal processing.
Documentation
use std::ops::{Deref, DerefMut};

use super::FilterBase;

use crate::Result;
use crate::algorithms::Rls;
use crate::filters::ProcessingMode;
use crate::types::{Float, WindowSize};

#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct RlsOptions<F: Float> {
    pub forgetting_factor: F,
    pub p_init_scale: F,
    pub window_size: usize,
}
impl<F: Float> Default for RlsOptions<F> {
    fn default() -> Self {
        #[allow(
            clippy::unwrap_used,
            clippy::missing_panics_doc,
            reason = "Trivial conversions"
        )]
        Self {
            forgetting_factor: F::from(0.999).unwrap(),
            p_init_scale: F::from(0.001).unwrap(),
            window_size: 8,
        }
    }
}

#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct RlsFilter<F: Float> {
    inner: FilterBase<F, Rls<F>>,
}
impl<F: Float> RlsFilter<F> {
    /// # Errors
    ///
    /// Returns an error if options.forgetting factor <= 0.0 or > 1.0, or if init scale <= 0.0.
    #[allow(clippy::needless_pass_by_value, reason = "All fields are moved")]
    pub fn new(options: RlsOptions<F>, mode: ProcessingMode) -> Result<Self> {
        let window_size = WindowSize::new(options.window_size)?;
        let block_size = mode.block_size()?;

        let rls = Rls::new(options.forgetting_factor, options.p_init_scale, window_size)?;
        let filter = FilterBase::new(rls, block_size);

        Ok(Self { inner: filter })
    }
}
impl<F: Float> Deref for RlsFilter<F> {
    type Target = FilterBase<F, Rls<F>>;

    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}
impl<F: Float> DerefMut for RlsFilter<F> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.inner
    }
}

#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::indexing_slicing, reason = "Tests")]
mod tests {
    use super::*;
    use crate::types::BlockSize;

    #[test]
    fn new_works() {
        let forgetting_factor = 0.999;
        let p_init_scale = 1e-4;
        let window_size = 16;

        let filter = RlsFilter::new(
            RlsOptions {
                forgetting_factor,
                p_init_scale,
                window_size,
            },
            ProcessingMode::Sample,
        )
        .unwrap();
        let expected_inner = FilterBase::new(
            Rls::new(
                forgetting_factor,
                p_init_scale,
                WindowSize::new(window_size).unwrap(),
            )
            .unwrap(),
            BlockSize::new(1).unwrap(),
        );

        assert_eq!(filter.inner, expected_inner);
        assert_eq!(filter.window_size(), window_size);
    }

    #[test]
    fn load_weights_works() {
        let forgetting_factor = 0.999;
        let p_init_scale = 1e-4;
        let weights = vec![1.0, 2.0, 3.0];

        let filter = {
            let mut filter = RlsFilter::new(
                RlsOptions {
                    forgetting_factor,
                    p_init_scale,
                    window_size: weights.len(),
                },
                ProcessingMode::Sample,
            )
            .unwrap();
            filter.load_weights(weights.clone()).unwrap();
            filter
        };

        let expected_inner = {
            let mut expected_inner = FilterBase::new(
                Rls::new(
                    forgetting_factor,
                    p_init_scale,
                    WindowSize::new(weights.len()).unwrap(),
                )
                .unwrap(),
                BlockSize::new(1).unwrap(),
            );
            expected_inner.load_weights(weights.clone()).unwrap();
            expected_inner
        };

        assert_eq!(filter.inner, expected_inner);
        assert_eq!(filter.window_size(), weights.len());
    }
}