class_docstr: Abstract base class for models based on GPyTorch models.
  
  The easiest way to use this is to subclass a model from a GPyTorch model
  class (e.g. an `ExactGP`) and this `GPyTorchModel`. See e.g. `SingleTaskGP`.
  
  :meta private:
class_docstr_tok: ["Abstract","base","class","for","models","based","on","GPyTorch","models",".","The","easiest","way","to","use","this","is","to","subclass","a","model","from","a","GPyTorch","model","class","(","e.g",".","an","`","ExactGP","`",")","and","this","`","GPyTorchModel","`",".","See","e.g",".","`","SingleTaskGP","`",".",":","meta","private",":"]
docstr: Computes the posterior over model outputs at the provided points.
  
  Args:
      X: A `(batch_shape) x q x d`-dim Tensor, where `d` is the dimension
          of the feature space and `q` is the number of points considered
          jointly.
      observation_noise: If True, add the observation noise from the
          likelihood to the posterior. If a Tensor, use it directly as the
          observation noise (must be of shape `(batch_shape) x q`). It is
          assumed to be in the outcome-transformed space if an outcome
          transform is used.
      posterior_transform: An optional PosteriorTransform.
  
  Returns:
      A `GPyTorchPosterior` object, representing a batch of `b` joint
      distributions over `q` points. Includes observation noise if
      specified.
docstr_tok: ["Computes","the","posterior","over","model","outputs","at","the","provided","points",".","Args",":","X",":","A","`","(","batch_shape",")","x","q","x","d","`","-dim","Tensor",",","where","`","d","`","is","the","dimension","of","the","feature","space","and","`","q","`","is","the","number","of","points","considered","jointly",".","observation_noise",":","If","True",",","add","the","observation","noise","from","the","likelihood","to","the","posterior",".","If","a","Tensor",",","use","it","directly","as","the","observation","noise","(","must","be","of","shape","`","(","batch_shape",")","x","q","`",")",".","It","is","assumed","to","be","in","the","outcome-transformed","space","if","an","outcome","transform","is","used",".","posterior_transform",":","An","optional","PosteriorTransform",".","Returns",":","A","`","GPyTorchPosterior","`","object",",","representing","a","batch","of","`","b","`","joint","distributions","over","`","q","`","points",".","Includes","observation","noise","if","specified","."]
code: def posterior(
      self,
      X: Tensor,
      observation_noise: Union[bool, Tensor] = False,
      posterior_transform: Optional[PosteriorTransform] = None,
      **kwargs: Any,
  ) -> Union[GPyTorchPosterior, TransformedPosterior]:
      r"""Computes the posterior over model outputs at the provided points.
  
      Args:
          X: A `(batch_shape) x q x d`-dim Tensor, where `d` is the dimension
              of the feature space and `q` is the number of points considered
              jointly.
          observation_noise: If True, add the observation noise from the
              likelihood to the posterior. If a Tensor, use it directly as the
              observation noise (must be of shape `(batch_shape) x q`). It is
              assumed to be in the outcome-transformed space if an outcome
              transform is used.
          posterior_transform: An optional PosteriorTransform.
  
      Returns:
          A `GPyTorchPosterior` object, representing a batch of `b` joint
          distributions over `q` points. Includes observation noise if
          specified.
      """
      self.eval()  # make sure model is in eval mode
      # input transforms are applied at `posterior` in `eval` mode, and at
      # `model.forward()` at the training time
      X = self.transform_inputs(X)
      with gpt_posterior_settings():
          # NOTE: BoTorch's GPyTorchModels also inherit from GPyTorch's ExactGP, thus
          # self(X) calls GPyTorch's ExactGP's __call__, which computes the posterior,
          # rather than e.g. SingleTaskGP's forward, which computes the prior.
          mvn = self(X)
          if observation_noise is not False:
              if isinstance(observation_noise, torch.Tensor):
                  # TODO: Make sure observation noise is transformed correctly
                  self._validate_tensor_args(X=X, Y=observation_noise)
                  if observation_noise.size(-1) == 1:
                      observation_noise = observation_noise.squeeze(-1)
                  mvn = self.likelihood(mvn, X, noise=observation_noise)
              else:
                  mvn = self.likelihood(mvn, X)
      posterior = GPyTorchPosterior(distribution=mvn)
      if hasattr(self, "outcome_transform"):
          posterior = self.outcome_transform.untransform_posterior(
              posterior
          )
      if posterior_transform is not None:
          return posterior_transform(posterior)
      return posterior
  
imports: from __future__ import annotations
  import itertools
  import warnings
  from abc import ABC
  from copy import deepcopy
  from typing import Any, List, Optional, Tuple, TYPE_CHECKING, Union
  import torch
  from botorch.acquisition.objective import PosteriorTransform
  from botorch.exceptions.errors import BotorchTensorDimensionError, InputDataError
  from botorch.exceptions.warnings import _get_single_precision_warning, BotorchTensorDimensionWarning
  from botorch.models.model import Model, ModelList
  from botorch.models.utils import _make_X_full, add_output_dim, gpt_posterior_settings, mod_batch_shape, multioutput_to_batch_mode_transform
  from botorch.posteriors.fully_bayesian import GaussianMixturePosterior
  from botorch.posteriors.gpytorch import GPyTorchPosterior
  from botorch.utils.multitask import separate_mtmvn
  from botorch.utils.transforms import is_ensemble
  from gpytorch.distributions import MultitaskMultivariateNormal, MultivariateNormal
  from gpytorch.likelihoods.gaussian_likelihood import FixedNoiseGaussianLikelihood
  from linear_operator.operators import BlockDiagLinearOperator, CatLinearOperator
  from torch import Tensor
label_desc: Use image node_id 4 for calling the GPyTorchModel obj's underlying member method code with example usage: obj.posterior(X, observation_noise, posterior_transform) and returns: posterior, posterior_transform