Source code for smac.acquisition.function.confidence_bound

from __future__ import annotations

from typing import Any

import numpy as np

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 LCB(AbstractAcquisitionFunction): r"""Computes the lower confidence bound for a given x over the best so far value as acquisition value. :math:`LCB(X) = \mu(\mathbf{X}) - \sqrt(\beta_t)\sigma(\mathbf{X})` [SKKS10]_ with :math:`\beta_t = 2 \log( |D| t^2 / \beta)` :math:`\text{Input space} D` :math:`\text{Number of input dimensions} |D|` :math:`\text{Number of data points} t` :math:`\text{Exploration/exploitation tradeoff} \beta` Returns -LCB(X) as the acquisition_function optimizer maximizes the acquisition value. Parameters ---------- beta : float, defaults to 1.0 Controls the balance between exploration and exploitation of the acquisition function. Attributes ---------- _beta : float Exploration-exploitation trade-off parameter. _num_data : int Number of data points seen so far. """ def __init__(self, beta: float = 1.0) -> None: super(LCB, self).__init__() self._beta: float = beta self._num_data: int | None = None @property def name(self) -> str: # noqa: D102 return "Lower Confidence Bound" @property def meta(self) -> dict[str, Any]: # noqa: D102 meta = super().meta meta.update({"beta": self._beta}) return meta def _update(self, **kwargs: Any) -> None: """Update acsquisition function attributes Parameters ---------- num_data : int Number of data points """ assert "num_data" in kwargs self._num_data = kwargs["num_data"] def _compute(self, X: np.ndarray) -> np.ndarray: """Compute LCB acquisition value Parameters ---------- X : np.ndarray [N, D] The input points where the acquisition function should be evaluated. The dimensionality of X is (N, D), with N as the number of points to evaluate at and D is the number of dimensions of one X. Returns ------- np.ndarray [N,1] Acquisition function values wrt X. Raises ------ ValueError If `update` has not been called before. Number of data points is unspecified in this case. """ assert self._model is not None if self._num_data is None: raise ValueError( "No current number of data points specified. Call `update` to inform the acqusition function." ) if len(X.shape) == 1: X = X[:, np.newaxis] m, var_ = self._model.predict_marginalized(X) std = np.sqrt(var_) beta_t = 2 * np.log((X.shape[1] * self._num_data**2) / self._beta) return -(m - np.sqrt(beta_t) * std)