1use crate::Dataset;
2use crate::DatasetError;
3use crate::transform::RngSource;
4use rand::prelude::SliceRandom;
5use rand::rngs::StdRng;
6use std::marker::PhantomData;
7use std::sync::Arc;
8
9#[inline(always)]
19pub fn iota(size: usize) -> Vec<usize> {
20 (0..size).collect()
21}
22
23#[inline(always)]
33pub fn shuffled_indices(size: usize, rng: &mut StdRng) -> Vec<usize> {
34 let mut indices = iota(size);
35 indices.shuffle(rng);
36 indices
37}
38
39#[derive(Clone)]
43pub struct SelectionDataset<D, I>
44where
45 D: Dataset<I>,
46 I: Clone + Send + Sync,
47{
48 pub wrapped: Arc<D>,
50
51 pub indices: Vec<usize>,
53
54 input: PhantomData<I>,
55}
56
57impl<D, I> SelectionDataset<D, I>
58where
59 D: Dataset<I>,
60 I: Clone + Send + Sync,
61{
62 pub fn from_indices_checked<S>(dataset: S, indices: Vec<usize>) -> Self
76 where
77 S: Into<Arc<D>>,
78 {
79 let dataset = dataset.into();
80
81 let size = dataset.len();
82 if let Some(idx) = indices.iter().find(|&i| *i >= size) {
83 panic!("Index out of bounds for wrapped dataset size: {idx} >= {size}");
84 }
85
86 Self::from_indices_unchecked(dataset, indices)
87 }
88
89 pub fn from_indices_unchecked<S>(dataset: S, indices: Vec<usize>) -> Self
100 where
101 S: Into<Arc<D>>,
102 {
103 Self {
104 wrapped: dataset.into(),
105 indices,
106 input: PhantomData,
107 }
108 }
109
110 pub fn new_select_all<S>(dataset: S) -> Self
124 where
125 S: Into<Arc<D>>,
126 {
127 let dataset = dataset.into();
128 let size = dataset.len();
129 Self::from_indices_unchecked(dataset, iota(size))
130 }
131
132 pub fn new_shuffled<S, R>(dataset: S, rng_source: R) -> Self
146 where
147 S: Into<Arc<D>>,
148 R: Into<RngSource>,
149 {
150 let mut this = Self::new_select_all(dataset);
151 this.shuffle(rng_source);
152 this
153 }
154
155 pub fn shuffle<R>(&mut self, rng_source: R)
163 where
164 R: Into<RngSource>,
165 {
166 let mut rng: StdRng = rng_source.into().into();
167 self.indices.shuffle(&mut rng)
168 }
169
170 pub fn slice(&self, start: usize, end: usize) -> Self {
183 Self::from_indices_unchecked(self.wrapped.clone(), self.indices[start..end].to_vec())
184 }
185
186 pub fn split(&self, num: usize) -> Vec<Self> {
200 let n = self.indices.len();
201
202 let mut current = 0;
203 let mut datasets = Vec::with_capacity(num);
204
205 let batch_size = n / num;
206 for i in 0..num {
207 let start = current;
208 let mut end = current + batch_size;
209
210 if i == (num - 1) {
211 end = n;
212 }
213
214 let dataset = self.slice(start, end);
215
216 current += batch_size;
217 datasets.push(dataset);
218 }
219
220 datasets
221 }
222}
223
224impl<D, I> Dataset<I> for SelectionDataset<D, I>
225where
226 D: Dataset<I>,
227 I: Clone + Send + Sync,
228{
229 fn get(&self, index: usize) -> Result<I, DatasetError> {
230 let Some(&index) = self.indices.get(index) else {
231 panic!(
232 "Index out of bounds for SelectionDataset: {index} >= {}",
233 self.indices.len()
234 );
235 };
236 self.wrapped.get(index)
237 }
238
239 fn get_many(&self, indexes: Vec<usize>) -> Result<Vec<I>, DatasetError> {
240 let translated: Vec<usize> = indexes
241 .into_iter()
242 .map(|i| {
243 self.indices.get(i).copied().unwrap_or_else(|| {
244 panic!(
245 "Index out of bounds for SelectionDataset: {i} >= {}",
246 self.indices.len()
247 )
248 })
249 })
250 .collect();
251 self.wrapped.get_many(translated)
252 }
253
254 fn len(&self) -> usize {
255 self.indices.len()
256 }
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262 use crate::FakeDataset;
263 use rand::SeedableRng;
264
265 #[test]
266 fn test_iota() {
267 let size = 10;
268 let indices = iota(size);
269 assert_eq!(indices.len(), size);
270 assert_eq!(indices, vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
271 }
272
273 #[test]
274 fn test_shuffled_indices_same_seed_is_deterministic() {
275 let size = 10;
276
277 let mut rng1 = StdRng::seed_from_u64(10);
278 let mut rng2 = StdRng::seed_from_u64(10);
281
282 let mut expected = iota(size);
283 expected.shuffle(&mut rng1);
284
285 let indices = shuffled_indices(size, &mut rng2);
286
287 assert_eq!(indices, expected);
288 }
289
290 #[test]
291 fn test_shuffled_indices_forked_rngs_differ() {
292 let size = 10;
293
294 let mut rng1 = StdRng::seed_from_u64(10);
295 let mut rng2 = rng1.fork();
296
297 let mut a = iota(size);
298 let mut b = iota(size);
299
300 a.shuffle(&mut rng1);
301 b.shuffle(&mut rng2);
302
303 assert_ne!(a, b);
304 }
305
306 #[should_panic(expected = "Index out of bounds for wrapped dataset size: 300 >= 27")]
307 #[test]
308 fn test_from_indices_checked_panics() {
309 let source_dataset = FakeDataset::<String>::new(27);
310 let indices: Vec<usize> = vec![15, 1, 12, 300];
311 SelectionDataset::from_indices_checked(source_dataset, indices);
312 }
313
314 #[test]
315 fn test_checked_selection_dataset() {
316 let source_dataset = FakeDataset::<String>::new(27);
317
318 let indices: Vec<usize> = vec![15, 1, 12, 12];
319 let expected: Vec<String> = indices
320 .iter()
321 .map(|i| source_dataset.get(*i).unwrap())
322 .collect();
323
324 let selection = SelectionDataset::from_indices_checked(source_dataset, indices.clone());
325
326 assert_eq!(&selection.indices, &indices);
327
328 let items = selection.iter().map(Result::unwrap).collect::<Vec<_>>();
329
330 assert_eq!(items, expected);
331 }
332
333 #[test]
334 fn test_selection_dataset_get_many() {
335 let source_dataset = FakeDataset::<String>::new(27);
336
337 let indices: Vec<usize> = vec![15, 1, 12, 12];
338 let selection = SelectionDataset::from_indices_checked(source_dataset, indices);
339
340 let requested = vec![3, 0, 2, 0];
342 let expected: Vec<String> = requested
343 .iter()
344 .map(|&i| selection.get(i).unwrap())
345 .collect();
346
347 let items = selection.get_many(requested).unwrap();
348
349 assert_eq!(items, expected);
350 }
351
352 #[test]
353 #[should_panic(expected = "Index out of bounds for SelectionDataset: 4 >= 4")]
354 fn test_selection_dataset_get_many_out_of_bounds() {
355 let source_dataset = FakeDataset::<String>::new(27);
356 let indices: Vec<usize> = vec![15, 1, 12, 12];
357 let selection = SelectionDataset::from_indices_checked(source_dataset, indices);
358
359 let _ = selection.get_many(vec![0, 4]);
360 }
361
362 #[test]
363 fn test_shuffled_dataset() {
364 let dataset = FakeDataset::<String>::new(27);
365 let source_items = dataset.iter().map(Result::unwrap).collect::<Vec<_>>();
366
367 let selection = SelectionDataset::new_shuffled(dataset, 42);
368
369 let indices = shuffled_indices(source_items.len(), &mut StdRng::seed_from_u64(42));
370
371 assert_eq!(&selection.indices, &indices);
372 assert_eq!(selection.len(), source_items.len());
373
374 let expected_items: Vec<_> = indices
375 .iter()
376 .map(|&i| source_items[i].to_string())
377 .collect();
378 assert_eq!(
379 &selection.iter().map(Result::unwrap).collect::<Vec<_>>(),
380 &expected_items
381 );
382 }
383
384 #[test]
385 fn test_slice() {
386 let dataset = FakeDataset::<String>::new(27);
387 let source_items = dataset.iter().map(Result::unwrap).collect::<Vec<_>>();
388
389 let selection = SelectionDataset::new_select_all(dataset);
390
391 let start = 5;
392 let end = 15;
393 let sliced_selection = selection.slice(start, end);
394
395 assert_eq!(sliced_selection.len(), end - start);
396
397 #[allow(clippy::needless_range_loop)]
398 for i in start..end {
399 assert_eq!(
400 sliced_selection.get(i - start).unwrap(),
401 source_items[i].to_string()
402 );
403 }
404 }
405
406 #[test]
407 fn test_split() {
408 let dataset = FakeDataset::<String>::new(28);
409 let source_items = dataset.iter().map(Result::unwrap).collect::<Vec<_>>();
410
411 let selection = SelectionDataset::new_select_all(dataset);
412
413 let split_contents: Vec<Vec<_>> = selection
414 .split(3)
415 .iter()
416 .map(|d| d.iter().map(Result::unwrap).collect::<Vec<_>>())
417 .collect();
418 assert_eq!(
419 split_contents,
420 vec![
421 source_items[0..9].to_vec(),
422 source_items[9..18].to_vec(),
423 source_items[18..28].to_vec(),
424 ]
425 );
426 }
427}