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

Diamond cond with OrConjunction #219

Merged
merged 6 commits into from
Jun 8, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
22 changes: 20 additions & 2 deletions ConfigSpace/c_util.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ cpdef np.ndarray change_hp_value(
cdef Hyperparameter current
cdef str current_name
cdef list disabled
cdef set hps_to_be_activate
cdef set visited
cdef dict activated_values
cdef int active
Expand All @@ -287,6 +288,12 @@ cpdef np.ndarray change_hp_value(
# Hyperparameters which are going to be set to inactive
disabled = []

# Hyperparameters which are going to be set activate, we introduce this to resolve the conflict that might be raised
# by OrConjunction:
# Suppose that we have a parent HP_p whose possible values are A, B, C; a child HP_d is activate if
# HP_p is A or B. Then when HP_p switches from A to B, HP_d needs to remain activate.
hps_to_be_activate = set()

# Activate hyperparameters if their parent node got activated
children = children_of[hp_name]
if len(children) > 0:
Expand All @@ -301,7 +308,7 @@ cpdef np.ndarray change_hp_value(
if current_name in visited:
continue
visited.add(current_name)
if current_name in disabled:
if current_name in hps_to_be_activate:
continue

current_idx = configuration_space._hyperparameter_idx[current_name]
Expand All @@ -315,6 +322,16 @@ cpdef np.ndarray change_hp_value(
active = False
break

if active:
hps_to_be_activate.add(current_idx)
if current_value == current_value:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems odd? current_value == current_value, is this not always true?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really, if current_value is deactivated, then this equation does not hold (Similar code can be found under line 335 and 344)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, two NaNs compare as unequal, see https://en.wikipedia.org/wiki/NaN#Comparison_with_NaN. This is the fastest way to check for NaN values because it does not require any additional function calls.

children_ = children_of[current_name]
if len(children_) > 0:
to_visit.extendleft(children_)

if current_name in disabled:
continue

if active and not current_value == current_value:
default_value = current.normalized_default_value
configuration_array[current_idx] = default_value
Expand Down Expand Up @@ -343,6 +360,7 @@ cpdef np.ndarray change_hp_value(
to_disable.add(ch.name)

for idx in disabled:
configuration_array[idx] = NaN
if idx not in hps_to_be_activate:
configuration_array[idx] = NaN

return configuration_array
29 changes: 29 additions & 0 deletions test/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,35 @@ def test_check_neighbouring_config_diamond(self):

np.testing.assert_almost_equal(new_array, expected_array)

def test_check_neighbouring_config_diamond_or_conjunction(self):
diamond = ConfigurationSpace()
top = CategoricalHyperparameter('top', [0, 1], 0)
middle = CategoricalHyperparameter('middle', [0, 1], 1)
bottom_left = CategoricalHyperparameter('bottom_left', [0, 1], 1)
bottom_right = CategoricalHyperparameter('bottom_right', [0, 1, 2, 3], 1)

diamond.add_hyperparameters([top, bottom_left, bottom_right, middle])
diamond.add_condition(EqualsCondition(middle, top, 0))
diamond.add_condition(EqualsCondition(bottom_left, middle, 0))
diamond.add_condition(OrConjunction(EqualsCondition(bottom_right, middle, 1),
EqualsCondition(bottom_right, top, 1)))

config = Configuration(diamond, {'top': 0, 'middle': 1, 'bottom_right': 1})
hp_name = "top"
index = diamond.get_idx_by_hyperparameter_name(hp_name)
neighbor_value = 1

new_array = ConfigSpace.c_util.change_hp_value(
diamond,
config.get_array(),
hp_name,
neighbor_value,
index
)
expected_array = np.array([1, np.nan, np.nan, 1])

np.testing.assert_almost_equal(new_array, expected_array)

def test_check_neighbouring_config_diamond_str(self):
diamond = ConfigurationSpace()
head = CategoricalHyperparameter('head', ['red', 'green'])
Expand Down