class_docstr: Analytic Preferential Expected Utility of Best Options, i.e., Analytical EUBO
class_docstr_tok: ["Analytic","Preferential","Expected","Utility","of","Best","Options",",","i.e.",",","Analytical","EUBO"]
docstr: Evaluate analytical EUBO on the candidate set X.
  
  Args:
      X: A `batch_shape x q x d`-dim Tensor, where `q = 2` if `previous_winner`
          is not `None`, and `q = 1` otherwise.
  
  Returns:
      The acquisition value for each batch as a tensor of shape `batch_shape`.
docstr_tok: ["Evaluate","analytical","EUBO","on","the","candidate","set","X.","Args",":","X",":","A","`","batch_shape","x","q","x","d","`","-dim","Tensor",",","where","`","q","=","2","`","if","`","previous_winner","`","is","not","`","None","`",",","and","`","q","=","1","`","otherwise",".","Returns",":","The","acquisition","value","for","each","batch","as","a","tensor","of","shape","`","batch_shape","`","."]
code: def forward(self, X: Tensor) -> Tensor:
      r"""Evaluate analytical EUBO on the candidate set X.
  
      Args:
          X: A `batch_shape x q x d`-dim Tensor, where `q = 2` if `previous_winner`
              is not `None`, and `q = 1` otherwise.
  
      Returns:
          The acquisition value for each batch as a tensor of shape `batch_shape`.
      """
      if not (
          ((X.shape[-2] == 2) and (self.previous_winner is None))
          or ((X.shape[-2] == 1) and (self.previous_winner is not None))
      ):
          raise UnsupportedError(
              f"{self.__class__.__name__} only support q=2 or q=1"
              "with a previous winner specified"
          )
  
      Y = X if self.outcome_model is None else self.outcome_model(X)
  
      if self.previous_winner is not None:
          Y = torch.cat(
              [Y, match_batch_shape(self.previous_winner, Y)], dim=-2
          )
  
      pref_posterior = self.model.posterior(Y)
      pref_mean = pref_posterior.mean.squeeze(-1)
      pref_cov = pref_posterior.covariance_matrix
      delta = pref_mean[..., 0] - pref_mean[..., 1]
  
      w = torch.tensor(
          [1.0, -1.0], dtype=pref_cov.dtype, device=pref_cov.device
      )
      var = w @ pref_cov @ w
      sigma = torch.sqrt(var.clamp(min=SIGMA_JITTER))
  
      u = delta / sigma
  
      ucdf = self.std_norm.cdf(u)
      updf = torch.exp(self.std_norm.log_prob(u))
      acqf_val = sigma * (updf + u * ucdf)
      if self.previous_winner is None:
          acqf_val = acqf_val + pref_mean[..., 1]
      return acqf_val
  
file_path: botorch/botorch/acquisition/preference.py
imports: from __future__ import annotations
  from typing import Any, Optional
  import torch
  from botorch.acquisition import AnalyticAcquisitionFunction
  from botorch.acquisition.monte_carlo import MCAcquisitionFunction
  from botorch.exceptions.errors import UnsupportedError
  from botorch.models.deterministic import DeterministicModel
  from botorch.models.model import Model
  from botorch.utils.transforms import match_batch_shape, t_batch_mode_transform
  from torch import Tensor
  from torch.distributions import Bernoulli, Normal
label_desc: Use image node_id 2 for calling the AnalyticExpectedUtilityOfBestOption obj's underlying member method code with example usage: obj.forward(X) and returns: acqf_val