|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
3 | | -from typing import Any |
| 3 | +from typing import Any, TypedDict |
4 | 4 | from typing import TYPE_CHECKING |
5 | 5 |
|
6 | 6 | import numpy as np |
|
17 | 17 | from ._gp import convert_inf |
18 | 18 | from ._gp import GPRegressor |
19 | 19 | from ._gp import KernelParamsTensor |
20 | | -from ._optim import suggest_by_carbo |
| 20 | +from ._optim import evaluate_by_carbo, suggest_by_carbo |
21 | 21 |
|
22 | 22 |
|
23 | 23 | if TYPE_CHECKING: |
|
34 | 34 | _ROBUST_PARAMS_KEY = "robust_params" |
35 | 35 |
|
36 | 36 |
|
| 37 | +class EvaluationResult(TypedDict): |
| 38 | + trial: FrozenTrial |
| 39 | + # robust parameters can also be obtained from sampler.get_robust_params_from_trial(self["trial"]), |
| 40 | + # but please note that it raises exception when the attribute is not defined. |
| 41 | + robust_params: dict[str, Any] |
| 42 | + worst_robust_params: dict[str, Any] |
| 43 | + worst_robust_acqf_val: float |
| 44 | + |
| 45 | + |
37 | 46 | def _standardize_values(values: np.ndarray) -> tuple[np.ndarray, np.ndarray, np.ndarray]: |
38 | 47 | clipped_values = convert_inf(values) |
39 | 48 | means = np.mean(clipped_values, axis=0) |
@@ -285,6 +294,90 @@ def after_trial( |
285 | 294 | _process_constraints_after_trial(self._constraints_func, study, trial, state) |
286 | 295 | self._independent_sampler.after_trial(study, trial, state, values) |
287 | 296 |
|
| 297 | + def reevaluate_trials( |
| 298 | + self, |
| 299 | + study: Study, |
| 300 | + ) -> Sequence[EvaluationResult]: |
| 301 | + if study._is_multi_objective(): |
| 302 | + raise ValueError("CARBOSampler does not support multi-objective optimization.") |
| 303 | + |
| 304 | + trials = study._get_trials(deepcopy=False, states=(TrialState.COMPLETE,), use_cache=True) |
| 305 | + search_space = self.infer_relative_search_space(study, trials[0]) |
| 306 | + if search_space == {}: |
| 307 | + return [] |
| 308 | + |
| 309 | + X_train, y_train = self._preproc(study, trials, search_space) |
| 310 | + gpr = GPRegressor( |
| 311 | + X_train, y_train, kernel_params=self._kernel_params_cache |
| 312 | + ).fit_kernel_params(self._log_prior, self._minimum_noise, self._deterministic) |
| 313 | + self._kernel_params_cache = gpr.kernel_params.clone() |
| 314 | + constraint_vals = ( |
| 315 | + None if self._constraints_func is None else _get_constraint_vals(study, trials) |
| 316 | + ) |
| 317 | + if constraint_vals is None: |
| 318 | + constraints_gpr_list = None |
| 319 | + constraints_threshold_list = None |
| 320 | + else: |
| 321 | + _cache_list = ( |
| 322 | + self._constraints_kernel_params_cache_list |
| 323 | + if self._constraints_kernel_params_cache_list is not None |
| 324 | + else [None] * constraint_vals.shape[-1] # type: ignore[list-item] |
| 325 | + ) |
| 326 | + stded_c_vals, means, stdevs = _standardize_values(-constraint_vals) |
| 327 | + constraints_threshold_list = (-means / np.maximum(EPS, stdevs)).tolist() |
| 328 | + C_train = torch.from_numpy(stded_c_vals) |
| 329 | + constraints_gpr_list = [ |
| 330 | + GPRegressor(X_train, c_train, kernel_params=cache).fit_kernel_params( |
| 331 | + self._log_prior, self._minimum_noise, self._deterministic |
| 332 | + ) |
| 333 | + for cache, c_train in zip(_cache_list, C_train.T) |
| 334 | + ] |
| 335 | + |
| 336 | + lows, highs, is_log = _get_dist_info_as_arrays(search_space) |
| 337 | + |
| 338 | + robust_params = np.empty((len(trials), len(search_space)), dtype=float) |
| 339 | + for i, t in enumerate(trials): |
| 340 | + for d, (name, dist) in enumerate(search_space.items()): |
| 341 | + if _ROBUST_PARAMS_KEY in t.system_attrs: |
| 342 | + robust_params[i, d] = t.system_attrs[_ROBUST_PARAMS_KEY][name] |
| 343 | + else: |
| 344 | + robust_params[i, d] = t.params[name] |
| 345 | + robust_params = normalize_params(robust_params, is_log, lows, highs) |
| 346 | + |
| 347 | + results = [] |
| 348 | + |
| 349 | + for i, trial in enumerate(trials): |
| 350 | + worst_robust_params, worst_robust_acqf_val = evaluate_by_carbo( |
| 351 | + robust_params=robust_params[i], |
| 352 | + gpr=gpr, |
| 353 | + constraints_gpr_list=constraints_gpr_list, |
| 354 | + constraints_threshold_list=constraints_threshold_list, |
| 355 | + rng=self._rng.rng, |
| 356 | + rho=self._rho, |
| 357 | + beta=self._beta, |
| 358 | + n_local_search=self._n_local_search, |
| 359 | + local_radius=self._local_ratio / 2, |
| 360 | + ) |
| 361 | + |
| 362 | + result: EvaluationResult = { |
| 363 | + "trial": trial, |
| 364 | + "robust_params": trial.system_attrs[_ROBUST_PARAMS_KEY] |
| 365 | + if _ROBUST_PARAMS_KEY in trial.system_attrs |
| 366 | + else trial.params, |
| 367 | + "worst_robust_params": { |
| 368 | + name: float(param_value) |
| 369 | + for name, param_value in zip( |
| 370 | + search_space, |
| 371 | + unnormalize_params(worst_robust_params[None], is_log, lows, highs)[0], |
| 372 | + ) |
| 373 | + }, |
| 374 | + "worst_robust_acqf_val": worst_robust_acqf_val, |
| 375 | + } |
| 376 | + |
| 377 | + results.append(result) |
| 378 | + |
| 379 | + return results |
| 380 | + |
288 | 381 |
|
289 | 382 | def _get_constraint_vals(study: Study, trials: list[FrozenTrial]) -> np.ndarray: |
290 | 383 | _constraint_vals = [ |
|
0 commit comments