Skip to content

Commit 5ea076a

Browse files
committed
Update ConfigSpace, and docs
1 parent e9d71a5 commit 5ea076a

File tree

5 files changed

+184
-83
lines changed

5 files changed

+184
-83
lines changed

docs/advanced_usage/1_components.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,15 +96,18 @@ The acquisition maximizer also incorporates the [Random Design][random-design].
9696
## [Initial Design][smac.initial_design.abstract_initial_design]
9797

9898
The surrogate model needs data to be trained. Therefore, the initial design is used to generate the initial data points.
99-
We provide random, latin hypercube, sobol, factorial and default initial designs. The default initial design uses
100-
the default configuration from the configuration space and with the factorial initial design, we generate corner
101-
points of the configuration space. The sobol sequences are an example of quasi-random low-discrepancy sequences and
102-
the latin hypercube design is a statistical method for generating a near-random sample of parameter values from
99+
We provide [random][smac.initial_design.random_design], [latin hypercube][smac.initial_design.latin_hypercube_design], [sobol][smac.initial_design.sobol_design], [factorial][smac.initial_design.factorial_design] and [default-based][smac.initial_design.default_design] initial designs. The [default-based][smac.initial_design.default_design] initial design uses
100+
the default configuration from the configuration space and with the [factorial initial design][smac.initial_design.factorial_design], we generate corner
101+
points of the configuration space. The [sobol initial design][smac.initial_design.sobol_design] uses quasi-random low-discrepancy sequences and
102+
the [latin hypercube initial design][smac.initial_design.latin_hypercube_design] is a statistical method for generating a near-random sample of parameter values from
103103
a multidimensional distribution.
104104

105105
The initial design configurations are yielded by the config selector first. Moreover, the config selector keeps
106106
track of which configurations already have been returned to make sure a configuration is not returned twice.
107107

108+
!!! warning
109+
Distributions provided in the initial design are ignored.
110+
108111
[](){#random-design}
109112
## [Random Design][smac.initial_design.random_design]
110113

examples/1_basics/6_priors.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,10 +192,10 @@ def train(self, config: Configuration, seed: int = 0) -> float:
192192

193193
if __name__ == "__main__":
194194
mlp = MLP()
195-
default_config = mlp.configspace.get_default_configuration()
195+
default_config = mlp.prior_configspace.get_default_configuration()
196196

197197
# Define our environment variables
198-
scenario = Scenario(mlp.configspace, n_trials=40)
198+
scenario = Scenario(mlp.prior_configspace, n_trials=40)
199199

200200
# We also want to include our default configuration in the initial design
201201
initial_design = HyperparameterOptimizationFacade.get_initial_design(

smac/initial_design/random_design.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from ConfigSpace import Configuration
44

55
from smac.initial_design.abstract_initial_design import AbstractInitialDesign
6+
from smac.utils.configspace import create_uniform_configspace_copy
67

78
__copyright__ = "Copyright 2025, Leibniz University Hanover, Institute of AI"
89
__license__ = "3-clause BSD"
@@ -12,10 +13,12 @@ class RandomInitialDesign(AbstractInitialDesign):
1213
"""Initial design that evaluates random configurations."""
1314

1415
def _select_configurations(self) -> list[Configuration]:
16+
uniform_configspace = create_uniform_configspace_copy(self._configspace)
17+
1518
if self._n_configs == 1:
16-
configs = [self._configspace.sample_configuration()]
19+
configs = [uniform_configspace.sample_configuration()]
1720
else:
18-
configs = self._configspace.sample_configuration(size=self._n_configs)
21+
configs = uniform_configspace.sample_configuration(size=self._n_configs)
1922
for config in configs:
2023
config.origin = "Initial Design: Random"
2124
return configs

smac/utils/configspace.py

Lines changed: 41 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
BetaIntegerHyperparameter,
1212
CategoricalHyperparameter,
1313
Constant,
14+
FloatHyperparameter,
1415
IntegerHyperparameter,
1516
NormalFloatHyperparameter,
1617
NormalIntegerHyperparameter,
@@ -206,12 +207,13 @@ def transform_continuous_designs(
206207
configs : list[Configuration]
207208
Continuous transformed configs.
208209
"""
209-
params = configspace.get_hyperparameters()
210+
params = list(configspace.values())
210211
for idx, param in enumerate(params):
211-
if isinstance(param, IntegerHyperparameter):
212-
design[:, idx] = param._inverse_transform(param._transform(design[:, idx]))
213-
elif isinstance(param, NumericalHyperparameter):
214-
continue
212+
if isinstance(param, NumericalHyperparameter):
213+
if isinstance(param, IntegerHyperparameter):
214+
design[:, idx] = param.to_vector(param.to_value(design[:, idx]))
215+
elif isinstance(param, NumericalHyperparameter) and not isinstance(param, IntegerHyperparameter):
216+
continue
215217
elif isinstance(param, Constant):
216218
design_ = np.zeros(np.array(design.shape) + np.array((0, 1)))
217219
design_[:, :idx] = design[:, :idx]
@@ -243,73 +245,37 @@ def transform_continuous_designs(
243245
return configs
244246

245247

246-
# def check_subspace_points(
247-
# X: np.ndarray,
248-
# cont_dims: np.ndarray | list = [],
249-
# cat_dims: np.ndarray | list = [],
250-
# bounds_cont: np.ndarray | None = None,
251-
# bounds_cat: list[tuple] | None = None,
252-
# expand_bound: bool = False,
253-
# ) -> np.ndarray:
254-
# """Check which points are place inside a given subspace.
255-
256-
# Parameters
257-
# ----------
258-
# X: Optional[np.ndarray(N,D)],
259-
# points to be checked, where D = D_cont + D_cat
260-
# cont_dims: Union[np.ndarray(D_cont), List]
261-
# which dimensions represent continuous hyperparameters
262-
# cat_dims: Union[np.ndarray(D_cat), List]
263-
# which dimensions represent categorical hyperparameters
264-
# bounds_cont: optional[List[Tuple]]
265-
# subspaces bounds of categorical hyperparameters, its length is the number of continuous hyperparameters
266-
# bounds_cat: Optional[List[Tuple]]
267-
# subspaces bounds of continuous hyperparameters, its length is the number of categorical hyperparameters
268-
# expand_bound: bool
269-
# if the bound needs to be expanded to contain more points rather than the points inside the subregion
270-
# Return
271-
# ----------
272-
# indices_in_ss:np.ndarray(N)
273-
# indices of data that included in subspaces
274-
# """
275-
# if len(X.shape) == 1:
276-
# X = X[np.newaxis, :]
277-
# if len(cont_dims) == 0 and len(cat_dims) == 0:
278-
# return np.ones(X.shape[0], dtype=bool)
279-
280-
# if len(cont_dims) > 0:
281-
# if bounds_cont is None:
282-
# raise ValueError("bounds_cont must be given if cont_dims provided")
283-
284-
# if len(bounds_cont.shape) != 2 or bounds_cont.shape[1] != 2 or bounds_cont.shape[0] != len(cont_dims):
285-
# raise ValueError(
286-
# f"bounds_cont (with shape {bounds_cont.shape}) should be an array with shape of"
287-
# f"({len(cont_dims)}, 2)"
288-
# )
289-
290-
# data_in_ss = np.all(X[:, cont_dims] <= bounds_cont[:, 1], axis=1) & np.all(
291-
# X[:, cont_dims] >= bounds_cont[:, 0], axis=1
292-
# )
293-
294-
# if expand_bound:
295-
# bound_left = bounds_cont[:, 0] - np.min(X[data_in_ss][:, cont_dims] - bounds_cont[:, 0], axis=0)
296-
# bound_right = bounds_cont[:, 1] + np.min(bounds_cont[:, 1] - X[data_in_ss][:, cont_dims], axis=0)
297-
# data_in_ss = np.all(X[:, cont_dims] <= bound_right, axis=1) & np.all(X[:, cont_dims] >= bound_left,
298-
# axis=1)
299-
# else:
300-
# data_in_ss = np.ones(X.shape[0], dtype=bool)
301-
302-
# if len(cat_dims) == 0:
303-
# return data_in_ss
304-
# if bounds_cat is None:
305-
# raise ValueError("bounds_cat must be given if cat_dims provided")
306-
307-
# if len(bounds_cat) != len(cat_dims):
308-
# raise ValueError(
309-
# f"bounds_cat ({len(bounds_cat)}) and cat_dims ({len(cat_dims)}) must have " f"the same number of elements"
310-
# )
311-
312-
# for bound_cat, cat_dim in zip(bounds_cat, cat_dims):
313-
# data_in_ss &= np.in1d(X[:, cat_dim], bound_cat)
314-
315-
# return data_in_ss
248+
def create_uniform_configspace_copy(
249+
configspace: ConfigurationSpace,
250+
) -> ConfigurationSpace:
251+
"""Creates a copy of the given configuration space with uniform transformed hyperparameters.
252+
253+
Parameters
254+
----------
255+
configspace : ConfigurationSpace
256+
The configuration space to be transformed.
257+
258+
Returns
259+
-------
260+
ConfigurationSpace
261+
A new configuration space with uniform transformed hyperparameters.
262+
"""
263+
configspace_name = configspace.name
264+
configspace_meta = configspace.meta
265+
random_state = configspace.random.get_state()
266+
new_configuration_space = ConfigurationSpace(name=configspace_name, meta=configspace_meta)
267+
new_configuration_space.random.set_state(random_state)
268+
269+
for hyperparameter in configspace.values():
270+
if isinstance(hyperparameter, CategoricalHyperparameter):
271+
new_hyperparameter = hyperparameter.to_uniform()
272+
elif isinstance(hyperparameter, IntegerHyperparameter):
273+
new_hyperparameter = hyperparameter.to_uniform()
274+
elif isinstance(hyperparameter, FloatHyperparameter):
275+
new_hyperparameter = hyperparameter.to_uniform()
276+
else:
277+
new_hyperparameter = hyperparameter
278+
new_configuration_space.add(new_hyperparameter)
279+
conditions = configspace.conditions
280+
new_configuration_space.add(conditions)
281+
return new_configuration_space
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import pytest
2+
from ConfigSpace import (
3+
BetaFloatHyperparameter,
4+
BetaIntegerHyperparameter,
5+
CategoricalHyperparameter,
6+
ConfigurationSpace,
7+
Constant,
8+
NormalFloatHyperparameter,
9+
NormalIntegerHyperparameter,
10+
UniformFloatHyperparameter,
11+
UniformIntegerHyperparameter,
12+
)
13+
from smac.utils.configspace import create_uniform_configspace_copy
14+
15+
16+
@pytest.fixture
17+
def non_uniform_configspace():
18+
configspace = ConfigurationSpace(
19+
name="test_configspace", seed=42, meta={"info": "test configspace"}
20+
)
21+
configspace.add(
22+
BetaFloatHyperparameter(
23+
"beta_float",
24+
alpha=2.0,
25+
beta=5.0,
26+
lower=0.0,
27+
upper=1.0,
28+
default_value=0.5,
29+
log=False,
30+
)
31+
)
32+
configspace.add(
33+
BetaIntegerHyperparameter(
34+
"beta_int",
35+
alpha=2.0,
36+
beta=5.0,
37+
lower=1,
38+
upper=10,
39+
default_value=5,
40+
log=False,
41+
)
42+
)
43+
configspace.add(
44+
NormalFloatHyperparameter(
45+
"normal_float",
46+
mu=0.0,
47+
sigma=1.0,
48+
lower=-3.0,
49+
upper=3.0,
50+
default_value=0.0,
51+
log=False,
52+
)
53+
)
54+
configspace.add(
55+
NormalIntegerHyperparameter(
56+
"normal_int", mu=5, sigma=2, lower=1, upper=10, default_value=5, log=False
57+
)
58+
)
59+
configspace.add(
60+
UniformFloatHyperparameter(
61+
"uniform_float", lower=0.0, upper=10.0, default_value=5.0, log=False
62+
)
63+
)
64+
configspace.add(
65+
UniformIntegerHyperparameter(
66+
"uniform_int", lower=1, upper=100, default_value=50, log=False
67+
)
68+
)
69+
configspace.add(
70+
CategoricalHyperparameter(
71+
"categorical",
72+
choices=["red", "green", "blue"],
73+
default_value="green",
74+
weights=[0.2, 0.5, 0.3],
75+
)
76+
)
77+
configspace.add(Constant("constant", value=3.14))
78+
return configspace
79+
80+
81+
@pytest.fixture
82+
def uniform_configspace():
83+
configspace = ConfigurationSpace(
84+
name="test_configspace", seed=42, meta={"info": "test configspace"}
85+
)
86+
configspace.add(
87+
UniformFloatHyperparameter(
88+
"beta_float", lower=0.0, upper=1.0, default_value=0.5, log=False
89+
)
90+
)
91+
configspace.add(
92+
UniformIntegerHyperparameter(
93+
"beta_int", lower=1, upper=10, default_value=5, log=False
94+
)
95+
)
96+
configspace.add(
97+
UniformFloatHyperparameter(
98+
"normal_float", lower=-3.0, upper=3.0, default_value=0.0, log=False
99+
)
100+
)
101+
configspace.add(
102+
UniformIntegerHyperparameter(
103+
"normal_int", lower=1, upper=10, default_value=5, log=False
104+
)
105+
)
106+
configspace.add(
107+
UniformFloatHyperparameter(
108+
"uniform_float", lower=0.0, upper=10.0, default_value=5.0, log=False
109+
)
110+
)
111+
configspace.add(
112+
UniformIntegerHyperparameter(
113+
"uniform_int", lower=1, upper=100, default_value=50, log=False
114+
)
115+
)
116+
configspace.add(
117+
CategoricalHyperparameter(
118+
"categorical", choices=["red", "green", "blue"], default_value="green"
119+
)
120+
)
121+
configspace.add(Constant("constant", value=3.14))
122+
return configspace
123+
124+
125+
def test_create_uniform_configspace_copy(
126+
non_uniform_configspace: ConfigurationSpace, uniform_configspace: ConfigurationSpace
127+
):
128+
adapted_configspace = create_uniform_configspace_copy(non_uniform_configspace)
129+
assert adapted_configspace == uniform_configspace

0 commit comments

Comments
 (0)