Source code for smac.initial_design.initial_design

from typing import List, Optional

import logging
from collections import OrderedDict

import numpy as np
from ConfigSpace.configuration_space import Configuration, ConfigurationSpace
from ConfigSpace.hyperparameters import (
    CategoricalHyperparameter,
    Constant,
    NumericalHyperparameter,
    OrdinalHyperparameter,
)
from ConfigSpace.util import ForbiddenValueError, deactivate_inactive_hyperparameters

from smac.utils.io.traj_logging import TrajLogger

__author__ = "Marius Lindauer"
__copyright__ = "Copyright 2019, AutoML"
__license__ = "3-clause BSD"


[docs]class InitialDesign: """Base class for initial design strategies that evaluates multiple configurations. Parameters ---------- cs: ConfigurationSpace configuration space object rng: np.random.RandomState Random state traj_logger: TrajLogger Trajectory logging to add new incumbents found by the initial design. ta_run_limit: int Number of iterations allowed for the target algorithm configs: Optional[List[Configuration]] List of initial configurations. Disables the arguments ``n_configs_x_params`` if given. Either this, or ``n_configs_x_params`` or ``init_budget`` must be provided. n_configs_x_params: int how many configurations will be used at most in the initial design (X*D). Either this, or ``init_budget`` or ``configs`` must be provided. Disables the argument ``n_configs_x_params`` if given. max_config_fracs: float use at most X*budget in the initial design. Not active if a time limit is given. init_budget : int, optional Maximal initial budget (disables the arguments ``n_configs_x_params`` and ``configs`` if both are given). Either this, or ``n_configs_x_params`` or ``configs`` must be provided. Attributes ---------- cs : ConfigurationSpace configs : List[Configuration] List of configurations to be evaluated """ def __init__( self, cs: ConfigurationSpace, rng: np.random.RandomState, traj_logger: TrajLogger, ta_run_limit: int, configs: Optional[List[Configuration]] = None, n_configs_x_params: Optional[int] = 10, max_config_fracs: float = 0.25, init_budget: Optional[int] = None, ): self.cs = cs self.rng = rng self.traj_logger = traj_logger self.configs = configs self.logger = logging.getLogger(self.__module__ + "." + self.__class__.__name__) n_params = len(self.cs.get_hyperparameters()) if init_budget is not None: self.init_budget = init_budget if n_configs_x_params is not None: self.logger.debug( "Ignoring argument `n_configs_x_params` (value %d).", n_configs_x_params, ) elif configs is not None: self.init_budget = len(configs) elif n_configs_x_params is not None: self.init_budget = int(max(1, min(n_configs_x_params * n_params, (max_config_fracs * ta_run_limit)))) else: raise ValueError( "Need to provide either argument `init_budget`, `configs` or " "`n_configs_x_params`, but provided none of them." ) if self.init_budget > ta_run_limit: raise ValueError( "Initial budget %d cannot be higher than the run limit %d." % (self.init_budget, ta_run_limit) ) self.logger.info("Running initial design for %d configurations" % self.init_budget)
[docs] def select_configurations(self) -> List[Configuration]: """Selects the initial configurations.""" if self.init_budget == 0: return [] if self.configs is None: self.configs = self._select_configurations() for config in self.configs: if config.origin is None: config.origin = "Initial design" # add this incumbent right away to have an entry to time point 0 self.traj_logger.add_entry(train_perf=2**31, incumbent_id=1, incumbent=self.configs[0]) # removing duplicates # (Reference: https://stackoverflow.com/questions/7961363/removing-duplicates-in-lists) self.configs = list(OrderedDict.fromkeys(self.configs)) return self.configs
def _select_configurations(self) -> List[Configuration]: raise NotImplementedError def _transform_continuous_designs( self, design: np.ndarray, origin: str, cs: ConfigurationSpace ) -> List[Configuration]: params = cs.get_hyperparameters() for idx, param in enumerate(params): if isinstance(param, NumericalHyperparameter): continue elif isinstance(param, Constant): # add a vector with zeros 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 in LHD") self.logger.debug("Initial Design") configs = [] for vector in design: try: conf = deactivate_inactive_hyperparameters(configuration=None, configuration_space=cs, vector=vector) except ForbiddenValueError: continue conf.origin = origin configs.append(conf) self.logger.debug(conf) self.logger.debug("Size of initial design: %d" % (len(configs))) return configs