diff --git a/python/ray/tune/suggest/hyperopt.py b/python/ray/tune/suggest/hyperopt.py index 5d1258da6cf4..0205f0646ac4 100644 --- a/python/ray/tune/suggest/hyperopt.py +++ b/python/ray/tune/suggest/hyperopt.py @@ -472,6 +472,8 @@ def resolve_value(par: str, domain: Domain) -> Any: dict(enumerate(category)), prefix=f"{par}/{i}" ) if isinstance(category, list) + and len(category) > 0 + and isinstance(category[0], Domain) else resolve_value(f"{par}/{i}", category) if isinstance(category, Domain) else category diff --git a/python/ray/tune/tests/test_sample.py b/python/ray/tune/tests/test_sample.py index ba250b8b26a9..d7459ea4c740 100644 --- a/python/ray/tune/tests/test_sample.py +++ b/python/ray/tune/tests/test_sample.py @@ -982,6 +982,38 @@ def testConvertHyperOpt(self): self.assertTrue(5 <= config["a"] <= 6) self.assertTrue(8 <= config["b"] <= 9) + def testConvertHyperOptChooseFromListOfList(self): + from ray.tune.suggest.hyperopt import HyperOptSearch + from hyperopt import hp + + config = { + "a": tune.choice([[1, 2], [3, 4]]), + } + converted_config = HyperOptSearch.convert_search_space(config) + hyperopt_config = { + "a": hp.choice("a", [[1, 2], [3, 4]]), + } + + searcher1 = HyperOptSearch( + space=converted_config, random_state_seed=1234, metric="a", mode="max" + ) + searcher2 = HyperOptSearch( + space=hyperopt_config, random_state_seed=1234, metric="a", mode="max" + ) + + config1 = searcher1.suggest("0") + config2 = searcher2.suggest("0") + + self.assertEqual(config1, config2) + + # Hyperopt natively converts list to tuple. + # Try out the following script: + # ``` + # a = HyperOptSearch.convert_search_space({"a": tune.choice([[1,2], [3,4]])}) + # print(hyperopt.pyll.stochastic.sample(a)) + # ``` + self.assertTrue(config1.get("a") in [(1, 2), (3, 4)]) + def testConvertHyperOptNested(self): from ray.tune.suggest.hyperopt import HyperOptSearch