class_docstr: This is a multi-task dataset that is constructed from the datasets of
  individual tasks. It offers functionality to combine parts of individual
  datasets to construct the inputs necessary for the `MultiTaskGP` models.
  
  The datasets of individual tasks are allowed to represent different sets
  of features. When there are heterogeneous feature sets, calling
  `MultiTaskDataset.X` will result in an error.
docstr: Construct a `MultiTaskDataset`.
  
  Args:
      datasets: A list of the datasets of individual tasks. Each dataset
          is expected to contain data for only one outcome.
      target_outcome_name: Name of the target outcome to be modeled.
      task_feature_index: If the task feature is included in the Xs of the
          individual datasets, this should be used to specify its index.
          If omitted, the task feature will be appended while concatenating Xs.
          If given, we sanity-check that the names of the task features
          match between all datasets.
code: def __init__(
      self,
      datasets: List[SupervisedDataset],
      target_outcome_name: str,
      task_feature_index: Optional[int] = None,
  ):
      """Construct a `MultiTaskDataset`.
  
      Args:
          datasets: A list of the datasets of individual tasks. Each dataset
              is expected to contain data for only one outcome.
          target_outcome_name: Name of the target outcome to be modeled.
          task_feature_index: If the task feature is included in the Xs of the
              individual datasets, this should be used to specify its index.
              If omitted, the task feature will be appended while concatenating Xs.
              If given, we sanity-check that the names of the task features
              match between all datasets.
      """
      self.datasets: Dict[str, SupervisedDataset] = {
          ds.outcome_names[0]: ds for ds in datasets
      }
      self.target_outcome_name = target_outcome_name
      self.task_feature_index = task_feature_index
      self._validate_datasets(datasets=datasets)
      self.feature_names = self.datasets[
          target_outcome_name
      ].feature_names
      self.outcome_names = [target_outcome_name]
  
      # Check if the datasets have identical feature sets.
      self.has_heterogeneous_features = any(
          datasets[0].feature_names != ds.feature_names
          for ds in datasets[1:]
      )
  
imports: from __future__ import annotations
  import collections
  import warnings
  from typing import Any, Dict, List, Optional, Union
  import torch
  from botorch.exceptions.errors import InputDataError, UnsupportedError
  from botorch.utils.containers import BotorchContainer, SliceContainer
  from torch import long, ones, Tensor
label_desc: Use image node_id 1 to create a new MultiTaskDataset object from inherited base classes: SupervisedDataset with example: obj = MultiTaskDataset(datasets, target_outcome_name, task_feature_index)