From 69404b652a44bd4dbf2050ea097321f0d306e5ad Mon Sep 17 00:00:00 2001 From: Carl Assmann Date: Wed, 15 Sep 2021 08:25:52 +0000 Subject: [PATCH 1/7] [tune]: OptunaSearch check space compatibility --- python/ray/tune/suggest/optuna.py | 60 +++++++++++++++++++++++-- python/ray/tune/tests/test_searchers.py | 10 +++++ 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/python/ray/tune/suggest/optuna.py b/python/ray/tune/suggest/optuna.py index 6532d067bf02..f9d3d758cea8 100644 --- a/python/ray/tune/suggest/optuna.py +++ b/python/ray/tune/suggest/optuna.py @@ -54,7 +54,6 @@ class _OptunaTrialSuggestCaptor: `suggest_` callables with a function capturing the returned value, which will be saved in the ``captured_values`` dict. """ - def __init__(self, ot_trial: OptunaTrial) -> None: self.ot_trial = ot_trial self.captured_values: Dict[str, Any] = {} @@ -126,6 +125,12 @@ class OptunaSearch(Searcher): needing to re-compute the trial. Must be the same length as points_to_evaluate. + ..warning:: + When using evaluated_rewards, the search space `space` must + be provided as a :class:`dict` with parameter names as keys and + ``optuna.distributions``. The define-by-run definition is not + yet supported. + Tune automatically converts search spaces to Optuna's format: .. code-block:: python @@ -151,7 +156,7 @@ class OptunaSearch(Searcher): from ray.tune.suggest.optuna import OptunaSearch import optuna - config = { + space = { "a": optuna.distributions.UniformDistribution(6, 8), "b": optuna.distributions.LogUniformDistribution(1e-4, 1e-2), } @@ -177,11 +182,53 @@ def define_search_space(trial: optuna.Trial): mode="min") tune.run(trainable, search_alg=optuna_search) + + You can pass configs that will be evaluated first using + `points_to_evaluate`: + + .. code-block:: python + + from ray.tune.suggest.optuna import OptunaSearch + import optuna + + space = { + "a": optuna.distributions.UniformDistribution(6, 8), + "b": optuna.distributions.LogUniformDistribution(1e-4, 1e-2), + } + + optuna_search = OptunaSearch( + space, + points_to_evaluate=[{"a": 6.5, "b": 5e-4}, {"a": 7.5, "b": 1e-3}] + metric="loss", + mode="min") + + tune.run(trainable, search_alg=optuna_search) + + Avoid re-running evaluated trials by passing the rewards together with + `points_to_evaluate`: + + .. code-block:: python + + from ray.tune.suggest.optuna import OptunaSearch + import optuna + + space = { + "a": optuna.distributions.UniformDistribution(6, 8), + "b": optuna.distributions.LogUniformDistribution(1e-4, 1e-2), + } + + optuna_search = OptunaSearch( + space, + points_to_evaluate=[{"a": 6.5, "b": 5e-4}, {"a": 7.5, "b": 1e-3}] + evaluated_rewards=[0.89, 0.42] + metric="loss", + mode="min") + + tune.run(trainable, search_alg=optuna_search) .. versionadded:: 0.8.8 """ - def __init__(self, space: Optional[Union[Dict[str, "OptunaDistribution"], List[ Tuple], Callable[["OptunaTrial"], Optional[Dict[ @@ -402,7 +449,12 @@ def add_evaluated_point(self, cls=self.__class__.__name__, metric=self._metric, mode=self._mode)) - + if callable(self._space): + raise TypeError( + "Define-by-run function passed in `space` argument is not" + "yet supported when using `evaluated_rewards`. Please provide" + "a 'OptunaDistribution' dict.") + ot_trial_state = OptunaTrialState.COMPLETE if error: ot_trial_state = OptunaTrialState.FAIL diff --git a/python/ray/tune/tests/test_searchers.py b/python/ray/tune/tests/test_searchers.py index 5bd3eafae2d7..0d872cad7ff2 100644 --- a/python/ray/tune/tests/test_searchers.py +++ b/python/ray/tune/tests/test_searchers.py @@ -305,6 +305,16 @@ def testOptuna(self): self.assertTrue( searcher._ot_study.trials[-1].state == TrialState.PRUNED) + dbr_searcher = OptunaSearch( + lambda trial: { + self.param_name: trial.suggest_float(self.param_name, 0.0, 5.0), + }, + metric="metric", + mode="max", + ) + with self.assertRaises(TypeError): + dbr_searcher.add_evaluated_point(point, 1.0) + def testHEBO(self): from ray.tune.suggest.hebo import HEBOSearch From f6e539a9a40c8b549b885317c4d7cc7a330727b4 Mon Sep 17 00:00:00 2001 From: Carl Assmann Date: Wed, 15 Sep 2021 08:31:40 +0000 Subject: [PATCH 2/7] run scripts/format.sh --- python/ray/tune/suggest/optuna.py | 61 ++++++----- python/ray/tune/tests/test_searchers.py | 140 ++++++++++++------------ 2 files changed, 99 insertions(+), 102 deletions(-) diff --git a/python/ray/tune/suggest/optuna.py b/python/ray/tune/suggest/optuna.py index f9d3d758cea8..614974686774 100644 --- a/python/ray/tune/suggest/optuna.py +++ b/python/ray/tune/suggest/optuna.py @@ -182,10 +182,10 @@ def define_search_space(trial: optuna.Trial): mode="min") tune.run(trainable, search_alg=optuna_search) - + You can pass configs that will be evaluated first using `points_to_evaluate`: - + .. code-block:: python from ray.tune.suggest.optuna import OptunaSearch @@ -230,9 +230,11 @@ def define_search_space(trial: optuna.Trial): """ def __init__(self, - space: Optional[Union[Dict[str, "OptunaDistribution"], List[ - Tuple], Callable[["OptunaTrial"], Optional[Dict[ - str, Any]]]]] = None, + space: Optional[Union[Dict[str, + "OptunaDistribution"], List[Tuple], + Callable[["OptunaTrial"], + Optional[Dict[str, + Any]]]]] = None, metric: Optional[str] = None, mode: Optional[str] = None, points_to_evaluate: Optional[List[Dict]] = None, @@ -241,18 +243,17 @@ def __init__(self, evaluated_rewards: Optional[List] = None): assert ot is not None, ( "Optuna must be installed! Run `pip install optuna`.") - super(OptunaSearch, self).__init__( - metric=metric, - mode=mode, - max_concurrent=None, - use_early_stopped_trials=None) + super(OptunaSearch, self).__init__(metric=metric, + mode=mode, + max_concurrent=None, + use_early_stopped_trials=None) if isinstance(space, dict) and space: resolved_vars, domain_vars, grid_vars = parse_spec_vars(space) if domain_vars or grid_vars: logger.warning( - UNRESOLVED_SEARCH_SPACE.format( - par="space", cls=type(self).__name__)) + UNRESOLVED_SEARCH_SPACE.format(par="space", + cls=type(self).__name__)) space = self.convert_search_space(space) else: # Flatten to support nested dicts @@ -334,9 +335,11 @@ def set_search_properties(self, metric: Optional[str], mode: Optional[str], self._setup_study(mode) return True - def _suggest_from_define_by_run_func( - self, func: Callable[["OptunaTrial"], Optional[Dict[str, Any]]], - ot_trial: "OptunaTrial") -> Dict: + def _suggest_from_define_by_run_func(self, + func: Callable[["OptunaTrial"], + Optional[Dict[str, + Any]]], + ot_trial: "OptunaTrial") -> Dict: captor = _OptunaTrialSuggestCaptor(ot_trial) time_start = time.time() ret = func(captor) @@ -368,14 +371,13 @@ def _suggest_from_define_by_run_func( def suggest(self, trial_id: str) -> Optional[Dict]: if not self._space: raise RuntimeError( - UNDEFINED_SEARCH_SPACE.format( - cls=self.__class__.__name__, space="space")) + UNDEFINED_SEARCH_SPACE.format(cls=self.__class__.__name__, + space="space")) if not self._metric or not self._mode: raise RuntimeError( - UNDEFINED_METRIC_MODE.format( - cls=self.__class__.__name__, - metric=self._metric, - mode=self._mode)) + UNDEFINED_METRIC_MODE.format(cls=self.__class__.__name__, + metric=self._metric, + mode=self._mode)) if isinstance(self._space, list): # Keep for backwards compatibility @@ -387,8 +389,8 @@ def suggest(self, trial_id: str) -> Optional[Dict]: # getattr will fetch the trial.suggest_ function on Optuna trials params = { - args[0] if len(args) > 0 else kwargs["name"]: getattr( - ot_trial, fn)(*args, **kwargs) + args[0] if len(args) > 0 else kwargs["name"]: + getattr(ot_trial, fn)(*args, **kwargs) for (fn, args, kwargs) in self._space } elif callable(self._space): @@ -441,20 +443,19 @@ def add_evaluated_point(self, intermediate_values: Optional[List[float]] = None): if not self._space: raise RuntimeError( - UNDEFINED_SEARCH_SPACE.format( - cls=self.__class__.__name__, space="space")) + UNDEFINED_SEARCH_SPACE.format(cls=self.__class__.__name__, + space="space")) if not self._metric or not self._mode: raise RuntimeError( - UNDEFINED_METRIC_MODE.format( - cls=self.__class__.__name__, - metric=self._metric, - mode=self._mode)) + UNDEFINED_METRIC_MODE.format(cls=self.__class__.__name__, + metric=self._metric, + mode=self._mode)) if callable(self._space): raise TypeError( "Define-by-run function passed in `space` argument is not" "yet supported when using `evaluated_rewards`. Please provide" "a 'OptunaDistribution' dict.") - + ot_trial_state = OptunaTrialState.COMPLETE if error: ot_trial_state = OptunaTrialState.FAIL diff --git a/python/ray/tune/tests/test_searchers.py b/python/ray/tune/tests/test_searchers.py index 0d872cad7ff2..8bea4b84f843 100644 --- a/python/ray/tune/tests/test_searchers.py +++ b/python/ray/tune/tests/test_searchers.py @@ -26,7 +26,6 @@ class InvalidValuesTest(unittest.TestCase): Implicitly tests automatic config conversion and default (anonymous) mode handling. """ - def setUp(self): self.config = {"report": tune.uniform(0.0, 5.0)} @@ -48,19 +47,17 @@ def testAx(self): converted_config = AxSearch.convert_search_space(self.config) # At least one nan, inf, -inf and float client = AxClient(random_seed=4321) - client.create_experiment( - parameters=converted_config, - objective_name="_metric", - minimize=False) + client.create_experiment(parameters=converted_config, + objective_name="_metric", + minimize=False) searcher = AxSearch(ax_client=client) - out = tune.run( - _invalid_objective, - search_alg=searcher, - metric="_metric", - mode="max", - num_samples=4, - reuse_actors=False) + out = tune.run(_invalid_objective, + search_alg=searcher, + metric="_metric", + mode="max", + num_samples=4, + reuse_actors=False) best_trial = out.best_trial self.assertLessEqual(best_trial.config["report"], 2.0) @@ -83,14 +80,13 @@ def testBayesOpt(self): def testBlendSearch(self): from ray.tune.suggest.flaml import BlendSearch - out = tune.run( - _invalid_objective, - search_alg=BlendSearch(), - config=self.config, - metric="_metric", - mode="max", - num_samples=8, - reuse_actors=False) + out = tune.run(_invalid_objective, + search_alg=BlendSearch(), + config=self.config, + metric="_metric", + mode="max", + num_samples=8, + reuse_actors=False) best_trial = out.best_trial self.assertLessEqual(best_trial.config["report"], 2.0) @@ -98,13 +94,12 @@ def testBlendSearch(self): def testBOHB(self): from ray.tune.suggest.bohb import TuneBOHB - out = tune.run( - _invalid_objective, - search_alg=TuneBOHB(seed=1000), - config=self.config, - mode="max", - num_samples=8, - reuse_actors=False) + out = tune.run(_invalid_objective, + search_alg=TuneBOHB(seed=1000), + config=self.config, + mode="max", + num_samples=8, + reuse_actors=False) best_trial = out.best_trial self.assertLessEqual(best_trial.config["report"], 2.0) @@ -112,14 +107,13 @@ def testBOHB(self): def testCFO(self): from ray.tune.suggest.flaml import CFO - out = tune.run( - _invalid_objective, - search_alg=CFO(), - config=self.config, - metric="_metric", - mode="max", - num_samples=16, - reuse_actors=False) + out = tune.run(_invalid_objective, + search_alg=CFO(), + config=self.config, + metric="_metric", + mode="max", + num_samples=16, + reuse_actors=False) best_trial = out.best_trial self.assertLessEqual(best_trial.config["report"], 2.0) @@ -129,13 +123,13 @@ def testDragonfly(self): np.random.seed(1000) # At least one nan, inf, -inf and float - out = tune.run( - _invalid_objective, - search_alg=DragonflySearch(domain="euclidean", optimizer="random"), - config=self.config, - mode="max", - num_samples=8, - reuse_actors=False) + out = tune.run(_invalid_objective, + search_alg=DragonflySearch(domain="euclidean", + optimizer="random"), + config=self.config, + mode="max", + num_samples=8, + reuse_actors=False) best_trial = out.best_trial self.assertLessEqual(best_trial.config["point"], 2.0) @@ -209,13 +203,12 @@ def testSkopt(self): np.random.seed(1234) # At least one nan, inf, -inf and float - out = tune.run( - _invalid_objective, - search_alg=SkOptSearch(), - config=self.config, - mode="max", - num_samples=8, - reuse_actors=False) + out = tune.run(_invalid_objective, + search_alg=SkOptSearch(), + config=self.config, + mode="max", + num_samples=8, + reuse_actors=False) best_trial = out.best_trial self.assertLessEqual(best_trial.config["report"], 2.0) @@ -228,13 +221,12 @@ def testZOOpt(self): np.random.seed(1000) # At least one nan, inf, -inf and float - out = tune.run( - _invalid_objective, - search_alg=ZOOptSearch(budget=100, parallel_num=4), - config=self.config, - mode="max", - num_samples=8, - reuse_actors=False) + out = tune.run(_invalid_objective, + search_alg=ZOOptSearch(budget=100, parallel_num=4), + config=self.config, + mode="max", + num_samples=8, + reuse_actors=False) best_trial = out.best_trial self.assertLessEqual(best_trial.config["report"], 2.0) @@ -244,7 +236,6 @@ class AddEvaluatedPointTest(unittest.TestCase): """ Test add_evaluated_point method in searchers that support it. """ - def setUp(self): self.param_name = "report" self.valid_value = 1.0 @@ -265,14 +256,13 @@ def testOptuna(self): from ray.tune.suggest.optuna import OptunaSearch from optuna.trial import TrialState - searcher = OptunaSearch( - space=self.space, - metric="metric", - mode="max", - points_to_evaluate=[{ - self.param_name: self.valid_value - }], - evaluated_rewards=[1.0]) + searcher = OptunaSearch(space=self.space, + metric="metric", + mode="max", + points_to_evaluate=[{ + self.param_name: self.valid_value + }], + evaluated_rewards=[1.0]) self.assertGreater(len(searcher._ot_study.trials), 0) @@ -288,26 +278,32 @@ def testOptuna(self): self.assertEqual(len(searcher._ot_study.trials), 0) - searcher.add_evaluated_point( - point, 1.0, intermediate_values=[0.8, 0.9]) + searcher.add_evaluated_point(point, + 1.0, + intermediate_values=[0.8, 0.9]) self.assertEqual(len(searcher._ot_study.trials), 1) self.assertTrue( searcher._ot_study.trials[-1].state == TrialState.COMPLETE) - searcher.add_evaluated_point( - point, 1.0, intermediate_values=[0.8, 0.9], error=True) + searcher.add_evaluated_point(point, + 1.0, + intermediate_values=[0.8, 0.9], + error=True) self.assertEqual(len(searcher._ot_study.trials), 2) self.assertTrue(searcher._ot_study.trials[-1].state == TrialState.FAIL) - searcher.add_evaluated_point( - point, 1.0, intermediate_values=[0.8, 0.9], pruned=True) + searcher.add_evaluated_point(point, + 1.0, + intermediate_values=[0.8, 0.9], + pruned=True) self.assertEqual(len(searcher._ot_study.trials), 3) self.assertTrue( searcher._ot_study.trials[-1].state == TrialState.PRUNED) dbr_searcher = OptunaSearch( lambda trial: { - self.param_name: trial.suggest_float(self.param_name, 0.0, 5.0), + self.param_name: trial.suggest_float(self.param_name, 0.0, 5.0 + ), }, metric="metric", mode="max", From daf20ca5e64e129b8242cd2b525fc1c10e2e48f4 Mon Sep 17 00:00:00 2001 From: Carl Assmann Date: Thu, 16 Sep 2021 12:15:54 +0000 Subject: [PATCH 3/7] Undo incorrect formatting --- python/ray/tune/suggest/optuna.py | 61 +++++------ python/ray/tune/tests/test_searchers.py | 140 ++++++++++++------------ 2 files changed, 102 insertions(+), 99 deletions(-) diff --git a/python/ray/tune/suggest/optuna.py b/python/ray/tune/suggest/optuna.py index 614974686774..f9d3d758cea8 100644 --- a/python/ray/tune/suggest/optuna.py +++ b/python/ray/tune/suggest/optuna.py @@ -182,10 +182,10 @@ def define_search_space(trial: optuna.Trial): mode="min") tune.run(trainable, search_alg=optuna_search) - + You can pass configs that will be evaluated first using `points_to_evaluate`: - + .. code-block:: python from ray.tune.suggest.optuna import OptunaSearch @@ -230,11 +230,9 @@ def define_search_space(trial: optuna.Trial): """ def __init__(self, - space: Optional[Union[Dict[str, - "OptunaDistribution"], List[Tuple], - Callable[["OptunaTrial"], - Optional[Dict[str, - Any]]]]] = None, + space: Optional[Union[Dict[str, "OptunaDistribution"], List[ + Tuple], Callable[["OptunaTrial"], Optional[Dict[ + str, Any]]]]] = None, metric: Optional[str] = None, mode: Optional[str] = None, points_to_evaluate: Optional[List[Dict]] = None, @@ -243,17 +241,18 @@ def __init__(self, evaluated_rewards: Optional[List] = None): assert ot is not None, ( "Optuna must be installed! Run `pip install optuna`.") - super(OptunaSearch, self).__init__(metric=metric, - mode=mode, - max_concurrent=None, - use_early_stopped_trials=None) + super(OptunaSearch, self).__init__( + metric=metric, + mode=mode, + max_concurrent=None, + use_early_stopped_trials=None) if isinstance(space, dict) and space: resolved_vars, domain_vars, grid_vars = parse_spec_vars(space) if domain_vars or grid_vars: logger.warning( - UNRESOLVED_SEARCH_SPACE.format(par="space", - cls=type(self).__name__)) + UNRESOLVED_SEARCH_SPACE.format( + par="space", cls=type(self).__name__)) space = self.convert_search_space(space) else: # Flatten to support nested dicts @@ -335,11 +334,9 @@ def set_search_properties(self, metric: Optional[str], mode: Optional[str], self._setup_study(mode) return True - def _suggest_from_define_by_run_func(self, - func: Callable[["OptunaTrial"], - Optional[Dict[str, - Any]]], - ot_trial: "OptunaTrial") -> Dict: + def _suggest_from_define_by_run_func( + self, func: Callable[["OptunaTrial"], Optional[Dict[str, Any]]], + ot_trial: "OptunaTrial") -> Dict: captor = _OptunaTrialSuggestCaptor(ot_trial) time_start = time.time() ret = func(captor) @@ -371,13 +368,14 @@ def _suggest_from_define_by_run_func(self, def suggest(self, trial_id: str) -> Optional[Dict]: if not self._space: raise RuntimeError( - UNDEFINED_SEARCH_SPACE.format(cls=self.__class__.__name__, - space="space")) + UNDEFINED_SEARCH_SPACE.format( + cls=self.__class__.__name__, space="space")) if not self._metric or not self._mode: raise RuntimeError( - UNDEFINED_METRIC_MODE.format(cls=self.__class__.__name__, - metric=self._metric, - mode=self._mode)) + UNDEFINED_METRIC_MODE.format( + cls=self.__class__.__name__, + metric=self._metric, + mode=self._mode)) if isinstance(self._space, list): # Keep for backwards compatibility @@ -389,8 +387,8 @@ def suggest(self, trial_id: str) -> Optional[Dict]: # getattr will fetch the trial.suggest_ function on Optuna trials params = { - args[0] if len(args) > 0 else kwargs["name"]: - getattr(ot_trial, fn)(*args, **kwargs) + args[0] if len(args) > 0 else kwargs["name"]: getattr( + ot_trial, fn)(*args, **kwargs) for (fn, args, kwargs) in self._space } elif callable(self._space): @@ -443,19 +441,20 @@ def add_evaluated_point(self, intermediate_values: Optional[List[float]] = None): if not self._space: raise RuntimeError( - UNDEFINED_SEARCH_SPACE.format(cls=self.__class__.__name__, - space="space")) + UNDEFINED_SEARCH_SPACE.format( + cls=self.__class__.__name__, space="space")) if not self._metric or not self._mode: raise RuntimeError( - UNDEFINED_METRIC_MODE.format(cls=self.__class__.__name__, - metric=self._metric, - mode=self._mode)) + UNDEFINED_METRIC_MODE.format( + cls=self.__class__.__name__, + metric=self._metric, + mode=self._mode)) if callable(self._space): raise TypeError( "Define-by-run function passed in `space` argument is not" "yet supported when using `evaluated_rewards`. Please provide" "a 'OptunaDistribution' dict.") - + ot_trial_state = OptunaTrialState.COMPLETE if error: ot_trial_state = OptunaTrialState.FAIL diff --git a/python/ray/tune/tests/test_searchers.py b/python/ray/tune/tests/test_searchers.py index 8bea4b84f843..0d872cad7ff2 100644 --- a/python/ray/tune/tests/test_searchers.py +++ b/python/ray/tune/tests/test_searchers.py @@ -26,6 +26,7 @@ class InvalidValuesTest(unittest.TestCase): Implicitly tests automatic config conversion and default (anonymous) mode handling. """ + def setUp(self): self.config = {"report": tune.uniform(0.0, 5.0)} @@ -47,17 +48,19 @@ def testAx(self): converted_config = AxSearch.convert_search_space(self.config) # At least one nan, inf, -inf and float client = AxClient(random_seed=4321) - client.create_experiment(parameters=converted_config, - objective_name="_metric", - minimize=False) + client.create_experiment( + parameters=converted_config, + objective_name="_metric", + minimize=False) searcher = AxSearch(ax_client=client) - out = tune.run(_invalid_objective, - search_alg=searcher, - metric="_metric", - mode="max", - num_samples=4, - reuse_actors=False) + out = tune.run( + _invalid_objective, + search_alg=searcher, + metric="_metric", + mode="max", + num_samples=4, + reuse_actors=False) best_trial = out.best_trial self.assertLessEqual(best_trial.config["report"], 2.0) @@ -80,13 +83,14 @@ def testBayesOpt(self): def testBlendSearch(self): from ray.tune.suggest.flaml import BlendSearch - out = tune.run(_invalid_objective, - search_alg=BlendSearch(), - config=self.config, - metric="_metric", - mode="max", - num_samples=8, - reuse_actors=False) + out = tune.run( + _invalid_objective, + search_alg=BlendSearch(), + config=self.config, + metric="_metric", + mode="max", + num_samples=8, + reuse_actors=False) best_trial = out.best_trial self.assertLessEqual(best_trial.config["report"], 2.0) @@ -94,12 +98,13 @@ def testBlendSearch(self): def testBOHB(self): from ray.tune.suggest.bohb import TuneBOHB - out = tune.run(_invalid_objective, - search_alg=TuneBOHB(seed=1000), - config=self.config, - mode="max", - num_samples=8, - reuse_actors=False) + out = tune.run( + _invalid_objective, + search_alg=TuneBOHB(seed=1000), + config=self.config, + mode="max", + num_samples=8, + reuse_actors=False) best_trial = out.best_trial self.assertLessEqual(best_trial.config["report"], 2.0) @@ -107,13 +112,14 @@ def testBOHB(self): def testCFO(self): from ray.tune.suggest.flaml import CFO - out = tune.run(_invalid_objective, - search_alg=CFO(), - config=self.config, - metric="_metric", - mode="max", - num_samples=16, - reuse_actors=False) + out = tune.run( + _invalid_objective, + search_alg=CFO(), + config=self.config, + metric="_metric", + mode="max", + num_samples=16, + reuse_actors=False) best_trial = out.best_trial self.assertLessEqual(best_trial.config["report"], 2.0) @@ -123,13 +129,13 @@ def testDragonfly(self): np.random.seed(1000) # At least one nan, inf, -inf and float - out = tune.run(_invalid_objective, - search_alg=DragonflySearch(domain="euclidean", - optimizer="random"), - config=self.config, - mode="max", - num_samples=8, - reuse_actors=False) + out = tune.run( + _invalid_objective, + search_alg=DragonflySearch(domain="euclidean", optimizer="random"), + config=self.config, + mode="max", + num_samples=8, + reuse_actors=False) best_trial = out.best_trial self.assertLessEqual(best_trial.config["point"], 2.0) @@ -203,12 +209,13 @@ def testSkopt(self): np.random.seed(1234) # At least one nan, inf, -inf and float - out = tune.run(_invalid_objective, - search_alg=SkOptSearch(), - config=self.config, - mode="max", - num_samples=8, - reuse_actors=False) + out = tune.run( + _invalid_objective, + search_alg=SkOptSearch(), + config=self.config, + mode="max", + num_samples=8, + reuse_actors=False) best_trial = out.best_trial self.assertLessEqual(best_trial.config["report"], 2.0) @@ -221,12 +228,13 @@ def testZOOpt(self): np.random.seed(1000) # At least one nan, inf, -inf and float - out = tune.run(_invalid_objective, - search_alg=ZOOptSearch(budget=100, parallel_num=4), - config=self.config, - mode="max", - num_samples=8, - reuse_actors=False) + out = tune.run( + _invalid_objective, + search_alg=ZOOptSearch(budget=100, parallel_num=4), + config=self.config, + mode="max", + num_samples=8, + reuse_actors=False) best_trial = out.best_trial self.assertLessEqual(best_trial.config["report"], 2.0) @@ -236,6 +244,7 @@ class AddEvaluatedPointTest(unittest.TestCase): """ Test add_evaluated_point method in searchers that support it. """ + def setUp(self): self.param_name = "report" self.valid_value = 1.0 @@ -256,13 +265,14 @@ def testOptuna(self): from ray.tune.suggest.optuna import OptunaSearch from optuna.trial import TrialState - searcher = OptunaSearch(space=self.space, - metric="metric", - mode="max", - points_to_evaluate=[{ - self.param_name: self.valid_value - }], - evaluated_rewards=[1.0]) + searcher = OptunaSearch( + space=self.space, + metric="metric", + mode="max", + points_to_evaluate=[{ + self.param_name: self.valid_value + }], + evaluated_rewards=[1.0]) self.assertGreater(len(searcher._ot_study.trials), 0) @@ -278,32 +288,26 @@ def testOptuna(self): self.assertEqual(len(searcher._ot_study.trials), 0) - searcher.add_evaluated_point(point, - 1.0, - intermediate_values=[0.8, 0.9]) + searcher.add_evaluated_point( + point, 1.0, intermediate_values=[0.8, 0.9]) self.assertEqual(len(searcher._ot_study.trials), 1) self.assertTrue( searcher._ot_study.trials[-1].state == TrialState.COMPLETE) - searcher.add_evaluated_point(point, - 1.0, - intermediate_values=[0.8, 0.9], - error=True) + searcher.add_evaluated_point( + point, 1.0, intermediate_values=[0.8, 0.9], error=True) self.assertEqual(len(searcher._ot_study.trials), 2) self.assertTrue(searcher._ot_study.trials[-1].state == TrialState.FAIL) - searcher.add_evaluated_point(point, - 1.0, - intermediate_values=[0.8, 0.9], - pruned=True) + searcher.add_evaluated_point( + point, 1.0, intermediate_values=[0.8, 0.9], pruned=True) self.assertEqual(len(searcher._ot_study.trials), 3) self.assertTrue( searcher._ot_study.trials[-1].state == TrialState.PRUNED) dbr_searcher = OptunaSearch( lambda trial: { - self.param_name: trial.suggest_float(self.param_name, 0.0, 5.0 - ), + self.param_name: trial.suggest_float(self.param_name, 0.0, 5.0), }, metric="metric", mode="max", From f50e43dc7228a6c6e9c8819e24935cd83d4e0e1f Mon Sep 17 00:00:00 2001 From: Carl Assmann Date: Thu, 16 Sep 2021 12:42:32 +0000 Subject: [PATCH 4/7] add changes in docstring as suggested --- python/ray/tune/suggest/optuna.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/python/ray/tune/suggest/optuna.py b/python/ray/tune/suggest/optuna.py index f9d3d758cea8..cdf396d599f7 100644 --- a/python/ray/tune/suggest/optuna.py +++ b/python/ray/tune/suggest/optuna.py @@ -54,6 +54,7 @@ class _OptunaTrialSuggestCaptor: `suggest_` callables with a function capturing the returned value, which will be saved in the ``captured_values`` dict. """ + def __init__(self, ot_trial: OptunaTrial) -> None: self.ot_trial = ot_trial self.captured_values: Dict[str, Any] = {} @@ -126,10 +127,11 @@ class OptunaSearch(Searcher): points_to_evaluate. ..warning:: - When using evaluated_rewards, the search space `space` must + When using ``evaluated_rewards``, the search space ``space`` must be provided as a :class:`dict` with parameter names as keys and - ``optuna.distributions``. The define-by-run definition is not - yet supported. + ``optuna.distributions`` instances as values. The define-by-run + search space definition is not yet supported with this + functionality. Tune automatically converts search spaces to Optuna's format: @@ -184,8 +186,8 @@ def define_search_space(trial: optuna.Trial): tune.run(trainable, search_alg=optuna_search) You can pass configs that will be evaluated first using - `points_to_evaluate`: - + ``points_to_evaluate``: + .. code-block:: python from ray.tune.suggest.optuna import OptunaSearch @@ -454,7 +456,7 @@ def add_evaluated_point(self, "Define-by-run function passed in `space` argument is not" "yet supported when using `evaluated_rewards`. Please provide" "a 'OptunaDistribution' dict.") - + ot_trial_state = OptunaTrialState.COMPLETE if error: ot_trial_state = OptunaTrialState.FAIL From ee95af67c210b91e5e9e534bd91f8a6bdcbd0cf5 Mon Sep 17 00:00:00 2001 From: Carl Assmann Date: Thu, 16 Sep 2021 12:50:38 +0000 Subject: [PATCH 5/7] run format.sh with correct yapf version --- python/ray/tune/suggest/optuna.py | 13 +++++++------ python/ray/tune/tests/test_searchers.py | 9 ++++++--- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/python/ray/tune/suggest/optuna.py b/python/ray/tune/suggest/optuna.py index cdf396d599f7..5afa6a0f8648 100644 --- a/python/ray/tune/suggest/optuna.py +++ b/python/ray/tune/suggest/optuna.py @@ -127,11 +127,11 @@ class OptunaSearch(Searcher): points_to_evaluate. ..warning:: - When using ``evaluated_rewards``, the search space ``space`` must - be provided as a :class:`dict` with parameter names as keys and - ``optuna.distributions`` instances as values. The define-by-run - search space definition is not yet supported with this - functionality. + When using ``evaluated_rewards``, the search space ``space`` + must be provided as a :class:`dict` with parameter names as + keys and ``optuna.distributions`` instances as values. The + define-by-run search space definition is not yet supported with + this functionality. Tune automatically converts search spaces to Optuna's format: @@ -184,7 +184,7 @@ def define_search_space(trial: optuna.Trial): mode="min") tune.run(trainable, search_alg=optuna_search) - + You can pass configs that will be evaluated first using ``points_to_evaluate``: @@ -231,6 +231,7 @@ def define_search_space(trial: optuna.Trial): .. versionadded:: 0.8.8 """ + def __init__(self, space: Optional[Union[Dict[str, "OptunaDistribution"], List[ Tuple], Callable[["OptunaTrial"], Optional[Dict[ diff --git a/python/ray/tune/tests/test_searchers.py b/python/ray/tune/tests/test_searchers.py index 0d872cad7ff2..f0a2c01df59b 100644 --- a/python/ray/tune/tests/test_searchers.py +++ b/python/ray/tune/tests/test_searchers.py @@ -305,10 +305,13 @@ def testOptuna(self): self.assertTrue( searcher._ot_study.trials[-1].state == TrialState.PRUNED) + def dbr_space(trial): + return { + self.param_name: trial.suggest_float(self.param_name, 0.0, 5.0) + } + dbr_searcher = OptunaSearch( - lambda trial: { - self.param_name: trial.suggest_float(self.param_name, 0.0, 5.0), - }, + space=dbr_space, metric="metric", mode="max", ) From 7bfa56f630460f8ac9afd4616ac35c6ec2ff63ab Mon Sep 17 00:00:00 2001 From: Carl Assmann Date: Thu, 16 Sep 2021 15:04:33 +0200 Subject: [PATCH 6/7] fix typo in docstring Co-authored-by: Antoni Baum --- python/ray/tune/suggest/optuna.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/ray/tune/suggest/optuna.py b/python/ray/tune/suggest/optuna.py index 5afa6a0f8648..bc98dc4b5664 100644 --- a/python/ray/tune/suggest/optuna.py +++ b/python/ray/tune/suggest/optuna.py @@ -454,9 +454,9 @@ def add_evaluated_point(self, mode=self._mode)) if callable(self._space): raise TypeError( - "Define-by-run function passed in `space` argument is not" - "yet supported when using `evaluated_rewards`. Please provide" - "a 'OptunaDistribution' dict.") + "Define-by-run function passed in `space` argument is not " + "yet supported when using `evaluated_rewards`. Please provide " + "an 'OptunaDistribution' dict.") ot_trial_state = OptunaTrialState.COMPLETE if error: From 26553a5bd2054f0b62b2634e981a22b3896bd3cb Mon Sep 17 00:00:00 2001 From: Kai Fricke Date: Thu, 23 Sep 2021 15:15:32 +0100 Subject: [PATCH 7/7] Update python/ray/tune/suggest/optuna.py --- python/ray/tune/suggest/optuna.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/ray/tune/suggest/optuna.py b/python/ray/tune/suggest/optuna.py index 0f3cd0feda60..c69ef4fa34c3 100644 --- a/python/ray/tune/suggest/optuna.py +++ b/python/ray/tune/suggest/optuna.py @@ -421,7 +421,8 @@ def add_evaluated_point(self, raise TypeError( "Define-by-run function passed in `space` argument is not " "yet supported when using `evaluated_rewards`. Please provide " - "an 'OptunaDistribution' dict.") + "an `OptunaDistribution` dict or pass a Ray Tune " + "search space to `tune.run()`.") ot_trial_state = OptunaTrialState.COMPLETE if error: