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.

Parameters#

configs : List[Configuration] List of configuration objects.

Returns#

np.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)

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.

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.

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 = configspace.get_hyperparameters()
    for idx, param in enumerate(params):
        if isinstance(param, IntegerHyperparameter):
            design[:, idx] = param._inverse_transform(param._transform(design[:, idx]))
        elif isinstance(param, NumericalHyperparameter):
            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