class_docstr: A multi-output GP model with independent GPs for the outputs.
  
  This model supports different-shaped training inputs for each of its
  sub-models. It can be used with any number of single-output
  `GPyTorchModel`\s and the models can be of different types. Use this model
  when you have independent outputs with different training data. When
  modeling correlations between outputs, use `MultiTaskGP`.
  
  Internally, this model is just a list of individual models, but it implements
  the same input/output interface as all other BoTorch models. This makes it
  very flexible and convenient to work with. The sequential evaluation comes
  at a performance cost though - if you are using a block design (i.e. the
  same number of training example for each output, and a similar model
  structure, you should consider using a batched GP model instead, such as
  `SingleTaskGP` with batched inputs).
class_docstr_tok: ["A","multi-output","GP","model","with","independent","GPs","for","the","outputs",".","This","model","supports","different-shaped","training","inputs","for","each","of","its","sub-models",".","It","can","be","used","with","any","number","of","single-output","`","GPyTorchModel","`","\\s","and","the","models","can","be","of","different","types",".","Use","this","model","when","you","have","independent","outputs","with","different","training","data",".","When","modeling","correlations","between","outputs",",","use","`","MultiTaskGP","`",".","Internally",",","this","model","is","just","a","list","of","individual","models",",","but","it","implements","the","same","input\/output","interface","as","all","other","BoTorch","models",".","This","makes","it","very","flexible","and","convenient","to","work","with",".","The","sequential","evaluation","comes","at","a","performance","cost","though","-","if","you","are","using","a","block","design","(","i.e",".","the","same","number","of","training","example","for","each","output",",","and","a","similar","model","structure",",","you","should","consider","using","a","batched","GP","model","instead",",","such","as","`","SingleTaskGP","`","with","batched","inputs",")","."]
docstr: Condition the model on new observations.
  
  Args:
      X: A `m`-list of `batch_shape x n' x d`-dim Tensors, where `d` is the
          dimension of the feature space, `n'` is the number of points
          per batch, and `batch_shape` is the batch shape (must be compatible
          with the batch shape of the model).
      Y: A `batch_shape' x n' x m`-dim Tensor, where `m` is the number of
          model outputs, `n'` is the number of points per batch, and
          `batch_shape'` is the batch shape of the observations.
          `batch_shape'` must be broadcastable to `batch_shape` using
          standard broadcasting semantics. If `Y` has fewer batch dimensions
          than `X`, its is assumed that the missing batch dimensions are
          the same for all `Y`.
      kwargs: Keyword arguments passed to
          `IndependentModelList.get_fantasy_model`.
  
  Returns:
      A `ModelListGP` representing the original model
      conditioned on the new observations `(X, Y)` (and possibly noise
      observations passed in via kwargs). Here the `i`-th model has
      `n_i + n'` training examples, where the `n'` training examples have
      been added and all test-time caches have been updated.
docstr_tok: ["Condition","the","model","on","new","observations",".","Args",":","X",":","A","`","m","`","-list","of","`","batch_shape","x","n","'","x","d","`","-dim","Tensors",",","where","`","d","`","is","the","dimension","of","the","feature","space",",","`","n","'","`","is","the","number","of","points","per","batch",",","and","`","batch_shape","`","is","the","batch","shape","(","must","be","compatible","with","the","batch","shape","of","the","model",")",".","Y",":","A","`","batch_shape","'","x","n","'","x","m","`","-dim","Tensor",",","where","`","m","`","is","the","number","of","model","outputs",",","`","n","'","`","is","the","number","of","points","per","batch",",","and","`","batch_shape","'","`","is","the","batch","shape","of","the","observations",".","`","batch_shape","'","`","must","be","broadcastable","to","`","batch_shape","`","using","standard","broadcasting","semantics",".","If","`","Y","`","has","fewer","batch","dimensions","than","`","X","`",",","its","is","assumed","that","the","missing","batch","dimensions","are","the","same","for","all","`","Y","`",".","kwargs",":","Keyword","arguments","passed","to","`","IndependentModelList.get_fantasy_model","`",".","Returns",":","A","`","ModelListGP","`","representing","the","original","model","conditioned","on","the","new","observations","`","(","X",",","Y",")","`","(","and","possibly","noise","observations","passed","in","via","kwargs",")",".","Here","the","`","i","`","-th","model","has","`","n_i","+","n","'","`","training","examples",",","where","the","`","n","'","`","training","examples","have","been","added","and","all","test-time","caches","have","been","updated","."]
code: def condition_on_observations(
      self, X: List[Tensor], Y: Tensor, **kwargs: Any
  ) -> ModelListGP:
      r"""Condition the model on new observations.
  
      Args:
          X: A `m`-list of `batch_shape x n' x d`-dim Tensors, where `d` is the
              dimension of the feature space, `n'` is the number of points
              per batch, and `batch_shape` is the batch shape (must be compatible
              with the batch shape of the model).
          Y: A `batch_shape' x n' x m`-dim Tensor, where `m` is the number of
              model outputs, `n'` is the number of points per batch, and
              `batch_shape'` is the batch shape of the observations.
              `batch_shape'` must be broadcastable to `batch_shape` using
              standard broadcasting semantics. If `Y` has fewer batch dimensions
              than `X`, its is assumed that the missing batch dimensions are
              the same for all `Y`.
          kwargs: Keyword arguments passed to
              `IndependentModelList.get_fantasy_model`.
  
      Returns:
          A `ModelListGP` representing the original model
          conditioned on the new observations `(X, Y)` (and possibly noise
          observations passed in via kwargs). Here the `i`-th model has
          `n_i + n'` training examples, where the `n'` training examples have
          been added and all test-time caches have been updated.
      """
      if Y.shape[-1] != self.num_outputs:
          raise BotorchTensorDimensionError(
              "Incorrect number of outputs for observations. Received "
              f"{Y.shape[-1]} observation outputs, but model has "
              f"{self.num_outputs} outputs."
          )
      if len(X) != self.num_outputs:
          raise BotorchTensorDimensionError(
              "Incorrect number of inputs for observations. Received "
              f"{len(X)} observation inputs, but model has "
              f"{self.num_outputs} outputs."
          )
      if "noise" in kwargs:
          noise = kwargs.pop("noise")
          if noise.shape != Y.shape[-noise.dim() :]:
              raise BotorchTensorDimensionError(
                  "The shape of observation noise does not agree with the outcomes. "
                  f"Received {noise.shape} noise with {Y.shape} outcomes."
              )
  
      else:
          noise = None
      targets = []
      inputs = []
      noises = []
      i = 0
      for model in self.models:
          j = i + model.num_outputs
          y_i = torch.cat([Y[..., k] for k in range(i, j)], dim=-1)
          X_i = torch.cat([X[k] for k in range(i, j)], dim=-2)
          if noise is None:
              noise_i = None
          else:
              noise_i = torch.cat(
                  [noise[..., k] for k in range(i, j)], dim=-1
              )
          if hasattr(model, "outcome_transform"):
              y_i, noise_i = model.outcome_transform(y_i, noise_i)
              if noise_i is not None:
                  noise_i = noise_i.squeeze(0)
          targets.append(y_i)
          inputs.append(X_i)
          noises.append(noise_i)
          i += model.num_outputs
  
      kwargs_ = (
          {**kwargs, "noise": noises} if noise is not None else kwargs
      )
      return super().get_fantasy_model(inputs, targets, **kwargs_)
  
file_path: botorch/botorch/models/model_list_gp_regression.py
imports: from __future__ import annotations
  from copy import deepcopy
  from typing import Any, List
  import torch
  from botorch.exceptions.errors import BotorchTensorDimensionError
  from botorch.models.gpytorch import GPyTorchModel, ModelListGPyTorchModel
  from botorch.models.model import FantasizeMixin
  from gpytorch.models import IndependentModelList
  from torch import Tensor
label_desc: Use image node_id 2 for calling the ModelListGP obj's underlying member method code with example usage: obj.condition_on_observations(X, Y) and returns: super