burn_dataset/transform/
partial.rs1use crate::Dataset;
2use std::error::Error;
3use std::{marker::PhantomData, sync::Arc};
4
5#[derive(new, Clone)]
7pub struct PartialDataset<D, I> {
8 dataset: D,
9 start_index: usize,
10 end_index: usize,
11 input: PhantomData<I>,
12}
13
14impl<D, I> PartialDataset<D, I>
15where
16 D: Dataset<I>,
17{
18 pub fn split(dataset: D, num: usize) -> Vec<PartialDataset<Arc<D>, I>> {
20 let dataset = Arc::new(dataset); let mut current = 0;
23 let mut datasets = Vec::with_capacity(num);
24
25 let batch_size = dataset.len() / num;
26
27 for i in 0..num {
28 let start = current;
29 let mut end = current + batch_size;
30
31 if i == (num - 1) {
32 end = dataset.len();
33 }
34
35 let dataset = PartialDataset::new(dataset.clone(), start, end);
36
37 current += batch_size;
38 datasets.push(dataset);
39 }
40
41 datasets
42 }
43
44 pub fn split_chunks(
46 dataset: D,
47 num: usize,
48 batch_size: usize,
49 ) -> Vec<PartialDataset<Arc<D>, I>> {
50 let dataset = Arc::new(dataset); let total_items = dataset.len();
52
53 let total_batches = total_items.div_ceil(batch_size);
55 let batches_per_split = total_batches / num;
56 let extra_batches = total_batches % num;
57
58 let mut datasets = Vec::with_capacity(num);
59 let mut current_batch = 0;
60
61 for i in 0..num {
62 let split_batches = if i < extra_batches {
64 batches_per_split + 1
65 } else {
66 batches_per_split
67 };
68
69 let start_batch = current_batch;
70 let end_batch = start_batch + split_batches;
71
72 let start_index = start_batch * batch_size;
73 let end_index = core::cmp::min(end_batch * batch_size, total_items);
74
75 if start_index < total_items {
76 datasets.push(PartialDataset::new(dataset.clone(), start_index, end_index));
77 }
78
79 current_batch = end_batch;
80 }
81
82 datasets
83 }
84}
85
86impl<D, I, E> Dataset<I, E> for PartialDataset<D, I>
87where
88 D: Dataset<I, E>,
89 I: Clone + Send + Sync,
90 E: Error + Send + Sync + 'static,
91{
92 fn get(&self, index: usize) -> Result<I, E> {
93 let index = index + self.start_index;
94 assert!(
95 index >= self.start_index && index < self.end_index,
96 "Index out of bounds for PartialDataset: {} >= {}",
97 index,
98 self.end_index
99 );
100 self.dataset.get(index)
101 }
102
103 fn get_many(&self, indexes: Vec<usize>) -> Result<Vec<I>, E> {
104 let translated = indexes
105 .into_iter()
106 .map(|index| {
107 let index = index + self.start_index;
108 assert!(
109 index >= self.start_index && index < self.end_index,
110 "Index out of bounds for PartialDataset: {} >= {}",
111 index,
112 self.end_index
113 );
114 index
115 })
116 .collect();
117 self.dataset.get_many(translated)
118 }
119
120 fn len(&self) -> usize {
121 usize::min(self.end_index - self.start_index, self.dataset.len())
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128 use crate::FakeDataset;
129 use std::collections::HashSet;
130
131 #[test]
132 fn test_start_from_beginning() {
133 let dataset_original = FakeDataset::<String>::new(27);
134 let mut items_original_1 = HashSet::new();
135 let mut items_original_2 = HashSet::new();
136 let mut items_partial = HashSet::new();
137 dataset_original
138 .iter()
139 .map(Result::unwrap)
140 .enumerate()
141 .for_each(|(i, item)| {
142 match i >= 10 {
143 true => items_original_2.insert(item),
144 false => items_original_1.insert(item),
145 };
146 });
147
148 let dataset_partial = PartialDataset::new(dataset_original, 0, 10);
149
150 for item in dataset_partial.iter().map(Result::unwrap) {
151 items_partial.insert(item);
152 }
153
154 assert_eq!(dataset_partial.len(), 10);
155 assert_eq!(items_original_1, items_partial);
156 for item in items_original_2 {
157 assert!(!items_partial.contains(&item));
158 }
159 }
160
161 #[test]
162 fn test_start_inside() {
163 let dataset_original = FakeDataset::<String>::new(27);
164 let mut items_original_1 = HashSet::new();
165 let mut items_original_2 = HashSet::new();
166 let mut items_partial = HashSet::new();
167
168 dataset_original
169 .iter()
170 .map(Result::unwrap)
171 .enumerate()
172 .for_each(|(i, item)| {
173 match !(10..20).contains(&i) {
174 true => items_original_2.insert(item),
175 false => items_original_1.insert(item),
176 };
177 });
178
179 let dataset_partial = PartialDataset::new(dataset_original, 10, 20);
180 for item in dataset_partial.iter().map(Result::unwrap) {
181 items_partial.insert(item);
182 }
183
184 assert_eq!(dataset_partial.len(), 10);
185 assert_eq!(items_original_1, items_partial);
186 for item in items_original_2 {
187 assert!(!items_partial.contains(&item));
188 }
189 }
190
191 #[test]
192 fn test_get_many() {
193 let dataset_original = FakeDataset::<String>::new(27);
194 let source_items: Vec<String> = dataset_original.iter().map(Result::unwrap).collect();
195
196 let dataset_partial = PartialDataset::new(dataset_original, 10, 20);
197
198 let requested = vec![5, 0, 3, 0];
200 let expected: Vec<String> = requested
201 .iter()
202 .map(|&i| source_items[10 + i].clone())
203 .collect();
204
205 let items = dataset_partial.get_many(requested).unwrap();
206
207 assert_eq!(items, expected);
208 }
209
210 #[test]
211 #[should_panic(expected = "Index out of bounds for PartialDataset: 20 >= 20")]
212 fn test_get_many_out_of_bounds() {
213 let dataset_original = FakeDataset::<String>::new(27);
214 let dataset_partial = PartialDataset::new(dataset_original, 10, 20);
215
216 let _ = dataset_partial.get_many(vec![0, 10]);
217 }
218
219 #[test]
220 fn test_split_contains_all_items_without_duplicates() {
221 let dataset_original = FakeDataset::<String>::new(27);
222 let mut items_original = Vec::new();
223 let mut items_partial = Vec::new();
224 for item in dataset_original.iter().map(Result::unwrap) {
225 items_original.push(item);
226 }
227
228 let dataset_partials = PartialDataset::split(dataset_original, 4);
229 let expected_len = [6, 6, 6, 9];
230
231 for (i, dataset) in dataset_partials.iter().enumerate() {
232 assert_eq!(dataset.len(), expected_len[i]);
233 for item in dataset.iter().map(Result::unwrap) {
234 items_partial.push(item);
235 }
236 }
237
238 assert_eq!(items_original, items_partial);
239 }
240
241 #[test]
242 fn test_split_chunks_contains_all_items_without_duplicates() {
243 let dataset_original = FakeDataset::<String>::new(27);
244 let mut items_original = Vec::new();
245 let mut items_partial = Vec::new();
246 for item in dataset_original.iter().map(Result::unwrap) {
247 items_original.push(item);
248 }
249
250 let dataset_partials = PartialDataset::split_chunks(dataset_original, 4, 5);
251 let expected_len = [10, 10, 5, 2];
254
255 for (i, dataset) in dataset_partials.iter().enumerate() {
256 assert_eq!(dataset.len(), expected_len[i]);
257 for item in dataset.iter().map(Result::unwrap) {
258 items_partial.push(item);
259 }
260 }
261
262 assert_eq!(items_original, items_partial);
263 }
264}