Source code for smac.acquisition.function.probability_improvement

from __future__ import annotations

from typing import Any

import numpy as np
from scipy.stats import norm

from smac.acquisition.function.abstract_acquisition_function import (
    AbstractAcquisitionFunction,
)
from smac.utils.logging import get_logger

__copyright__ = "Copyright 2022, automl.org"
__license__ = "3-clause BSD"

logger = get_logger(__name__)


[docs]class PI(AbstractAcquisitionFunction): r"""Probability of Improvement :math:`P(f_{t+1}(\mathbf{X})\geq f(\mathbf{X^+}))` :math:`:= \Phi(\\frac{ \mu(\mathbf{X})-f(\mathbf{X^+}) } { \sigma(\mathbf{X}) })` with :math:`f(X^+)` as the incumbent and :math:`\Phi` the cdf of the standard normal. Parameters ---------- xi : float, defaults to 0.0 Controls the balance between exploration and exploitation of the acquisition function. """ def __init__(self, xi: float = 0.0): super(PI, self).__init__() self._xi: float = xi self._eta: float | None = None @property def name(self) -> str: # noqa: D102 return "Probability of Improvement" @property def meta(self) -> dict[str, Any]: # noqa: D102 meta = super().meta meta.update({"xi": self._xi}) return meta def _update(self, **kwargs: Any) -> None: """Update acsquisition function attributes Parameters ---------- eta : float Function value of current incumbent. xi : float, optional Exploration-exploitation trade-off parameter """ assert "eta" in kwargs self._eta = kwargs["eta"] if "xi" in kwargs and kwargs["xi"] is not None: self._xi = kwargs["xi"] def _compute(self, X: np.ndarray) -> np.ndarray: """Compute the PI value. Parameters ---------- X: np.ndarray [N, D] Points to evaluate PI. N is the number of points and D the dimension for the points. Returns ------- np.ndarray [N, 1] Expected Improvement of X. Raises ------ ValueError If `update` has not been called before (current incumbent value `eta` unspecified). """ assert self._model is not None if self._eta is None: raise ValueError( "No current best specified. Call update(" "eta=<float>) to inform the acquisition function " "about the current best value." ) if len(X.shape) == 1: X = X[:, np.newaxis] m, var_ = self._model.predict_marginalized(X) std = np.sqrt(var_) return norm.cdf((self._eta - m - self._xi) / std)