-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsmac.py
255 lines (227 loc) · 8.53 KB
/
smac.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
import functools
from typing import Any, Optional
import ConfigSpace
from ConfigSpace import Configuration
from smac import (
Scenario,
AlgorithmConfigurationFacade,
RandomFacade,
MultiFidelityFacade,
HyperparameterOptimizationFacade,
HyperbandFacade,
BlackBoxFacade,
)
from smac.facade import AbstractFacade
from smac.initial_design import (
RandomInitialDesign,
DefaultInitialDesign,
FactorialInitialDesign,
SobolInitialDesign,
LatinHypercubeInitialDesign,
)
from smac.main.config_selector import ConfigSelector
from gama.genetic_programming.components import Individual
from gama.genetic_programming.operations import _config_to_primitive_node
from gama.genetic_programming.longitudinal.operations_longitudinal import (
_config_to_longitudinal_primitive_node,
)
from gama.genetic_programming.operator_set import OperatorSet
COMPONENTS_MAPPING = {
"initial_design": {
"RandomInitialDesign": RandomInitialDesign,
"DefaultInitialDesign": DefaultInitialDesign,
"FactorialInitialDesign": FactorialInitialDesign,
"LatinHypercubeInitialDesign": LatinHypercubeInitialDesign,
"SobolInitialDesign": SobolInitialDesign,
},
"facade": {
"RandomFacade": RandomFacade,
"MultiFidelityFacade": MultiFidelityFacade,
"HyperparameterOptimizationFacade": HyperparameterOptimizationFacade,
"HyperbandFacade": HyperbandFacade,
"BlackBoxFacade": BlackBoxFacade,
"AlgorithmConfigurationFacade": AlgorithmConfigurationFacade,
},
}
def get_component(
scenario: Scenario,
component_type: str,
component_mapping: dict,
component_params: dict,
) -> Any:
"""Returns an instance of a component based on its type and parameters."""
if component_type in component_mapping:
comp_class = component_mapping[component_type]
else:
raise ValueError(
f"BayesianOptimisation: Invalid component type "
f"'{component_type}' passed to 'get_component'."
)
return comp_class(scenario=scenario, **component_params)
def get_smac(
configSpace: ConfigSpace.ConfigurationSpace,
scenario_params: Optional[dict] = None,
initial_design_params: Optional[dict] = None,
facade_params: Optional[dict] = None,
) -> AbstractFacade:
if scenario_params:
scenario = Scenario(configSpace, **scenario_params)
else:
scenario = Scenario(configSpace)
if initial_design_params:
if initial_design_params.get("type") is None:
raise ValueError(
"BayesianOptimisation: facade_params must contain a 'type' attribute."
)
initial_design_params_without_type = initial_design_params.copy()
initial_design_params_without_type.pop("type")
initial_design = get_component(
scenario=scenario,
component_type=initial_design_params["type"],
component_mapping=COMPONENTS_MAPPING["initial_design"],
component_params={**initial_design_params_without_type},
)
else:
initial_design = RandomInitialDesign(scenario=scenario)
if facade_params:
if facade_params.get("type") is None:
raise ValueError(
"BayesianOptimisation: facade_params must contain a 'type' attribute."
)
facade_params_without_type = facade_params.copy()
facade_params_without_type.pop("type")
facade = get_component(
scenario=scenario,
component_type=facade_params["type"],
component_mapping=COMPONENTS_MAPPING["facade"],
component_params={
**facade_params_without_type,
"initial_design": initial_design,
"target_function": dummy_smac_train,
},
)
else:
facade = AlgorithmConfigurationFacade(
scenario=scenario,
initial_design=initial_design,
target_function=dummy_smac_train,
logging_level=0,
config_selector=ConfigSelector(scenario, retries=1000),
)
return facade
def dummy_smac_train(config: Configuration, seed: int = 0) -> None:
_, _ = config, seed
raise NotImplementedError(
"BayesianOptimisation: dummy_smac_train should not have been called."
"Current version does not support smac.Optimize() training procedure in "
"favour to the Ask&Tell SMAC's interface. Operations.evaluate should be used "
"instead through the GAMA multi-processing interface."
)
def config_to_individual(
config: Configuration,
operations: OperatorSet,
) -> Individual:
"""Convert a SMAC configuration to a GAMA individual."""
if config is None:
raise ValueError(
"BayesianOptimisation: config_to_individual received a None config."
)
if (config_space := operations.get_search_space()) is None: # type: ignore
raise ValueError(
"BayesianOptimisation: Operations.get_search_space() returned None."
)
if (
"preprocessors" in config_space.meta
and config_space.meta["preprocessors"] in config.keys()
):
max_length = 2
else:
max_length = 1
return Individual(
main_node=_config_to_primitive_node(
config=config,
config_meta=config_space.meta,
conditions=config_space.get_conditions(),
config_length=max_length,
),
to_pipeline=operations._safe_compile or operations._compile,
)
def config_to_longitudinal_individual(
config: Configuration,
operations: OperatorSet,
) -> Individual:
"""Convert a SMAC configuration to a GAMA individual."""
if config is None:
raise ValueError(
"BayesianOptimisation: config_to_individual received a None config."
)
if (config_space := operations.get_search_space()) is None: # type: ignore
raise ValueError(
"BayesianOptimisation: Operations.get_search_space() returned None."
)
if (
"preprocessors" in config_space.meta
and config_space.meta["preprocessors"] in config.keys()
):
max_length = 2
else:
max_length = 1
return Individual(
main_node=_config_to_longitudinal_primitive_node(
config=config,
config_meta=config_space.meta,
conditions=config_space.get_conditions(),
config_length=max_length,
),
to_pipeline=operations._safe_compile or operations._compile,
)
def validate_info_config(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
if not hasattr(result, "config") or result.config is None:
raise ValueError(
f"BayesianOptimisation: Function "
f"'{func.__name__}' returned info with None config."
)
return result
return wrapper
def validate_future_valid(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
future = args[0]
if future is None or not hasattr(future, "result") or future.result is None:
raise ValueError(
f"BayesianOptimisation: Function '{func.__name__}' "
f"received a Future object or its result is not valid."
)
result = future.result
if not hasattr(result, "individual") or result.individual is None:
raise ValueError(
"BayesianOptimisation: Future object's result is missing 'individual' "
"attribute."
)
if (
not hasattr(result.individual, "fitness")
or result.individual.fitness is None
):
raise ValueError(
"BayesianOptimisation: Future object's result's individual is missing "
"'fitness' attribute."
)
required_attrs = ["values", "wallclock_time", "start_time", "process_time"]
for attr in required_attrs:
if not hasattr(result.individual.fitness, attr):
raise ValueError(
f"BayesianOptimisation: Future object's result's individual's "
f"fitness is missing '{attr}' attribute."
)
if hasattr(result.individual.fitness, "start_time") and not callable(
getattr(result.individual.fitness.start_time, "timestamp", None)
):
raise ValueError(
"BayesianOptimisation: start_time object does not have a timestamp "
"method."
)
return func(*args, **kwargs)
return wrapper