Skip to content

Commit d64d7e0

Browse files
Sunny Shenfacebook-github-bot
authored andcommitted
Update SOBOL transition criterion to excldue ABANDONED and FAILED trials in initalization budget (#4776)
Summary: Exclude ABANDONED and FAILED trials from initalization budget transition criterion f1020104666 has 3 abandoned trials (missing qps metrics) and 2 completed trials. But it hit a dead end: - Can't generate more Sobol trials (blocked by criterion 1 - 'block_gen_if_met': True) - Can't transition to BO (blocked by criterion 2 - not enough COMPLETED trials) `block_gen_if_met = True` is a sensible default as we don't necessarily want to spend more SOBOL trials just to meet the max_parallelism, but we should excldue FAILED/ABANDONED trials to prevent issues like f1020104666 This means that if we have 3 COMPLETED trials and 2 FAILED/ABANDONED trials, we will stay in SOBOL (previous behavior: move to BO). Intuitively it shouldn't make a big difference in optimization result as BO model is probably not good with 3 datapoints, and probably behaves like SOBOL anyway (except for BO is more likely to generate boundary points). Reviewed By: saitcakmak Differential Revision: D90807058
1 parent f575efe commit d64d7e0

File tree

3 files changed

+60
-3
lines changed

3 files changed

+60
-3
lines changed

ax/api/utils/generation_strategy_dispatch.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,9 @@ def _get_sobol_node(
4545
- If the initialization budget is not specified, it defaults to 5.
4646
- The TC will not block generation if `allow_exceeding_initialization_budget`
4747
is set to True.
48-
- The TC is currently not restricted to any trial statuses and will
49-
count all trials.
48+
- The TC excludes FAILED and ABANDONED trials from the count, so that
49+
more trials can be generated to meet the
50+
`min_observed_initialization_trials` requirement.
5051
- `use_existing_trials_for_initialization` controls whether trials previously
5152
attached to the experiment are counted as part of the initialization budget.
5253
- MinTrials enforcing the minimum number of observed initialization trials.
@@ -72,6 +73,7 @@ def _get_sobol_node(
7273
block_gen_if_met=(not allow_exceeding_initialization_budget),
7374
block_transition_if_unmet=True,
7475
use_all_trials_in_exp=use_existing_trials_for_initialization,
76+
not_in_statuses=[TrialStatus.FAILED, TrialStatus.ABANDONED],
7577
),
7678
MinTrials( # This represents minimum observed trials requirement.
7779
threshold=min_observed_initialization_trials,

ax/api/utils/structs.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,10 @@ class GenerationStrategyDispatchStruct:
5959
``choose_generation_strategy``. This is an advanced option
6060
and should not be considered a part of the public API.
6161
initialization_budget: The number of trials to use for initialization.
62-
If ``None``, a default budget of 5 trials is used.
62+
If ``None``, a default budget of 5 trials is used. Note that FAILED
63+
and ABANDONED trials are excluded from this count, allowing more
64+
trials to be generated to meet the
65+
`min_observed_initialization_trials` requirement.
6366
initialization_random_seed: The random seed to use with the Sobol generator
6467
that generates the initialization trials.
6568
initialize_with_center: If True, the center of the search space is used as the

ax/api/utils/tests/test_generation_strategy_dispatch.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ def test_choose_gs_fast_with_options(self) -> None:
102102
block_gen_if_met=False,
103103
block_transition_if_unmet=True,
104104
use_all_trials_in_exp=False,
105+
not_in_statuses=[TrialStatus.FAILED, TrialStatus.ABANDONED],
105106
),
106107
MinTrials(
107108
threshold=4,
@@ -370,3 +371,54 @@ def test_choose_gs_with_custom_botorch_acqf_class(self) -> None:
370371
self.assertEqual(
371372
mbm_spec.generator_kwargs["surrogate_spec"], expected_ss
372373
)
374+
375+
def test_abandoned_and_failed_trials_excluded_from_initialization_budget(
376+
self,
377+
) -> None:
378+
"""Test that FAILED and ABANDONED trials don't count toward init budget."""
379+
struct = GenerationStrategyDispatchStruct(
380+
method="fast",
381+
initialization_budget=5,
382+
allow_exceeding_initialization_budget=False,
383+
)
384+
gs = choose_generation_strategy(struct=struct)
385+
386+
# Verify the first MinTrials criterion excludes FAILED and ABANDONED
387+
sobol_node = gs._nodes[1] # Node 0 is Center
388+
first_tc = assert_is_instance(sobol_node._transition_criteria[0], MinTrials)
389+
self.assertEqual(
390+
first_tc.not_in_statuses, [TrialStatus.FAILED, TrialStatus.ABANDONED]
391+
)
392+
self.assertEqual(first_tc.threshold, 5)
393+
self.assertTrue(first_tc.block_gen_if_met)
394+
395+
# Test the actual behavior: Generate 5 trials, mark 3 as ABANDONED,
396+
# verify that Sobol can still generate more trials
397+
experiment = get_branin_experiment()
398+
gs.experiment = experiment
399+
400+
# Generate 5 initial trials
401+
for _ in range(5):
402+
gr = gs.gen_single_trial(experiment)
403+
trial = experiment.new_trial(generator_run=gr)
404+
trial.mark_running(no_runner_required=True)
405+
406+
# Mark trials 2, 3, 4 as ABANDONED
407+
if trial.index in [2, 3, 4]:
408+
trial.mark_abandoned()
409+
else:
410+
trial.mark_completed()
411+
412+
# Check we have 2 COMPLETED and 3 ABANDONED
413+
self.assertEqual(
414+
len(experiment.trial_indices_by_status[TrialStatus.COMPLETED]), 2
415+
)
416+
self.assertEqual(
417+
len(experiment.trial_indices_by_status[TrialStatus.ABANDONED]), 3
418+
)
419+
420+
# Should still be able to generate from Sobol since only 2 "valid" trials exist
421+
gr = gs.gen_single_trial(experiment)
422+
self.assertIsNotNone(gr)
423+
# Verify it's from Sobol
424+
self.assertEqual(gr._generator_key, "Sobol")

0 commit comments

Comments
 (0)