We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[Bug] delete field when using optuna sweeper would throw error
# 🐛 Bug
## Description
<!-- A clear and concise description of what the bug is. -->
I was using optuna sweeper but I noticed that if i delete a field by `~field` it would throw an error.

## To reproduce
Let's [start with this](https://github.com/facebookresearch/hydra/tree/main/examples/advanced/defaults_list_interpolation). Let's modify `conf/db/sqlite.yaml` to be this
```yaml
name: sqlite

model:
  name: boring
```
then create a directory called `exp`, and create a file `exp/exp.yaml`
```yaml
# @package _global_

defaults:
  - override /hydra/sweeper: optuna
  - override /db: sqlite
```
Now run the command `python my_app.py -m +exp=exp ~db.model` it would have the error
> Could not delete from config. The value of 'db.model' is {'name': 'boring'} and not None.

However if I did `python my_app.py +exp=exp ~db.model` (not activating sweeper), then the code would run correctly, making the db to be sqlite (the default is mysql) and the `model.name` part deleted. 

## System information
- **Hydra Version** :  1.0.0.rc1
- **Python version** : 3.7.10
- **Operating system** : Ubuntu 18.04.2 LTS


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `plugins/hydra_optuna_sweeper/hydra_plugins/hydra_optuna_sweeper/_impl.py`
Content:
```
1 # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2 import logging
3 import sys
4 from typing import Any, Dict, List, MutableMapping, MutableSequence, Optional
5 
6 import optuna
7 from hydra.core.override_parser.overrides_parser import OverridesParser
8 from hydra.core.override_parser.types import (
9     ChoiceSweep,
10     IntervalSweep,
11     Override,
12     RangeSweep,
13     Transformer,
14 )
15 from hydra.core.plugins import Plugins
16 from hydra.plugins.sweeper import Sweeper
17 from hydra.types import HydraContext, TaskFunction
18 from omegaconf import DictConfig, OmegaConf
19 from optuna.distributions import (
20     BaseDistribution,
21     CategoricalChoiceType,
22     CategoricalDistribution,
23     DiscreteUniformDistribution,
24     IntLogUniformDistribution,
25     IntUniformDistribution,
26     LogUniformDistribution,
27     UniformDistribution,
28 )
29 
30 from .config import Direction, DistributionConfig, DistributionType
31 
32 log = logging.getLogger(__name__)
33 
34 
35 def create_optuna_distribution_from_config(
36     config: MutableMapping[str, Any]
37 ) -> BaseDistribution:
38     kwargs = dict(config)
39     if isinstance(config["type"], str):
40         kwargs["type"] = DistributionType[config["type"]]
41     param = DistributionConfig(**kwargs)
42     if param.type == DistributionType.categorical:
43         assert param.choices is not None
44         return CategoricalDistribution(param.choices)
45     if param.type == DistributionType.int:
46         assert param.low is not None
47         assert param.high is not None
48         if param.log:
49             return IntLogUniformDistribution(int(param.low), int(param.high))
50         step = int(param.step) if param.step is not None else 1
51         return IntUniformDistribution(int(param.low), int(param.high), step=step)
52     if param.type == DistributionType.float:
53         assert param.low is not None
54         assert param.high is not None
55         if param.log:
56             return LogUniformDistribution(param.low, param.high)
57         if param.step is not None:
58             return DiscreteUniformDistribution(param.low, param.high, param.step)
59         return UniformDistribution(param.low, param.high)
60     raise NotImplementedError(f"{param.type} is not supported by Optuna sweeper.")
61 
62 
63 def create_optuna_distribution_from_override(override: Override) -> Any:
64     value = override.value()
65     if not override.is_sweep_override():
66         return value
67 
68     choices: List[CategoricalChoiceType] = []
69     if override.is_choice_sweep():
70         assert isinstance(value, ChoiceSweep)
71         for x in override.sweep_iterator(transformer=Transformer.encode):
72             assert isinstance(
73                 x, (str, int, float, bool)
74             ), f"A choice sweep expects str, int, float, or bool type. Got {type(x)}."
75             choices.append(x)
76         return CategoricalDistribution(choices)
77 
78     if override.is_range_sweep():
79         assert isinstance(value, RangeSweep)
80         assert value.start is not None
81         assert value.stop is not None
82         if value.shuffle:
83             for x in override.sweep_iterator(transformer=Transformer.encode):
84                 assert isinstance(
85                     x, (str, int, float, bool)
86                 ), f"A choice sweep expects str, int, float, or bool type. Got {type(x)}."
87                 choices.append(x)
88             return CategoricalDistribution(choices)
89         return IntUniformDistribution(
90             int(value.start), int(value.stop), step=int(value.step)
91         )
92 
93     if override.is_interval_sweep():
94         assert isinstance(value, IntervalSweep)
95         assert value.start is not None
96         assert value.end is not None
97         if "log" in value.tags:
98             if isinstance(value.start, int) and isinstance(value.end, int):
99                 return IntLogUniformDistribution(int(value.start), int(value.end))
100             return LogUniformDistribution(value.start, value.end)
101         else:
102             if isinstance(value.start, int) and isinstance(value.end, int):
103                 return IntUniformDistribution(value.start, value.end)
104             return UniformDistribution(value.start, value.end)
105 
106     raise NotImplementedError(f"{override} is not supported by Optuna sweeper.")
107 
108 
109 class OptunaSweeperImpl(Sweeper):
110     def __init__(
111         self,
112         sampler: Any,
113         direction: Any,
114         storage: Optional[str],
115         study_name: Optional[str],
116         n_trials: int,
117         n_jobs: int,
118         search_space: Optional[DictConfig],
119     ) -> None:
120         self.sampler = sampler
121         self.direction = direction
122         self.storage = storage
123         self.study_name = study_name
124         self.n_trials = n_trials
125         self.n_jobs = n_jobs
126         self.search_space = {}
127         if search_space:
128             assert isinstance(search_space, DictConfig)
129             self.search_space = {
130                 str(x): create_optuna_distribution_from_config(y)
131                 for x, y in search_space.items()
132             }
133         self.job_idx: int = 0
134 
135     def setup(
136         self,
137         *,
138         hydra_context: HydraContext,
139         task_function: TaskFunction,
140         config: DictConfig,
141     ) -> None:
142         self.job_idx = 0
143         self.config = config
144         self.hydra_context = hydra_context
145         self.launcher = Plugins.instance().instantiate_launcher(
146             config=config, hydra_context=hydra_context, task_function=task_function
147         )
148         self.sweep_dir = config.hydra.sweep.dir
149 
150     def sweep(self, arguments: List[str]) -> None:
151         assert self.config is not None
152         assert self.launcher is not None
153         assert self.hydra_context is not None
154         assert self.job_idx is not None
155 
156         parser = OverridesParser.create()
157         parsed = parser.parse_overrides(arguments)
158 
159         search_space = dict(self.search_space)
160         fixed_params = dict()
161         for override in parsed:
162             value = create_optuna_distribution_from_override(override)
163             if isinstance(value, BaseDistribution):
164                 search_space[override.get_key_element()] = value
165             else:
166                 fixed_params[override.get_key_element()] = value
167         # Remove fixed parameters from Optuna search space.
168         for param_name in fixed_params:
169             if param_name in search_space:
170                 del search_space[param_name]
171 
172         directions: List[str]
173         if isinstance(self.direction, MutableSequence):
174             directions = [
175                 d.name if isinstance(d, Direction) else d for d in self.direction
176             ]
177         else:
178             if isinstance(self.direction, str):
179                 directions = [self.direction]
180             else:
181                 directions = [self.direction.name]
182 
183         study = optuna.create_study(
184             study_name=self.study_name,
185             storage=self.storage,
186             sampler=self.sampler,
187             directions=directions,
188             load_if_exists=True,
189         )
190         log.info(f"Study name: {study.study_name}")
191         log.info(f"Storage: {self.storage}")
192         log.info(f"Sampler: {type(self.sampler).__name__}")
193         log.info(f"Directions: {directions}")
194 
195         batch_size = self.n_jobs
196         n_trials_to_go = self.n_trials
197 
198         while n_trials_to_go > 0:
199             batch_size = min(n_trials_to_go, batch_size)
200 
201             trials = [study.ask() for _ in range(batch_size)]
202             overrides = []
203             for trial in trials:
204                 for param_name, distribution in search_space.items():
205                     trial._suggest(param_name, distribution)
206 
207                 params = dict(trial.params)
208                 params.update(fixed_params)
209                 overrides.append(tuple(f"{name}={val}" for name, val in params.items()))
210 
211             returns = self.launcher.launch(overrides, initial_job_idx=self.job_idx)
212             self.job_idx += len(returns)
213             for trial, ret in zip(trials, returns):
214                 values: Optional[List[float]] = None
215                 state: optuna.trial.TrialState = optuna.trial.TrialState.COMPLETE
216                 try:
217                     if len(directions) == 1:
218                         try:
219                             values = [float(ret.return_value)]
220                         except (ValueError, TypeError):
221                             raise ValueError(
222                                 f"Return value must be float-castable. Got '{ret.return_value}'."
223                             ).with_traceback(sys.exc_info()[2])
224                     else:
225                         try:
226                             values = [float(v) for v in ret.return_value]
227                         except (ValueError, TypeError):
228                             raise ValueError(
229                                 "Return value must be a list or tuple of float-castable values."
230                                 f" Got '{ret.return_value}'."
231                             ).with_traceback(sys.exc_info()[2])
232                         if len(values) != len(directions):
233                             raise ValueError(
234                                 "The number of the values and the number of the objectives are"
235                                 f" mismatched. Expect {len(directions)}, but actually {len(values)}."
236                             )
237                     study.tell(trial=trial, state=state, values=values)
238                 except Exception as e:
239                     state = optuna.trial.TrialState.FAIL
240                     study.tell(trial=trial, state=state, values=values)
241                     raise e
242 
243             n_trials_to_go -= batch_size
244 
245         results_to_serialize: Dict[str, Any]
246         if len(directions) < 2:
247             best_trial = study.best_trial
248             results_to_serialize = {
249                 "name": "optuna",
250                 "best_params": best_trial.params,
251                 "best_value": best_trial.value,
252             }
253             log.info(f"Best parameters: {best_trial.params}")
254             log.info(f"Best value: {best_trial.value}")
255         else:
256             best_trials = study.best_trials
257             pareto_front = [
258                 {"params": t.params, "values": t.values} for t in best_trials
259             ]
260             results_to_serialize = {
261                 "name": "optuna",
262                 "solutions": pareto_front,
263             }
264             log.info(f"Number of Pareto solutions: {len(best_trials)}")
265             for t in best_trials:
266                 log.info(f"    Values: {t.values}, Params: {t.params}")
267         OmegaConf.save(
268             OmegaConf.create(results_to_serialize),
269             f"{self.config.hydra.sweep.dir}/optimization_results.yaml",
270         )
271
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/plugins/hydra_optuna_sweeper/hydra_plugins/hydra_optuna_sweeper/_impl.py b/plugins/hydra_optuna_sweeper/hydra_plugins/hydra_optuna_sweeper/_impl.py
--- a/plugins/hydra_optuna_sweeper/hydra_plugins/hydra_optuna_sweeper/_impl.py
+++ b/plugins/hydra_optuna_sweeper/hydra_plugins/hydra_optuna_sweeper/_impl.py
@@ -61,10 +61,10 @@
 
 
 def create_optuna_distribution_from_override(override: Override) -> Any:
-    value = override.value()
     if not override.is_sweep_override():
-        return value
+        return override.get_value_element_as_str()
 
+    value = override.value()
     choices: List[CategoricalChoiceType] = []
     if override.is_choice_sweep():
         assert isinstance(value, ChoiceSweep)

{"golden_diff": "diff --git a/plugins/hydra_optuna_sweeper/hydra_plugins/hydra_optuna_sweeper/_impl.py b/plugins/hydra_optuna_sweeper/hydra_plugins/hydra_optuna_sweeper/_impl.py\n--- a/plugins/hydra_optuna_sweeper/hydra_plugins/hydra_optuna_sweeper/_impl.py\n+++ b/plugins/hydra_optuna_sweeper/hydra_plugins/hydra_optuna_sweeper/_impl.py\n@@ -61,10 +61,10 @@\n \n \n def create_optuna_distribution_from_override(override: Override) -> Any:\n-    value = override.value()\n     if not override.is_sweep_override():\n-        return value\n+        return override.get_value_element_as_str()\n \n+    value = override.value()\n     choices: List[CategoricalChoiceType] = []\n     if override.is_choice_sweep():\n         assert isinstance(value, ChoiceSweep)\n", "issue": "[Bug] delete field when using optuna sweeper would throw error\n# \ud83d\udc1b Bug\r\n## Description\r\n<!-- A clear and concise description of what the bug is. -->\r\nI was using optuna sweeper but I noticed that if i delete a field by `~field` it would throw an error.\r\n\r\n## To reproduce\r\nLet's [start with this](https://github.com/facebookresearch/hydra/tree/main/examples/advanced/defaults_list_interpolation). Let's modify `conf/db/sqlite.yaml` to be this\r\n```yaml\r\nname: sqlite\r\n\r\nmodel:\r\n  name: boring\r\n```\r\nthen create a directory called `exp`, and create a file `exp/exp.yaml`\r\n```yaml\r\n# @package _global_\r\n\r\ndefaults:\r\n  - override /hydra/sweeper: optuna\r\n  - override /db: sqlite\r\n```\r\nNow run the command `python my_app.py -m +exp=exp ~db.model` it would have the error\r\n> Could not delete from config. The value of 'db.model' is {'name': 'boring'} and not None.\r\n\r\nHowever if I did `python my_app.py +exp=exp ~db.model` (not activating sweeper), then the code would run correctly, making the db to be sqlite (the default is mysql) and the `model.name` part deleted. \r\n\r\n## System information\r\n- **Hydra Version** :  1.0.0.rc1\r\n- **Python version** : 3.7.10\r\n- **Operating system** : Ubuntu 18.04.2 LTS\r\n\n", "before_files": [{"content": "# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\nimport logging\nimport sys\nfrom typing import Any, Dict, List, MutableMapping, MutableSequence, Optional\n\nimport optuna\nfrom hydra.core.override_parser.overrides_parser import OverridesParser\nfrom hydra.core.override_parser.types import (\n    ChoiceSweep,\n    IntervalSweep,\n    Override,\n    RangeSweep,\n    Transformer,\n)\nfrom hydra.core.plugins import Plugins\nfrom hydra.plugins.sweeper import Sweeper\nfrom hydra.types import HydraContext, TaskFunction\nfrom omegaconf import DictConfig, OmegaConf\nfrom optuna.distributions import (\n    BaseDistribution,\n    CategoricalChoiceType,\n    CategoricalDistribution,\n    DiscreteUniformDistribution,\n    IntLogUniformDistribution,\n    IntUniformDistribution,\n    LogUniformDistribution,\n    UniformDistribution,\n)\n\nfrom .config import Direction, DistributionConfig, DistributionType\n\nlog = logging.getLogger(__name__)\n\n\ndef create_optuna_distribution_from_config(\n    config: MutableMapping[str, Any]\n) -> BaseDistribution:\n    kwargs = dict(config)\n    if isinstance(config[\"type\"], str):\n        kwargs[\"type\"] = DistributionType[config[\"type\"]]\n    param = DistributionConfig(**kwargs)\n    if param.type == DistributionType.categorical:\n        assert param.choices is not None\n        return CategoricalDistribution(param.choices)\n    if param.type == DistributionType.int:\n        assert param.low is not None\n        assert param.high is not None\n        if param.log:\n            return IntLogUniformDistribution(int(param.low), int(param.high))\n        step = int(param.step) if param.step is not None else 1\n        return IntUniformDistribution(int(param.low), int(param.high), step=step)\n    if param.type == DistributionType.float:\n        assert param.low is not None\n        assert param.high is not None\n        if param.log:\n            return LogUniformDistribution(param.low, param.high)\n        if param.step is not None:\n            return DiscreteUniformDistribution(param.low, param.high, param.step)\n        return UniformDistribution(param.low, param.high)\n    raise NotImplementedError(f\"{param.type} is not supported by Optuna sweeper.\")\n\n\ndef create_optuna_distribution_from_override(override: Override) -> Any:\n    value = override.value()\n    if not override.is_sweep_override():\n        return value\n\n    choices: List[CategoricalChoiceType] = []\n    if override.is_choice_sweep():\n        assert isinstance(value, ChoiceSweep)\n        for x in override.sweep_iterator(transformer=Transformer.encode):\n            assert isinstance(\n                x, (str, int, float, bool)\n            ), f\"A choice sweep expects str, int, float, or bool type. Got {type(x)}.\"\n            choices.append(x)\n        return CategoricalDistribution(choices)\n\n    if override.is_range_sweep():\n        assert isinstance(value, RangeSweep)\n        assert value.start is not None\n        assert value.stop is not None\n        if value.shuffle:\n            for x in override.sweep_iterator(transformer=Transformer.encode):\n                assert isinstance(\n                    x, (str, int, float, bool)\n                ), f\"A choice sweep expects str, int, float, or bool type. Got {type(x)}.\"\n                choices.append(x)\n            return CategoricalDistribution(choices)\n        return IntUniformDistribution(\n            int(value.start), int(value.stop), step=int(value.step)\n        )\n\n    if override.is_interval_sweep():\n        assert isinstance(value, IntervalSweep)\n        assert value.start is not None\n        assert value.end is not None\n        if \"log\" in value.tags:\n            if isinstance(value.start, int) and isinstance(value.end, int):\n                return IntLogUniformDistribution(int(value.start), int(value.end))\n            return LogUniformDistribution(value.start, value.end)\n        else:\n            if isinstance(value.start, int) and isinstance(value.end, int):\n                return IntUniformDistribution(value.start, value.end)\n            return UniformDistribution(value.start, value.end)\n\n    raise NotImplementedError(f\"{override} is not supported by Optuna sweeper.\")\n\n\nclass OptunaSweeperImpl(Sweeper):\n    def __init__(\n        self,\n        sampler: Any,\n        direction: Any,\n        storage: Optional[str],\n        study_name: Optional[str],\n        n_trials: int,\n        n_jobs: int,\n        search_space: Optional[DictConfig],\n    ) -> None:\n        self.sampler = sampler\n        self.direction = direction\n        self.storage = storage\n        self.study_name = study_name\n        self.n_trials = n_trials\n        self.n_jobs = n_jobs\n        self.search_space = {}\n        if search_space:\n            assert isinstance(search_space, DictConfig)\n            self.search_space = {\n                str(x): create_optuna_distribution_from_config(y)\n                for x, y in search_space.items()\n            }\n        self.job_idx: int = 0\n\n    def setup(\n        self,\n        *,\n        hydra_context: HydraContext,\n        task_function: TaskFunction,\n        config: DictConfig,\n    ) -> None:\n        self.job_idx = 0\n        self.config = config\n        self.hydra_context = hydra_context\n        self.launcher = Plugins.instance().instantiate_launcher(\n            config=config, hydra_context=hydra_context, task_function=task_function\n        )\n        self.sweep_dir = config.hydra.sweep.dir\n\n    def sweep(self, arguments: List[str]) -> None:\n        assert self.config is not None\n        assert self.launcher is not None\n        assert self.hydra_context is not None\n        assert self.job_idx is not None\n\n        parser = OverridesParser.create()\n        parsed = parser.parse_overrides(arguments)\n\n        search_space = dict(self.search_space)\n        fixed_params = dict()\n        for override in parsed:\n            value = create_optuna_distribution_from_override(override)\n            if isinstance(value, BaseDistribution):\n                search_space[override.get_key_element()] = value\n            else:\n                fixed_params[override.get_key_element()] = value\n        # Remove fixed parameters from Optuna search space.\n        for param_name in fixed_params:\n            if param_name in search_space:\n                del search_space[param_name]\n\n        directions: List[str]\n        if isinstance(self.direction, MutableSequence):\n            directions = [\n                d.name if isinstance(d, Direction) else d for d in self.direction\n            ]\n        else:\n            if isinstance(self.direction, str):\n                directions = [self.direction]\n            else:\n                directions = [self.direction.name]\n\n        study = optuna.create_study(\n            study_name=self.study_name,\n            storage=self.storage,\n            sampler=self.sampler,\n            directions=directions,\n            load_if_exists=True,\n        )\n        log.info(f\"Study name: {study.study_name}\")\n        log.info(f\"Storage: {self.storage}\")\n        log.info(f\"Sampler: {type(self.sampler).__name__}\")\n        log.info(f\"Directions: {directions}\")\n\n        batch_size = self.n_jobs\n        n_trials_to_go = self.n_trials\n\n        while n_trials_to_go > 0:\n            batch_size = min(n_trials_to_go, batch_size)\n\n            trials = [study.ask() for _ in range(batch_size)]\n            overrides = []\n            for trial in trials:\n                for param_name, distribution in search_space.items():\n                    trial._suggest(param_name, distribution)\n\n                params = dict(trial.params)\n                params.update(fixed_params)\n                overrides.append(tuple(f\"{name}={val}\" for name, val in params.items()))\n\n            returns = self.launcher.launch(overrides, initial_job_idx=self.job_idx)\n            self.job_idx += len(returns)\n            for trial, ret in zip(trials, returns):\n                values: Optional[List[float]] = None\n                state: optuna.trial.TrialState = optuna.trial.TrialState.COMPLETE\n                try:\n                    if len(directions) == 1:\n                        try:\n                            values = [float(ret.return_value)]\n                        except (ValueError, TypeError):\n                            raise ValueError(\n                                f\"Return value must be float-castable. Got '{ret.return_value}'.\"\n                            ).with_traceback(sys.exc_info()[2])\n                    else:\n                        try:\n                            values = [float(v) for v in ret.return_value]\n                        except (ValueError, TypeError):\n                            raise ValueError(\n                                \"Return value must be a list or tuple of float-castable values.\"\n                                f\" Got '{ret.return_value}'.\"\n                            ).with_traceback(sys.exc_info()[2])\n                        if len(values) != len(directions):\n                            raise ValueError(\n                                \"The number of the values and the number of the objectives are\"\n                                f\" mismatched. Expect {len(directions)}, but actually {len(values)}.\"\n                            )\n                    study.tell(trial=trial, state=state, values=values)\n                except Exception as e:\n                    state = optuna.trial.TrialState.FAIL\n                    study.tell(trial=trial, state=state, values=values)\n                    raise e\n\n            n_trials_to_go -= batch_size\n\n        results_to_serialize: Dict[str, Any]\n        if len(directions) < 2:\n            best_trial = study.best_trial\n            results_to_serialize = {\n                \"name\": \"optuna\",\n                \"best_params\": best_trial.params,\n                \"best_value\": best_trial.value,\n            }\n            log.info(f\"Best parameters: {best_trial.params}\")\n            log.info(f\"Best value: {best_trial.value}\")\n        else:\n            best_trials = study.best_trials\n            pareto_front = [\n                {\"params\": t.params, \"values\": t.values} for t in best_trials\n            ]\n            results_to_serialize = {\n                \"name\": \"optuna\",\n                \"solutions\": pareto_front,\n            }\n            log.info(f\"Number of Pareto solutions: {len(best_trials)}\")\n            for t in best_trials:\n                log.info(f\"    Values: {t.values}, Params: {t.params}\")\n        OmegaConf.save(\n            OmegaConf.create(results_to_serialize),\n            f\"{self.config.hydra.sweep.dir}/optimization_results.yaml\",\n        )\n", "path": "plugins/hydra_optuna_sweeper/hydra_plugins/hydra_optuna_sweeper/_impl.py"}], "after_files": [{"content": "# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\nimport logging\nimport sys\nfrom typing import Any, Dict, List, MutableMapping, MutableSequence, Optional\n\nimport optuna\nfrom hydra.core.override_parser.overrides_parser import OverridesParser\nfrom hydra.core.override_parser.types import (\n    ChoiceSweep,\n    IntervalSweep,\n    Override,\n    RangeSweep,\n    Transformer,\n)\nfrom hydra.core.plugins import Plugins\nfrom hydra.plugins.sweeper import Sweeper\nfrom hydra.types import HydraContext, TaskFunction\nfrom omegaconf import DictConfig, OmegaConf\nfrom optuna.distributions import (\n    BaseDistribution,\n    CategoricalChoiceType,\n    CategoricalDistribution,\n    DiscreteUniformDistribution,\n    IntLogUniformDistribution,\n    IntUniformDistribution,\n    LogUniformDistribution,\n    UniformDistribution,\n)\n\nfrom .config import Direction, DistributionConfig, DistributionType\n\nlog = logging.getLogger(__name__)\n\n\ndef create_optuna_distribution_from_config(\n    config: MutableMapping[str, Any]\n) -> BaseDistribution:\n    kwargs = dict(config)\n    if isinstance(config[\"type\"], str):\n        kwargs[\"type\"] = DistributionType[config[\"type\"]]\n    param = DistributionConfig(**kwargs)\n    if param.type == DistributionType.categorical:\n        assert param.choices is not None\n        return CategoricalDistribution(param.choices)\n    if param.type == DistributionType.int:\n        assert param.low is not None\n        assert param.high is not None\n        if param.log:\n            return IntLogUniformDistribution(int(param.low), int(param.high))\n        step = int(param.step) if param.step is not None else 1\n        return IntUniformDistribution(int(param.low), int(param.high), step=step)\n    if param.type == DistributionType.float:\n        assert param.low is not None\n        assert param.high is not None\n        if param.log:\n            return LogUniformDistribution(param.low, param.high)\n        if param.step is not None:\n            return DiscreteUniformDistribution(param.low, param.high, param.step)\n        return UniformDistribution(param.low, param.high)\n    raise NotImplementedError(f\"{param.type} is not supported by Optuna sweeper.\")\n\n\ndef create_optuna_distribution_from_override(override: Override) -> Any:\n    if not override.is_sweep_override():\n        return override.get_value_element_as_str()\n\n    value = override.value()\n    choices: List[CategoricalChoiceType] = []\n    if override.is_choice_sweep():\n        assert isinstance(value, ChoiceSweep)\n        for x in override.sweep_iterator(transformer=Transformer.encode):\n            assert isinstance(\n                x, (str, int, float, bool)\n            ), f\"A choice sweep expects str, int, float, or bool type. Got {type(x)}.\"\n            choices.append(x)\n        return CategoricalDistribution(choices)\n\n    if override.is_range_sweep():\n        assert isinstance(value, RangeSweep)\n        assert value.start is not None\n        assert value.stop is not None\n        if value.shuffle:\n            for x in override.sweep_iterator(transformer=Transformer.encode):\n                assert isinstance(\n                    x, (str, int, float, bool)\n                ), f\"A choice sweep expects str, int, float, or bool type. Got {type(x)}.\"\n                choices.append(x)\n            return CategoricalDistribution(choices)\n        return IntUniformDistribution(\n            int(value.start), int(value.stop), step=int(value.step)\n        )\n\n    if override.is_interval_sweep():\n        assert isinstance(value, IntervalSweep)\n        assert value.start is not None\n        assert value.end is not None\n        if \"log\" in value.tags:\n            if isinstance(value.start, int) and isinstance(value.end, int):\n                return IntLogUniformDistribution(int(value.start), int(value.end))\n            return LogUniformDistribution(value.start, value.end)\n        else:\n            if isinstance(value.start, int) and isinstance(value.end, int):\n                return IntUniformDistribution(value.start, value.end)\n            return UniformDistribution(value.start, value.end)\n\n    raise NotImplementedError(f\"{override} is not supported by Optuna sweeper.\")\n\n\nclass OptunaSweeperImpl(Sweeper):\n    def __init__(\n        self,\n        sampler: Any,\n        direction: Any,\n        storage: Optional[str],\n        study_name: Optional[str],\n        n_trials: int,\n        n_jobs: int,\n        search_space: Optional[DictConfig],\n    ) -> None:\n        self.sampler = sampler\n        self.direction = direction\n        self.storage = storage\n        self.study_name = study_name\n        self.n_trials = n_trials\n        self.n_jobs = n_jobs\n        self.search_space = {}\n        if search_space:\n            assert isinstance(search_space, DictConfig)\n            self.search_space = {\n                str(x): create_optuna_distribution_from_config(y)\n                for x, y in search_space.items()\n            }\n        self.job_idx: int = 0\n\n    def setup(\n        self,\n        *,\n        hydra_context: HydraContext,\n        task_function: TaskFunction,\n        config: DictConfig,\n    ) -> None:\n        self.job_idx = 0\n        self.config = config\n        self.hydra_context = hydra_context\n        self.launcher = Plugins.instance().instantiate_launcher(\n            config=config, hydra_context=hydra_context, task_function=task_function\n        )\n        self.sweep_dir = config.hydra.sweep.dir\n\n    def sweep(self, arguments: List[str]) -> None:\n        assert self.config is not None\n        assert self.launcher is not None\n        assert self.hydra_context is not None\n        assert self.job_idx is not None\n\n        parser = OverridesParser.create()\n        parsed = parser.parse_overrides(arguments)\n\n        search_space = dict(self.search_space)\n        fixed_params = dict()\n        for override in parsed:\n            value = create_optuna_distribution_from_override(override)\n            if isinstance(value, BaseDistribution):\n                search_space[override.get_key_element()] = value\n            else:\n                fixed_params[override.get_key_element()] = value\n        # Remove fixed parameters from Optuna search space.\n        for param_name in fixed_params:\n            if param_name in search_space:\n                del search_space[param_name]\n\n        directions: List[str]\n        if isinstance(self.direction, MutableSequence):\n            directions = [\n                d.name if isinstance(d, Direction) else d for d in self.direction\n            ]\n        else:\n            if isinstance(self.direction, str):\n                directions = [self.direction]\n            else:\n                directions = [self.direction.name]\n\n        study = optuna.create_study(\n            study_name=self.study_name,\n            storage=self.storage,\n            sampler=self.sampler,\n            directions=directions,\n            load_if_exists=True,\n        )\n        log.info(f\"Study name: {study.study_name}\")\n        log.info(f\"Storage: {self.storage}\")\n        log.info(f\"Sampler: {type(self.sampler).__name__}\")\n        log.info(f\"Directions: {directions}\")\n\n        batch_size = self.n_jobs\n        n_trials_to_go = self.n_trials\n\n        while n_trials_to_go > 0:\n            batch_size = min(n_trials_to_go, batch_size)\n\n            trials = [study.ask() for _ in range(batch_size)]\n            overrides = []\n            for trial in trials:\n                for param_name, distribution in search_space.items():\n                    trial._suggest(param_name, distribution)\n\n                params = dict(trial.params)\n                params.update(fixed_params)\n                overrides.append(tuple(f\"{name}={val}\" for name, val in params.items()))\n\n            returns = self.launcher.launch(overrides, initial_job_idx=self.job_idx)\n            self.job_idx += len(returns)\n            for trial, ret in zip(trials, returns):\n                values: Optional[List[float]] = None\n                state: optuna.trial.TrialState = optuna.trial.TrialState.COMPLETE\n                try:\n                    if len(directions) == 1:\n                        try:\n                            values = [float(ret.return_value)]\n                        except (ValueError, TypeError):\n                            raise ValueError(\n                                f\"Return value must be float-castable. Got '{ret.return_value}'.\"\n                            ).with_traceback(sys.exc_info()[2])\n                    else:\n                        try:\n                            values = [float(v) for v in ret.return_value]\n                        except (ValueError, TypeError):\n                            raise ValueError(\n                                \"Return value must be a list or tuple of float-castable values.\"\n                                f\" Got '{ret.return_value}'.\"\n                            ).with_traceback(sys.exc_info()[2])\n                        if len(values) != len(directions):\n                            raise ValueError(\n                                \"The number of the values and the number of the objectives are\"\n                                f\" mismatched. Expect {len(directions)}, but actually {len(values)}.\"\n                            )\n                    study.tell(trial=trial, state=state, values=values)\n                except Exception as e:\n                    state = optuna.trial.TrialState.FAIL\n                    study.tell(trial=trial, state=state, values=values)\n                    raise e\n\n            n_trials_to_go -= batch_size\n\n        results_to_serialize: Dict[str, Any]\n        if len(directions) < 2:\n            best_trial = study.best_trial\n            results_to_serialize = {\n                \"name\": \"optuna\",\n                \"best_params\": best_trial.params,\n                \"best_value\": best_trial.value,\n            }\n            log.info(f\"Best parameters: {best_trial.params}\")\n            log.info(f\"Best value: {best_trial.value}\")\n        else:\n            best_trials = study.best_trials\n            pareto_front = [\n                {\"params\": t.params, \"values\": t.values} for t in best_trials\n            ]\n            results_to_serialize = {\n                \"name\": \"optuna\",\n                \"solutions\": pareto_front,\n            }\n            log.info(f\"Number of Pareto solutions: {len(best_trials)}\")\n            for t in best_trials:\n                log.info(f\"    Values: {t.values}, Params: {t.params}\")\n        OmegaConf.save(\n            OmegaConf.create(results_to_serialize),\n            f\"{self.config.hydra.sweep.dir}/optimization_results.yaml\",\n        )\n", "path": "plugins/hydra_optuna_sweeper/hydra_plugins/hydra_optuna_sweeper/_impl.py"}]}