diff --git a/CHANGELOG.md b/CHANGELOG.md index dbcac80e4c..5ffa77ab52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 2.0.3 + +## Bugfixes +- Fix path for dask scheduler file (#1055). +- Add OrdinalHyperparameter for random forest imputer (#1065). +- Configurations that fail to become incumbents will be added to the rejected lists (#1069). + # 2.0.2 ## Improvements diff --git a/smac/intensifier/abstract_intensifier.py b/smac/intensifier/abstract_intensifier.py index b944867273..cb537e9ccb 100644 --- a/smac/intensifier/abstract_intensifier.py +++ b/smac/intensifier/abstract_intensifier.py @@ -571,8 +571,12 @@ def update_incumbents(self, config: Configuration) -> None: if len(previous_incumbents) == len(new_incumbents): if previous_incumbents == new_incumbents: - # No changes in the incumbents - self._remove_rejected_config(config_id) + # No changes in the incumbents, we need this clause because we can't use set difference then + if config_id in new_incumbent_ids: + self._remove_rejected_config(config_id) + else: + # config worse than incumbents and thus rejected + self._add_rejected_config(config_id) return else: # In this case, we have to determine which config replaced which incumbent and reject it diff --git a/smac/model/random_forest/abstract_random_forest.py b/smac/model/random_forest/abstract_random_forest.py index d4f1f7ce30..e407331be5 100644 --- a/smac/model/random_forest/abstract_random_forest.py +++ b/smac/model/random_forest/abstract_random_forest.py @@ -6,6 +6,7 @@ from ConfigSpace import ( CategoricalHyperparameter, Constant, + OrdinalHyperparameter, UniformFloatHyperparameter, UniformIntegerHyperparameter, ) @@ -36,12 +37,14 @@ def _impute_inactive(self, X: np.ndarray) -> np.ndarray: self._conditional[idx] = True if isinstance(hp, CategoricalHyperparameter): self._impute_values[idx] = len(hp.choices) + elif isinstance(hp, OrdinalHyperparameter): + self._impute_values[idx] = len(hp.sequence) elif isinstance(hp, (UniformFloatHyperparameter, UniformIntegerHyperparameter)): self._impute_values[idx] = -1 elif isinstance(hp, Constant): self._impute_values[idx] = 1 else: - raise ValueError + raise ValueError(f"Unsupported hyperparameter type: {type(hp)}") if self._conditional[idx] is True: nonfinite_mask = ~np.isfinite(X[:, idx]) diff --git a/smac/model/random_forest/random_forest.py b/smac/model/random_forest/random_forest.py index 6d7840d2ac..634e0af06f 100644 --- a/smac/model/random_forest/random_forest.py +++ b/smac/model/random_forest/random_forest.py @@ -87,7 +87,9 @@ def __init__( self._rf_opts.compute_law_of_total_variance = False self._rf: BinaryForest | None = None self._log_y = log_y - self._rng = regression.default_random_engine(seed) + + # Case to `int` incase we get an `np.integer` type + self._rng = regression.default_random_engine(int(seed)) self._n_trees = n_trees self._n_points_per_tree = n_points_per_tree diff --git a/smac/runner/dask_runner.py b/smac/runner/dask_runner.py index b9aade4015..d4bb528bdc 100644 --- a/smac/runner/dask_runner.py +++ b/smac/runner/dask_runner.py @@ -91,7 +91,7 @@ def __init__( ) if self._scenario.output_directory is not None: - self._scheduler_file = self._scenario.output_directory / ".dask_scheduler_file" + self._scheduler_file = Path(self._scenario.output_directory, ".dask_scheduler_file") self._client.write_scheduler_file(scheduler_file=str(self._scheduler_file)) else: # We just use their set up diff --git a/tests/test_intensifier/test_abstract_intensifier.py b/tests/test_intensifier/test_abstract_intensifier.py index ce980c49b9..b8dc91a1cb 100644 --- a/tests/test_intensifier/test_abstract_intensifier.py +++ b/tests/test_intensifier/test_abstract_intensifier.py @@ -109,6 +109,48 @@ def test_incumbent_selection_multi_objective(make_scenario, configspace_small, m assert intensifier.get_incumbents() == [config] +def test_config_rejection_single_objective(configspace_small, make_scenario): + """ Tests whether configs are rejected properly if they are worse than the incumbent. """ + scenario = make_scenario(configspace_small, use_instances=False) + runhistory = RunHistory() + intensifier = Intensifier(scenario=scenario) + intensifier.runhistory = runhistory + + configs = configspace_small.sample_configuration(3) + + runhistory.add(config=configs[0], + cost=5, + time=0.0, + seed=0, + status=StatusType.SUCCESS, + force_update=True) + intensifier.update_incumbents(configs[0]) + + assert intensifier._rejected_config_ids == [] + + # add config that yielded better results, updating incumbent and sending prior incumbent to rejected + runhistory.add(config=configs[1], + cost=1, + time=0.0, + seed=0, + status=StatusType.SUCCESS, + force_update=True) + intensifier.update_incumbents(config=configs[1]) + + assert intensifier._rejected_config_ids == [1] + + # add config that is no better should thus go to rejected + runhistory.add(config=configs[2], + cost=1, + time=0.0, + seed=0, + status=StatusType.SUCCESS, + force_update=True) + intensifier.update_incumbents(config=configs[2]) + + assert intensifier._rejected_config_ids == [1, 3] + + def test_incumbent_differences(make_scenario, configspace_small): pass diff --git a/tests/test_model/test_rf.py b/tests/test_model/test_rf.py index a30995b237..e59b2ded11 100644 --- a/tests/test_model/test_rf.py +++ b/tests/test_model/test_rf.py @@ -257,24 +257,36 @@ def test_with_ordinal(): def test_impute_inactive_hyperparameters(): cs = ConfigurationSpace(seed=0) - a = cs.add_hyperparameter(CategoricalHyperparameter("a", [0, 1])) + a = cs.add_hyperparameter(CategoricalHyperparameter("a", [0, 1, 2])) b = cs.add_hyperparameter(CategoricalHyperparameter("b", [0, 1])) c = cs.add_hyperparameter(UniformFloatHyperparameter("c", 0, 1)) + d = cs.add_hyperparameter(OrdinalHyperparameter("d", [0, 1, 2])) cs.add_condition(EqualsCondition(b, a, 1)) cs.add_condition(EqualsCondition(c, a, 0)) + cs.add_condition(EqualsCondition(d, a, 2)) configs = cs.sample_configuration(size=100) config_array = convert_configurations_to_array(configs) for line in config_array: if line[0] == 0: assert np.isnan(line[1]) + assert np.isnan(line[3]) elif line[0] == 1: assert np.isnan(line[2]) + assert np.isnan(line[3]) + elif line[0] == 2: + assert np.isnan(line[1]) + assert np.isnan(line[2]) model = RandomForest(configspace=cs) config_array = model._impute_inactive(config_array) for line in config_array: if line[0] == 0: assert line[1] == 2 + assert line[3] == 3 elif line[0] == 1: assert line[2] == -1 + assert line[3] == 3 + elif line[0] == 2: + assert line[1] == 2 + assert line[2] == -1