Skip to content

Commit 7691e52

Browse files
committed
Merge commits
1 parent e9d71a5 commit 7691e52

File tree

5 files changed

+182
-85
lines changed

5 files changed

+182
-85
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 & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from ConfigSpace import Configuration
44

55
from smac.initial_design.abstract_initial_design import AbstractInitialDesign
6-
6+
from smac.utils.configspace import create_uniform_configspace_copy
77
__copyright__ = "Copyright 2025, Leibniz University Hanover, Institute of AI"
88
__license__ = "3-clause BSD"
99

@@ -12,10 +12,12 @@ class RandomInitialDesign(AbstractInitialDesign):
1212
"""Initial design that evaluates random configurations."""
1313

1414
def _select_configurations(self) -> list[Configuration]:
15+
uniform_configspace = create_uniform_configspace_copy(self._configspace)
16+
1517
if self._n_configs == 1:
16-
configs = [self._configspace.sample_configuration()]
18+
configs = [uniform_configspace.sample_configuration()]
1719
else:
18-
configs = self._configspace.sample_configuration(size=self._n_configs)
20+
configs = uniform_configspace.sample_configuration(size=self._n_configs)
1921
for config in configs:
2022
config.origin = "Initial Design: Random"
2123
return configs

smac/utils/configspace.py

Lines changed: 39 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
NumericalHyperparameter,
1818
OrdinalHyperparameter,
1919
UniformFloatHyperparameter,
20+
FloatHyperparameter,
2021
UniformIntegerHyperparameter,
2122
)
2223
from ConfigSpace.util import (
@@ -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]
@@ -242,74 +244,35 @@ def transform_continuous_designs(
242244

243245
return configs
244246

245-
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
247+
def create_uniform_configspace_copy(configspace:ConfigurationSpace) -> ConfigurationSpace:
248+
"""Creates a copy of the given configuration space where all hyperparameters are transformed to uniform hyperparameters.
249+
250+
Parameters
251+
----------
252+
configspace : ConfigurationSpace
253+
The configuration space to be transformed.
254+
255+
Returns
256+
-------
257+
ConfigurationSpace
258+
A new configuration space with all hyperparameters transformed to uniform hyperparameters.
259+
"""
260+
configspace_name = configspace.name
261+
configspace_meta = configspace.meta
262+
random_state = configspace.random.get_state()
263+
new_configuration_space = ConfigurationSpace(name = configspace_name, meta = configspace_meta)
264+
new_configuration_space.random.set_state(random_state)
265+
266+
for hyperparameter in configspace.values():
267+
if isinstance(hyperparameter, CategoricalHyperparameter):
268+
new_hyperparameter = hyperparameter.to_uniform()
269+
elif isinstance(hyperparameter, IntegerHyperparameter):
270+
new_hyperparameter = hyperparameter.to_uniform()
271+
elif isinstance(hyperparameter, FloatHyperparameter):
272+
new_hyperparameter = hyperparameter.to_uniform()
273+
else:
274+
new_hyperparameter = hyperparameter
275+
new_configuration_space.add(new_hyperparameter)
276+
conditions = configspace.conditions
277+
new_configuration_space.add(conditions)
278+
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)