from __future__ import annotations
from typing import Any
import numpy as np
from smac.multi_objective.abstract_multi_objective_algorithm import (
AbstractMultiObjectiveAlgorithm,
)
from smac.scenario import Scenario
[docs]
class ParEGO(AbstractMultiObjectiveAlgorithm):
"""ParEGO implementation based on https://www.cs.bham.ac.uk/~jdk/UKCI-2015.pdf.
Parameters
----------
scenario : Scenario
rho : float, defaults to 0.05
A small positive value.
seed : int | None, defaults to None
"""
def __init__(
self,
scenario: Scenario,
rho: float = 0.05,
seed: int | None = None,
):
super(ParEGO, self).__init__()
if seed is None:
seed = scenario.seed
self._n_objectives = scenario.count_objectives()
self._seed = seed
self._rng = np.random.RandomState(seed)
self._rho = rho
# Will be set on starting an SMBO iteration
self._theta: np.ndarray | None = None
@property
def meta(self) -> dict[str, Any]: # noqa: D102
meta = super().meta
meta.update(
{
"name": self.__class__.__name__,
"rho": self._rho,
"seed": self._seed,
}
)
return meta
[docs]
def update_on_iteration_start(self) -> None: # noqa: D102
self._theta = self._rng.rand(self._n_objectives)
# Normalize so that all theta values sum up to 1
self._theta = self._theta / (np.sum(self._theta) + 1e-10)
[docs]
def __call__(self, values: list[float]) -> float: # noqa: D102
# Weight the values
if self._theta is None:
raise ValueError("Iteration not yet initalized; Call `update_on_iteration_start()` first")
theta_f = self._theta * values
return float(np.max(theta_f, axis=0) + self._rho * np.sum(theta_f, axis=0))