Skip to content

Configspace

smac.utils.configspace #

convert_configurations_to_array #

convert_configurations_to_array(
    configs: list[Configuration],
) -> ndarray

Impute inactive hyperparameters in configurations with their default.

PARAMETER DESCRIPTION
configs

List of configuration objects.

TYPE: List[Configuration]

RETURNS DESCRIPTION
ndarray
Source code in smac/utils/configspace.py
def convert_configurations_to_array(configs: list[Configuration]) -> np.ndarray:
    """Impute inactive hyperparameters in configurations with their default.

    Parameters
    ----------
    configs : List[Configuration]
        List of configuration objects.

    Returns
    -------
    np.ndarray
    """
    return np.array([config.get_array() for config in configs], dtype=np.float64)

create_uniform_configspace_copy #

create_uniform_configspace_copy(
    configspace: ConfigurationSpace,
) -> ConfigurationSpace

Creates a copy of the given configuration space with uniform transformed hyperparameters.

PARAMETER DESCRIPTION
configspace

The configuration space to be transformed.

TYPE: ConfigurationSpace

RETURNS DESCRIPTION
ConfigurationSpace

A new configuration space with uniform transformed hyperparameters.

Source code in smac/utils/configspace.py
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

get_conditional_hyperparameters #

get_conditional_hyperparameters(
    X: ndarray, Y: ndarray | None = None
) -> ndarray

Returns conditional hyperparameters if values with -1 or smaller are observed. X is used if Y is not specified.

Source code in smac/utils/configspace.py
def get_conditional_hyperparameters(X: np.ndarray, Y: np.ndarray | None = None) -> np.ndarray:
    """Returns conditional hyperparameters if values with -1 or smaller are observed. X is used
    if Y is not specified.
    """
    # Taking care of conditional hyperparameters according to Levesque et al.
    X_cond = X <= -1

    if Y is not None:
        Y_cond = Y <= -1
    else:
        Y_cond = X <= -1

    active = ~((np.expand_dims(X_cond, axis=1) != Y_cond).any(axis=2))
    return active

get_config_hash #

get_config_hash(
    config: Configuration, chars: int = 6
) -> str

Returns a hash of the configuration.

Source code in smac/utils/configspace.py
def get_config_hash(config: Configuration, chars: int = 6) -> str:
    """Returns a hash of the configuration."""
    return hashlib.sha1(str(config).encode("utf-8")).hexdigest()[:chars]

get_types #

get_types(
    configspace: ConfigurationSpace,
    instance_features: dict[str, list[float]] | None = None,
) -> tuple[list[int], list[tuple[float, float]]]

Return the types of the hyperparameters and the bounds of the hyperparameters and instance features.

Warning

The bounds for the instance features are not added in this function.

Source code in smac/utils/configspace.py
def get_types(
    configspace: ConfigurationSpace,
    instance_features: dict[str, list[float]] | None = None,
) -> tuple[list[int], list[tuple[float, float]]]:
    """Return the types of the hyperparameters and the bounds of the
    hyperparameters and instance features.

    Warning
    -------
    The bounds for the instance features are *not* added in this function.
    """
    # Extract types vector for rf from config space and the bounds
    types = [0] * len(list(configspace.values()))
    bounds = [(np.nan, np.nan)] * len(types)

    for i, param in enumerate(list(configspace.values())):
        parents = configspace.parents_of[param.name]
        if len(parents) == 0:
            can_be_inactive = False
        else:
            can_be_inactive = True

        if isinstance(param, (CategoricalHyperparameter)):
            n_cats = len(param.choices)
            if can_be_inactive:
                n_cats = len(param.choices) + 1
            types[i] = n_cats
            bounds[i] = (int(n_cats), np.nan)
        elif isinstance(param, (OrdinalHyperparameter)):
            n_cats = len(param.sequence)
            types[i] = 0
            if can_be_inactive:
                bounds[i] = (0, int(n_cats))
            else:
                bounds[i] = (0, int(n_cats) - 1)
        elif isinstance(param, Constant):
            # For constants we simply set types to 0 which makes it a numerical parameter
            if can_be_inactive:
                bounds[i] = (2, np.nan)
                types[i] = 2
            else:
                bounds[i] = (0, np.nan)
                types[i] = 0
            # and we leave the bounds to be 0 for now
        elif isinstance(param, UniformFloatHyperparameter):
            # Are sampled on the unit hypercube thus the bounds
            # are always 0.0, 1.0
            if can_be_inactive:
                bounds[i] = (-1.0, 1.0)
            else:
                bounds[i] = (0, 1.0)
        elif isinstance(param, UniformIntegerHyperparameter):
            if can_be_inactive:
                bounds[i] = (-1.0, 1.0)
            else:
                bounds[i] = (0, 1.0)
        elif isinstance(param, NormalFloatHyperparameter):
            if can_be_inactive:
                raise ValueError("Inactive parameters not supported for Beta and Normal Hyperparameters")

            bounds[i] = (param.lower_vectorized, param.upper_vectorized)
        elif isinstance(param, NormalIntegerHyperparameter):
            if can_be_inactive:
                raise ValueError("Inactive parameters not supported for Beta and Normal Hyperparameters")

            bounds[i] = (param.lower_vectorized, param.upper_vectorized)
        elif isinstance(param, BetaFloatHyperparameter):
            if can_be_inactive:
                raise ValueError("Inactive parameters not supported for Beta and Normal Hyperparameters")

            bounds[i] = (param.lower_vectorized, param.upper_vectorized)
        elif isinstance(param, BetaIntegerHyperparameter):
            if can_be_inactive:
                raise ValueError("Inactive parameters not supported for Beta and Normal Hyperparameters")

            bounds[i] = (param.lower_vectorized, param.upper_vectorized)
        elif not isinstance(
            param,
            (
                UniformFloatHyperparameter,
                UniformIntegerHyperparameter,
                OrdinalHyperparameter,
                CategoricalHyperparameter,
                NormalFloatHyperparameter,
                NormalIntegerHyperparameter,
                BetaFloatHyperparameter,
                BetaIntegerHyperparameter,
            ),
        ):
            raise TypeError("Unknown hyperparameter type %s" % type(param))

    if instance_features is not None:
        n_features = len(list(instance_features.values())[0])
        types = types + [0] * n_features

    return types, bounds

print_config_changes #

print_config_changes(
    incumbent: Configuration | None,
    challenger: Configuration | None,
    logger: Logger,
) -> None

Compares two configurations and prints the differences.

Source code in smac/utils/configspace.py
def print_config_changes(
    incumbent: Configuration | None,
    challenger: Configuration | None,
    logger: logging.Logger,
) -> None:
    """Compares two configurations and prints the differences."""
    if incumbent is None or challenger is None:
        return

    inc_keys = set(incumbent.keys())
    all_keys = inc_keys.union(challenger.keys())

    lines = []
    for k in sorted(all_keys):
        inc_k = incumbent.get(k, "-inactive-")
        cha_k = challenger.get(k, "-inactive-")
        lines.append(f"--- {k}: {inc_k} -> {cha_k}" + " (unchanged)" if inc_k == cha_k else "")

    msg = "\n".join(lines)
    logger.debug(msg)

transform_continuous_designs #

transform_continuous_designs(
    design: ndarray,
    origin: str,
    configspace: ConfigurationSpace,
) -> list[Configuration]

Transforms the continuous designs into a discrete list of configurations.

PARAMETER DESCRIPTION
design

Array of hyperparameters originating from the initial design strategy.

TYPE: ndarray

origin

Label for a configuration where it originated from.

TYPE: str | None, defaults to None

configspace

TYPE: ConfigurationSpace

RETURNS DESCRIPTION
configs

Continuous transformed configs.

TYPE: list[Configuration]

Source code in smac/utils/configspace.py
def transform_continuous_designs(
    design: np.ndarray, origin: str, configspace: ConfigurationSpace
) -> list[Configuration]:
    """Transforms the continuous designs into a discrete list of configurations.

    Parameters
    ----------
    design : np.ndarray
        Array of hyperparameters originating from the initial design strategy.
    origin : str | None, defaults to None
        Label for a configuration where it originated from.
    configspace : ConfigurationSpace

    Returns
    -------
    configs : list[Configuration]
        Continuous transformed configs.
    """
    params = list(configspace.values())
    for idx, param in enumerate(params):
        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]
            design_[:, idx + 1 :] = design[:, idx:]
            design = design_
        elif isinstance(param, CategoricalHyperparameter):
            v_design = design[:, idx]
            v_design[v_design == 1] = 1 - 10**-10
            design[:, idx] = np.array(v_design * len(param.choices), dtype=int)
        elif isinstance(param, OrdinalHyperparameter):
            v_design = design[:, idx]
            v_design[v_design == 1] = 1 - 10**-10
            design[:, idx] = np.array(v_design * len(param.sequence), dtype=int)
        else:
            raise ValueError("Hyperparameter not supported when transforming a continuous design.")

    configs = []
    for vector in design:
        try:
            conf = deactivate_inactive_hyperparameters(
                configuration=None, configuration_space=configspace, vector=vector
            )
        except ForbiddenValueError:
            continue

        conf.origin = origin
        configs.append(conf)

    return configs