def tell(self, candidate: p.Parameter, value: float) -> None: """Provides the optimizer with the evaluation of a fitness value for a candidate. Parameters ---------- x: np.ndarray point where the function was evaluated value: float value of the function Note ---- The candidate should generally be one provided by `ask()`, but can be also a non-asked candidate. To create a p.Parameter instance from args and kwargs, you can use `optimizer.create_candidate.from_call(*args, **kwargs)`. """ if not isinstance(candidate, p.Parameter): raise TypeError( "'tell' must be provided with the candidate (use optimizer.create_candidate.from_call(*args, **kwargs)) " "if you want to inoculate a point that as not been asked for" ) candidate.freeze() # make sure it is not modified somewhere # call callbacks for logging etc... for callback in self._callbacks.get("tell", []): callback(self, candidate, value) data = candidate.get_standardized_data(reference=self.parametrization) self._update_archive_and_bests(data, value) if candidate.uid in self._asked: self._internal_tell_candidate(candidate, value) self._asked.remove(candidate.uid) else: self._internal_tell_not_asked(candidate, value) self._num_tell_not_asked += 1 self._num_tell += 1
def _internal_tell_not_asked(self, candidate: p.Parameter, loss: float) -> None: next_id = self.trials.new_trial_ids(1) new_trial = hyperopt.rand.suggest(next_id, self.domain, self.trials, self._rng.randint(2**31 - 1)) self.trials.insert_trial_docs(new_trial) self.trials.refresh() tid = next_id[0] if self._transform: data = candidate.get_standardized_data( reference=self.parametrization) data = self._transform.forward(data) self.trials._dynamic_trials[tid]["misc"]["vals"] = { f"x_{i}": [data[i]] for i in range(len(data)) } else: null_config: dict = { k: [] for k in self.trials._dynamic_trials[tid]["misc"] ["vals"].keys() } new_vals: dict = _hp_parametrization_to_dict(candidate, default=null_config) self.trials._dynamic_trials[tid]["misc"]["vals"] = new_vals self.trials.refresh() candidate._meta["trial_id"] = tid self._internal_tell_candidate(candidate, loss)
def _internal_tell_candidate(self, candidate: p.Parameter, value: float) -> None: """Returns value for a point which was "asked" (none asked point cannot be "tell") """ x = candidate.get_standardized_data(reference=self.parametrization) assert self._messaging_thread is not None, 'Start by using "ask" method, instead of "tell" method' if not self._messaging_thread.is_alive(): # optimizer is done self._check_error() return messages = [m for m in self._messaging_thread.messages if m.meta.get("asked", False) and not m.done] messages = [m for m in messages if m.meta["uid"] == candidate.uid] if not messages: raise RuntimeError(f"No message for evaluated point {x}: {self._messaging_thread.messages}") messages[0].result = value # post the value, and the thread will deal with it
def _update_archive_and_bests(self, candidate: p.Parameter, loss: tp.FloatLoss) -> None: x = candidate.get_standardized_data(reference=self.parametrization) if not isinstance( loss, (Real, float) ): # using "float" along "Real" because mypy does not understand "Real" for now Issue #3186 raise TypeError( f'"tell" method only supports float values but the passed loss was: {loss} (type: {type(loss)}.' ) if np.isnan(loss) or loss == np.inf: warnings.warn(f"Updating fitness with {loss} value", errors.BadLossWarning) mvalue: tp.Optional[utils.MultiValue] = None if x not in self.archive: self.archive[x] = utils.MultiValue(candidate, loss, reference=self.parametrization) else: mvalue = self.archive[x] mvalue.add_evaluation(loss) # both parameters should be non-None if mvalue.parameter.loss > candidate.loss: # type: ignore mvalue.parameter = candidate # keep best candidate # update current best records # this may have to be improved if we want to keep more kinds of best losss for name in self.current_bests: if mvalue is self.current_bests[name]: # reboot best = min(self.archive.values(), key=lambda mv, n=name: mv.get_estimation(n) ) # type: ignore # rebuild best point may change, and which value did not track the updated value anyway self.current_bests[name] = best else: if self.archive[x].get_estimation( name) <= self.current_bests[name].get_estimation(name): self.current_bests[name] = self.archive[x] # deactivated checks # if not (np.isnan(loss) or loss == np.inf): # if not self.current_bests[name].x in self.archive: # bval = self.current_bests[name].get_estimation(name) # avals = (min(v.get_estimation(name) for v in self.archive.values()), # max(v.get_estimation(name) for v in self.archive.values())) # raise RuntimeError(f"Best value should exist in the archive at num_tell={self.num_tell})\n" # f"Best value is {bval} and archive is within range {avals} for {name}") if self.pruning is not None: self.archive = self.pruning(self.archive)
def _internal_tell_candidate(self, candidate: p.Parameter, loss: tp.FloatLoss) -> None: """Called whenever calling :code:`tell` on a candidate that was "asked".""" data = candidate.get_standardized_data(reference=self.parametrization) self._internal_tell(data, loss)
def _internal_tell_candidate(self, candidate: p.Parameter, value: float) -> None: """Called whenever calling "tell" on a candidate that was "asked". """ data = candidate.get_standardized_data(reference=self.parametrization) self._internal_tell(data, value)