lumen-dataset 0.4.0

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,
} 

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 = B::Output;
    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 = B::Output;
    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 = B::Output;
    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<B::Output> 
where 
    D: Dataset,
    B: Batcher<Item = D::Item> 
{
    let begin = *cursor;
    if begin >= loader.dataset.len() {
        None
    } else {
        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];
            items.push(loader.dataset.get(index).unwrap());
        }        
        *cursor += items.len();
        let batch = loader.batcher.batch(items);
        Some(batch)
    }
}