ruda_dataset/transform/
partial.rs1use crate::Dataset;
2use std::{marker::PhantomData, sync::Arc};
3
4#[derive(new, Clone)]
6pub struct PartialDataset<D, I> {
7 dataset: D,
8 start_index: usize,
9 end_index: usize,
10 input: PhantomData<I>,
11}
12
13impl<D, I> PartialDataset<D, I>
14where
15 D: Dataset<I>,
16{
17 pub fn split(dataset: D, num: usize) -> Vec<PartialDataset<Arc<D>, I>> {
19 let dataset = Arc::new(dataset); let mut current = 0;
22 let mut datasets = Vec::with_capacity(num);
23
24 let batch_size = dataset.len() / num;
25
26 for i in 0..num {
27 let start = current;
28 let mut end = current + batch_size;
29
30 if i == (num - 1) {
31 end = dataset.len();
32 }
33
34 let dataset = PartialDataset::new(dataset.clone(), start, end);
35
36 current += batch_size;
37 datasets.push(dataset);
38 }
39
40 datasets
41 }
42
43 pub fn split_chunks(
45 dataset: D,
46 num: usize,
47 batch_size: usize,
48 ) -> Vec<PartialDataset<Arc<D>, I>> {
49 let dataset = Arc::new(dataset); let total_items = dataset.len();
51
52 let total_batches = total_items.div_ceil(batch_size);
54 let batches_per_split = total_batches / num;
55 let extra_batches = total_batches % num;
56
57 let mut datasets = Vec::with_capacity(num);
58 let mut current_batch = 0;
59
60 for i in 0..num {
61 let split_batches = if i < extra_batches {
63 batches_per_split + 1
64 } else {
65 batches_per_split
66 };
67
68 let start_batch = current_batch;
69 let end_batch = start_batch + split_batches;
70
71 let start_index = start_batch * batch_size;
72 let end_index = core::cmp::min(end_batch * batch_size, total_items);
73
74 if start_index < total_items {
75 datasets.push(PartialDataset::new(dataset.clone(), start_index, end_index));
76 }
77
78 current_batch = end_batch;
79 }
80
81 datasets
82 }
83}
84
85impl<D, I> Dataset<I> for PartialDataset<D, I>
86where
87 D: Dataset<I>,
88 I: Clone + Send + Sync,
89{
90 fn get(&self, index: usize) -> Option<I> {
91 let index = index + self.start_index;
92 if index < self.start_index || index >= self.end_index {
93 return None;
94 }
95 self.dataset.get(index)
96 }
97
98 fn get_many(&self, indices: &[usize]) -> Option<Vec<I>> {
99 if indices.is_empty() { return Some(Vec::new()); }
100 let translated = indices.iter().map(|&index| {
101 let index = index + self.start_index;
102 (index >= self.start_index && index < self.end_index).then_some(index)
103 }).collect::<Option<Vec<_>>>()?;
104 self.dataset.get_many(&translated)
105 }
106
107 fn len(&self) -> usize {
108 usize::min(self.end_index - self.start_index, self.dataset.len())
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115 use crate::FakeDataset;
116 use std::collections::HashSet;
117
118 #[test]
119 fn test_start_from_beginning() {
120 let dataset_original = FakeDataset::<String>::new(27);
121 let mut items_original_1 = HashSet::new();
122 let mut items_original_2 = HashSet::new();
123 let mut items_partial = HashSet::new();
124 dataset_original.iter().enumerate().for_each(|(i, item)| {
125 match i >= 10 {
126 true => items_original_2.insert(item),
127 false => items_original_1.insert(item),
128 };
129 });
130
131 let dataset_partial = PartialDataset::new(dataset_original, 0, 10);
132
133 for item in dataset_partial.iter() {
134 items_partial.insert(item);
135 }
136
137 assert_eq!(dataset_partial.len(), 10);
138 assert_eq!(items_original_1, items_partial);
139 for item in items_original_2 {
140 assert!(!items_partial.contains(&item));
141 }
142 }
143
144 #[test]
145 fn test_start_inside() {
146 let dataset_original = FakeDataset::<String>::new(27);
147 let mut items_original_1 = HashSet::new();
148 let mut items_original_2 = HashSet::new();
149 let mut items_partial = HashSet::new();
150
151 dataset_original.iter().enumerate().for_each(|(i, item)| {
152 match !(10..20).contains(&i) {
153 true => items_original_2.insert(item),
154 false => items_original_1.insert(item),
155 };
156 });
157
158 let dataset_partial = PartialDataset::new(dataset_original, 10, 20);
159 for item in dataset_partial.iter() {
160 items_partial.insert(item);
161 }
162
163 assert_eq!(dataset_partial.len(), 10);
164 assert_eq!(items_original_1, items_partial);
165 for item in items_original_2 {
166 assert!(!items_partial.contains(&item));
167 }
168 }
169
170 #[test]
171 fn test_split_contains_all_items_without_duplicates() {
172 let dataset_original = FakeDataset::<String>::new(27);
173 let mut items_original = Vec::new();
174 let mut items_partial = Vec::new();
175 for item in dataset_original.iter() {
176 items_original.push(item);
177 }
178
179 let dataset_partials = PartialDataset::split(dataset_original, 4);
180 let expected_len = [6, 6, 6, 9];
181
182 for (i, dataset) in dataset_partials.iter().enumerate() {
183 assert_eq!(dataset.len(), expected_len[i]);
184 for item in dataset.iter() {
185 items_partial.push(item);
186 }
187 }
188
189 assert_eq!(items_original, items_partial);
190 }
191
192 #[test]
193 fn test_split_chunks_contains_all_items_without_duplicates() {
194 let dataset_original = FakeDataset::<String>::new(27);
195 let mut items_original = Vec::new();
196 let mut items_partial = Vec::new();
197 for item in dataset_original.iter() {
198 items_original.push(item);
199 }
200
201 let dataset_partials = PartialDataset::split_chunks(dataset_original, 4, 5);
202 let expected_len = [10, 10, 5, 2];
205
206 for (i, dataset) in dataset_partials.iter().enumerate() {
207 assert_eq!(dataset.len(), expected_len[i]);
208 for item in dataset.iter() {
209 items_partial.push(item);
210 }
211 }
212
213 assert_eq!(items_original, items_partial);
214 }
215}