Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions docs/advanced_usage/1_components.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,18 @@ The acquisition maximizer also incorporates the [Random Design][random-design].
## [Initial Design][smac.initial_design.abstract_initial_design]

The surrogate model needs data to be trained. Therefore, the initial design is used to generate the initial data points.
We provide random, latin hypercube, sobol, factorial and default initial designs. The default initial design uses
the default configuration from the configuration space and with the factorial initial design, we generate corner
points of the configuration space. The sobol sequences are an example of quasi-random low-discrepancy sequences and
the latin hypercube design is a statistical method for generating a near-random sample of parameter values from
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
the default configuration from the configuration space and with the [factorial initial design][smac.initial_design.factorial_design], we generate corner
points of the configuration space. The [sobol initial design][smac.initial_design.sobol_design] uses quasi-random low-discrepancy sequences and
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
a multidimensional distribution.

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

!!! warning
Distributions provided in the initial design are ignored.

[](){#random-design}
## [Random Design][smac.initial_design.random_design]

Expand Down
4 changes: 2 additions & 2 deletions examples/1_basics/6_priors.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,10 @@ def train(self, config: Configuration, seed: int = 0) -> float:

if __name__ == "__main__":
mlp = MLP()
default_config = mlp.configspace.get_default_configuration()
default_config = mlp.prior_configspace.get_default_configuration()

# Define our environment variables
scenario = Scenario(mlp.configspace, n_trials=40)
scenario = Scenario(mlp.prior_configspace, n_trials=40)

# We also want to include our default configuration in the initial design
initial_design = HyperparameterOptimizationFacade.get_initial_design(
Expand Down
7 changes: 5 additions & 2 deletions smac/initial_design/random_design.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from ConfigSpace import Configuration

from smac.initial_design.abstract_initial_design import AbstractInitialDesign
from smac.utils.configspace import create_uniform_configspace_copy

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

def _select_configurations(self) -> list[Configuration]:
uniform_configspace = create_uniform_configspace_copy(self._configspace)

if self._n_configs == 1:
configs = [self._configspace.sample_configuration()]
configs = [uniform_configspace.sample_configuration()]
else:
configs = self._configspace.sample_configuration(size=self._n_configs)
configs = uniform_configspace.sample_configuration(size=self._n_configs)
for config in configs:
config.origin = "Initial Design: Random"
return configs
116 changes: 41 additions & 75 deletions smac/utils/configspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
BetaIntegerHyperparameter,
CategoricalHyperparameter,
Constant,
FloatHyperparameter,
IntegerHyperparameter,
NormalFloatHyperparameter,
NormalIntegerHyperparameter,
Expand Down Expand Up @@ -206,12 +207,13 @@ def transform_continuous_designs(
configs : list[Configuration]
Continuous transformed configs.
"""
params = configspace.get_hyperparameters()
params = list(configspace.values())
for idx, param in enumerate(params):
if isinstance(param, IntegerHyperparameter):
design[:, idx] = param._inverse_transform(param._transform(design[:, idx]))
elif isinstance(param, NumericalHyperparameter):
continue
if isinstance(param, NumericalHyperparameter):
if isinstance(param, IntegerHyperparameter):
design[:, idx] = param.to_vector(param.to_value(design[:, idx]))
elif isinstance(param, NumericalHyperparameter) and not isinstance(param, IntegerHyperparameter):
continue
elif isinstance(param, Constant):
design_ = np.zeros(np.array(design.shape) + np.array((0, 1)))
design_[:, :idx] = design[:, :idx]
Expand Down Expand Up @@ -243,73 +245,37 @@ def transform_continuous_designs(
return configs


# def check_subspace_points(
# X: np.ndarray,
# cont_dims: np.ndarray | list = [],
# cat_dims: np.ndarray | list = [],
# bounds_cont: np.ndarray | None = None,
# bounds_cat: list[tuple] | None = None,
# expand_bound: bool = False,
# ) -> np.ndarray:
# """Check which points are place inside a given subspace.

# Parameters
# ----------
# X: Optional[np.ndarray(N,D)],
# points to be checked, where D = D_cont + D_cat
# cont_dims: Union[np.ndarray(D_cont), List]
# which dimensions represent continuous hyperparameters
# cat_dims: Union[np.ndarray(D_cat), List]
# which dimensions represent categorical hyperparameters
# bounds_cont: optional[List[Tuple]]
# subspaces bounds of categorical hyperparameters, its length is the number of continuous hyperparameters
# bounds_cat: Optional[List[Tuple]]
# subspaces bounds of continuous hyperparameters, its length is the number of categorical hyperparameters
# expand_bound: bool
# if the bound needs to be expanded to contain more points rather than the points inside the subregion
# Return
# ----------
# indices_in_ss:np.ndarray(N)
# indices of data that included in subspaces
# """
# if len(X.shape) == 1:
# X = X[np.newaxis, :]
# if len(cont_dims) == 0 and len(cat_dims) == 0:
# return np.ones(X.shape[0], dtype=bool)

# if len(cont_dims) > 0:
# if bounds_cont is None:
# raise ValueError("bounds_cont must be given if cont_dims provided")

# if len(bounds_cont.shape) != 2 or bounds_cont.shape[1] != 2 or bounds_cont.shape[0] != len(cont_dims):
# raise ValueError(
# f"bounds_cont (with shape {bounds_cont.shape}) should be an array with shape of"
# f"({len(cont_dims)}, 2)"
# )

# data_in_ss = np.all(X[:, cont_dims] <= bounds_cont[:, 1], axis=1) & np.all(
# X[:, cont_dims] >= bounds_cont[:, 0], axis=1
# )

# if expand_bound:
# bound_left = bounds_cont[:, 0] - np.min(X[data_in_ss][:, cont_dims] - bounds_cont[:, 0], axis=0)
# bound_right = bounds_cont[:, 1] + np.min(bounds_cont[:, 1] - X[data_in_ss][:, cont_dims], axis=0)
# data_in_ss = np.all(X[:, cont_dims] <= bound_right, axis=1) & np.all(X[:, cont_dims] >= bound_left,
# axis=1)
# else:
# data_in_ss = np.ones(X.shape[0], dtype=bool)

# if len(cat_dims) == 0:
# return data_in_ss
# if bounds_cat is None:
# raise ValueError("bounds_cat must be given if cat_dims provided")

# if len(bounds_cat) != len(cat_dims):
# raise ValueError(
# f"bounds_cat ({len(bounds_cat)}) and cat_dims ({len(cat_dims)}) must have " f"the same number of elements"
# )

# for bound_cat, cat_dim in zip(bounds_cat, cat_dims):
# data_in_ss &= np.in1d(X[:, cat_dim], bound_cat)

# return data_in_ss
def create_uniform_configspace_copy(
configspace: ConfigurationSpace,
) -> ConfigurationSpace:
"""Creates a copy of the given configuration space with uniform transformed hyperparameters.

Parameters
----------
configspace : ConfigurationSpace
The configuration space to be transformed.

Returns
-------
ConfigurationSpace
A new configuration space with uniform transformed hyperparameters.
"""
configspace_name = configspace.name
configspace_meta = configspace.meta
random_state = configspace.random.get_state()
new_configuration_space = ConfigurationSpace(name=configspace_name, meta=configspace_meta)
new_configuration_space.random.set_state(random_state)

for hyperparameter in configspace.values():
if isinstance(hyperparameter, CategoricalHyperparameter):
new_hyperparameter = hyperparameter.to_uniform()
elif isinstance(hyperparameter, IntegerHyperparameter):
new_hyperparameter = hyperparameter.to_uniform()
elif isinstance(hyperparameter, FloatHyperparameter):
new_hyperparameter = hyperparameter.to_uniform()
else:
new_hyperparameter = hyperparameter
new_configuration_space.add(new_hyperparameter)
conditions = configspace.conditions
new_configuration_space.add(conditions)
return new_configuration_space
129 changes: 129 additions & 0 deletions tests/test_utils/test_configspace.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import pytest
from ConfigSpace import (
BetaFloatHyperparameter,
BetaIntegerHyperparameter,
CategoricalHyperparameter,
ConfigurationSpace,
Constant,
NormalFloatHyperparameter,
NormalIntegerHyperparameter,
UniformFloatHyperparameter,
UniformIntegerHyperparameter,
)
from smac.utils.configspace import create_uniform_configspace_copy


@pytest.fixture
def non_uniform_configspace():
configspace = ConfigurationSpace(
name="test_configspace", seed=42, meta={"info": "test configspace"}
)
configspace.add(
BetaFloatHyperparameter(
"beta_float",
alpha=2.0,
beta=5.0,
lower=0.0,
upper=1.0,
default_value=0.5,
log=False,
)
)
configspace.add(
BetaIntegerHyperparameter(
"beta_int",
alpha=2.0,
beta=5.0,
lower=1,
upper=10,
default_value=5,
log=False,
)
)
configspace.add(
NormalFloatHyperparameter(
"normal_float",
mu=0.0,
sigma=1.0,
lower=-3.0,
upper=3.0,
default_value=0.0,
log=False,
)
)
configspace.add(
NormalIntegerHyperparameter(
"normal_int", mu=5, sigma=2, lower=1, upper=10, default_value=5, log=False
)
)
configspace.add(
UniformFloatHyperparameter(
"uniform_float", lower=0.0, upper=10.0, default_value=5.0, log=False
)
)
configspace.add(
UniformIntegerHyperparameter(
"uniform_int", lower=1, upper=100, default_value=50, log=False
)
)
configspace.add(
CategoricalHyperparameter(
"categorical",
choices=["red", "green", "blue"],
default_value="green",
weights=[0.2, 0.5, 0.3],
)
)
configspace.add(Constant("constant", value=3.14))
return configspace


@pytest.fixture
def uniform_configspace():
configspace = ConfigurationSpace(
name="test_configspace", seed=42, meta={"info": "test configspace"}
)
configspace.add(
UniformFloatHyperparameter(
"beta_float", lower=0.0, upper=1.0, default_value=0.5, log=False
)
)
configspace.add(
UniformIntegerHyperparameter(
"beta_int", lower=1, upper=10, default_value=5, log=False
)
)
configspace.add(
UniformFloatHyperparameter(
"normal_float", lower=-3.0, upper=3.0, default_value=0.0, log=False
)
)
configspace.add(
UniformIntegerHyperparameter(
"normal_int", lower=1, upper=10, default_value=5, log=False
)
)
configspace.add(
UniformFloatHyperparameter(
"uniform_float", lower=0.0, upper=10.0, default_value=5.0, log=False
)
)
configspace.add(
UniformIntegerHyperparameter(
"uniform_int", lower=1, upper=100, default_value=50, log=False
)
)
configspace.add(
CategoricalHyperparameter(
"categorical", choices=["red", "green", "blue"], default_value="green"
)
)
configspace.add(Constant("constant", value=3.14))
return configspace


def test_create_uniform_configspace_copy(
non_uniform_configspace: ConfigurationSpace, uniform_configspace: ConfigurationSpace
):
adapted_configspace = create_uniform_configspace_copy(non_uniform_configspace)
assert adapted_configspace == uniform_configspace
Loading