class_docstr: Abstract base class for multi-task models based on GPyTorch models.
  
  This class provides the `posterior` method to models that implement a
  "long-format" multi-task GP in the style of `MultiTaskGP`.
  
  :meta private:
class_docstr_tok: ["Abstract","base","class","for","multi-task","models","based","on","GPyTorch","models",".","This","class","provides","the","`","posterior","`","method","to","models","that","implement","a","''","long-format","''","multi-task","GP","in","the","style","of","`","MultiTaskGP","`",".",":","meta","private",":"]
docstr: Computes the posterior over model outputs at the provided points.
  
  Args:
      X: A tensor of shape `batch_shape x q x d` or `batch_shape x q x (d + 1)`,
          where `d` is the dimension of the feature space (not including task
          indices) and `q` is the number of points considered jointly. The `+ 1`
          dimension is the optional task feature / index. If given, the model
          produces the outputs for the given task indices. If omitted, the
          model produces outputs for tasks in in `self._output_tasks` (specified
          as `output_tasks` while constructing the model), which can overwritten
          using `output_indices`.
      output_indices: A list of indices, corresponding to the tasks over
          which to compute the posterior. Only used if `X` does not include the
          task feature. If omitted, defaults to `self._output_tasks`.
      observation_noise: If True, add observation noise from the respective
          likelihoods. If a Tensor, specifies the observation noise levels
          to add.
      posterior_transform: An optional PosteriorTransform.
  
  Returns:
      A `GPyTorchPosterior` object, representing `batch_shape` joint
      distributions over `q` points. If the task features are included in `X`,
      the posterior will be single output. Otherwise, the posterior will be
      single or multi output corresponding to the tasks included in
      either the `output_indices` or `self._output_tasks`.
docstr_tok: ["Computes","the","posterior","over","model","outputs","at","the","provided","points",".","Args",":","X",":","A","tensor","of","shape","`","batch_shape","x","q","x","d","`","or","`","batch_shape","x","q","x","(","d","+","1",")","`",",","where","`","d","`","is","the","dimension","of","the","feature","space","(","not","including","task","indices",")","and","`","q","`","is","the","number","of","points","considered","jointly",".","The","`","+","1","`","dimension","is","the","optional","task","feature","\/","index",".","If","given",",","the","model","produces","the","outputs","for","the","given","task","indices",".","If","omitted",",","the","model","produces","outputs","for","tasks","in","in","`","self._output_tasks","`","(","specified","as","`","output_tasks","`","while","constructing","the","model",")",",","which","can","overwritten","using","`","output_indices","`",".","output_indices",":","A","list","of","indices",",","corresponding","to","the","tasks","over","which","to","compute","the","posterior",".","Only","used","if","`","X","`","does","not","include","the","task","feature",".","If","omitted",",","defaults","to","`","self._output_tasks","`",".","observation_noise",":","If","True",",","add","observation","noise","from","the","respective","likelihoods",".","If","a","Tensor",",","specifies","the","observation","noise","levels","to","add",".","posterior_transform",":","An","optional","PosteriorTransform",".","Returns",":","A","`","GPyTorchPosterior","`","object",",","representing","`","batch_shape","`","joint","distributions","over","`","q","`","points",".","If","the","task","features","are","included","in","`","X","`",",","the","posterior","will","be","single","output",".","Otherwise",",","the","posterior","will","be","single","or","multi","output","corresponding","to","the","tasks","included","in","either","the","`","output_indices","`","or","`","self._output_tasks","`","."]
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 tensor of shape `batch_shape x q x d` or `batch_shape x q x (d + 1)`,
              where `d` is the dimension of the feature space (not including task
              indices) and `q` is the number of points considered jointly. The `+ 1`
              dimension is the optional task feature / index. If given, the model
              produces the outputs for the given task indices. If omitted, the
              model produces outputs for tasks in in `self._output_tasks` (specified
              as `output_tasks` while constructing the model), which can overwritten
              using `output_indices`.
          output_indices: A list of indices, corresponding to the tasks over
              which to compute the posterior. Only used if `X` does not include the
              task feature. If omitted, defaults to `self._output_tasks`.
          observation_noise: If True, add observation noise from the respective
              likelihoods. If a Tensor, specifies the observation noise levels
              to add.
          posterior_transform: An optional PosteriorTransform.
  
      Returns:
          A `GPyTorchPosterior` object, representing `batch_shape` joint
          distributions over `q` points. If the task features are included in `X`,
          the posterior will be single output. Otherwise, the posterior will be
          single or multi output corresponding to the tasks included in
          either the `output_indices` or `self._output_tasks`.
      """
      includes_task_feature = (
          X.shape[-1] == self.num_non_task_features + 1
      )
      if includes_task_feature:
          # Make sure all task feature values are valid.
          task_features = X[..., self._task_feature].unique()
          if not (
              (task_features >= 0).all()
              and (task_features < self.num_tasks).all()
          ):
              raise ValueError(
                  "Expected all task features in `X` to be between 0 and "
                  f"self.num_tasks - 1. Got {task_features}."
              )
          if output_indices is not None:
              raise ValueError(
                  "`output_indices` must be None when `X` includes task features."
              )
          num_outputs = 1
          X_full = X
      else:
          # Add the task features to construct the full X for evaluation.
          if output_indices is None:
              output_indices = self._output_tasks
          num_outputs = len(output_indices)
          if not all(0 <= i < self.num_tasks for i in output_indices):
              raise ValueError(
                  "Expected `output_indices` to be between 0 and self.num_tasks - 1. "
                  f"Got {output_indices}."
              )
          X_full = _make_X_full(
              X=X, output_indices=output_indices, tf=self._task_feature
          )
  
      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_full = self.transform_inputs(X_full)
      with gpt_posterior_settings():
          mvn = self(X_full)
          if observation_noise is not False:
              raise NotImplementedError(
                  "Specifying observation noise is not yet supported by "
                  f"{self.__class__.__name__}."
              )
      # If single-output, return the posterior of a single-output model
      if num_outputs == 1:
          posterior = GPyTorchPosterior(distribution=mvn)
      else:
          # Otherwise, make a MultitaskMultivariateNormal out of this
          mtmvn = MultitaskMultivariateNormal(
              mean=mvn.mean.view(
                  *mvn.mean.shape[:-1], num_outputs, -1
              ).transpose(-1, -2),
              covariance_matrix=mvn.lazy_covariance_matrix,
              interleaved=False,
          )
          posterior = GPyTorchPosterior(distribution=mtmvn)
      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 1 for calling the MultiTaskGPyTorchModel obj's underlying member method code with example usage: obj.posterior(X, output_indices, observation_noise, posterior_transform) and returns: posterior, posterior_transform