class_docstr: Abstract base class for models based on multi-output GPyTorch models.
  
  This is meant to be used with a gpytorch ModelList wrapper for independent
  evaluation of submodels. Those submodels can themselves be multi-output
  models, in which case the task covariances will be ignored.
  
  :meta private:
class_docstr_tok: ["Abstract","base","class","for","models","based","on","multi-output","GPyTorch","models",".","This","is","meant","to","be","used","with","a","gpytorch","ModelList","wrapper","for","independent","evaluation","of","submodels",".","Those","submodels","can","themselves","be","multi-output","models",",","in","which","case","the","task","covariances","will","be","ignored",".",":","meta","private",":"]
docstr: Computes the posterior over model outputs at the provided points.
  If any model returns a MultitaskMultivariateNormal posterior, then that
  will be split into individual MVNs per task, with inter-task covariance
  ignored.
  
  Args:
      X: A `b x q x d`-dim Tensor, where `d` is the dimension of the
          feature space, `q` is the number of points considered jointly,
          and `b` is the batch dimension.
      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
          respective likelihoods to the posterior. If a Tensor of shape
          `(batch_shape) x q x m`, use it directly as the observation
          noise (with `observation_noise[...,i]` added to the posterior
          of the `i`-th model).
      posterior_transform: An optional PosteriorTransform.
  
  Returns:
      - If no `posterior_transform` is provided and the component models have no
          `outcome_transform`, or if the component models only use linear outcome
          transforms like `Standardize` (i.e. not `Log`), returns a
          `GPyTorchPosterior` or `GaussianMixturePosterior` object,
          representing `batch_shape` joint distributions over `q` points
          and the outputs selected by `output_indices` each. Includes
          measurement noise if `observation_noise` is specified.
      - If no `posterior_transform` is provided and component models have
          nonlinear transforms like `Log`, returns a `PosteriorList` with
          sub-posteriors of type `TransformedPosterior`
      - If `posterior_transform` is provided, that posterior transform will be
         applied and will determine the return type. This could potentially be
         any subclass of `Posterior`, but common choices give a
         `GPyTorchPosterior`.
docstr_tok: ["Computes","the","posterior","over","model","outputs","at","the","provided","points",".","If","any","model","returns","a","MultitaskMultivariateNormal","posterior",",","then","that","will","be","split","into","individual","MVNs","per","task",",","with","inter-task","covariance","ignored",".","Args",":","X",":","A","`","b","x","q","x","d","`","-dim","Tensor",",","where","`","d","`","is","the","dimension","of","the","feature","space",",","`","q","`","is","the","number","of","points","considered","jointly",",","and","`","b","`","is","the","batch","dimension",".","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","respective","likelihoods","to","the","posterior",".","If","a","Tensor","of","shape","`","(","batch_shape",")","x","q","x","m","`",",","use","it","directly","as","the","observation","noise","(","with","`","observation_noise","[","...",",","i","]","`","added","to","the","posterior","of","the","`","i","`","-th","model",")",".","posterior_transform",":","An","optional","PosteriorTransform",".","Returns",":","-","If","no","`","posterior_transform","`","is","provided","and","the","component","models","have","no","`","outcome_transform","`",",","or","if","the","component","models","only","use","linear","outcome","transforms","like","`","Standardize","`","(","i.e",".","not","`","Log","`",")",",","returns","a","`","GPyTorchPosterior","`","or","`","GaussianMixturePosterior","`","object",",","representing","`","batch_shape","`","joint","distributions","over","`","q","`","points","and","the","outputs","selected","by","`","output_indices","`","each",".","Includes","measurement","noise","if","`","observation_noise","`","is","specified",".","-","If","no","`","posterior_transform","`","is","provided","and","component","models","have","nonlinear","transforms","like","`","Log","`",",","returns","a","`","PosteriorList","`","with","sub-posteriors","of","type","`","TransformedPosterior","`","-","If","`","posterior_transform","`","is","provided",",","that","posterior","transform","will","be","applied","and","will","determine","the","return","type",".","This","could","potentially","be","any","subclass","of","`","Posterior","`",",","but","common","choices","give","a","`","GPyTorchPosterior","`","."]
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, PosteriorList]:
      r"""Computes the posterior over model outputs at the provided points.
      If any model returns a MultitaskMultivariateNormal posterior, then that
      will be split into individual MVNs per task, with inter-task covariance
      ignored.
  
      Args:
          X: A `b x q x d`-dim Tensor, where `d` is the dimension of the
              feature space, `q` is the number of points considered jointly,
              and `b` is the batch dimension.
          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
              respective likelihoods to the posterior. If a Tensor of shape
              `(batch_shape) x q x m`, use it directly as the observation
              noise (with `observation_noise[...,i]` added to the posterior
              of the `i`-th model).
          posterior_transform: An optional PosteriorTransform.
  
      Returns:
          - If no `posterior_transform` is provided and the component models have no
              `outcome_transform`, or if the component models only use linear outcome
              transforms like `Standardize` (i.e. not `Log`), returns a
              `GPyTorchPosterior` or `GaussianMixturePosterior` object,
              representing `batch_shape` joint distributions over `q` points
              and the outputs selected by `output_indices` each. Includes
              measurement noise if `observation_noise` is specified.
          - If no `posterior_transform` is provided and component models have
              nonlinear transforms like `Log`, returns a `PosteriorList` with
              sub-posteriors of type `TransformedPosterior`
          - If `posterior_transform` is provided, that posterior transform will be
             applied and will determine the return type. This could potentially be
             any subclass of `Posterior`, but common choices give a
             `GPyTorchPosterior`.
      """
  
      # Nonlinear transforms untransform to a `TransformedPosterior`,
      # which can't be made into a `GPyTorchPosterior`
      returns_untransformed = any(
          hasattr(mod, "outcome_transform")
          and (not mod.outcome_transform._is_linear)
          for mod in self.models
      )
      # NOTE: We're not passing in the posterior transform here. We'll apply it later.
      posterior = ModelList.posterior(
          self,
          X=X,
          output_indices=output_indices,
          observation_noise=observation_noise,
          **kwargs,
      )
      if not returns_untransformed:
          mvns = [p.distribution for p in posterior.posteriors]
          if any(
              isinstance(m, MultitaskMultivariateNormal) for m in mvns
          ):
              mvn_list = []
              for mvn in mvns:
                  if len(mvn.event_shape) == 2:
                      # We separate MTMVNs into independent-across-task MVNs for
                      # the convenience of using BlockDiagLinearOperator below.
                      # (b x q x m x m) -> list of m (b x q x 1 x 1)
                      mvn_list.extend(separate_mtmvn(mvn))
                  else:
                      mvn_list.append(mvn)
              mean = torch.stack([mvn.mean for mvn in mvn_list], dim=-1)
              covars = CatLinearOperator(
                  *[
                      mvn.lazy_covariance_matrix.unsqueeze(-3)
                      for mvn in mvn_list
                  ],
                  dim=-3,
              )  # List of m (b x q x 1 x 1) -> (b x q x m x 1 x 1)
              mvn = MultitaskMultivariateNormal(
                  mean=mean,
                  covariance_matrix=BlockDiagLinearOperator(
                      covars, block_dim=-3
                  ).to(
                      X
                  ),  # (b x q x m x 1 x 1) -> (b x q x m x m)
                  interleaved=False,
              )
          else:
              mvn = (
                  mvns[0]
                  if len(mvns) == 1
                  else MultitaskMultivariateNormal.from_independent_mvns(
                      mvns=mvns
                  )
              )
          # Return the result as a GPyTorchPosterior/GaussianMixturePosterior.
          if any(is_ensemble(m) for m in self.models):
              # Mixing fully Bayesian and other GP models is currently
              # not supported.
              posterior = GaussianMixturePosterior(distribution=mvn)
          else:
              posterior = GPyTorchPosterior(distribution=mvn)
      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 2 for calling the ModelListGPyTorchModel obj's underlying member method code with example usage: obj.posterior(X, output_indices, observation_noise, posterior_transform) and returns: posterior, posterior_transform