def test_check_neighbouring_config_diamond_str(self): diamond = ConfigurationSpace() head = CategoricalHyperparameter('head', ['red', 'green']) left = CategoricalHyperparameter('left', ['red', 'green']) right = CategoricalHyperparameter('right', ['red', 'green', 'blue', 'yellow']) bottom = CategoricalHyperparameter('bottom', ['red', 'green']) diamond.add_hyperparameters([head, left, right, bottom]) diamond.add_condition(EqualsCondition(left, head, 'red')) diamond.add_condition(EqualsCondition(right, head, 'red')) diamond.add_condition( AndConjunction(EqualsCondition(bottom, left, 'green'), EqualsCondition(bottom, right, 'green'))) config = Configuration(diamond, { 'bottom': 'red', 'head': 'red', 'left': 'green', 'right': 'green' }) hp_name = "head" index = diamond.get_idx_by_hyperparameter_name(hp_name) neighbor_value = 1 new_array = ConfigSpaceNNI.c_util.change_hp_value( diamond, config.get_array(), hp_name, neighbor_value, index) expected_array = np.array([1, np.nan, np.nan, np.nan]) np.testing.assert_almost_equal(new_array, expected_array)
def test_check_forbidden_with_sampled_vector_configuration(self): cs = ConfigurationSpace() metric = CategoricalHyperparameter("metric", ["minkowski", "other"]) cs.add_hyperparameter(metric) forbidden = ForbiddenEqualsClause(metric, "other") cs.add_forbidden_clause(forbidden) configuration = Configuration(cs, vector=np.ones(1, dtype=float)) self.assertRaisesRegexp(ValueError, "violates forbidden clause", cs._check_forbidden, configuration.get_array())
def get_one_exchange_neighbourhood( configuration: Configuration, seed: int, num_neighbors: int=4, stdev: float=0.2, ) -> Generator[Configuration, None, None]: """Return all configurations in a one-exchange neighborhood. The method is implemented as defined by: Frank Hutter, Holger H. Hoos and Kevin Leyton-Brown Sequential Model-Based Optimization for General Algorithm Configuration In: Proceedings of the conference on Learning and Intelligent OptimizatioN (LION 5) """ random = np.random.RandomState(seed) hyperparameters_list = list( list(configuration.configuration_space._hyperparameters.keys()) ) hyperparameters_list_length = len(hyperparameters_list) hyperparameters_used = [hp.name for hp in configuration.configuration_space.get_hyperparameters() if hp.get_num_neighbors(configuration.get(hp.name)) == 0 and configuration.get(hp.name) is not None] number_of_usable_hyperparameters = sum(np.isfinite(configuration.get_array())) n_neighbors_per_hp = { hp.name: num_neighbors if np.isinf(hp.get_num_neighbors(configuration.get(hp.name))) else hp.get_num_neighbors(configuration.get(hp.name)) for hp in configuration.configuration_space.get_hyperparameters() } finite_neighbors_stack = {} # type: Dict configuration_space = configuration.configuration_space # type: ConfigSpaceNNI while len(hyperparameters_used) < number_of_usable_hyperparameters: index = int(random.randint(hyperparameters_list_length)) hp_name = hyperparameters_list[index] if n_neighbors_per_hp[hp_name] == 0: continue else: neighbourhood = [] number_of_sampled_neighbors = 0 array = configuration.get_array() # type: np.ndarray value = array[index] # type: float # Check for NaNs (inactive value) if value != value: continue iteration = 0 hp = configuration_space.get_hyperparameter(hp_name) # type: Hyperparameter num_neighbors_for_hp = hp.get_num_neighbors(configuration.get(hp_name)) while True: # Obtain neigbors differently for different possible numbers of # neighbors if num_neighbors_for_hp == 0: break # No infinite loops elif iteration > 100: break elif np.isinf(num_neighbors_for_hp): if number_of_sampled_neighbors >= 1: break # TODO if code becomes slow remove the isinstance! if isinstance(hp, (UniformFloatHyperparameter, UniformIntegerHyperparameter)): neighbor = hp.get_neighbors(value, random, number=1, std=stdev)[0] else: neighbor = hp.get_neighbors(value, random, number=1)[0] else: if iteration > 0: break if hp_name not in finite_neighbors_stack: neighbors = hp.get_neighbors(value, random) random.shuffle(neighbors) finite_neighbors_stack[hp_name] = neighbors else: neighbors = finite_neighbors_stack[hp_name] neighbor = neighbors.pop() # Check all newly obtained neigbors new_array = array.copy() new_array = ConfigSpaceNNI.c_util.change_hp_value( configuration_space=configuration_space, configuration_array=new_array, hp_name=hp_name, hp_value=neighbor, index=index) try: # Populating a configuration from an array does not check # if it is a legal configuration - check this (slow) new_configuration = Configuration(configuration_space, vector=new_array) # type: Configuration # Only rigorously check every tenth configuration ( # because moving around in the neighborhood should # just work!) if np.random.random() > 0.95: new_configuration.is_valid_configuration() else: configuration_space._check_forbidden(new_array) neighbourhood.append(new_configuration) except ForbiddenValueError as e: pass iteration += 1 if len(neighbourhood) > 0: number_of_sampled_neighbors += 1 # Some infinite loop happened and no valid neighbor was found OR # no valid neighbor is available for a categorical if len(neighbourhood) == 0: hyperparameters_used.append(hp_name) n_neighbors_per_hp[hp_name] = 0 hyperparameters_used.append(hp_name) else: if hp_name not in hyperparameters_used: n_ = neighbourhood.pop() n_neighbors_per_hp[hp_name] -= 1 if n_neighbors_per_hp[hp_name] == 0: hyperparameters_used.append(hp_name) yield n_
def get_random_neighbor(configuration: Configuration, seed: int) -> Configuration: """Draw a random neighbor by changing one parameter of a configuration. * If the parameter is categorical, it changes it to another value. * If the parameter is ordinal, it changes it to the next higher or lower value. * If parameter is a float, draw a random sample If changing a parameter activates new parameters or deactivates previously active parameters, the configuration will be rejected. If more than 10000 configurations were rejected, this function raises a ValueError. Parameters ---------- configuration : Configuration seed : int Used to generate a random state. Returns ------- Configuration The new neighbor. """ random = np.random.RandomState(seed) rejected = True values = copy.deepcopy(configuration.get_dictionary()) while rejected: # First, choose an active hyperparameter active = False iteration = 0 while not active: iteration += 1 if configuration._num_hyperparameters > 1: rand_idx = random.randint(0, configuration._num_hyperparameters - 1) else: rand_idx = 0 value = configuration.get_array()[rand_idx] if np.isfinite(value): active = True hp_name = configuration.configuration_space \ .get_hyperparameter_by_idx(rand_idx) hp = configuration.configuration_space.get_hyperparameter(hp_name) # Only choose if there is a possibility of finding a neigboor if not hp.has_neighbors(): active = False if iteration > 10000: raise ValueError('Probably caught in an infinite loop.') # Get a neighboor and adapt the rest of the configuration if necessary neighbor = hp.get_neighbors(value, random, number=1, transform=True)[0] previous_value = values[hp.name] values[hp.name] = neighbor try: new_configuration = Configuration( configuration.configuration_space, values=values) rejected = False except ValueError as e: values[hp.name] = previous_value return new_configuration