Skip to content

Commit 2baa1a6

Browse files
author
timruhkopf
committed
[feature] following issue #946 #946, when using purely ask and tell, then smac does not need to know about the target_function and we can just drop the requirement. Should the user then however decide to call optimize, this must raise an ValueError
1 parent 7f1ce0d commit 2baa1a6

File tree

2 files changed

+31
-8
lines changed

2 files changed

+31
-8
lines changed

smac/facade/abstract_facade.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,12 @@ class AbstractFacade:
5858
----------
5959
scenario : Scenario
6060
The scenario object, holding all environmental information.
61-
target_function : Callable | str | AbstractRunner
61+
target_function : Callable | str | AbstractRunner | None, defaults to None
6262
This function is called internally to judge a trial's performance. If a string is passed,
6363
it is assumed to be a script. In this case, ``TargetFunctionScriptRunner`` is used to run the script.
64+
In the rare case that only ``ask`` and ``tell`` and not ``optimize`` is used to optimize
65+
the hyperparameters, the target_function argument can be None, because SMAC no longer is
66+
charge of the evaluation of the configuration and thus does not need to know about it.
6467
model : AbstractModel | None, defaults to None
6568
The surrogate model.
6669
acquisition_function : AbstractAcquisitionFunction | None, defaults to None
@@ -105,8 +108,8 @@ class AbstractFacade:
105108
def __init__(
106109
self,
107110
scenario: Scenario,
108-
target_function: Callable | str | AbstractRunner,
109111
*,
112+
target_function: Callable | str | AbstractRunner | None = None,
110113
model: AbstractModel | None = None,
111114
acquisition_function: AbstractAcquisitionFunction | None = None,
112115
acquisition_maximizer: AbstractAcquisitionMaximizer | None = None,
@@ -175,8 +178,10 @@ def __init__(
175178
self._overwrite = overwrite
176179

177180
# Prepare the algorithm executer
178-
runner: AbstractRunner
179-
if isinstance(target_function, AbstractRunner):
181+
runner: AbstractRunner | None
182+
if isinstance(target_function, AbstractRunner) or target_function is None:
183+
# in case the target_function is None (e.g. we purely use ask & tell)
184+
# we let smbo.optimize raise an error
180185
runner = target_function
181186
elif isinstance(target_function, str):
182187
runner = TargetFunctionScriptRunner(
@@ -192,7 +197,7 @@ def __init__(
192197
)
193198

194199
# In case of multiple jobs, we need to wrap the runner again using DaskParallelRunner
195-
if (n_workers := scenario.n_workers) > 1 or dask_client is not None:
200+
if ((n_workers := scenario.n_workers) > 1 or dask_client is not None) and runner is not None:
196201
if dask_client is not None and n_workers > 1:
197202
logger.warning(
198203
"Provided `dask_client`. Ignore `scenario.n_workers`, directly set `n_workers` in `dask_client`."
@@ -261,7 +266,7 @@ def meta(self) -> dict[str, Any]:
261266

262267
meta = {
263268
"facade": {"name": self.__class__.__name__},
264-
"runner": self._runner.meta,
269+
"runner": self._runner.meta if self._runner is not None else None,
265270
"model": self._model.meta,
266271
"acquisition_maximizer": self._acquisition_maximizer.meta,
267272
"acquisition_function": self._acquisition_function.meta,

smac/main/smbo.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,10 @@ class SMBO:
4040
----------
4141
scenario : Scenario
4242
The scenario object, holding all environmental information.
43-
runner : AbstractRunner
43+
runner : AbstractRunner | None
4444
The runner (containing the target function) is called internally to judge a trial's performance.
45+
In the rare case that ``optimize`` is never called and SMBO is operated with ``ask`` and ``tell`` only,
46+
the runner is allowed to be None
4547
runhistory : Runhistory
4648
The runhistory stores all trials.
4749
intensifier : AbstractIntensifier
@@ -60,7 +62,7 @@ class SMBO:
6062
def __init__(
6163
self,
6264
scenario: Scenario,
63-
runner: AbstractRunner,
65+
runner: AbstractRunner | None,
6466
runhistory: RunHistory,
6567
intensifier: AbstractIntensifier,
6668
overwrite: bool = False,
@@ -290,6 +292,11 @@ def optimize(self, *, data_to_scatter: dict[str, Any] | None = None) -> Configur
290292
callback.on_start(self)
291293

292294
dask_data_to_scatter = {}
295+
if self._runner is None:
296+
raise ValueError(
297+
"Runner is not set in SMBO. Likely issue is that the target_function was not set in the Facade."
298+
)
299+
293300
if isinstance(self._runner, DaskParallelRunner) and data_to_scatter is not None:
294301
dask_data_to_scatter = dict(data_to_scatter=self._runner._client.scatter(data_to_scatter, broadcast=True))
295302
elif data_to_scatter is not None:
@@ -435,6 +442,12 @@ def _add_results(self) -> None:
435442
"""Adds results from the runner to the runhistory. Although most of the functionality could be written
436443
in the tell method, we separate it here to make it accessible for the automatic optimization procedure only.
437444
"""
445+
if self._runner is None:
446+
raise ValueError(
447+
"Runner is not set in SMBO. Likely issue is that the target_function was not set "
448+
"in the Facade. So we cannot query the runner for results."
449+
)
450+
438451
# Check if there is any result
439452
for trial_info, trial_value in self._runner.iter_results():
440453
# Add the results of the run to the run history
@@ -578,6 +591,11 @@ def validate(
578591
The averaged cost of the configuration. In case of multi-fidelity, the cost of each objective is
579592
averaged.
580593
"""
594+
if self._runner is None:
595+
raise ValueError(
596+
"Runner is not set in SMBO. Likely issue is that the target_function was not set in the Facade."
597+
)
598+
581599
if seed is None:
582600
seed = self._scenario.seed
583601

0 commit comments

Comments
 (0)