We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
SMAC optimizer messes up mlos_bench logging
SMAC optimizer completely overrides our logging setup and installs its own formatter, output handler, and so on. As a result, as soon as SMAC optimizer is initialized, mlos_bench stops writing to its log file, and all logging goes to stdout, in different format, and at different log level (always INFO). We need to find a way to make SMAC use our logger instead of setting up its own from scratch

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py`
Content:
```
1 #
2 # Copyright (c) Microsoft Corporation.
3 # Licensed under the MIT License.
4 #
5 """
6 Contains the wrapper class for SMAC Bayesian optimizers.
7 See Also: <https://automl.github.io/SMAC3/main/index.html>
8 """
9 
10 from pathlib import Path
11 from typing import Dict, List, Optional, TYPE_CHECKING
12 from tempfile import TemporaryDirectory
13 
14 import ConfigSpace
15 import numpy.typing as npt
16 import pandas as pd
17 
18 from mlos_core.optimizers.bayesian_optimizers.bayesian_optimizer import BaseBayesianOptimizer
19 from mlos_core.spaces.adapters.adapter import BaseSpaceAdapter
20 
21 
22 class SmacOptimizer(BaseBayesianOptimizer):
23     """Wrapper class for SMAC based Bayesian optimization.
24 
25     Parameters
26     ----------
27     parameter_space : ConfigSpace.ConfigurationSpace
28         The parameter space to optimize.
29 
30     space_adapter : BaseSpaceAdapter
31         The space adapter class to employ for parameter space transformations.
32 
33     seed : Optional[int]
34         By default SMAC uses a known seed (0) to keep results reproducible.
35         However, if a `None` seed is explicitly provided, we let a random seed be produced by SMAC.
36 
37     run_name : Optional[str]
38         Name of this run. This is used to easily distinguish across different runs.
39         If set to `None` (default), SMAC will generate a hash from metadata.
40 
41     output_directory : Optional[str]
42         The directory where SMAC output will saved. If set to `None` (default), a temporary dir will be used.
43 
44     max_trials : int
45         Maximum number of trials (i.e., function evaluations) to be run. Defaults to 100.
46         Note that modifying this value directly affects the value of `n_random_init`, if latter is set to `None`.
47 
48     n_random_init : Optional[int]
49         Number of points evaluated at start to bootstrap the optimizer. Defaults to 10.
50 
51     n_random_probability: Optional[float]
52         Probability of choosing to evaluate a random configuration during optimization.
53         Defaults to `0.1`. Setting this to a higher value favors exploration over exploitation.
54     """
55 
56     def __init__(self, *,  # pylint: disable=too-many-locals
57                  parameter_space: ConfigSpace.ConfigurationSpace,
58                  space_adapter: Optional[BaseSpaceAdapter] = None,
59                  seed: Optional[int] = 0,
60                  run_name: Optional[str] = None,
61                  output_directory: Optional[str] = None,
62                  max_trials: int = 100,
63                  n_random_init: Optional[int] = 10,
64                  n_random_probability: Optional[float] = 0.1):
65 
66         super().__init__(
67             parameter_space=parameter_space,
68             space_adapter=space_adapter,
69         )
70 
71         # Declare at the top because we need it in __del__/cleanup()
72         self._temp_output_directory: Optional[TemporaryDirectory] = None
73 
74         # pylint: disable=import-outside-toplevel
75         from smac import HyperparameterOptimizationFacade as Optimizer_Smac
76         from smac import Scenario
77         from smac.intensifier.abstract_intensifier import AbstractIntensifier
78         from smac.initial_design import LatinHypercubeInitialDesign
79         from smac.main.config_selector import ConfigSelector
80         from smac.random_design.probability_design import ProbabilityRandomDesign
81         from smac.runhistory import TrialInfo
82 
83         # Store for TrialInfo instances returned by .ask()
84         self.trial_info_map: Dict[ConfigSpace.Configuration, TrialInfo] = {}
85 
86         # The default when not specified is to use a known seed (0) to keep results reproducible.
87         # However, if a `None` seed is explicitly provided, we let a random seed be produced by SMAC.
88         # https://automl.github.io/SMAC3/main/api/smac.scenario.html#smac.scenario.Scenario
89         seed = -1 if seed is None else seed
90 
91         # Create temporary directory for SMAC output (if none provided)
92         if output_directory is None:
93             # pylint: disable=consider-using-with
94             try:
95                 self._temp_output_directory = TemporaryDirectory(ignore_cleanup_errors=True)  # Argument added in Python 3.10
96             except TypeError:
97                 self._temp_output_directory = TemporaryDirectory()
98             output_directory = self._temp_output_directory.name
99 
100         scenario: Scenario = Scenario(
101             self.optimizer_parameter_space,
102             name=run_name,
103             output_directory=Path(output_directory),
104             deterministic=True,
105             n_trials=max_trials,
106             seed=seed or -1,  # if -1, SMAC will generate a random seed internally
107             n_workers=1,  # Use a single thread for evaluating trials
108         )
109         intensifier: AbstractIntensifier = Optimizer_Smac.get_intensifier(scenario, max_config_calls=1)
110         config_selector: ConfigSelector = ConfigSelector(scenario, retrain_after=1)
111 
112         initial_design: Optional[LatinHypercubeInitialDesign] = None
113         if n_random_init is not None:
114             initial_design = LatinHypercubeInitialDesign(scenario=scenario, n_configs=n_random_init)
115         random_design: Optional[ProbabilityRandomDesign] = None
116         if n_random_probability is not None:
117             random_design = ProbabilityRandomDesign(probability=n_random_probability)
118 
119         self.base_optimizer = Optimizer_Smac(
120             scenario,
121             SmacOptimizer._dummy_target_func,
122             initial_design=initial_design,
123             intensifier=intensifier,
124             random_design=random_design,
125             config_selector=config_selector,
126             overwrite=True,
127         )
128 
129     def __del__(self) -> None:
130         # Best-effort attempt to clean up, in case the user forgets to call .cleanup()
131         self.cleanup()
132 
133     @staticmethod
134     def _dummy_target_func(config: ConfigSpace.Configuration, seed: int = 0) -> None:
135         """Dummy target function for SMAC optimizer.
136 
137         Since we only use the ask-and-tell interface, this is never called.
138 
139         Parameters
140         ----------
141         config : ConfigSpace.Configuration
142             Configuration to evaluate.
143 
144         seed : int
145             Random seed to use for the target function. Not actually used.
146         """
147         # NOTE: Providing a target function when using the ask-and-tell interface is an imperfection of the API
148         # -- this planned to be fixed in some future release: https://github.com/automl/SMAC3/issues/946
149         raise RuntimeError('This function should never be called.')
150 
151     def _register(self, configurations: pd.DataFrame, scores: pd.Series, context: Optional[pd.DataFrame] = None) -> None:
152         """Registers the given configurations and scores.
153 
154         Parameters
155         ----------
156         configurations : pd.DataFrame
157             Dataframe of configurations / parameters. The columns are parameter names and the rows are the configurations.
158 
159         scores : pd.Series
160             Scores from running the configurations. The index is the same as the index of the configurations.
161 
162         context : pd.DataFrame
163             Not Yet Implemented.
164         """
165         from smac.runhistory import StatusType, TrialInfo, TrialValue  # pylint: disable=import-outside-toplevel
166 
167         if context is not None:
168             raise NotImplementedError()
169 
170         # Register each trial (one-by-one)
171         for config, score in zip(self._to_configspace_configs(configurations), scores.tolist()):
172             # Retrieve previously generated TrialInfo (returned by .ask()) or create new TrialInfo instance
173             info: TrialInfo = self.trial_info_map.get(config, TrialInfo(config=config, seed=self.base_optimizer.scenario.seed))
174             value: TrialValue = TrialValue(cost=score, time=0.0, status=StatusType.SUCCESS)
175             self.base_optimizer.tell(info, value, save=False)
176 
177         # Save optimizer once we register all configs
178         self.base_optimizer.optimizer.save()
179 
180     def _suggest(self, context: Optional[pd.DataFrame] = None) -> pd.DataFrame:
181         """Suggests a new configuration.
182 
183         Parameters
184         ----------
185         context : pd.DataFrame
186             Not Yet Implemented.
187 
188         Returns
189         -------
190         configuration : pd.DataFrame
191             Pandas dataframe with a single row. Column names are the parameter names.
192         """
193         if TYPE_CHECKING:
194             from smac.runhistory import TrialInfo  # pylint: disable=import-outside-toplevel
195 
196         if context is not None:
197             raise NotImplementedError()
198 
199         trial: TrialInfo = self.base_optimizer.ask()
200         self.trial_info_map[trial.config] = trial
201         return pd.DataFrame([trial.config], columns=list(self.optimizer_parameter_space.keys()))
202 
203     def register_pending(self, configurations: pd.DataFrame, context: Optional[pd.DataFrame] = None) -> None:
204         raise NotImplementedError()
205 
206     def surrogate_predict(self, configurations: pd.DataFrame, context: Optional[pd.DataFrame] = None) -> npt.NDArray:
207         from smac.utils.configspace import convert_configurations_to_array  # pylint: disable=import-outside-toplevel
208 
209         if context is not None:
210             raise NotImplementedError()
211         if self._space_adapter:
212             raise NotImplementedError()
213 
214         # pylint: disable=protected-access
215         if len(self._observations) < self.base_optimizer._initial_design._n_configs:
216             raise RuntimeError('Surrogate model can make predictions *only* after all initial points have been evaluated')
217         if self.base_optimizer._config_selector._model is None:
218             raise RuntimeError('Surrogate model is not yet trained')
219 
220         configs: npt.NDArray = convert_configurations_to_array(self._to_configspace_configs(configurations))
221         mean_predictions, _ = self.base_optimizer._config_selector._model.predict(configs)
222         return mean_predictions.reshape(-1,)
223 
224     def acquisition_function(self, configurations: pd.DataFrame, context: Optional[pd.DataFrame] = None) -> npt.NDArray:
225         if context is not None:
226             raise NotImplementedError()
227         if self._space_adapter:
228             raise NotImplementedError()
229 
230         # pylint: disable=protected-access
231         if self.base_optimizer._config_selector._acquisition_function is None:
232             raise RuntimeError('Acquisition function is not yet initialized')
233 
234         configs: list = self._to_configspace_configs(configurations)
235         return self.base_optimizer._config_selector._acquisition_function(configs).reshape(-1,)
236 
237     def cleanup(self) -> None:
238         if self._temp_output_directory is not None:
239             self._temp_output_directory.cleanup()
240             self._temp_output_directory = None
241 
242     def _to_configspace_configs(self, configurations: pd.DataFrame) -> List[ConfigSpace.Configuration]:
243         """Convert a dataframe of configurations to a list of ConfigSpace configurations.
244 
245         Parameters
246         ----------
247         configurations : pd.DataFrame
248             Dataframe of configurations / parameters. The columns are parameter names and the rows are the configurations.
249 
250         Returns
251         -------
252         configurations : list
253             List of ConfigSpace configurations.
254         """
255         return [
256             ConfigSpace.Configuration(self.optimizer_parameter_space, values=config.to_dict())
257             for (_, config) in configurations.iterrows()
258         ]
259
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py b/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py
--- a/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py
+++ b/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py
@@ -124,6 +124,7 @@
             random_design=random_design,
             config_selector=config_selector,
             overwrite=True,
+            logging_level=False,  # Use the existing logger
         )
 
     def __del__(self) -> None:

{"golden_diff": "diff --git a/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py b/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py\n--- a/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py\n+++ b/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py\n@@ -124,6 +124,7 @@\n             random_design=random_design,\n             config_selector=config_selector,\n             overwrite=True,\n+            logging_level=False,  # Use the existing logger\n         )\n \n     def __del__(self) -> None:\n", "issue": "SMAC optimizer messes up mlos_bench logging\nSMAC optimizer completely overrides our logging setup and installs its own formatter, output handler, and so on. As a result, as soon as SMAC optimizer is initialized, mlos_bench stops writing to its log file, and all logging goes to stdout, in different format, and at different log level (always INFO). We need to find a way to make SMAC use our logger instead of setting up its own from scratch\n", "before_files": [{"content": "#\n# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n#\n\"\"\"\nContains the wrapper class for SMAC Bayesian optimizers.\nSee Also: <https://automl.github.io/SMAC3/main/index.html>\n\"\"\"\n\nfrom pathlib import Path\nfrom typing import Dict, List, Optional, TYPE_CHECKING\nfrom tempfile import TemporaryDirectory\n\nimport ConfigSpace\nimport numpy.typing as npt\nimport pandas as pd\n\nfrom mlos_core.optimizers.bayesian_optimizers.bayesian_optimizer import BaseBayesianOptimizer\nfrom mlos_core.spaces.adapters.adapter import BaseSpaceAdapter\n\n\nclass SmacOptimizer(BaseBayesianOptimizer):\n    \"\"\"Wrapper class for SMAC based Bayesian optimization.\n\n    Parameters\n    ----------\n    parameter_space : ConfigSpace.ConfigurationSpace\n        The parameter space to optimize.\n\n    space_adapter : BaseSpaceAdapter\n        The space adapter class to employ for parameter space transformations.\n\n    seed : Optional[int]\n        By default SMAC uses a known seed (0) to keep results reproducible.\n        However, if a `None` seed is explicitly provided, we let a random seed be produced by SMAC.\n\n    run_name : Optional[str]\n        Name of this run. This is used to easily distinguish across different runs.\n        If set to `None` (default), SMAC will generate a hash from metadata.\n\n    output_directory : Optional[str]\n        The directory where SMAC output will saved. If set to `None` (default), a temporary dir will be used.\n\n    max_trials : int\n        Maximum number of trials (i.e., function evaluations) to be run. Defaults to 100.\n        Note that modifying this value directly affects the value of `n_random_init`, if latter is set to `None`.\n\n    n_random_init : Optional[int]\n        Number of points evaluated at start to bootstrap the optimizer. Defaults to 10.\n\n    n_random_probability: Optional[float]\n        Probability of choosing to evaluate a random configuration during optimization.\n        Defaults to `0.1`. Setting this to a higher value favors exploration over exploitation.\n    \"\"\"\n\n    def __init__(self, *,  # pylint: disable=too-many-locals\n                 parameter_space: ConfigSpace.ConfigurationSpace,\n                 space_adapter: Optional[BaseSpaceAdapter] = None,\n                 seed: Optional[int] = 0,\n                 run_name: Optional[str] = None,\n                 output_directory: Optional[str] = None,\n                 max_trials: int = 100,\n                 n_random_init: Optional[int] = 10,\n                 n_random_probability: Optional[float] = 0.1):\n\n        super().__init__(\n            parameter_space=parameter_space,\n            space_adapter=space_adapter,\n        )\n\n        # Declare at the top because we need it in __del__/cleanup()\n        self._temp_output_directory: Optional[TemporaryDirectory] = None\n\n        # pylint: disable=import-outside-toplevel\n        from smac import HyperparameterOptimizationFacade as Optimizer_Smac\n        from smac import Scenario\n        from smac.intensifier.abstract_intensifier import AbstractIntensifier\n        from smac.initial_design import LatinHypercubeInitialDesign\n        from smac.main.config_selector import ConfigSelector\n        from smac.random_design.probability_design import ProbabilityRandomDesign\n        from smac.runhistory import TrialInfo\n\n        # Store for TrialInfo instances returned by .ask()\n        self.trial_info_map: Dict[ConfigSpace.Configuration, TrialInfo] = {}\n\n        # The default when not specified is to use a known seed (0) to keep results reproducible.\n        # However, if a `None` seed is explicitly provided, we let a random seed be produced by SMAC.\n        # https://automl.github.io/SMAC3/main/api/smac.scenario.html#smac.scenario.Scenario\n        seed = -1 if seed is None else seed\n\n        # Create temporary directory for SMAC output (if none provided)\n        if output_directory is None:\n            # pylint: disable=consider-using-with\n            try:\n                self._temp_output_directory = TemporaryDirectory(ignore_cleanup_errors=True)  # Argument added in Python 3.10\n            except TypeError:\n                self._temp_output_directory = TemporaryDirectory()\n            output_directory = self._temp_output_directory.name\n\n        scenario: Scenario = Scenario(\n            self.optimizer_parameter_space,\n            name=run_name,\n            output_directory=Path(output_directory),\n            deterministic=True,\n            n_trials=max_trials,\n            seed=seed or -1,  # if -1, SMAC will generate a random seed internally\n            n_workers=1,  # Use a single thread for evaluating trials\n        )\n        intensifier: AbstractIntensifier = Optimizer_Smac.get_intensifier(scenario, max_config_calls=1)\n        config_selector: ConfigSelector = ConfigSelector(scenario, retrain_after=1)\n\n        initial_design: Optional[LatinHypercubeInitialDesign] = None\n        if n_random_init is not None:\n            initial_design = LatinHypercubeInitialDesign(scenario=scenario, n_configs=n_random_init)\n        random_design: Optional[ProbabilityRandomDesign] = None\n        if n_random_probability is not None:\n            random_design = ProbabilityRandomDesign(probability=n_random_probability)\n\n        self.base_optimizer = Optimizer_Smac(\n            scenario,\n            SmacOptimizer._dummy_target_func,\n            initial_design=initial_design,\n            intensifier=intensifier,\n            random_design=random_design,\n            config_selector=config_selector,\n            overwrite=True,\n        )\n\n    def __del__(self) -> None:\n        # Best-effort attempt to clean up, in case the user forgets to call .cleanup()\n        self.cleanup()\n\n    @staticmethod\n    def _dummy_target_func(config: ConfigSpace.Configuration, seed: int = 0) -> None:\n        \"\"\"Dummy target function for SMAC optimizer.\n\n        Since we only use the ask-and-tell interface, this is never called.\n\n        Parameters\n        ----------\n        config : ConfigSpace.Configuration\n            Configuration to evaluate.\n\n        seed : int\n            Random seed to use for the target function. Not actually used.\n        \"\"\"\n        # NOTE: Providing a target function when using the ask-and-tell interface is an imperfection of the API\n        # -- this planned to be fixed in some future release: https://github.com/automl/SMAC3/issues/946\n        raise RuntimeError('This function should never be called.')\n\n    def _register(self, configurations: pd.DataFrame, scores: pd.Series, context: Optional[pd.DataFrame] = None) -> None:\n        \"\"\"Registers the given configurations and scores.\n\n        Parameters\n        ----------\n        configurations : pd.DataFrame\n            Dataframe of configurations / parameters. The columns are parameter names and the rows are the configurations.\n\n        scores : pd.Series\n            Scores from running the configurations. The index is the same as the index of the configurations.\n\n        context : pd.DataFrame\n            Not Yet Implemented.\n        \"\"\"\n        from smac.runhistory import StatusType, TrialInfo, TrialValue  # pylint: disable=import-outside-toplevel\n\n        if context is not None:\n            raise NotImplementedError()\n\n        # Register each trial (one-by-one)\n        for config, score in zip(self._to_configspace_configs(configurations), scores.tolist()):\n            # Retrieve previously generated TrialInfo (returned by .ask()) or create new TrialInfo instance\n            info: TrialInfo = self.trial_info_map.get(config, TrialInfo(config=config, seed=self.base_optimizer.scenario.seed))\n            value: TrialValue = TrialValue(cost=score, time=0.0, status=StatusType.SUCCESS)\n            self.base_optimizer.tell(info, value, save=False)\n\n        # Save optimizer once we register all configs\n        self.base_optimizer.optimizer.save()\n\n    def _suggest(self, context: Optional[pd.DataFrame] = None) -> pd.DataFrame:\n        \"\"\"Suggests a new configuration.\n\n        Parameters\n        ----------\n        context : pd.DataFrame\n            Not Yet Implemented.\n\n        Returns\n        -------\n        configuration : pd.DataFrame\n            Pandas dataframe with a single row. Column names are the parameter names.\n        \"\"\"\n        if TYPE_CHECKING:\n            from smac.runhistory import TrialInfo  # pylint: disable=import-outside-toplevel\n\n        if context is not None:\n            raise NotImplementedError()\n\n        trial: TrialInfo = self.base_optimizer.ask()\n        self.trial_info_map[trial.config] = trial\n        return pd.DataFrame([trial.config], columns=list(self.optimizer_parameter_space.keys()))\n\n    def register_pending(self, configurations: pd.DataFrame, context: Optional[pd.DataFrame] = None) -> None:\n        raise NotImplementedError()\n\n    def surrogate_predict(self, configurations: pd.DataFrame, context: Optional[pd.DataFrame] = None) -> npt.NDArray:\n        from smac.utils.configspace import convert_configurations_to_array  # pylint: disable=import-outside-toplevel\n\n        if context is not None:\n            raise NotImplementedError()\n        if self._space_adapter:\n            raise NotImplementedError()\n\n        # pylint: disable=protected-access\n        if len(self._observations) < self.base_optimizer._initial_design._n_configs:\n            raise RuntimeError('Surrogate model can make predictions *only* after all initial points have been evaluated')\n        if self.base_optimizer._config_selector._model is None:\n            raise RuntimeError('Surrogate model is not yet trained')\n\n        configs: npt.NDArray = convert_configurations_to_array(self._to_configspace_configs(configurations))\n        mean_predictions, _ = self.base_optimizer._config_selector._model.predict(configs)\n        return mean_predictions.reshape(-1,)\n\n    def acquisition_function(self, configurations: pd.DataFrame, context: Optional[pd.DataFrame] = None) -> npt.NDArray:\n        if context is not None:\n            raise NotImplementedError()\n        if self._space_adapter:\n            raise NotImplementedError()\n\n        # pylint: disable=protected-access\n        if self.base_optimizer._config_selector._acquisition_function is None:\n            raise RuntimeError('Acquisition function is not yet initialized')\n\n        configs: list = self._to_configspace_configs(configurations)\n        return self.base_optimizer._config_selector._acquisition_function(configs).reshape(-1,)\n\n    def cleanup(self) -> None:\n        if self._temp_output_directory is not None:\n            self._temp_output_directory.cleanup()\n            self._temp_output_directory = None\n\n    def _to_configspace_configs(self, configurations: pd.DataFrame) -> List[ConfigSpace.Configuration]:\n        \"\"\"Convert a dataframe of configurations to a list of ConfigSpace configurations.\n\n        Parameters\n        ----------\n        configurations : pd.DataFrame\n            Dataframe of configurations / parameters. The columns are parameter names and the rows are the configurations.\n\n        Returns\n        -------\n        configurations : list\n            List of ConfigSpace configurations.\n        \"\"\"\n        return [\n            ConfigSpace.Configuration(self.optimizer_parameter_space, values=config.to_dict())\n            for (_, config) in configurations.iterrows()\n        ]\n", "path": "mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py"}], "after_files": [{"content": "#\n# Copyright (c) Microsoft Corporation.\n# Licensed under the MIT License.\n#\n\"\"\"\nContains the wrapper class for SMAC Bayesian optimizers.\nSee Also: <https://automl.github.io/SMAC3/main/index.html>\n\"\"\"\n\nfrom pathlib import Path\nfrom typing import Dict, List, Optional, TYPE_CHECKING\nfrom tempfile import TemporaryDirectory\n\nimport ConfigSpace\nimport numpy.typing as npt\nimport pandas as pd\n\nfrom mlos_core.optimizers.bayesian_optimizers.bayesian_optimizer import BaseBayesianOptimizer\nfrom mlos_core.spaces.adapters.adapter import BaseSpaceAdapter\n\n\nclass SmacOptimizer(BaseBayesianOptimizer):\n    \"\"\"Wrapper class for SMAC based Bayesian optimization.\n\n    Parameters\n    ----------\n    parameter_space : ConfigSpace.ConfigurationSpace\n        The parameter space to optimize.\n\n    space_adapter : BaseSpaceAdapter\n        The space adapter class to employ for parameter space transformations.\n\n    seed : Optional[int]\n        By default SMAC uses a known seed (0) to keep results reproducible.\n        However, if a `None` seed is explicitly provided, we let a random seed be produced by SMAC.\n\n    run_name : Optional[str]\n        Name of this run. This is used to easily distinguish across different runs.\n        If set to `None` (default), SMAC will generate a hash from metadata.\n\n    output_directory : Optional[str]\n        The directory where SMAC output will saved. If set to `None` (default), a temporary dir will be used.\n\n    max_trials : int\n        Maximum number of trials (i.e., function evaluations) to be run. Defaults to 100.\n        Note that modifying this value directly affects the value of `n_random_init`, if latter is set to `None`.\n\n    n_random_init : Optional[int]\n        Number of points evaluated at start to bootstrap the optimizer. Defaults to 10.\n\n    n_random_probability: Optional[float]\n        Probability of choosing to evaluate a random configuration during optimization.\n        Defaults to `0.1`. Setting this to a higher value favors exploration over exploitation.\n    \"\"\"\n\n    def __init__(self, *,  # pylint: disable=too-many-locals\n                 parameter_space: ConfigSpace.ConfigurationSpace,\n                 space_adapter: Optional[BaseSpaceAdapter] = None,\n                 seed: Optional[int] = 0,\n                 run_name: Optional[str] = None,\n                 output_directory: Optional[str] = None,\n                 max_trials: int = 100,\n                 n_random_init: Optional[int] = 10,\n                 n_random_probability: Optional[float] = 0.1):\n\n        super().__init__(\n            parameter_space=parameter_space,\n            space_adapter=space_adapter,\n        )\n\n        # Declare at the top because we need it in __del__/cleanup()\n        self._temp_output_directory: Optional[TemporaryDirectory] = None\n\n        # pylint: disable=import-outside-toplevel\n        from smac import HyperparameterOptimizationFacade as Optimizer_Smac\n        from smac import Scenario\n        from smac.intensifier.abstract_intensifier import AbstractIntensifier\n        from smac.initial_design import LatinHypercubeInitialDesign\n        from smac.main.config_selector import ConfigSelector\n        from smac.random_design.probability_design import ProbabilityRandomDesign\n        from smac.runhistory import TrialInfo\n\n        # Store for TrialInfo instances returned by .ask()\n        self.trial_info_map: Dict[ConfigSpace.Configuration, TrialInfo] = {}\n\n        # The default when not specified is to use a known seed (0) to keep results reproducible.\n        # However, if a `None` seed is explicitly provided, we let a random seed be produced by SMAC.\n        # https://automl.github.io/SMAC3/main/api/smac.scenario.html#smac.scenario.Scenario\n        seed = -1 if seed is None else seed\n\n        # Create temporary directory for SMAC output (if none provided)\n        if output_directory is None:\n            # pylint: disable=consider-using-with\n            try:\n                self._temp_output_directory = TemporaryDirectory(ignore_cleanup_errors=True)  # Argument added in Python 3.10\n            except TypeError:\n                self._temp_output_directory = TemporaryDirectory()\n            output_directory = self._temp_output_directory.name\n\n        scenario: Scenario = Scenario(\n            self.optimizer_parameter_space,\n            name=run_name,\n            output_directory=Path(output_directory),\n            deterministic=True,\n            n_trials=max_trials,\n            seed=seed or -1,  # if -1, SMAC will generate a random seed internally\n            n_workers=1,  # Use a single thread for evaluating trials\n        )\n        intensifier: AbstractIntensifier = Optimizer_Smac.get_intensifier(scenario, max_config_calls=1)\n        config_selector: ConfigSelector = ConfigSelector(scenario, retrain_after=1)\n\n        initial_design: Optional[LatinHypercubeInitialDesign] = None\n        if n_random_init is not None:\n            initial_design = LatinHypercubeInitialDesign(scenario=scenario, n_configs=n_random_init)\n        random_design: Optional[ProbabilityRandomDesign] = None\n        if n_random_probability is not None:\n            random_design = ProbabilityRandomDesign(probability=n_random_probability)\n\n        self.base_optimizer = Optimizer_Smac(\n            scenario,\n            SmacOptimizer._dummy_target_func,\n            initial_design=initial_design,\n            intensifier=intensifier,\n            random_design=random_design,\n            config_selector=config_selector,\n            overwrite=True,\n            logging_level=False,  # Use the existing logger\n        )\n\n    def __del__(self) -> None:\n        # Best-effort attempt to clean up, in case the user forgets to call .cleanup()\n        self.cleanup()\n\n    @staticmethod\n    def _dummy_target_func(config: ConfigSpace.Configuration, seed: int = 0) -> None:\n        \"\"\"Dummy target function for SMAC optimizer.\n\n        Since we only use the ask-and-tell interface, this is never called.\n\n        Parameters\n        ----------\n        config : ConfigSpace.Configuration\n            Configuration to evaluate.\n\n        seed : int\n            Random seed to use for the target function. Not actually used.\n        \"\"\"\n        # NOTE: Providing a target function when using the ask-and-tell interface is an imperfection of the API\n        # -- this planned to be fixed in some future release: https://github.com/automl/SMAC3/issues/946\n        raise RuntimeError('This function should never be called.')\n\n    def _register(self, configurations: pd.DataFrame, scores: pd.Series, context: Optional[pd.DataFrame] = None) -> None:\n        \"\"\"Registers the given configurations and scores.\n\n        Parameters\n        ----------\n        configurations : pd.DataFrame\n            Dataframe of configurations / parameters. The columns are parameter names and the rows are the configurations.\n\n        scores : pd.Series\n            Scores from running the configurations. The index is the same as the index of the configurations.\n\n        context : pd.DataFrame\n            Not Yet Implemented.\n        \"\"\"\n        from smac.runhistory import StatusType, TrialInfo, TrialValue  # pylint: disable=import-outside-toplevel\n\n        if context is not None:\n            raise NotImplementedError()\n\n        # Register each trial (one-by-one)\n        for config, score in zip(self._to_configspace_configs(configurations), scores.tolist()):\n            # Retrieve previously generated TrialInfo (returned by .ask()) or create new TrialInfo instance\n            info: TrialInfo = self.trial_info_map.get(config, TrialInfo(config=config, seed=self.base_optimizer.scenario.seed))\n            value: TrialValue = TrialValue(cost=score, time=0.0, status=StatusType.SUCCESS)\n            self.base_optimizer.tell(info, value, save=False)\n\n        # Save optimizer once we register all configs\n        self.base_optimizer.optimizer.save()\n\n    def _suggest(self, context: Optional[pd.DataFrame] = None) -> pd.DataFrame:\n        \"\"\"Suggests a new configuration.\n\n        Parameters\n        ----------\n        context : pd.DataFrame\n            Not Yet Implemented.\n\n        Returns\n        -------\n        configuration : pd.DataFrame\n            Pandas dataframe with a single row. Column names are the parameter names.\n        \"\"\"\n        if TYPE_CHECKING:\n            from smac.runhistory import TrialInfo  # pylint: disable=import-outside-toplevel\n\n        if context is not None:\n            raise NotImplementedError()\n\n        trial: TrialInfo = self.base_optimizer.ask()\n        self.trial_info_map[trial.config] = trial\n        return pd.DataFrame([trial.config], columns=list(self.optimizer_parameter_space.keys()))\n\n    def register_pending(self, configurations: pd.DataFrame, context: Optional[pd.DataFrame] = None) -> None:\n        raise NotImplementedError()\n\n    def surrogate_predict(self, configurations: pd.DataFrame, context: Optional[pd.DataFrame] = None) -> npt.NDArray:\n        from smac.utils.configspace import convert_configurations_to_array  # pylint: disable=import-outside-toplevel\n\n        if context is not None:\n            raise NotImplementedError()\n        if self._space_adapter:\n            raise NotImplementedError()\n\n        # pylint: disable=protected-access\n        if len(self._observations) < self.base_optimizer._initial_design._n_configs:\n            raise RuntimeError('Surrogate model can make predictions *only* after all initial points have been evaluated')\n        if self.base_optimizer._config_selector._model is None:\n            raise RuntimeError('Surrogate model is not yet trained')\n\n        configs: npt.NDArray = convert_configurations_to_array(self._to_configspace_configs(configurations))\n        mean_predictions, _ = self.base_optimizer._config_selector._model.predict(configs)\n        return mean_predictions.reshape(-1,)\n\n    def acquisition_function(self, configurations: pd.DataFrame, context: Optional[pd.DataFrame] = None) -> npt.NDArray:\n        if context is not None:\n            raise NotImplementedError()\n        if self._space_adapter:\n            raise NotImplementedError()\n\n        # pylint: disable=protected-access\n        if self.base_optimizer._config_selector._acquisition_function is None:\n            raise RuntimeError('Acquisition function is not yet initialized')\n\n        configs: list = self._to_configspace_configs(configurations)\n        return self.base_optimizer._config_selector._acquisition_function(configs).reshape(-1,)\n\n    def cleanup(self) -> None:\n        if self._temp_output_directory is not None:\n            self._temp_output_directory.cleanup()\n            self._temp_output_directory = None\n\n    def _to_configspace_configs(self, configurations: pd.DataFrame) -> List[ConfigSpace.Configuration]:\n        \"\"\"Convert a dataframe of configurations to a list of ConfigSpace configurations.\n\n        Parameters\n        ----------\n        configurations : pd.DataFrame\n            Dataframe of configurations / parameters. The columns are parameter names and the rows are the configurations.\n\n        Returns\n        -------\n        configurations : list\n            List of ConfigSpace configurations.\n        \"\"\"\n        return [\n            ConfigSpace.Configuration(self.optimizer_parameter_space, values=config.to_dict())\n            for (_, config) in configurations.iterrows()\n        ]\n", "path": "mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py"}]}