99
1010
1111class _ImageDataset (Dataset ):
12-
1312 def __init__ (self , length = 64 , image_size = 48 ):
1413 self .length = length
1514 self .image_size = image_size
@@ -55,7 +54,6 @@ def test_scheduled_batch_sampler_stable_epoch_length_and_composition():
5554
5655def test_sample_budget_schedule_is_created_once_and_cached ():
5756 class TrackingScheduledBatchSampler (ScheduledBatchSampler ):
58-
5957 def __init__ (self , * args , ** kwargs ):
6058 self .schedule_create_count = 0
6159 super ().__init__ (* args , ** kwargs )
@@ -75,6 +73,60 @@ def _create_sample_budget_schedule(self):
7573 assert batch_sampler .schedule_create_count == 1
7674
7775
76+ def test_zero_weight_choices_are_excluded_from_progressive_random_mix_and_sample_budget ():
77+ progressive_sampler = ScheduledBatchSampler (
78+ SequentialSampler (range (64 )),
79+ batch_sizes = (16 , 8 , 4 ),
80+ choice_weights = (1 , 0 , 0 ),
81+ choice_schedule = 'progressive' ,
82+ schedule_epochs = 3 ,
83+ schedule_random_mix = 0.1 ,
84+ )
85+
86+ for epoch in range (3 ):
87+ assert progressive_sampler .choice_weights_for_epoch (epoch )[1 :].tolist () == [0 , 0 ]
88+
89+ snap_sampler = ScheduledBatchSampler (
90+ SequentialSampler (range (64 )),
91+ batch_sizes = (16 , 12 , 8 , 4 ),
92+ choice_weights = (1 , 0 , 0 , 1 ),
93+ choice_schedule = 'progressive' ,
94+ schedule_epochs = 4 ,
95+ schedule_spread = 0 ,
96+ schedule_random_mix = 0 ,
97+ )
98+ assert snap_sampler .choice_weights_for_epoch (1 ).tolist () == [1 , 0 , 0 , 0 ]
99+ assert snap_sampler .choice_weights_for_epoch (2 ).tolist () == [0 , 0 , 0 , 1 ]
100+
101+ sample_budget_sampler = ScheduledBatchSampler (
102+ SequentialSampler (range (12 )),
103+ batch_sizes = (8 , 4 ),
104+ choice_weights = (1 , 0 ),
105+ )
106+ batches = list (sample_budget_sampler )
107+ assert _batch_signature (batches ) == [(0 , 8 )]
108+
109+
110+ def test_progressive_schedule_rejects_when_no_full_batch_fits ():
111+ with pytest .raises (ValueError , match = 'No full scheduled batch' ):
112+ ScheduledBatchSampler (
113+ SequentialSampler (range (3 )),
114+ batch_sizes = (8 , 4 ),
115+ choice_schedule = 'progressive' ,
116+ schedule_epochs = 3 ,
117+ )
118+
119+
120+ def test_sample_budget_schedule_rejects_when_no_full_batch_fits ():
121+ with pytest .raises (ValueError , match = 'No full scheduled batch' ):
122+ ScheduledBatchSampler (SequentialSampler (range (3 )), batch_sizes = (8 , 4 ))
123+
124+
125+ def test_scheduled_batch_sampler_rejects_empty_sampler ():
126+ with pytest .raises (ValueError , match = 'non-empty sampler' ):
127+ ScheduledBatchSampler (SequentialSampler (range (0 )), batch_sizes = (8 , 4 ))
128+
129+
78130def test_scheduled_batch_sampler_distributed_shapes_match ():
79131 dataset = list (range (96 ))
80132 rank_0_sampler = DistributedSampler (dataset , num_replicas = 2 , rank = 0 , shuffle = True , seed = 11 )
@@ -123,13 +175,13 @@ def test_progressive_schedule_moves_choices_and_preserves_constant_batch_budget(
123175 assert [index for batch in epoch_0 for index , _ in batch ] == list (range (1000 ))
124176 assert [index for batch in epoch_4 for index , _ in batch ] == list (range (1000 ))
125177 assert sum (choice for choice , _ in _batch_signature (epoch_0 )) < sum (
126- choice for choice , _ in _batch_signature (epoch_4 )
178+ choice
179+ for choice , _ in _batch_signature (epoch_4 )
127180 )
128181
129182
130183def test_progressive_schedule_is_created_when_iteration_starts ():
131184 class TrackingScheduledBatchSampler (ScheduledBatchSampler ):
132-
133185 def __init__ (self , * args , ** kwargs ):
134186 self .created_epochs = []
135187 super ().__init__ (* args , ** kwargs )
@@ -168,19 +220,21 @@ def test_progressive_schedule_infers_policy_average_batch_budget_and_cycles_indi
168220 schedule_random_mix = 0.1 ,
169221 )
170222 batch_sizes = torch .tensor (batch_sampler .batch_sizes , dtype = torch .float64 )
171- expected_average = torch .stack ([
172- torch .dot (batch_sampler .choice_weights_for_epoch (epoch ), batch_sizes )
173- for epoch in range (5 )
174- ]).mean ().item ()
223+ expected_average = (
224+ torch
225+ .stack ([torch .dot (batch_sampler .choice_weights_for_epoch (epoch ), batch_sizes ) for epoch in range (5 )])
226+ .mean ()
227+ .item ()
228+ )
175229
176230 assert batch_sampler .average_batch_size == pytest .approx (expected_average )
177231 assert len (batch_sampler ) == int (len (sampler ) / expected_average )
178232
179233 epoch_0 = list (batch_sampler )
180234 epoch_0_indices = [index for batch in epoch_0 for index , _ in batch ]
181235 assert len (epoch_0_indices ) > len (sampler )
182- assert epoch_0_indices [:len (sampler )] == list (range (len (sampler )))
183- assert epoch_0_indices [len (sampler ):] == list (range (len (epoch_0_indices ) - len (sampler )))
236+ assert epoch_0_indices [: len (sampler )] == list (range (len (sampler )))
237+ assert epoch_0_indices [len (sampler ) :] == list (range (len (epoch_0_indices ) - len (sampler )))
184238
185239 batch_sampler .set_epoch (4 )
186240 epoch_4 = list (batch_sampler )
@@ -190,10 +244,7 @@ def test_progressive_schedule_infers_policy_average_batch_budget_and_cycles_indi
190244
191245def test_progressive_schedule_distributed_shapes_match_and_advance ():
192246 dataset = list (range (96 ))
193- samplers = [
194- DistributedSampler (dataset , num_replicas = 2 , rank = rank , shuffle = True , seed = 11 )
195- for rank in range (2 )
196- ]
247+ samplers = [DistributedSampler (dataset , num_replicas = 2 , rank = rank , shuffle = True , seed = 11 ) for rank in range (2 )]
197248 batch_samplers = [
198249 ScheduledBatchSampler (
199250 sampler ,
@@ -220,16 +271,12 @@ def test_progressive_schedule_distributed_shapes_match_and_advance():
220271 for rank_batches in (* epoch_0 , * epoch_2 ):
221272 assert len (rank_batches ) == 8
222273 for rank_epochs in (epoch_0 , epoch_2 ):
223- rank_indices = [
224- {index for batch in rank_batches for index , _ in batch }
225- for rank_batches in rank_epochs
226- ]
274+ rank_indices = [{index for batch in rank_batches for index , _ in batch } for rank_batches in rank_epochs ]
227275 assert rank_indices [0 ].isdisjoint (rank_indices [1 ])
228276
229277
230278def test_scheduled_transform_dataset_preserves_sample_fields ():
231279 class ExtraFieldDataset (Dataset ):
232-
233280 def __getitem__ (self , index ):
234281 return index , index + 1 , f'sample-{ index } '
235282
@@ -349,6 +396,30 @@ def test_create_loader_rejects_scheduled_resolutions_with_multi_epochs_loader():
349396 )
350397
351398
399+ @pytest .mark .parametrize (
400+ 'scheduled_option' ,
401+ (
402+ {'batch_choice_seed' : 7 },
403+ {'batch_schedule_epochs' : 3 },
404+ {'batch_schedule_spread' : 0.5 },
405+ {'batch_schedule_random_mix' : 0.2 },
406+ ),
407+ )
408+ def test_create_loader_rejects_scheduled_options_without_input_size_choices (scheduled_option ):
409+ with pytest .raises (ValueError , match = 'input_size_choices' ):
410+ create_loader (
411+ _ImageDataset (length = 32 ),
412+ input_size = (3 , 32 , 32 ),
413+ batch_size = 4 ,
414+ is_training = True ,
415+ no_aug = True ,
416+ use_prefetcher = False ,
417+ num_workers = 0 ,
418+ persistent_workers = False ,
419+ ** scheduled_option ,
420+ )
421+
422+
352423def test_create_loader_standard_batching_path_is_unchanged ():
353424 dataset = _ImageDataset ()
354425 loader = create_loader (
0 commit comments