Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle invalid neighbors in local search gracefully #773

Merged
merged 4 commits into from
Sep 23, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion smac/configspace/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from ConfigSpace import ConfigurationSpace, Configuration, Constant, \
CategoricalHyperparameter, UniformFloatHyperparameter, \
UniformIntegerHyperparameter, InCondition
from ConfigSpace.exceptions import ForbiddenValueError
from ConfigSpace.read_and_write import pcs, pcs_new, json
from ConfigSpace.util import get_one_exchange_neighbourhood
from smac.configspace.util import convert_configurations_to_array
Expand All @@ -22,7 +23,8 @@
"pcs_new",
"json",
"get_one_exchange_neighbourhood",
"convert_configurations_to_array"
"convert_configurations_to_array",
"ForbiddenValueError"
]

get_one_exchange_neighbourhood = partial(get_one_exchange_neighbourhood, stdev=0.05, num_neighbors=8)
38 changes: 25 additions & 13 deletions smac/optimizer/ei_optimization.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
Configuration,
ConfigurationSpace,
convert_configurations_to_array,
ForbiddenValueError,
)
from smac.runhistory.runhistory import RunHistory
from smac.stats.stats import Stats
Expand Down Expand Up @@ -367,10 +368,14 @@ def _do_search(
n = next(neighborhood_iterator)
neighbors_generated[i] += 1
neighbors_for_i.append(n)
except ValueError as e:
# `neighborhood_iterator` raises `ValueError` with some probability when it reaches an invalid configuration.
self.logger.debug(e)
new_neighborhood[i] = True
except StopIteration:
obtain_n[i] = len(neighbors_for_i)
new_neighborhood[i] = True
break
obtain_n[i] = len(neighbors_for_i)
neighbors.extend(neighbors_for_i)

if len(neighbors) != 0:
Expand Down Expand Up @@ -398,18 +403,25 @@ def _do_search(

# Found a better configuration
if acq_val[acq_index] > acq_val_candidates[i]:
self.logger.debug(
"Local search %d: Switch to one of the neighbors (after %d configurations).",
i,
neighbors_looked_at[i],
)
candidates[i] = neighbors[acq_index]
acq_val_candidates[i] = acq_val[acq_index]
new_neighborhood[i] = True
improved[i] = True
local_search_steps[i] += 1
neighbors_w_equal_acq[i] = []
obtain_n[i] = 1
is_valid = False
try:
neighbors[acq_index].is_valid_configuration()
is_valid = True
except (ValueError, ForbiddenValueError) as e:
self.logger.debug("Local search %d: %s", i, e)
if is_valid:
self.logger.debug(
"Local search %d: Switch to one of the neighbors (after %d configurations).",
i,
neighbors_looked_at[i],
)
candidates[i] = neighbors[acq_index]
acq_val_candidates[i] = acq_val[acq_index]
new_neighborhood[i] = True
improved[i] = True
local_search_steps[i] += 1
neighbors_w_equal_acq[i] = []
obtain_n[i] = 1
# Found an equally well performing configuration, keeping it for plateau walking
elif acq_val[acq_index] == acq_val_candidates[i]:
neighbors_w_equal_acq[i].append(neighbors[acq_index])
Expand Down