From 58f9fb9045e63530692adc6306fc297e318f11f2 Mon Sep 17 00:00:00 2001 From: Janis Fix Date: Thu, 11 Jul 2024 14:44:46 +0200 Subject: [PATCH 1/4] v0.1.2 fix Adjust __get_state to ignore timer as class attribute to make it work with dask --- src/dehb/optimizers/dehb.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dehb/optimizers/dehb.py b/src/dehb/optimizers/dehb.py index 9968518..f116942 100644 --- a/src/dehb/optimizers/dehb.py +++ b/src/dehb/optimizers/dehb.py @@ -269,6 +269,7 @@ def __getstate__(self): d = dict(self.__dict__) d["client"] = None # hack to allow Dask client to be a class attribute d["logger"] = None # hack to allow logger object to be a class attribute + d["_runtime_budget_timer"] = None # hack to allow timer object to be a class attribute return d def __del__(self): From e277f36ceb4ab9287f912c51c4909000c95c21b1 Mon Sep 17 00:00:00 2001 From: Janis Fix Date: Mon, 12 Aug 2024 13:50:23 +0200 Subject: [PATCH 2/4] Add maintainer note to README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 3d7df39..4831d50 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ [![Static Badge](https://img.shields.io/badge/python-3.8%20%7C%203.9%20%7C%203.10%20%7C%203.11%20-blue)](https://pypi.org/project/dehb/) [![arXiv](https://img.shields.io/badge/arXiv-2105.09821-b31b1b.svg)](https://arxiv.org/abs/2105.09821) +> **_NOTE:_** Following the release of v0.1.2, this project will be maintained for stability and compatibility purposes but will no longer undergo active development. While new features will not be added, the repository will continue to receive necessary updates to ensure ongoing functionality. For any feature requests, please feel free to fork the repository and submit a pull request. + Welcome to DEHB, an algorithm for Hyperparameter Optimization (HPO). DEHB uses Differential Evolution (DE) under-the-hood as an Evolutionary Algorithm to power the black-box optimization that HPO problems pose. `dehb` is a python package implementing the [DEHB](https://arxiv.org/abs/2105.09821) algorithm. It offers an intuitive interface to optimize user-defined problems using DEHB. From 2d3990007c53422a4c6016d70b6913ff5fe69bd2 Mon Sep 17 00:00:00 2001 From: Neeratyoy Mallik Date: Fri, 25 Apr 2025 20:54:35 +0200 Subject: [PATCH 3/4] Update and rename LICENSE.txt to LICENSE --- LICENSE.txt => LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename LICENSE.txt => LICENSE (99%) diff --git a/LICENSE.txt b/LICENSE similarity index 99% rename from LICENSE.txt rename to LICENSE index d60ed71..5bad67a 100644 --- a/LICENSE.txt +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright [2021] [AutoML Freiburg] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 9dfb91c24a4df4a8b0fb6d90b8b6015d7170b3d9 Mon Sep 17 00:00:00 2001 From: Luca Date: Fri, 23 Jan 2026 18:02:26 +0100 Subject: [PATCH 4/4] Fixed Issue #99 where resuming a run previously run in parallel (n_workers>1) caused the history to be disordered. This fix adds the bracket_id to the history dataframe and sorts the history by (bracket_id, fidelity, config_id) to emulate a serial execution history without disordering allowing for the tell(resume=True) function to work as intended. Additionally, updated deprecated usage of config.get_dictionary() to dict(config). --- src/dehb/optimizers/dehb.py | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/src/dehb/optimizers/dehb.py b/src/dehb/optimizers/dehb.py index f116942..cadba8c 100644 --- a/src/dehb/optimizers/dehb.py +++ b/src/dehb/optimizers/dehb.py @@ -833,7 +833,7 @@ def _save_incumbent(self): res = {} if self.use_configspace: config = self.vector_to_configspace(self.inc_config) - res["config"] = config.get_dictionary() + res["config"] = dict(config) else: res["config"] = self.inc_config.tolist() res["score"] = self.inc_score @@ -850,8 +850,11 @@ def _save_history(self, name="history.parquet.gzip"): return try: history_path = self.output_path / name - history_df = pd.DataFrame(self.history, columns=["config_id", "config", "fitness", - "cost", "fidelity", "info"]) + # Persist bracket_id to reconstruct serial replay order later + history_df = pd.DataFrame( + self.history, + columns=["bracket_id", "config_id", "config", "fitness", "cost", "fidelity", "info"], + ) # Check if the 'info' column is empty or contains only None values if history_df["info"].apply(lambda x: (isinstance(x, dict) and len(x) == 0)).all(): # Drop the 'info' column @@ -936,12 +939,20 @@ def _load_checkpoint(self, run_dir: str): history_path = run_dir / "history.parquet.gzip" history = pd.read_parquet(history_path) - # Replay history + # Sort history to emulate serial execution order if bracket_id available + if "bracket_id" in history.columns: + history = history.sort_values(by=["bracket_id", "fidelity", "config_id"]).reset_index(drop=True) + else: + # Fallback ordering for older checkpoints + history = history.sort_values(by=["fidelity", "config_id"]).reset_index(drop=True) + + # Replay history in the chosen order for _, row in history.iterrows(): job_info = { "fidelity": row["fidelity"], "config_id": row["config_id"], "config": np.array(row["config"]), + **({"bracket_id": int(row["bracket_id"]) } if "bracket_id" in history.columns else {}), } result = { "fitness": row["fitness"], @@ -993,6 +1004,8 @@ def tell(self, job_info: dict, result: dict, replay: bool=False) -> None: job_info_container["fidelity"] = job_info["fidelity"] job_info_container["config"] = job_info["config"] job_info_container["config_id"] = job_info["config_id"] + if "bracket_id" in job_info: + job_info_container["bracket_id"] = job_info["bracket_id"] # Update entry in ConfigRepository self.config_repository.configs[job_info["config_id"]].config = job_info["config"] @@ -1036,8 +1049,16 @@ def tell(self, job_info: dict, result: dict, replay: bool=False) -> None: inc_changed = True # book-keeping self._update_trackers( - traj=self.inc_score, runtime=cost, history=( - config_id, config.tolist(), float(fitness), float(cost), float(fidelity), info, + traj=self.inc_score, + runtime=cost, + history=( + bracket_id, + config_id, + config.tolist(), + float(fitness), + float(cost), + float(fidelity), + info, ), ) @@ -1167,7 +1188,7 @@ def run(self, fevals=None, brackets=None, total_cost=None, single_node_with_gpus self.logger.info("Incumbent config: ") if self.use_configspace: config = self.vector_to_configspace(self.inc_config) - for k, v in config.get_dictionary().items(): + for k, v in dict(config).items(): self.logger.info(f"{k}: {v}") else: self.logger.info(f"{self.inc_config}")