hydra-optuna-sweeper 1.3.0.dev0__tar.gz → 1.4.0.dev5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hydra_optuna_sweeper-1.4.0.dev5/MANIFEST.in +3 -0
- {hydra-optuna-sweeper-1.3.0.dev0 → hydra_optuna_sweeper-1.4.0.dev5}/PKG-INFO +21 -7
- {hydra-optuna-sweeper-1.3.0.dev0 → hydra_optuna_sweeper-1.4.0.dev5}/hydra_optuna_sweeper.egg-info/PKG-INFO +21 -7
- {hydra-optuna-sweeper-1.3.0.dev0 → hydra_optuna_sweeper-1.4.0.dev5}/hydra_optuna_sweeper.egg-info/SOURCES.txt +3 -1
- {hydra-optuna-sweeper-1.3.0.dev0 → hydra_optuna_sweeper-1.4.0.dev5}/hydra_optuna_sweeper.egg-info/requires.txt +1 -0
- {hydra-optuna-sweeper-1.3.0.dev0 → hydra_optuna_sweeper-1.4.0.dev5}/hydra_plugins/hydra_optuna_sweeper/__init__.py +1 -1
- {hydra-optuna-sweeper-1.3.0.dev0 → hydra_optuna_sweeper-1.4.0.dev5}/hydra_plugins/hydra_optuna_sweeper/_impl.py +21 -11
- {hydra-optuna-sweeper-1.3.0.dev0 → hydra_optuna_sweeper-1.4.0.dev5}/hydra_plugins/hydra_optuna_sweeper/config.py +0 -1
- hydra_optuna_sweeper-1.4.0.dev5/hydra_plugins/hydra_optuna_sweeper/py.typed +0 -0
- {hydra-optuna-sweeper-1.3.0.dev0 → hydra_optuna_sweeper-1.4.0.dev5}/setup.py +7 -5
- hydra_optuna_sweeper-1.4.0.dev5/tests/test_optuna_sweeper_plugin.py +405 -0
- hydra-optuna-sweeper-1.3.0.dev0/MANIFEST.in +0 -3
- {hydra-optuna-sweeper-1.3.0.dev0 → hydra_optuna_sweeper-1.4.0.dev5}/README.md +0 -0
- {hydra-optuna-sweeper-1.3.0.dev0 → hydra_optuna_sweeper-1.4.0.dev5}/hydra_optuna_sweeper.egg-info/dependency_links.txt +0 -0
- {hydra-optuna-sweeper-1.3.0.dev0 → hydra_optuna_sweeper-1.4.0.dev5}/hydra_optuna_sweeper.egg-info/top_level.txt +0 -0
- {hydra-optuna-sweeper-1.3.0.dev0 → hydra_optuna_sweeper-1.4.0.dev5}/hydra_plugins/hydra_optuna_sweeper/optuna_sweeper.py +0 -0
- {hydra-optuna-sweeper-1.3.0.dev0 → hydra_optuna_sweeper-1.4.0.dev5}/pyproject.toml +0 -0
- {hydra-optuna-sweeper-1.3.0.dev0 → hydra_optuna_sweeper-1.4.0.dev5}/setup.cfg +0 -0
|
@@ -1,20 +1,34 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: hydra-optuna-sweeper
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0.dev5
|
|
4
4
|
Summary: Hydra Optuna Sweeper plugin
|
|
5
5
|
Home-page: https://github.com/facebookresearch/hydra/
|
|
6
6
|
Author: Toshihiko Yanase, Hiroyuki Vincent Yamazaki
|
|
7
7
|
Author-email: toshihiko.yanase@gmail.com, hiroyuki.vincent.yamazaki@gmail.com
|
|
8
|
-
|
|
9
|
-
Classifier: Programming Language :: Python :: 3.6
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
8
|
+
License: MIT
|
|
13
9
|
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
14
|
Classifier: Operating System :: POSIX :: Linux
|
|
15
15
|
Classifier: Operating System :: MacOS
|
|
16
16
|
Classifier: Development Status :: 4 - Beta
|
|
17
|
+
Requires-Python: >=3.10
|
|
17
18
|
Description-Content-Type: text/markdown
|
|
19
|
+
Requires-Dist: hydra-core>=1.1.0.dev7
|
|
20
|
+
Requires-Dist: optuna<3.0.0,>=2.10.0
|
|
21
|
+
Requires-Dist: sqlalchemy~=1.3.0
|
|
22
|
+
Dynamic: author
|
|
23
|
+
Dynamic: author-email
|
|
24
|
+
Dynamic: classifier
|
|
25
|
+
Dynamic: description
|
|
26
|
+
Dynamic: description-content-type
|
|
27
|
+
Dynamic: home-page
|
|
28
|
+
Dynamic: license
|
|
29
|
+
Dynamic: requires-dist
|
|
30
|
+
Dynamic: requires-python
|
|
31
|
+
Dynamic: summary
|
|
18
32
|
|
|
19
33
|
# Hydra Optuna Sweeper
|
|
20
34
|
|
|
@@ -1,20 +1,34 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: hydra-optuna-sweeper
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0.dev5
|
|
4
4
|
Summary: Hydra Optuna Sweeper plugin
|
|
5
5
|
Home-page: https://github.com/facebookresearch/hydra/
|
|
6
6
|
Author: Toshihiko Yanase, Hiroyuki Vincent Yamazaki
|
|
7
7
|
Author-email: toshihiko.yanase@gmail.com, hiroyuki.vincent.yamazaki@gmail.com
|
|
8
|
-
|
|
9
|
-
Classifier: Programming Language :: Python :: 3.6
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
8
|
+
License: MIT
|
|
13
9
|
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
14
|
Classifier: Operating System :: POSIX :: Linux
|
|
15
15
|
Classifier: Operating System :: MacOS
|
|
16
16
|
Classifier: Development Status :: 4 - Beta
|
|
17
|
+
Requires-Python: >=3.10
|
|
17
18
|
Description-Content-Type: text/markdown
|
|
19
|
+
Requires-Dist: hydra-core>=1.1.0.dev7
|
|
20
|
+
Requires-Dist: optuna<3.0.0,>=2.10.0
|
|
21
|
+
Requires-Dist: sqlalchemy~=1.3.0
|
|
22
|
+
Dynamic: author
|
|
23
|
+
Dynamic: author-email
|
|
24
|
+
Dynamic: classifier
|
|
25
|
+
Dynamic: description
|
|
26
|
+
Dynamic: description-content-type
|
|
27
|
+
Dynamic: home-page
|
|
28
|
+
Dynamic: license
|
|
29
|
+
Dynamic: requires-dist
|
|
30
|
+
Dynamic: requires-python
|
|
31
|
+
Dynamic: summary
|
|
18
32
|
|
|
19
33
|
# Hydra Optuna Sweeper
|
|
20
34
|
|
|
@@ -10,4 +10,6 @@ hydra_optuna_sweeper.egg-info/top_level.txt
|
|
|
10
10
|
hydra_plugins/hydra_optuna_sweeper/__init__.py
|
|
11
11
|
hydra_plugins/hydra_optuna_sweeper/_impl.py
|
|
12
12
|
hydra_plugins/hydra_optuna_sweeper/config.py
|
|
13
|
-
hydra_plugins/hydra_optuna_sweeper/optuna_sweeper.py
|
|
13
|
+
hydra_plugins/hydra_optuna_sweeper/optuna_sweeper.py
|
|
14
|
+
hydra_plugins/hydra_optuna_sweeper/py.typed
|
|
15
|
+
tests/test_optuna_sweeper_plugin.py
|
|
@@ -49,7 +49,7 @@ log = logging.getLogger(__name__)
|
|
|
49
49
|
|
|
50
50
|
|
|
51
51
|
def create_optuna_distribution_from_config(
|
|
52
|
-
config: MutableMapping[str, Any]
|
|
52
|
+
config: MutableMapping[str, Any],
|
|
53
53
|
) -> BaseDistribution:
|
|
54
54
|
kwargs = dict(config)
|
|
55
55
|
if isinstance(config["type"], str):
|
|
@@ -107,7 +107,9 @@ def create_optuna_distribution_from_override(override: Override) -> Any:
|
|
|
107
107
|
or isinstance(value.stop, float)
|
|
108
108
|
or isinstance(value.step, float)
|
|
109
109
|
):
|
|
110
|
-
return DiscreteUniformDistribution(
|
|
110
|
+
return DiscreteUniformDistribution(
|
|
111
|
+
float(value.start), float(value.stop), float(value.step)
|
|
112
|
+
)
|
|
111
113
|
return IntUniformDistribution(
|
|
112
114
|
int(value.start), int(value.stop), step=int(value.step)
|
|
113
115
|
)
|
|
@@ -130,20 +132,25 @@ def create_optuna_distribution_from_override(override: Override) -> Any:
|
|
|
130
132
|
|
|
131
133
|
def create_params_from_overrides(
|
|
132
134
|
arguments: List[str],
|
|
133
|
-
) -> Tuple[Dict[str, BaseDistribution], Dict[str, Any]]:
|
|
135
|
+
) -> Tuple[Dict[str, BaseDistribution], Dict[str, Any], List[str]]:
|
|
134
136
|
parser = OverridesParser.create()
|
|
135
137
|
parsed = parser.parse_overrides(arguments)
|
|
136
138
|
search_space_distributions = dict()
|
|
137
139
|
fixed_params = dict()
|
|
140
|
+
fixed_overrides = []
|
|
138
141
|
|
|
139
142
|
for override in parsed:
|
|
143
|
+
if override.is_delete():
|
|
144
|
+
assert override.input_line is not None
|
|
145
|
+
fixed_overrides.append(override.input_line)
|
|
146
|
+
continue
|
|
140
147
|
param_name = override.get_key_element()
|
|
141
148
|
value = create_optuna_distribution_from_override(override)
|
|
142
149
|
if isinstance(value, BaseDistribution):
|
|
143
150
|
search_space_distributions[param_name] = value
|
|
144
151
|
else:
|
|
145
152
|
fixed_params[param_name] = value
|
|
146
|
-
return search_space_distributions, fixed_params
|
|
153
|
+
return search_space_distributions, fixed_params, fixed_overrides
|
|
147
154
|
|
|
148
155
|
|
|
149
156
|
class OptunaSweeperImpl(Sweeper):
|
|
@@ -192,13 +199,11 @@ class OptunaSweeperImpl(Sweeper):
|
|
|
192
199
|
)
|
|
193
200
|
else:
|
|
194
201
|
deprecation_warning(
|
|
195
|
-
message=dedent(
|
|
196
|
-
f"""\
|
|
202
|
+
message=dedent(f"""\
|
|
197
203
|
`hydra.sweeper.search_space` is deprecated and will be removed in the next major release.
|
|
198
204
|
Please configure with `hydra.sweeper.params`.
|
|
199
205
|
{url}
|
|
200
|
-
"""
|
|
201
|
-
),
|
|
206
|
+
"""),
|
|
202
207
|
)
|
|
203
208
|
self.search_space_distributions = {
|
|
204
209
|
str(x): create_optuna_distribution_from_config(y)
|
|
@@ -232,6 +237,7 @@ class OptunaSweeperImpl(Sweeper):
|
|
|
232
237
|
trials: List[Trial],
|
|
233
238
|
search_space_distributions: Dict[str, BaseDistribution],
|
|
234
239
|
fixed_params: Dict[str, Any],
|
|
240
|
+
fixed_overrides: List[str],
|
|
235
241
|
) -> Sequence[Sequence[str]]:
|
|
236
242
|
overrides = []
|
|
237
243
|
for trial in trials:
|
|
@@ -254,7 +260,10 @@ class OptunaSweeperImpl(Sweeper):
|
|
|
254
260
|
params = dict(trial.params)
|
|
255
261
|
params.update(fixed_params)
|
|
256
262
|
|
|
257
|
-
overrides.append(
|
|
263
|
+
overrides.append(
|
|
264
|
+
tuple(f"{name}={val}" for name, val in params.items())
|
|
265
|
+
+ tuple(fixed_overrides)
|
|
266
|
+
)
|
|
258
267
|
return overrides
|
|
259
268
|
|
|
260
269
|
def _parse_sweeper_params_config(self) -> List[str]:
|
|
@@ -290,12 +299,13 @@ class OptunaSweeperImpl(Sweeper):
|
|
|
290
299
|
|
|
291
300
|
is_grid_sampler = (
|
|
292
301
|
isinstance(self.sampler, functools.partial)
|
|
293
|
-
and self.sampler.func == optuna.samplers.GridSampler
|
|
302
|
+
and self.sampler.func == optuna.samplers.GridSampler
|
|
294
303
|
)
|
|
295
304
|
|
|
296
305
|
(
|
|
297
306
|
override_search_space_distributions,
|
|
298
307
|
fixed_params,
|
|
308
|
+
fixed_overrides,
|
|
299
309
|
) = create_params_from_overrides(params_conf)
|
|
300
310
|
|
|
301
311
|
search_space_distributions = dict()
|
|
@@ -345,7 +355,7 @@ class OptunaSweeperImpl(Sweeper):
|
|
|
345
355
|
|
|
346
356
|
trials = [study.ask() for _ in range(batch_size)]
|
|
347
357
|
overrides = self._configure_trials(
|
|
348
|
-
trials, search_space_distributions, fixed_params
|
|
358
|
+
trials, search_space_distributions, fixed_params, fixed_overrides
|
|
349
359
|
)
|
|
350
360
|
|
|
351
361
|
returns = self.launcher.launch(overrides, initial_job_idx=self.job_idx)
|
|
File without changes
|
|
@@ -11,24 +11,26 @@ setup(
|
|
|
11
11
|
author="Toshihiko Yanase, Hiroyuki Vincent Yamazaki",
|
|
12
12
|
author_email="toshihiko.yanase@gmail.com, hiroyuki.vincent.yamazaki@gmail.com",
|
|
13
13
|
description="Hydra Optuna Sweeper plugin",
|
|
14
|
+
license="MIT",
|
|
14
15
|
long_description=(Path(__file__).parent / "README.md").read_text(),
|
|
15
16
|
long_description_content_type="text/markdown",
|
|
16
17
|
url="https://github.com/facebookresearch/hydra/",
|
|
17
18
|
packages=find_namespace_packages(include=["hydra_plugins.*"]),
|
|
18
19
|
classifiers=[
|
|
19
|
-
"License :: OSI Approved :: MIT License",
|
|
20
|
-
"Programming Language :: Python :: 3.6",
|
|
21
|
-
"Programming Language :: Python :: 3.7",
|
|
22
|
-
"Programming Language :: Python :: 3.8",
|
|
23
|
-
"Programming Language :: Python :: 3.9",
|
|
24
20
|
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Programming Language :: Python :: 3.14",
|
|
25
25
|
"Operating System :: POSIX :: Linux",
|
|
26
26
|
"Operating System :: MacOS",
|
|
27
27
|
"Development Status :: 4 - Beta",
|
|
28
28
|
],
|
|
29
|
+
python_requires=">=3.10",
|
|
29
30
|
install_requires=[
|
|
30
31
|
"hydra-core>=1.1.0.dev7",
|
|
31
32
|
"optuna>=2.10.0,<3.0.0",
|
|
33
|
+
"sqlalchemy~=1.3.0", # TODO: Unpin when upgrading to optuna v3.0
|
|
32
34
|
],
|
|
33
35
|
include_package_data=True,
|
|
34
36
|
)
|
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
from functools import partial
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any, List, Optional
|
|
7
|
+
|
|
8
|
+
import optuna
|
|
9
|
+
from hydra.core.override_parser.overrides_parser import OverridesParser
|
|
10
|
+
from hydra.core.plugins import Plugins
|
|
11
|
+
from hydra.plugins.sweeper import Sweeper
|
|
12
|
+
from hydra.test_utils.test_utils import (
|
|
13
|
+
TSweepRunner,
|
|
14
|
+
chdir_plugin_root,
|
|
15
|
+
run_process,
|
|
16
|
+
run_python_script,
|
|
17
|
+
)
|
|
18
|
+
from omegaconf import DictConfig, OmegaConf
|
|
19
|
+
from optuna.distributions import (
|
|
20
|
+
BaseDistribution,
|
|
21
|
+
CategoricalDistribution,
|
|
22
|
+
DiscreteUniformDistribution,
|
|
23
|
+
IntLogUniformDistribution,
|
|
24
|
+
IntUniformDistribution,
|
|
25
|
+
LogUniformDistribution,
|
|
26
|
+
UniformDistribution,
|
|
27
|
+
)
|
|
28
|
+
from optuna.samplers import RandomSampler
|
|
29
|
+
from pytest import mark, warns
|
|
30
|
+
|
|
31
|
+
from hydra_plugins.hydra_optuna_sweeper import _impl
|
|
32
|
+
from hydra_plugins.hydra_optuna_sweeper._impl import OptunaSweeperImpl
|
|
33
|
+
from hydra_plugins.hydra_optuna_sweeper.config import Direction
|
|
34
|
+
from hydra_plugins.hydra_optuna_sweeper.optuna_sweeper import OptunaSweeper
|
|
35
|
+
|
|
36
|
+
chdir_plugin_root()
|
|
37
|
+
|
|
38
|
+
SQLALCHEMY_UTC_WARNING_FILTER = (
|
|
39
|
+
r"-W ignore:datetime.datetime.utcfromtimestamp() is deprecated:"
|
|
40
|
+
r"DeprecationWarning:sqlalchemy.sql.sqltypes"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def run_optuna_script(cmd: List[str]) -> None:
|
|
45
|
+
run_python_script([SQLALCHEMY_UTC_WARNING_FILTER, *cmd])
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_discovery() -> None:
|
|
49
|
+
assert OptunaSweeper.__name__ in [
|
|
50
|
+
x.__name__ for x in Plugins.instance().discover(Sweeper)
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def check_distribution(expected: BaseDistribution, actual: BaseDistribution) -> None:
|
|
55
|
+
if not isinstance(expected, CategoricalDistribution):
|
|
56
|
+
assert expected == actual
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
assert isinstance(actual, CategoricalDistribution)
|
|
60
|
+
# shuffle() will randomize the order of items in choices.
|
|
61
|
+
assert set(expected.choices) == set(actual.choices)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@mark.parametrize(
|
|
65
|
+
"input, expected",
|
|
66
|
+
[
|
|
67
|
+
(
|
|
68
|
+
{"type": "categorical", "choices": [1, 2, 3]},
|
|
69
|
+
CategoricalDistribution([1, 2, 3]),
|
|
70
|
+
),
|
|
71
|
+
({"type": "int", "low": 0, "high": 10}, IntUniformDistribution(0, 10)),
|
|
72
|
+
(
|
|
73
|
+
{"type": "int", "low": 0, "high": 10, "step": 2},
|
|
74
|
+
IntUniformDistribution(0, 10, step=2),
|
|
75
|
+
),
|
|
76
|
+
({"type": "int", "low": 0, "high": 5}, IntUniformDistribution(0, 5)),
|
|
77
|
+
(
|
|
78
|
+
{"type": "int", "low": 1, "high": 100, "log": True},
|
|
79
|
+
IntLogUniformDistribution(1, 100),
|
|
80
|
+
),
|
|
81
|
+
({"type": "float", "low": 0, "high": 1}, UniformDistribution(0, 1)),
|
|
82
|
+
(
|
|
83
|
+
{"type": "float", "low": 0, "high": 10, "step": 2},
|
|
84
|
+
DiscreteUniformDistribution(0, 10, 2),
|
|
85
|
+
),
|
|
86
|
+
(
|
|
87
|
+
{"type": "float", "low": 1, "high": 100, "log": True},
|
|
88
|
+
LogUniformDistribution(1, 100),
|
|
89
|
+
),
|
|
90
|
+
],
|
|
91
|
+
)
|
|
92
|
+
def test_create_optuna_distribution_from_config(input: Any, expected: Any) -> None:
|
|
93
|
+
actual = _impl.create_optuna_distribution_from_config(input)
|
|
94
|
+
check_distribution(expected, actual)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@mark.parametrize(
|
|
98
|
+
"input, expected",
|
|
99
|
+
[
|
|
100
|
+
("key=choice(1,2)", CategoricalDistribution([1, 2])),
|
|
101
|
+
("key=choice(true, false)", CategoricalDistribution([True, False])),
|
|
102
|
+
("key=choice('hello', 'world')", CategoricalDistribution(["hello", "world"])),
|
|
103
|
+
("key=shuffle(range(1,3))", CategoricalDistribution((1, 2))),
|
|
104
|
+
("key=range(1,3)", IntUniformDistribution(1, 3)),
|
|
105
|
+
("key=interval(1, 5)", UniformDistribution(1, 5)),
|
|
106
|
+
("key=int(interval(1, 5))", IntUniformDistribution(1, 5)),
|
|
107
|
+
("key=tag(log, interval(1, 5))", LogUniformDistribution(1, 5)),
|
|
108
|
+
("key=tag(log, int(interval(1, 5)))", IntLogUniformDistribution(1, 5)),
|
|
109
|
+
("key=range(0.5, 5.5, step=1)", DiscreteUniformDistribution(0.5, 5.5, 1)),
|
|
110
|
+
],
|
|
111
|
+
)
|
|
112
|
+
def test_create_optuna_distribution_from_override(input: Any, expected: Any) -> None:
|
|
113
|
+
parser = OverridesParser.create()
|
|
114
|
+
parsed = parser.parse_overrides([input])[0]
|
|
115
|
+
actual = _impl.create_optuna_distribution_from_override(parsed)
|
|
116
|
+
check_distribution(expected, actual)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@mark.parametrize(
|
|
120
|
+
"input, expected",
|
|
121
|
+
[
|
|
122
|
+
(["key=choice(1,2)"], ({"key": CategoricalDistribution([1, 2])}, {}, [])),
|
|
123
|
+
(["key=5"], ({}, {"key": "5"}, [])),
|
|
124
|
+
(
|
|
125
|
+
["key1=choice(1,2)", "key2=5"],
|
|
126
|
+
({"key1": CategoricalDistribution([1, 2])}, {"key2": "5"}, []),
|
|
127
|
+
),
|
|
128
|
+
(
|
|
129
|
+
["key1=choice(1,2)", "key2=5", "key3=range(1,3)"],
|
|
130
|
+
(
|
|
131
|
+
{
|
|
132
|
+
"key1": CategoricalDistribution([1, 2]),
|
|
133
|
+
"key3": IntUniformDistribution(1, 3),
|
|
134
|
+
},
|
|
135
|
+
{"key2": "5"},
|
|
136
|
+
[],
|
|
137
|
+
),
|
|
138
|
+
),
|
|
139
|
+
(["~z"], ({}, {}, ["~z"])),
|
|
140
|
+
(["~z=10"], ({}, {}, ["~z=10"])),
|
|
141
|
+
],
|
|
142
|
+
)
|
|
143
|
+
def test_create_params_from_overrides(input: Any, expected: Any) -> None:
|
|
144
|
+
actual = _impl.create_params_from_overrides(input)
|
|
145
|
+
assert actual == expected
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def test_launch_jobs(hydra_sweep_runner: TSweepRunner) -> None:
|
|
149
|
+
sweep = hydra_sweep_runner(
|
|
150
|
+
calling_file=None,
|
|
151
|
+
calling_module="hydra.test_utils.a_module",
|
|
152
|
+
config_path="configs",
|
|
153
|
+
config_name="compose.yaml",
|
|
154
|
+
task_function=None,
|
|
155
|
+
overrides=[
|
|
156
|
+
"hydra/sweeper=optuna",
|
|
157
|
+
"hydra/launcher=basic",
|
|
158
|
+
"hydra.sweeper.n_trials=8",
|
|
159
|
+
"hydra.sweeper.n_jobs=3",
|
|
160
|
+
],
|
|
161
|
+
)
|
|
162
|
+
with sweep:
|
|
163
|
+
assert sweep.returns is None
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@mark.parametrize("with_commandline", (True, False))
|
|
167
|
+
def test_optuna_example(with_commandline: bool, tmpdir: Path) -> None:
|
|
168
|
+
storage = "sqlite:///" + os.path.join(str(tmpdir), "test.db")
|
|
169
|
+
study_name = "test-optuna-example"
|
|
170
|
+
cmd = [
|
|
171
|
+
"example/sphere.py",
|
|
172
|
+
"--multirun",
|
|
173
|
+
"hydra.sweep.dir=" + str(tmpdir),
|
|
174
|
+
"hydra.job.chdir=True",
|
|
175
|
+
"hydra.sweeper.n_trials=20",
|
|
176
|
+
"hydra.sweeper.n_jobs=1",
|
|
177
|
+
f"hydra.sweeper.storage={storage}",
|
|
178
|
+
f"hydra.sweeper.study_name={study_name}",
|
|
179
|
+
"hydra/sweeper/sampler=tpe",
|
|
180
|
+
"hydra.sweeper.sampler.seed=123",
|
|
181
|
+
"~z",
|
|
182
|
+
]
|
|
183
|
+
if with_commandline:
|
|
184
|
+
cmd += [
|
|
185
|
+
"x=choice(0, 1, 2)",
|
|
186
|
+
"y=0", # Fixed parameter
|
|
187
|
+
]
|
|
188
|
+
run_optuna_script(cmd)
|
|
189
|
+
returns = OmegaConf.load(f"{tmpdir}/optimization_results.yaml")
|
|
190
|
+
study = optuna.load_study(storage=storage, study_name=study_name)
|
|
191
|
+
best_trial = study.best_trial
|
|
192
|
+
assert isinstance(returns, DictConfig)
|
|
193
|
+
assert returns.name == "optuna"
|
|
194
|
+
assert returns["best_params"]["x"] == best_trial.params["x"]
|
|
195
|
+
if with_commandline:
|
|
196
|
+
assert "y" not in returns["best_params"]
|
|
197
|
+
assert "y" not in best_trial.params
|
|
198
|
+
else:
|
|
199
|
+
assert returns["best_params"]["y"] == best_trial.params["y"]
|
|
200
|
+
assert returns["best_value"] == best_trial.value
|
|
201
|
+
# Check the search performance of the TPE sampler.
|
|
202
|
+
# The threshold is the 95th percentile calculated with 1000 different seed values
|
|
203
|
+
# to make the test robust against the detailed implementation of the sampler.
|
|
204
|
+
# See https://github.com/facebookresearch/hydra/pull/1746#discussion_r681549830.
|
|
205
|
+
assert returns["best_value"] <= 2.27
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
@mark.parametrize("num_trials", (10, 1))
|
|
209
|
+
def test_example_with_grid_sampler(
|
|
210
|
+
tmpdir: Path,
|
|
211
|
+
num_trials: int,
|
|
212
|
+
) -> None:
|
|
213
|
+
storage = "sqlite:///" + os.path.join(str(tmpdir), "test.db")
|
|
214
|
+
study_name = "test-grid-sampler"
|
|
215
|
+
cmd = [
|
|
216
|
+
"example/sphere.py",
|
|
217
|
+
"--multirun",
|
|
218
|
+
"--config-dir=tests/conf",
|
|
219
|
+
"--config-name=test_grid",
|
|
220
|
+
"hydra.sweep.dir=" + str(tmpdir),
|
|
221
|
+
"hydra.job.chdir=False",
|
|
222
|
+
f"hydra.sweeper.n_trials={num_trials}",
|
|
223
|
+
"hydra.sweeper.n_jobs=1",
|
|
224
|
+
f"hydra.sweeper.storage={storage}",
|
|
225
|
+
f"hydra.sweeper.study_name={study_name}",
|
|
226
|
+
]
|
|
227
|
+
run_optuna_script(cmd)
|
|
228
|
+
returns = OmegaConf.load(f"{tmpdir}/optimization_results.yaml")
|
|
229
|
+
assert isinstance(returns, DictConfig)
|
|
230
|
+
bv, bx, by, bz = (
|
|
231
|
+
returns["best_value"],
|
|
232
|
+
returns["best_params"]["x"],
|
|
233
|
+
returns["best_params"]["y"],
|
|
234
|
+
returns["best_params"]["z"],
|
|
235
|
+
)
|
|
236
|
+
if num_trials >= 12:
|
|
237
|
+
assert bv == 1 and abs(bx) == 1 and by == 0
|
|
238
|
+
else:
|
|
239
|
+
assert bx in [-1, 1] and by in [-1, 0]
|
|
240
|
+
assert bz in ["foo", "bar"]
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
@mark.parametrize("with_commandline", (True, False))
|
|
244
|
+
def test_optuna_multi_objective_example(with_commandline: bool, tmpdir: Path) -> None:
|
|
245
|
+
cmd = [
|
|
246
|
+
"example/multi-objective.py",
|
|
247
|
+
"--multirun",
|
|
248
|
+
"hydra.sweep.dir=" + str(tmpdir),
|
|
249
|
+
"hydra.job.chdir=True",
|
|
250
|
+
"hydra.sweeper.n_trials=20",
|
|
251
|
+
"hydra.sweeper.n_jobs=1",
|
|
252
|
+
"hydra/sweeper/sampler=random",
|
|
253
|
+
"hydra.sweeper.sampler.seed=123",
|
|
254
|
+
]
|
|
255
|
+
if with_commandline:
|
|
256
|
+
cmd += [
|
|
257
|
+
"x=range(0, 5)",
|
|
258
|
+
"y=range(0, 3)",
|
|
259
|
+
]
|
|
260
|
+
run_optuna_script(cmd)
|
|
261
|
+
returns = OmegaConf.load(f"{tmpdir}/optimization_results.yaml")
|
|
262
|
+
assert isinstance(returns, DictConfig)
|
|
263
|
+
assert returns.name == "optuna"
|
|
264
|
+
if with_commandline:
|
|
265
|
+
for trial_x in returns["solutions"]:
|
|
266
|
+
assert trial_x["params"]["x"] % 1 == 0
|
|
267
|
+
assert trial_x["params"]["y"] % 1 == 0
|
|
268
|
+
# The trials must not dominate each other.
|
|
269
|
+
for trial_y in returns["solutions"]:
|
|
270
|
+
assert not _dominates(trial_x, trial_y)
|
|
271
|
+
else:
|
|
272
|
+
for trial_x in returns["solutions"]:
|
|
273
|
+
assert trial_x["params"]["x"] % 1 in {0, 0.5}
|
|
274
|
+
assert trial_x["params"]["y"] % 1 in {0, 0.5}
|
|
275
|
+
# The trials must not dominate each other.
|
|
276
|
+
for trial_y in returns["solutions"]:
|
|
277
|
+
assert not _dominates(trial_x, trial_y)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def _dominates(values_x: List[float], values_y: List[float]) -> bool:
|
|
281
|
+
return all(x <= y for x, y in zip(values_x, values_y)) and any(
|
|
282
|
+
x < y for x, y in zip(values_x, values_y)
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def test_optuna_custom_search_space_example(tmpdir: Path) -> None:
|
|
287
|
+
max_z_difference_from_x = 0.3
|
|
288
|
+
cmd = [
|
|
289
|
+
"example/custom-search-space-objective.py",
|
|
290
|
+
"--multirun",
|
|
291
|
+
"hydra.sweep.dir=" + str(tmpdir),
|
|
292
|
+
"hydra.job.chdir=True",
|
|
293
|
+
"hydra.sweeper.n_trials=20",
|
|
294
|
+
"hydra.sweeper.n_jobs=1",
|
|
295
|
+
"hydra/sweeper/sampler=random",
|
|
296
|
+
"hydra.sweeper.sampler.seed=123",
|
|
297
|
+
f"max_z_difference_from_x={max_z_difference_from_x}",
|
|
298
|
+
]
|
|
299
|
+
run_optuna_script(cmd)
|
|
300
|
+
returns = OmegaConf.load(f"{tmpdir}/optimization_results.yaml")
|
|
301
|
+
assert isinstance(returns, DictConfig)
|
|
302
|
+
assert returns.name == "optuna"
|
|
303
|
+
assert (
|
|
304
|
+
abs(returns["best_params"]["x"] - returns["best_params"]["z"])
|
|
305
|
+
<= max_z_difference_from_x
|
|
306
|
+
)
|
|
307
|
+
w = returns["best_params"]["+w"]
|
|
308
|
+
assert 0 <= w <= 1
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
@mark.parametrize(
|
|
312
|
+
"search_space,params,raise_warning,msg",
|
|
313
|
+
[
|
|
314
|
+
(None, None, False, None),
|
|
315
|
+
(
|
|
316
|
+
{},
|
|
317
|
+
{},
|
|
318
|
+
True,
|
|
319
|
+
r"Both hydra.sweeper.params and hydra.sweeper.search_space are configured.*",
|
|
320
|
+
),
|
|
321
|
+
(
|
|
322
|
+
{},
|
|
323
|
+
None,
|
|
324
|
+
True,
|
|
325
|
+
r"`hydra.sweeper.search_space` is deprecated and will be removed in the next major release.*",
|
|
326
|
+
),
|
|
327
|
+
(None, {}, False, None),
|
|
328
|
+
],
|
|
329
|
+
)
|
|
330
|
+
def test_warnings(
|
|
331
|
+
tmpdir: Path,
|
|
332
|
+
search_space: Optional[DictConfig],
|
|
333
|
+
params: Optional[DictConfig],
|
|
334
|
+
raise_warning: bool,
|
|
335
|
+
msg: Optional[str],
|
|
336
|
+
) -> None:
|
|
337
|
+
partial_sweeper = partial(
|
|
338
|
+
OptunaSweeperImpl,
|
|
339
|
+
sampler=RandomSampler(),
|
|
340
|
+
direction=Direction.minimize,
|
|
341
|
+
storage=None,
|
|
342
|
+
study_name="test",
|
|
343
|
+
n_trials=1,
|
|
344
|
+
n_jobs=1,
|
|
345
|
+
max_failure_rate=0.0,
|
|
346
|
+
custom_search_space=None,
|
|
347
|
+
)
|
|
348
|
+
if search_space is not None:
|
|
349
|
+
search_space = OmegaConf.create(search_space)
|
|
350
|
+
if params is not None:
|
|
351
|
+
params = OmegaConf.create(params)
|
|
352
|
+
sweeper = partial_sweeper(search_space=search_space, params=params)
|
|
353
|
+
if raise_warning:
|
|
354
|
+
with warns(
|
|
355
|
+
UserWarning,
|
|
356
|
+
match=msg,
|
|
357
|
+
):
|
|
358
|
+
sweeper._process_searchspace_config()
|
|
359
|
+
else:
|
|
360
|
+
sweeper._process_searchspace_config()
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
@mark.parametrize("max_failure_rate", (0.5, 1.0))
|
|
364
|
+
def test_failure_rate(max_failure_rate: float, tmpdir: Path) -> None:
|
|
365
|
+
cmd = [
|
|
366
|
+
sys.executable,
|
|
367
|
+
"example/sphere.py",
|
|
368
|
+
"--multirun",
|
|
369
|
+
"hydra.sweep.dir=" + str(tmpdir),
|
|
370
|
+
"hydra.job.chdir=True",
|
|
371
|
+
"hydra.sweeper.n_trials=20",
|
|
372
|
+
"hydra.sweeper.n_jobs=2",
|
|
373
|
+
"hydra/sweeper/sampler=random",
|
|
374
|
+
"hydra.sweeper.sampler.seed=123",
|
|
375
|
+
f"hydra.sweeper.max_failure_rate={max_failure_rate}",
|
|
376
|
+
"error=true",
|
|
377
|
+
]
|
|
378
|
+
out, err = run_process(cmd, print_error=False, raise_exception=False)
|
|
379
|
+
error_string = "RuntimeError: cfg.error is True"
|
|
380
|
+
if max_failure_rate < 1.0:
|
|
381
|
+
assert error_string in err
|
|
382
|
+
else:
|
|
383
|
+
assert error_string not in err
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def test_example_with_deprecated_search_space(
|
|
387
|
+
tmpdir: Path,
|
|
388
|
+
) -> None:
|
|
389
|
+
cmd = [
|
|
390
|
+
"-W ignore::UserWarning",
|
|
391
|
+
"example/sphere.py",
|
|
392
|
+
"--multirun",
|
|
393
|
+
"--config-dir=tests/conf",
|
|
394
|
+
"--config-name=test_deprecated_search_space",
|
|
395
|
+
"hydra.sweep.dir=" + str(tmpdir),
|
|
396
|
+
"hydra.job.chdir=True",
|
|
397
|
+
"hydra.sweeper.n_trials=20",
|
|
398
|
+
"hydra.sweeper.n_jobs=1",
|
|
399
|
+
]
|
|
400
|
+
|
|
401
|
+
run_optuna_script(cmd)
|
|
402
|
+
returns = OmegaConf.load(f"{tmpdir}/optimization_results.yaml")
|
|
403
|
+
assert isinstance(returns, DictConfig)
|
|
404
|
+
assert returns.name == "optuna"
|
|
405
|
+
assert abs(returns["best_params"]["x"]) <= 5.5
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|