We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[Bug]: Nevergrad sweeper does not work with integers if there are less than 6 choices
Nevergrad sweeper complains if it has less than 6 values to sweep over in a range (e.g. `lower: 1` `upper:3`) and asks to use a list instead (`ValueError: For integers with 6 or fewer values, use a choice instead`). But if you use a list with integers it does not work because it assumes that choices contain only strings:

![Screen Shot 2020-08-15 at 7 42 39 PM](https://user-images.githubusercontent.com/24327668/90323444-84a3b680-df2f-11ea-8d77-1ce82ab9c7c6.png)

Line where the first error is raised: https://github.com/facebookresearch/hydra/blob/0e001afb2a55275b6f7dc33e79035dbf3a797c00/plugins/hydra_nevergrad_sweeper/hydra_plugins/hydra_nevergrad_sweeper/core.py#L178
Hydra Version: 1.0.0rc2


Of course I can give a string and then convert in my code, but it would probably be better to solve it differently? For example sliently treating it as a list without raising the first error ? Or at least to say in the raised error that you have to use a list and convert the str to int in your own code ? Not sure what is the best way..



--- 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_nevergrad_sweeper/hydra_plugins/hydra_nevergrad_sweeper/core.py`
Content:
```
1 # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2 import itertools
3 import logging
4 from dataclasses import dataclass
5 from typing import Any, Dict, List, Optional, Tuple
6 
7 from hydra.core.config_loader import ConfigLoader
8 from hydra.core.plugins import Plugins
9 from hydra.plugins.launcher import Launcher
10 from hydra.plugins.sweeper import Sweeper
11 from hydra.types import TaskFunction
12 from omegaconf import DictConfig, ListConfig, OmegaConf
13 
14 from .config import OptimConf, ScalarConfigSpec
15 
16 # pylint: disable=logging-fstring-interpolation,no-self-used
17 log = logging.getLogger(__name__)
18 
19 
20 @dataclass
21 class CommandlineSpec:
22     """Structured commandline specification
23     for sweepers handling categorical variables and bounded variables
24 
25     Attributes
26     ----------
27     bounds: Optional[Tuple[float, float]]
28         if present, this defines a bounded scalar between bounds[0]
29         and bounds[1]
30     options: Optional[List[Any]]
31         if present, this defines the options/choices of a categorical
32         variable
33     cast: str
34         the name of the variable type to cast it to ("int", "str"
35         or "float")
36     log: bool
37         for bounded scalars, whether it is log-distributed
38 
39     Note
40     ----
41     Exactly one of bounds or options must be provided
42     """
43 
44     bounds: Optional[Tuple[float, float]] = None
45     options: Optional[List[str]] = None
46     cast: str = "float"
47     log: bool = False
48 
49     def __post_init__(self) -> None:
50         if not (self.bounds is None) ^ (self.options is None):
51             raise ValueError("Exactly one of bounds or options must be specified")
52         if self.bounds is not None:
53             if self.cast == "str":
54                 raise ValueError(
55                     "Inconsistent specifications 'str' for bounded values."
56                 )
57             if self.bounds[0] > self.bounds[1]:
58                 raise ValueError(f"Bounds must be ordered, but got {self.bounds}")
59         if self.options is not None and self.log:
60             raise ValueError("Inconsistent 'log' specification for choice parameter")
61 
62     @classmethod
63     def parse(cls, string: str) -> "CommandlineSpec":
64         """Parses a commandline argument string
65 
66         Parameter
67         ---------
68         string: str
69             This can be:
70              - comma-separated values: for a choice parameter
71                Eg.: "a,b,c"
72              - colon-separated values for ranges of scalars.
73                Eg.: "0:10"
74             Colon-separeted can be appended to:
75              - cast to int/str/float (always defaults to float):
76                Eg: "float:0,4,10", "int:0:10"
77              - set log distribution for scalars
78                Eg: "int:log:4:1024"
79         """
80         available_modifiers = {"log", "float", "int", "str"}
81         colon_split = string.split(":")
82         modifiers = set(
83             itertools.takewhile(available_modifiers.__contains__, colon_split)
84         )
85         remain = colon_split[len(modifiers) :]
86         casts = list(modifiers - {"log"})
87         if len(remain) not in {1, 2}:
88             raise ValueError(
89                 "Can't interpret non-speficiations: {}.\nthis needs to be "
90                 "either colon or coma-separated values".format(":".join(remain))
91             )
92         if len(casts) > 1:
93             raise ValueError(f"Inconsistent specifications: {casts}")
94         if len(remain) == 1:  # choice argument
95             cast = casts[0] if casts else "str"
96             options = remain[0].split(",")
97             if len(options) < 2:
98                 raise ValueError("At least 2 options are required")
99             if not casts:
100                 try:  # default to float if possible and no spec provided
101                     _ = [float(x) for x in options]
102                     cast = "float"
103                 except ValueError:
104                     pass
105             return cls(options=options, cast=cast)
106         # bounded argument
107         bounds: Tuple[float, float] = tuple(float(x) for x in remain)  # type: ignore
108         cast = casts[0] if casts else "float"
109         return cls(bounds=bounds, cast=cast, log="log" in modifiers)
110 
111 
112 # pylint: disable=too-many-branches
113 def make_nevergrad_parameter(description: Any) -> Any:
114     """Returns a Nevergrad parameter from a definition string or object.
115 
116     Parameters
117     ----------
118     description: Any
119        * a commandline definition string. This can be:
120          - comma-separated values: for a choice parameter
121            Eg.: "a,b,c"
122            Note: sequences of increasing scalars provide a specific parametrization
123              compared to unordered categorical values
124          - ":"-separated values for ranges of scalars.
125            "int" and/or "log" modifiers can be added in front to cast to integer or
126            use log-distributed values (Eg: int:log:4:1024)
127          - anything else will be treated as a constant string
128        * a config definition dict for scalar parameters, with potential fields
129          init, lower, upper, step, log, integer
130        * a list for option parameters defined in config file
131 
132     Returns
133     -------
134     Parameter or str
135         A Parameter if the string fitted one of the definitions, else the input string
136     """
137     # lazy initialization to avoid overhead when loading hydra
138     import nevergrad as ng
139 
140     # revert config parsing
141 
142     if isinstance(description, (ListConfig, list)):
143         description = ",".join(description)
144     if isinstance(description, str):
145         # cast to spec if possible
146         try:
147             description = CommandlineSpec.parse(description)
148         except ValueError:
149             pass
150     # convert scalar commandline specs to dict
151     if isinstance(description, CommandlineSpec) and description.bounds is not None:
152         description = ScalarConfigSpec(
153             lower=description.bounds[0],
154             upper=description.bounds[1],
155             log=description.log,
156             integer=description.cast == "int",
157         )
158     # convert scalar config specs to dict
159     # convert dict to Scalar parameter instance
160     if isinstance(description, (dict, DictConfig)):
161         description = ScalarConfigSpec(**description)
162     if isinstance(description, ScalarConfigSpec):
163         init = ["init", "lower", "upper"]
164         init_params = {x: getattr(description, x) for x in init}
165         if not description.log:
166             scalar = ng.p.Scalar(**init_params)
167             if description.step is not None:
168                 scalar.set_mutation(sigma=description.step)
169         else:
170             if description.step is not None:
171                 init_params["exponent"] = description.step
172             scalar = ng.p.Log(**init_params)
173         if description.integer:
174             scalar.set_integer_casting()
175             a, b = scalar.bounds
176             if a is not None and b is not None and b - a <= 6:
177                 raise ValueError(
178                     "For integers with 6 or fewer values, use a choice instead"
179                 )
180         return scalar
181     # choices
182     if isinstance(description, CommandlineSpec):
183         assert description.options is not None
184         caster = {"int": int, "str": str, "float": float}[description.cast]
185         choices = [caster(x) for x in description.options]
186         ordered = all(isinstance(c, (int, float)) for c in choices)
187         ordered &= all(c0 <= c1 for c0, c1 in zip(choices[:-1], choices[1:]))
188         return ng.p.TransitionChoice(choices) if ordered else ng.p.Choice(choices)
189     # constant
190     if isinstance(description, (str, int, float)):
191         return description
192     raise TypeError(f"Unexpected parameter configuration: {description}")
193 
194 
195 class NevergradSweeper(Sweeper):
196     """Returns a Nevergrad parameter from a definition string.
197 
198     Parameters
199     ----------
200     config: DictConfig
201       the optimization process configuration
202     version: int
203       version of the API
204     """
205 
206     def __init__(
207         self, optim: OptimConf, version: int, parametrization: Optional[DictConfig],
208     ):
209         assert (
210             version == 1
211         ), f"Only version 1 of API is currently available (got {version})"
212         self.opt_config = optim
213         self.config: Optional[DictConfig] = None
214         self.launcher: Optional[Launcher] = None
215         self.job_results = None
216         self.parametrization: Dict[str, Any] = {}
217         if parametrization is not None:
218             assert isinstance(parametrization, DictConfig)
219             self.parametrization = {
220                 x: make_nevergrad_parameter(y) for x, y in parametrization.items()
221             }
222         self.job_idx: Optional[int] = None
223 
224     def setup(
225         self,
226         config: DictConfig,
227         config_loader: ConfigLoader,
228         task_function: TaskFunction,
229     ) -> None:
230         self.job_idx = 0
231         self.config = config
232         self.config_loader = config_loader
233         self.launcher = Plugins.instance().instantiate_launcher(
234             config=config, config_loader=config_loader, task_function=task_function
235         )
236 
237     def sweep(self, arguments: List[str]) -> None:
238         # lazy initialization to avoid overhead when loading hydra
239         import nevergrad as ng
240 
241         assert self.config is not None
242         assert self.launcher is not None
243         assert self.job_idx is not None
244         direction = -1 if self.opt_config.maximize else 1
245         name = "maximization" if self.opt_config.maximize else "minimization"
246         # Override the parametrization from commandline
247         params = dict(self.parametrization)
248         for s in arguments:
249             key, value = s.split("=", 1)
250             params[key] = make_nevergrad_parameter(value)
251         parametrization = ng.p.Dict(**params)
252         parametrization.descriptors.deterministic_function = not self.opt_config.noisy
253         parametrization.random_state.seed(self.opt_config.seed)
254         # log and build the optimizer
255         opt = self.opt_config.optimizer
256         remaining_budget = self.opt_config.budget
257         nw = self.opt_config.num_workers
258         log.info(
259             f"NevergradSweeper(optimizer={opt}, budget={remaining_budget}, "
260             f"num_workers={nw}) {name}"
261         )
262         log.info(f"with parametrization {parametrization}")
263         log.info(f"Sweep output dir: {self.config.hydra.sweep.dir}")
264         optimizer = ng.optimizers.registry[opt](parametrization, remaining_budget, nw)
265         # loop!
266         all_returns: List[Any] = []
267         best: Tuple[float, ng.p.Parameter] = (float("inf"), parametrization)
268         while remaining_budget > 0:
269             batch = min(nw, remaining_budget)
270             remaining_budget -= batch
271             candidates = [optimizer.ask() for _ in range(batch)]
272             overrides = list(
273                 tuple(f"{x}={y}" for x, y in c.value.items()) for c in candidates
274             )
275             self.validate_batch_is_legal(overrides)
276             returns = self.launcher.launch(overrides, initial_job_idx=self.job_idx)
277             self.job_idx += len(returns)
278             # would have been nice to avoid waiting for all jobs to finish
279             # aka batch size Vs steady state (launching a new job whenever one is done)
280             for cand, ret in zip(candidates, returns):
281                 loss = direction * ret.return_value
282                 optimizer.tell(cand, loss)
283                 if loss < best[0]:
284                     best = (loss, cand)
285             all_returns.extend(returns)
286         recom = optimizer.provide_recommendation()
287         results_to_serialize = {
288             "name": "nevergrad",
289             "best_evaluated_params": best[1].value,
290             "best_evaluated_result": direction * best[0],
291         }
292         OmegaConf.save(
293             OmegaConf.create(results_to_serialize),
294             f"{self.config.hydra.sweep.dir}/optimization_results.yaml",
295         )
296         log.info(
297             "Best parameters: %s", " ".join(f"{x}={y}" for x, y in recom.value.items())
298         )
299
```
--- 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_nevergrad_sweeper/hydra_plugins/hydra_nevergrad_sweeper/core.py b/plugins/hydra_nevergrad_sweeper/hydra_plugins/hydra_nevergrad_sweeper/core.py
--- a/plugins/hydra_nevergrad_sweeper/hydra_plugins/hydra_nevergrad_sweeper/core.py
+++ b/plugins/hydra_nevergrad_sweeper/hydra_plugins/hydra_nevergrad_sweeper/core.py
@@ -140,7 +140,7 @@
     # revert config parsing
 
     if isinstance(description, (ListConfig, list)):
-        description = ",".join(description)
+        description = ",".join(str(x) for x in description)
     if isinstance(description, str):
         # cast to spec if possible
         try:

{"golden_diff": "diff --git a/plugins/hydra_nevergrad_sweeper/hydra_plugins/hydra_nevergrad_sweeper/core.py b/plugins/hydra_nevergrad_sweeper/hydra_plugins/hydra_nevergrad_sweeper/core.py\n--- a/plugins/hydra_nevergrad_sweeper/hydra_plugins/hydra_nevergrad_sweeper/core.py\n+++ b/plugins/hydra_nevergrad_sweeper/hydra_plugins/hydra_nevergrad_sweeper/core.py\n@@ -140,7 +140,7 @@\n     # revert config parsing\n \n     if isinstance(description, (ListConfig, list)):\n-        description = \",\".join(description)\n+        description = \",\".join(str(x) for x in description)\n     if isinstance(description, str):\n         # cast to spec if possible\n         try:\n", "issue": "[Bug]: Nevergrad sweeper does not work with integers if there are less than 6 choices\nNevergrad sweeper complains if it has less than 6 values to sweep over in a range (e.g. `lower: 1` `upper:3`) and asks to use a list instead (`ValueError: For integers with 6 or fewer values, use a choice instead`). But if you use a list with integers it does not work because it assumes that choices contain only strings:\r\n\r\n![Screen Shot 2020-08-15 at 7 42 39 PM](https://user-images.githubusercontent.com/24327668/90323444-84a3b680-df2f-11ea-8d77-1ce82ab9c7c6.png)\r\n\r\nLine where the first error is raised: https://github.com/facebookresearch/hydra/blob/0e001afb2a55275b6f7dc33e79035dbf3a797c00/plugins/hydra_nevergrad_sweeper/hydra_plugins/hydra_nevergrad_sweeper/core.py#L178\r\nHydra Version: 1.0.0rc2\r\n\r\n\r\nOf course I can give a string and then convert in my code, but it would probably be better to solve it differently? For example sliently treating it as a list without raising the first error ? Or at least to say in the raised error that you have to use a list and convert the str to int in your own code ? Not sure what is the best way..\r\n\r\n\n", "before_files": [{"content": "# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\nimport itertools\nimport logging\nfrom dataclasses import dataclass\nfrom typing import Any, Dict, List, Optional, Tuple\n\nfrom hydra.core.config_loader import ConfigLoader\nfrom hydra.core.plugins import Plugins\nfrom hydra.plugins.launcher import Launcher\nfrom hydra.plugins.sweeper import Sweeper\nfrom hydra.types import TaskFunction\nfrom omegaconf import DictConfig, ListConfig, OmegaConf\n\nfrom .config import OptimConf, ScalarConfigSpec\n\n# pylint: disable=logging-fstring-interpolation,no-self-used\nlog = logging.getLogger(__name__)\n\n\n@dataclass\nclass CommandlineSpec:\n    \"\"\"Structured commandline specification\n    for sweepers handling categorical variables and bounded variables\n\n    Attributes\n    ----------\n    bounds: Optional[Tuple[float, float]]\n        if present, this defines a bounded scalar between bounds[0]\n        and bounds[1]\n    options: Optional[List[Any]]\n        if present, this defines the options/choices of a categorical\n        variable\n    cast: str\n        the name of the variable type to cast it to (\"int\", \"str\"\n        or \"float\")\n    log: bool\n        for bounded scalars, whether it is log-distributed\n\n    Note\n    ----\n    Exactly one of bounds or options must be provided\n    \"\"\"\n\n    bounds: Optional[Tuple[float, float]] = None\n    options: Optional[List[str]] = None\n    cast: str = \"float\"\n    log: bool = False\n\n    def __post_init__(self) -> None:\n        if not (self.bounds is None) ^ (self.options is None):\n            raise ValueError(\"Exactly one of bounds or options must be specified\")\n        if self.bounds is not None:\n            if self.cast == \"str\":\n                raise ValueError(\n                    \"Inconsistent specifications 'str' for bounded values.\"\n                )\n            if self.bounds[0] > self.bounds[1]:\n                raise ValueError(f\"Bounds must be ordered, but got {self.bounds}\")\n        if self.options is not None and self.log:\n            raise ValueError(\"Inconsistent 'log' specification for choice parameter\")\n\n    @classmethod\n    def parse(cls, string: str) -> \"CommandlineSpec\":\n        \"\"\"Parses a commandline argument string\n\n        Parameter\n        ---------\n        string: str\n            This can be:\n             - comma-separated values: for a choice parameter\n               Eg.: \"a,b,c\"\n             - colon-separated values for ranges of scalars.\n               Eg.: \"0:10\"\n            Colon-separeted can be appended to:\n             - cast to int/str/float (always defaults to float):\n               Eg: \"float:0,4,10\", \"int:0:10\"\n             - set log distribution for scalars\n               Eg: \"int:log:4:1024\"\n        \"\"\"\n        available_modifiers = {\"log\", \"float\", \"int\", \"str\"}\n        colon_split = string.split(\":\")\n        modifiers = set(\n            itertools.takewhile(available_modifiers.__contains__, colon_split)\n        )\n        remain = colon_split[len(modifiers) :]\n        casts = list(modifiers - {\"log\"})\n        if len(remain) not in {1, 2}:\n            raise ValueError(\n                \"Can't interpret non-speficiations: {}.\\nthis needs to be \"\n                \"either colon or coma-separated values\".format(\":\".join(remain))\n            )\n        if len(casts) > 1:\n            raise ValueError(f\"Inconsistent specifications: {casts}\")\n        if len(remain) == 1:  # choice argument\n            cast = casts[0] if casts else \"str\"\n            options = remain[0].split(\",\")\n            if len(options) < 2:\n                raise ValueError(\"At least 2 options are required\")\n            if not casts:\n                try:  # default to float if possible and no spec provided\n                    _ = [float(x) for x in options]\n                    cast = \"float\"\n                except ValueError:\n                    pass\n            return cls(options=options, cast=cast)\n        # bounded argument\n        bounds: Tuple[float, float] = tuple(float(x) for x in remain)  # type: ignore\n        cast = casts[0] if casts else \"float\"\n        return cls(bounds=bounds, cast=cast, log=\"log\" in modifiers)\n\n\n# pylint: disable=too-many-branches\ndef make_nevergrad_parameter(description: Any) -> Any:\n    \"\"\"Returns a Nevergrad parameter from a definition string or object.\n\n    Parameters\n    ----------\n    description: Any\n       * a commandline definition string. This can be:\n         - comma-separated values: for a choice parameter\n           Eg.: \"a,b,c\"\n           Note: sequences of increasing scalars provide a specific parametrization\n             compared to unordered categorical values\n         - \":\"-separated values for ranges of scalars.\n           \"int\" and/or \"log\" modifiers can be added in front to cast to integer or\n           use log-distributed values (Eg: int:log:4:1024)\n         - anything else will be treated as a constant string\n       * a config definition dict for scalar parameters, with potential fields\n         init, lower, upper, step, log, integer\n       * a list for option parameters defined in config file\n\n    Returns\n    -------\n    Parameter or str\n        A Parameter if the string fitted one of the definitions, else the input string\n    \"\"\"\n    # lazy initialization to avoid overhead when loading hydra\n    import nevergrad as ng\n\n    # revert config parsing\n\n    if isinstance(description, (ListConfig, list)):\n        description = \",\".join(description)\n    if isinstance(description, str):\n        # cast to spec if possible\n        try:\n            description = CommandlineSpec.parse(description)\n        except ValueError:\n            pass\n    # convert scalar commandline specs to dict\n    if isinstance(description, CommandlineSpec) and description.bounds is not None:\n        description = ScalarConfigSpec(\n            lower=description.bounds[0],\n            upper=description.bounds[1],\n            log=description.log,\n            integer=description.cast == \"int\",\n        )\n    # convert scalar config specs to dict\n    # convert dict to Scalar parameter instance\n    if isinstance(description, (dict, DictConfig)):\n        description = ScalarConfigSpec(**description)\n    if isinstance(description, ScalarConfigSpec):\n        init = [\"init\", \"lower\", \"upper\"]\n        init_params = {x: getattr(description, x) for x in init}\n        if not description.log:\n            scalar = ng.p.Scalar(**init_params)\n            if description.step is not None:\n                scalar.set_mutation(sigma=description.step)\n        else:\n            if description.step is not None:\n                init_params[\"exponent\"] = description.step\n            scalar = ng.p.Log(**init_params)\n        if description.integer:\n            scalar.set_integer_casting()\n            a, b = scalar.bounds\n            if a is not None and b is not None and b - a <= 6:\n                raise ValueError(\n                    \"For integers with 6 or fewer values, use a choice instead\"\n                )\n        return scalar\n    # choices\n    if isinstance(description, CommandlineSpec):\n        assert description.options is not None\n        caster = {\"int\": int, \"str\": str, \"float\": float}[description.cast]\n        choices = [caster(x) for x in description.options]\n        ordered = all(isinstance(c, (int, float)) for c in choices)\n        ordered &= all(c0 <= c1 for c0, c1 in zip(choices[:-1], choices[1:]))\n        return ng.p.TransitionChoice(choices) if ordered else ng.p.Choice(choices)\n    # constant\n    if isinstance(description, (str, int, float)):\n        return description\n    raise TypeError(f\"Unexpected parameter configuration: {description}\")\n\n\nclass NevergradSweeper(Sweeper):\n    \"\"\"Returns a Nevergrad parameter from a definition string.\n\n    Parameters\n    ----------\n    config: DictConfig\n      the optimization process configuration\n    version: int\n      version of the API\n    \"\"\"\n\n    def __init__(\n        self, optim: OptimConf, version: int, parametrization: Optional[DictConfig],\n    ):\n        assert (\n            version == 1\n        ), f\"Only version 1 of API is currently available (got {version})\"\n        self.opt_config = optim\n        self.config: Optional[DictConfig] = None\n        self.launcher: Optional[Launcher] = None\n        self.job_results = None\n        self.parametrization: Dict[str, Any] = {}\n        if parametrization is not None:\n            assert isinstance(parametrization, DictConfig)\n            self.parametrization = {\n                x: make_nevergrad_parameter(y) for x, y in parametrization.items()\n            }\n        self.job_idx: Optional[int] = None\n\n    def setup(\n        self,\n        config: DictConfig,\n        config_loader: ConfigLoader,\n        task_function: TaskFunction,\n    ) -> None:\n        self.job_idx = 0\n        self.config = config\n        self.config_loader = config_loader\n        self.launcher = Plugins.instance().instantiate_launcher(\n            config=config, config_loader=config_loader, task_function=task_function\n        )\n\n    def sweep(self, arguments: List[str]) -> None:\n        # lazy initialization to avoid overhead when loading hydra\n        import nevergrad as ng\n\n        assert self.config is not None\n        assert self.launcher is not None\n        assert self.job_idx is not None\n        direction = -1 if self.opt_config.maximize else 1\n        name = \"maximization\" if self.opt_config.maximize else \"minimization\"\n        # Override the parametrization from commandline\n        params = dict(self.parametrization)\n        for s in arguments:\n            key, value = s.split(\"=\", 1)\n            params[key] = make_nevergrad_parameter(value)\n        parametrization = ng.p.Dict(**params)\n        parametrization.descriptors.deterministic_function = not self.opt_config.noisy\n        parametrization.random_state.seed(self.opt_config.seed)\n        # log and build the optimizer\n        opt = self.opt_config.optimizer\n        remaining_budget = self.opt_config.budget\n        nw = self.opt_config.num_workers\n        log.info(\n            f\"NevergradSweeper(optimizer={opt}, budget={remaining_budget}, \"\n            f\"num_workers={nw}) {name}\"\n        )\n        log.info(f\"with parametrization {parametrization}\")\n        log.info(f\"Sweep output dir: {self.config.hydra.sweep.dir}\")\n        optimizer = ng.optimizers.registry[opt](parametrization, remaining_budget, nw)\n        # loop!\n        all_returns: List[Any] = []\n        best: Tuple[float, ng.p.Parameter] = (float(\"inf\"), parametrization)\n        while remaining_budget > 0:\n            batch = min(nw, remaining_budget)\n            remaining_budget -= batch\n            candidates = [optimizer.ask() for _ in range(batch)]\n            overrides = list(\n                tuple(f\"{x}={y}\" for x, y in c.value.items()) for c in candidates\n            )\n            self.validate_batch_is_legal(overrides)\n            returns = self.launcher.launch(overrides, initial_job_idx=self.job_idx)\n            self.job_idx += len(returns)\n            # would have been nice to avoid waiting for all jobs to finish\n            # aka batch size Vs steady state (launching a new job whenever one is done)\n            for cand, ret in zip(candidates, returns):\n                loss = direction * ret.return_value\n                optimizer.tell(cand, loss)\n                if loss < best[0]:\n                    best = (loss, cand)\n            all_returns.extend(returns)\n        recom = optimizer.provide_recommendation()\n        results_to_serialize = {\n            \"name\": \"nevergrad\",\n            \"best_evaluated_params\": best[1].value,\n            \"best_evaluated_result\": direction * best[0],\n        }\n        OmegaConf.save(\n            OmegaConf.create(results_to_serialize),\n            f\"{self.config.hydra.sweep.dir}/optimization_results.yaml\",\n        )\n        log.info(\n            \"Best parameters: %s\", \" \".join(f\"{x}={y}\" for x, y in recom.value.items())\n        )\n", "path": "plugins/hydra_nevergrad_sweeper/hydra_plugins/hydra_nevergrad_sweeper/core.py"}], "after_files": [{"content": "# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\nimport itertools\nimport logging\nfrom dataclasses import dataclass\nfrom typing import Any, Dict, List, Optional, Tuple\n\nfrom hydra.core.config_loader import ConfigLoader\nfrom hydra.core.plugins import Plugins\nfrom hydra.plugins.launcher import Launcher\nfrom hydra.plugins.sweeper import Sweeper\nfrom hydra.types import TaskFunction\nfrom omegaconf import DictConfig, ListConfig, OmegaConf\n\nfrom .config import OptimConf, ScalarConfigSpec\n\n# pylint: disable=logging-fstring-interpolation,no-self-used\nlog = logging.getLogger(__name__)\n\n\n@dataclass\nclass CommandlineSpec:\n    \"\"\"Structured commandline specification\n    for sweepers handling categorical variables and bounded variables\n\n    Attributes\n    ----------\n    bounds: Optional[Tuple[float, float]]\n        if present, this defines a bounded scalar between bounds[0]\n        and bounds[1]\n    options: Optional[List[Any]]\n        if present, this defines the options/choices of a categorical\n        variable\n    cast: str\n        the name of the variable type to cast it to (\"int\", \"str\"\n        or \"float\")\n    log: bool\n        for bounded scalars, whether it is log-distributed\n\n    Note\n    ----\n    Exactly one of bounds or options must be provided\n    \"\"\"\n\n    bounds: Optional[Tuple[float, float]] = None\n    options: Optional[List[str]] = None\n    cast: str = \"float\"\n    log: bool = False\n\n    def __post_init__(self) -> None:\n        if not (self.bounds is None) ^ (self.options is None):\n            raise ValueError(\"Exactly one of bounds or options must be specified\")\n        if self.bounds is not None:\n            if self.cast == \"str\":\n                raise ValueError(\n                    \"Inconsistent specifications 'str' for bounded values.\"\n                )\n            if self.bounds[0] > self.bounds[1]:\n                raise ValueError(f\"Bounds must be ordered, but got {self.bounds}\")\n        if self.options is not None and self.log:\n            raise ValueError(\"Inconsistent 'log' specification for choice parameter\")\n\n    @classmethod\n    def parse(cls, string: str) -> \"CommandlineSpec\":\n        \"\"\"Parses a commandline argument string\n\n        Parameter\n        ---------\n        string: str\n            This can be:\n             - comma-separated values: for a choice parameter\n               Eg.: \"a,b,c\"\n             - colon-separated values for ranges of scalars.\n               Eg.: \"0:10\"\n            Colon-separeted can be appended to:\n             - cast to int/str/float (always defaults to float):\n               Eg: \"float:0,4,10\", \"int:0:10\"\n             - set log distribution for scalars\n               Eg: \"int:log:4:1024\"\n        \"\"\"\n        available_modifiers = {\"log\", \"float\", \"int\", \"str\"}\n        colon_split = string.split(\":\")\n        modifiers = set(\n            itertools.takewhile(available_modifiers.__contains__, colon_split)\n        )\n        remain = colon_split[len(modifiers) :]\n        casts = list(modifiers - {\"log\"})\n        if len(remain) not in {1, 2}:\n            raise ValueError(\n                \"Can't interpret non-speficiations: {}.\\nthis needs to be \"\n                \"either colon or coma-separated values\".format(\":\".join(remain))\n            )\n        if len(casts) > 1:\n            raise ValueError(f\"Inconsistent specifications: {casts}\")\n        if len(remain) == 1:  # choice argument\n            cast = casts[0] if casts else \"str\"\n            options = remain[0].split(\",\")\n            if len(options) < 2:\n                raise ValueError(\"At least 2 options are required\")\n            if not casts:\n                try:  # default to float if possible and no spec provided\n                    _ = [float(x) for x in options]\n                    cast = \"float\"\n                except ValueError:\n                    pass\n            return cls(options=options, cast=cast)\n        # bounded argument\n        bounds: Tuple[float, float] = tuple(float(x) for x in remain)  # type: ignore\n        cast = casts[0] if casts else \"float\"\n        return cls(bounds=bounds, cast=cast, log=\"log\" in modifiers)\n\n\n# pylint: disable=too-many-branches\ndef make_nevergrad_parameter(description: Any) -> Any:\n    \"\"\"Returns a Nevergrad parameter from a definition string or object.\n\n    Parameters\n    ----------\n    description: Any\n       * a commandline definition string. This can be:\n         - comma-separated values: for a choice parameter\n           Eg.: \"a,b,c\"\n           Note: sequences of increasing scalars provide a specific parametrization\n             compared to unordered categorical values\n         - \":\"-separated values for ranges of scalars.\n           \"int\" and/or \"log\" modifiers can be added in front to cast to integer or\n           use log-distributed values (Eg: int:log:4:1024)\n         - anything else will be treated as a constant string\n       * a config definition dict for scalar parameters, with potential fields\n         init, lower, upper, step, log, integer\n       * a list for option parameters defined in config file\n\n    Returns\n    -------\n    Parameter or str\n        A Parameter if the string fitted one of the definitions, else the input string\n    \"\"\"\n    # lazy initialization to avoid overhead when loading hydra\n    import nevergrad as ng\n\n    # revert config parsing\n\n    if isinstance(description, (ListConfig, list)):\n        description = \",\".join(str(x) for x in description)\n    if isinstance(description, str):\n        # cast to spec if possible\n        try:\n            description = CommandlineSpec.parse(description)\n        except ValueError:\n            pass\n    # convert scalar commandline specs to dict\n    if isinstance(description, CommandlineSpec) and description.bounds is not None:\n        description = ScalarConfigSpec(\n            lower=description.bounds[0],\n            upper=description.bounds[1],\n            log=description.log,\n            integer=description.cast == \"int\",\n        )\n    # convert scalar config specs to dict\n    # convert dict to Scalar parameter instance\n    if isinstance(description, (dict, DictConfig)):\n        description = ScalarConfigSpec(**description)\n    if isinstance(description, ScalarConfigSpec):\n        init = [\"init\", \"lower\", \"upper\"]\n        init_params = {x: getattr(description, x) for x in init}\n        if not description.log:\n            scalar = ng.p.Scalar(**init_params)\n            if description.step is not None:\n                scalar.set_mutation(sigma=description.step)\n        else:\n            if description.step is not None:\n                init_params[\"exponent\"] = description.step\n            scalar = ng.p.Log(**init_params)\n        if description.integer:\n            scalar.set_integer_casting()\n            a, b = scalar.bounds\n            if a is not None and b is not None and b - a <= 6:\n                raise ValueError(\n                    \"For integers with 6 or fewer values, use a choice instead\"\n                )\n        return scalar\n    # choices\n    if isinstance(description, CommandlineSpec):\n        assert description.options is not None\n        caster = {\"int\": int, \"str\": str, \"float\": float}[description.cast]\n        choices = [caster(x) for x in description.options]\n        ordered = all(isinstance(c, (int, float)) for c in choices)\n        ordered &= all(c0 <= c1 for c0, c1 in zip(choices[:-1], choices[1:]))\n        return ng.p.TransitionChoice(choices) if ordered else ng.p.Choice(choices)\n    # constant\n    if isinstance(description, (str, int, float)):\n        return description\n    raise TypeError(f\"Unexpected parameter configuration: {description}\")\n\n\nclass NevergradSweeper(Sweeper):\n    \"\"\"Returns a Nevergrad parameter from a definition string.\n\n    Parameters\n    ----------\n    config: DictConfig\n      the optimization process configuration\n    version: int\n      version of the API\n    \"\"\"\n\n    def __init__(\n        self, optim: OptimConf, version: int, parametrization: Optional[DictConfig],\n    ):\n        assert (\n            version == 1\n        ), f\"Only version 1 of API is currently available (got {version})\"\n        self.opt_config = optim\n        self.config: Optional[DictConfig] = None\n        self.launcher: Optional[Launcher] = None\n        self.job_results = None\n        self.parametrization: Dict[str, Any] = {}\n        if parametrization is not None:\n            assert isinstance(parametrization, DictConfig)\n            self.parametrization = {\n                x: make_nevergrad_parameter(y) for x, y in parametrization.items()\n            }\n        self.job_idx: Optional[int] = None\n\n    def setup(\n        self,\n        config: DictConfig,\n        config_loader: ConfigLoader,\n        task_function: TaskFunction,\n    ) -> None:\n        self.job_idx = 0\n        self.config = config\n        self.config_loader = config_loader\n        self.launcher = Plugins.instance().instantiate_launcher(\n            config=config, config_loader=config_loader, task_function=task_function\n        )\n\n    def sweep(self, arguments: List[str]) -> None:\n        # lazy initialization to avoid overhead when loading hydra\n        import nevergrad as ng\n\n        assert self.config is not None\n        assert self.launcher is not None\n        assert self.job_idx is not None\n        direction = -1 if self.opt_config.maximize else 1\n        name = \"maximization\" if self.opt_config.maximize else \"minimization\"\n        # Override the parametrization from commandline\n        params = dict(self.parametrization)\n        for s in arguments:\n            key, value = s.split(\"=\", 1)\n            params[key] = make_nevergrad_parameter(value)\n        parametrization = ng.p.Dict(**params)\n        parametrization.descriptors.deterministic_function = not self.opt_config.noisy\n        parametrization.random_state.seed(self.opt_config.seed)\n        # log and build the optimizer\n        opt = self.opt_config.optimizer\n        remaining_budget = self.opt_config.budget\n        nw = self.opt_config.num_workers\n        log.info(\n            f\"NevergradSweeper(optimizer={opt}, budget={remaining_budget}, \"\n            f\"num_workers={nw}) {name}\"\n        )\n        log.info(f\"with parametrization {parametrization}\")\n        log.info(f\"Sweep output dir: {self.config.hydra.sweep.dir}\")\n        optimizer = ng.optimizers.registry[opt](parametrization, remaining_budget, nw)\n        # loop!\n        all_returns: List[Any] = []\n        best: Tuple[float, ng.p.Parameter] = (float(\"inf\"), parametrization)\n        while remaining_budget > 0:\n            batch = min(nw, remaining_budget)\n            remaining_budget -= batch\n            candidates = [optimizer.ask() for _ in range(batch)]\n            overrides = list(\n                tuple(f\"{x}={y}\" for x, y in c.value.items()) for c in candidates\n            )\n            self.validate_batch_is_legal(overrides)\n            returns = self.launcher.launch(overrides, initial_job_idx=self.job_idx)\n            self.job_idx += len(returns)\n            # would have been nice to avoid waiting for all jobs to finish\n            # aka batch size Vs steady state (launching a new job whenever one is done)\n            for cand, ret in zip(candidates, returns):\n                loss = direction * ret.return_value\n                optimizer.tell(cand, loss)\n                if loss < best[0]:\n                    best = (loss, cand)\n            all_returns.extend(returns)\n        recom = optimizer.provide_recommendation()\n        results_to_serialize = {\n            \"name\": \"nevergrad\",\n            \"best_evaluated_params\": best[1].value,\n            \"best_evaluated_result\": direction * best[0],\n        }\n        OmegaConf.save(\n            OmegaConf.create(results_to_serialize),\n            f\"{self.config.hydra.sweep.dir}/optimization_results.yaml\",\n        )\n        log.info(\n            \"Best parameters: %s\", \" \".join(f\"{x}={y}\" for x, y in recom.value.items())\n        )\n", "path": "plugins/hydra_nevergrad_sweeper/hydra_plugins/hydra_nevergrad_sweeper/core.py"}]}