class_docstr: Base class for batched multi-output GPyTorch models with independent outputs.
  
  This model should be used when the same training data is used for all outputs.
  Outputs are modeled independently by using a different batch for each output.
  
  :meta private:
class_docstr_tok: ["Base","class","for","batched","multi-output","GPyTorch","models","with","independent","outputs",".","This","model","should","be","used","when","the","same","training","data","is","used","for","all","outputs",".","Outputs","are","modeled","independently","by","using","a","different","batch","for","each","output",".",":","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.
      output_indices: A list of indices, corresponding to the outputs over
          which to compute the posterior (if the model is multi-output).
          Can be used to speed up computation if only a subset of the
          model's outputs are required for optimization. If omitted,
          computes the posterior over all model outputs.
      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 x m`).
      posterior_transform: An optional PosteriorTransform.
  
  Returns:
      A `GPyTorchPosterior` object, representing `batch_shape` joint
      distributions over `q` points and the outputs selected by
      `output_indices` each. 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",".","output_indices",":","A","list","of","indices",",","corresponding","to","the","outputs","over","which","to","compute","the","posterior","(","if","the","model","is","multi-output",")",".","Can","be","used","to","speed","up","computation","if","only","a","subset","of","the","model","'s","outputs","are","required","for","optimization",".","If","omitted",",","computes","the","posterior","over","all","model","outputs",".","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","x","m","`",")",".","posterior_transform",":","An","optional","PosteriorTransform",".","Returns",":","A","`","GPyTorchPosterior","`","object",",","representing","`","batch_shape","`","joint","distributions","over","`","q","`","points","and","the","outputs","selected","by","`","output_indices","`","each",".","Includes","observation","noise","if","specified","."]
code: def posterior(
      self,
      X: Tensor,
      output_indices: Optional[List[int]] = None,
      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.
          output_indices: A list of indices, corresponding to the outputs over
              which to compute the posterior (if the model is multi-output).
              Can be used to speed up computation if only a subset of the
              model's outputs are required for optimization. If omitted,
              computes the posterior over all model outputs.
          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 x m`).
          posterior_transform: An optional PosteriorTransform.
  
      Returns:
          A `GPyTorchPosterior` object, representing `batch_shape` joint
          distributions over `q` points and the outputs selected by
          `output_indices` each. 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():
          # insert a dimension for the output dimension
          if self._num_outputs > 1:
              X, output_dim_idx = add_output_dim(
                  X=X, original_batch_shape=self._input_batch_shape
              )
          # 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 self._num_outputs > 1:
                  noise_shape = X.shape[:-3] + torch.Size(
                      [self._num_outputs, X.shape[-2]]
                  )
              else:
                  noise_shape = X.shape[:-1]
              if torch.is_tensor(observation_noise):
                  # TODO: Validate noise shape
                  # make observation_noise `batch_shape x q x n`
                  if self.num_outputs > 1:
                      obs_noise = observation_noise.transpose(-1, -2)
                  else:
                      obs_noise = observation_noise.squeeze(-1)
                  mvn = self.likelihood(
                      mvn,
                      X,
                      noise=obs_noise.expand(noise_shape),
                  )
              elif isinstance(
                  self.likelihood, FixedNoiseGaussianLikelihood
              ):
                  # Use the mean of the previous noise values (TODO: be smarter here).
                  observation_noise = self.likelihood.noise.mean(
                      dim=-1, keepdim=True
                  )
                  mvn = self.likelihood(
                      mvn,
                      X,
                      noise=observation_noise.expand(noise_shape),
                  )
              else:
                  mvn = self.likelihood(mvn, X)
          if self._num_outputs > 1:
              mean_x = mvn.mean
              covar_x = mvn.lazy_covariance_matrix
              output_indices = output_indices or range(
                  self._num_outputs
              )
              mvns = [
                  MultivariateNormal(
                      mean_x.select(dim=output_dim_idx, index=t),
                      covar_x[(slice(None),) * output_dim_idx + (t,)],
                  )
                  for t in output_indices
              ]
              mvn = MultitaskMultivariateNormal.from_independent_mvns(
                  mvns=mvns
              )
  
      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 5 for calling the BatchedMultiOutputGPyTorchModel obj's underlying member method code with example usage: obj.posterior(X, output_indices, observation_noise, posterior_transform) and returns: posterior, posterior_transform