class_docstr: MC-based batch Log Noisy Expected Improvement.
  
  This function does not assume a `best_f` is known (which would require
  noiseless observations). Instead, it uses samples from the joint posterior
  over the `q` test points and previously observed points. A smooth approximation
  to the canonical improvement over previously observed points is computed
  for each sample and the logarithm of the average is returned.
  
  See [Ament2023logei]_ for details. Formally,
  
  `qLogNEI(X) ~ log(qNEI(X)) = Log E(max(max Y - max Y_baseline, 0))`,
  
  where `(Y, Y_baseline) ~ f((X, X_baseline)), X = (x_1,...,x_q)`.
  
  Example:
      >>> model = SingleTaskGP(train_X, train_Y)
      >>> sampler = SobolQMCNormalSampler(1024)
      >>> qLogNEI = qLogNoisyExpectedImprovement(model, train_X, sampler)
      >>> acqval = qLogNEI(test_X)
class_docstr_tok: ["MC-based","batch","Log","Noisy","Expected","Improvement",".","This","function","does","not","assume","a","`","best_f","`","is","known","(","which","would","require","noiseless","observations",")",".","Instead",",","it","uses","samples","from","the","joint","posterior","over","the","`","q","`","test","points","and","previously","observed","points",".","A","smooth","approximation","to","the","canonical","improvement","over","previously","observed","points","is","computed","for","each","sample","and","the","logarithm","of","the","average","is","returned",".","See","[","Ament2023logei","]","_","for","details",".","Formally",",","`","qLogNEI","(","X",")","~","log","(","qNEI","(","X",")",")","=","Log","E","(","max","(","max","Y","-","max","Y_baseline",",","0",")",")","`",",","where","`","(","Y",",","Y_baseline",")","~","f","(","(","X",",","X_baseline",")",")",",","X","=","(","x_1",",","...",",","x_q",")","`",".","Example",":",">",">",">","model","=","SingleTaskGP","(","train_X",",","train_Y",")",">",">",">","sampler","=","SobolQMCNormalSampler","(","1024",")",">",">",">","qLogNEI","=","qLogNoisyExpectedImprovement","(","model",",","train_X",",","sampler",")",">",">",">","acqval","=","qLogNEI","(","test_X",")"]
code: def _init_baseline(
      self,
      model: Model,
      X_baseline: Tensor,
      sampler: Optional[MCSampler] = None,
      objective: Optional[MCAcquisitionObjective] = None,
      posterior_transform: Optional[PosteriorTransform] = None,
      prune_baseline: bool = False,
      cache_root: bool = True,
      **kwargs: Any,
  ) -> None:
      CachedCholeskyMCSamplerMixin.__init__(
          self, model=model, cache_root=cache_root, sampler=sampler
      )
      if prune_baseline:
          X_baseline = prune_inferior_points(
              model=model,
              X=X_baseline,
              objective=objective,
              posterior_transform=posterior_transform,
              marginalize_dim=kwargs.get("marginalize_dim"),
              constraints=self._constraints,
          )
      self.register_buffer("X_baseline", X_baseline)
      # registering buffers for _get_samples_and_objectives in the next `if` block
      self.register_buffer("baseline_samples", None)
      self.register_buffer("baseline_obj", None)
      if self._cache_root:
          self.q_in = -1
          # set baseline samples
          with torch.no_grad():  # this is _get_samples_and_objectives(X_baseline)
              posterior = self.model.posterior(
                  X_baseline,
                  posterior_transform=self.posterior_transform,
              )
              # Note: The root decomposition is cached in two different places. It
              # may be confusing to have two different caches, but this is not
              # trivial to change since each is needed for a different reason:
              # - LinearOperator caching to `posterior.mvn` allows for reuse within
              #   this function, which may be helpful if the same root decomposition
              #   is produced by the calls to `self.base_sampler` and
              #   `self._cache_root_decomposition`.
              # - self._baseline_L allows a root decomposition to be persisted outside
              #   this method.
              self.baseline_samples = self.get_posterior_samples(
                  posterior
              )
              self.baseline_obj = self.objective(
                  self.baseline_samples, X=X_baseline
              )
  
          # We make a copy here because we will write an attribute `base_samples`
          # to `self.base_sampler.base_samples`, and we don't want to mutate
          # `self.sampler`.
          self.base_sampler = deepcopy(self.sampler)
          self.register_buffer(
              "_baseline_best_f",
              self._compute_best_feasible_objective(
                  samples=self.baseline_samples, obj=self.baseline_obj
              ),
          )
          self._baseline_L = self._compute_root_decomposition(
              posterior=posterior
          )
  
imports: from __future__ import annotations
  from copy import deepcopy
  from functools import partial
  from typing import Any, Callable, List, Optional, Tuple, TypeVar, Union
  import torch
  from botorch.acquisition.cached_cholesky import CachedCholeskyMCSamplerMixin
  from botorch.acquisition.monte_carlo import SampleReducingMCAcquisitionFunction
  from botorch.acquisition.objective import ConstrainedMCObjective, MCAcquisitionObjective, PosteriorTransform
  from botorch.acquisition.utils import compute_best_feasible_objective, prune_inferior_points
  from botorch.exceptions.errors import BotorchError
  from botorch.models.model import Model
  from botorch.sampling.base import MCSampler
  from botorch.utils.safe_math import fatmax, log_fatplus, log_softplus, logmeanexp, smooth_amax
  from botorch.utils.transforms import match_batch_shape
  from torch import Tensor
label_desc: Use image node_id 3 for calling the qLogNoisyExpectedImprovement obj's underlying member method code with example usage: obj._init_baseline(model, X_baseline, sampler, objective, posterior_transform, prune_baseline, cache_root) without return types