lumen-dataset 0.6.1

A tiny ML framework
Documentation
use rand::seq::SliceRandom;
use super::Dataset;
use super::Batcher;

pub struct DataLoader<D, B> 
where 
    D: Dataset,
    B: Batcher<Item = D::Item>
{
    dataset: D,
    batcher: B,
    batch_size: usize,
    shuffle: bool,
} 

#[derive(Debug, thiserror::Error)]
pub enum DataLoaderError {
    #[error("dataset error: {0}")]
    Dataset(String),

    #[error("batcher error: {0}")]
    Batcher(String),
}

pub struct DataLoaderIter<'a, D, B> 
where 
    D: Dataset,
    B: Batcher<Item = D::Item>
{
    loader: &'a DataLoader<D, B>,
    cursor: usize,
    indices: Vec<usize>,
}

pub struct DataLoaderIntoIter<D, B> 
where 
    D: Dataset,
    B: Batcher<Item = D::Item>
{
    loader: DataLoader<D, B>,
    cursor: usize,
    indices: Vec<usize>,
}

impl<D, B> DataLoader<D, B> 
where 
    D: Dataset,
    B: Batcher<Item = D::Item> 
{
    pub fn new(dataset: D, batcher: B, batch_size: usize, shuffle: bool) -> Self {
        Self {
            dataset,
            batcher,
            batch_size,
            shuffle,
        }
    }

    pub fn dataset_len(&self) -> usize {
        self.dataset.len()
    }

    pub fn batch_count(&self) -> usize {
        self.dataset.len() / self.batch_size
    }

    pub fn batch_size(&self) -> usize {
        self.batch_size
    }

    pub fn iter<'a>(&'a self) -> DataLoaderIter<'a, D, B> {
        let indices = self.get_iter_indices();
        DataLoaderIter { loader: self, cursor: 0, indices }
    }

    fn get_iter_indices(&self) -> Vec<usize> {
        let len = self.dataset.len();
        let mut indices: Vec<usize> = (0..len).collect();
        
        if self.shuffle {
            let mut rng = rand::rng();
            indices.shuffle(&mut rng);
        }

        indices
    }
}

impl<D, B> DataLoader<D, B> 
where 
    D: Dataset,
    B: Batcher<Item = D::Item> + Default
{
    pub fn from_dataset(dataset: D, batch_size: usize, shuffle: bool) -> Self {
        Self::new(dataset, B::default(), batch_size, shuffle)
    }
}

impl<D, B> Iterator for DataLoaderIntoIter<D, B> 
where 
    D: Dataset,
    B: Batcher<Item = D::Item> 
{
    type Item = Result<B::Output, DataLoaderError>;
    fn next(&mut self) -> Option<Self::Item> {
        iter_next(&self.loader, &mut self.cursor, &self.indices)
    }
}

impl<'a, D, B> Iterator for DataLoaderIter<'a, D, B> 
where 
    D: Dataset,
    B: Batcher<Item = D::Item> 
{
    type Item = Result<B::Output, DataLoaderError>;
    fn next(&mut self) -> Option<Self::Item> {
        iter_next(&self.loader, &mut self.cursor, &self.indices)
    }
}

impl<D, B> IntoIterator for DataLoader<D, B> 
where 
    D: Dataset,
    B: Batcher<Item = D::Item> 
{
    type IntoIter = DataLoaderIntoIter<D, B>;
    type Item = Result<B::Output, DataLoaderError>;
    fn into_iter(self) -> Self::IntoIter {
        let indices = self.get_iter_indices();
        DataLoaderIntoIter {
            loader: self, 
            cursor: 0,
            indices,
        } 
    }
}

fn iter_next<D, B>(loader: &DataLoader<D, B>, cursor: &mut usize, indices: &[usize]) -> Option<Result<B::Output, DataLoaderError>> 
where 
    D: Dataset,
    B: Batcher<Item = D::Item> 
{
    let begin = *cursor;
    if begin >= loader.dataset.len() {
        None
    } else {
        let mut f = || -> Result<B::Output, DataLoaderError> {
            let end = *cursor + loader.batch_size;
            let end = end.min(loader.dataset.len());
            let mut items = vec![];
            for index in begin..end {
                let index = indices[index];
                let item = loader.dataset
                    .get(index)
                    .map_err(|e| DataLoaderError::Dataset(e.to_string()))?
                    .expect("must valid item");
                items.push(item);
            }        
            *cursor += items.len();
            let batch = loader.batcher
                .batch(items)
                .map_err(|e| DataLoaderError::Batcher(e.to_string()))?;

            Ok(batch)
        };

        Some(f())
    }
}