class_docstr: The Multi-Task GP with the latent context embedding multioutput (LCE-M)
  kernel. See [Feng2020HDCPS]_ for a reference on the model and its use in Bayesian
  optimization.
docstr: Args:
      train_X: (n x d) X training data.
      train_Y: (n x 1) Y training data.
      task_feature: Column index of train_X to get context indices.
      train_Yvar: An optional (n x 1) tensor of observed variances of each
          training Y. If None, we infer the noise. Note that the inferred noise
          is common across all tasks.
      context_cat_feature: (n_contexts x k) one-hot encoded context
          features. Rows are ordered by context indices, where k is the
          number of categorical variables. If None, task indices will
          be used and k = 1.
      context_emb_feature: (n_contexts x m) pre-given continuous
          embedding features. Rows are ordered by context indices.
      embs_dim_list: Embedding dimension for each categorical variable.
          The length equals k. If None, the embedding dimension is set to 1
          for each categorical variable.
      output_tasks: A list of task indices for which to compute model
          outputs for. If omitted, return outputs for all task indices.
docstr_tok: ["Args",":","train_X",":","(","n","x","d",")","X","training","data",".","train_Y",":","(","n","x","1",")","Y","training","data",".","task_feature",":","Column","index","of","train_X","to","get","context","indices",".","train_Yvar",":","An","optional","(","n","x","1",")","tensor","of","observed","variances","of","each","training","Y",".","If","None",",","we","infer","the","noise",".","Note","that","the","inferred","noise","is","common","across","all","tasks",".","context_cat_feature",":","(","n_contexts","x","k",")","one-hot","encoded","context","features",".","Rows","are","ordered","by","context","indices",",","where","k","is","the","number","of","categorical","variables",".","If","None",",","task","indices","will","be","used","and","k","=","1.","context_emb_feature",":","(","n_contexts","x","m",")","pre-given","continuous","embedding","features",".","Rows","are","ordered","by","context","indices",".","embs_dim_list",":","Embedding","dimension","for","each","categorical","variable",".","The","length","equals","k.","If","None",",","the","embedding","dimension","is","set","to","1","for","each","categorical","variable",".","output_tasks",":","A","list","of","task","indices","for","which","to","compute","model","outputs","for",".","If","omitted",",","return","outputs","for","all","task","indices","."]
code: def __init__(
      self,
      train_X: Tensor,
      train_Y: Tensor,
      task_feature: int,
      train_Yvar: Optional[Tensor] = None,
      context_cat_feature: Optional[Tensor] = None,
      context_emb_feature: Optional[Tensor] = None,
      embs_dim_list: Optional[List[int]] = None,
      output_tasks: Optional[List[int]] = None,
      input_transform: Optional[InputTransform] = None,
      outcome_transform: Optional[OutcomeTransform] = None,
  ) -> None:
      r"""
      Args:
          train_X: (n x d) X training data.
          train_Y: (n x 1) Y training data.
          task_feature: Column index of train_X to get context indices.
          train_Yvar: An optional (n x 1) tensor of observed variances of each
              training Y. If None, we infer the noise. Note that the inferred noise
              is common across all tasks.
          context_cat_feature: (n_contexts x k) one-hot encoded context
              features. Rows are ordered by context indices, where k is the
              number of categorical variables. If None, task indices will
              be used and k = 1.
          context_emb_feature: (n_contexts x m) pre-given continuous
              embedding features. Rows are ordered by context indices.
          embs_dim_list: Embedding dimension for each categorical variable.
              The length equals k. If None, the embedding dimension is set to 1
              for each categorical variable.
          output_tasks: A list of task indices for which to compute model
              outputs for. If omitted, return outputs for all task indices.
  
      """
      super().__init__(
          train_X=train_X,
          train_Y=train_Y,
          task_feature=task_feature,
          train_Yvar=train_Yvar,
          output_tasks=output_tasks,
          input_transform=input_transform,
          outcome_transform=outcome_transform,
      )
      self.device = train_X.device
      #  context indices
      all_tasks = train_X[:, task_feature].unique()
      self.all_tasks = all_tasks.to(dtype=torch.long).tolist()
      self.all_tasks.sort()  # unique in python does automatic sort; add for safety
  
      if context_cat_feature is None:
          context_cat_feature = all_tasks.unsqueeze(-1).to(
              device=self.device
          )
      self.context_cat_feature = (
          context_cat_feature  # row indices = context indices
      )
      self.context_emb_feature = context_emb_feature
  
      #  construct emb_dims based on categorical features
      if embs_dim_list is None:
          #  set embedding_dim = 1 for each categorical variable
          embs_dim_list = [
              1 for _i in range(context_cat_feature.size(1))
          ]
      n_embs = sum(embs_dim_list)
      self.emb_dims = [
          (len(context_cat_feature[:, i].unique()), embs_dim_list[i])
          for i in range(context_cat_feature.size(1))
      ]
      # contruct embedding layer: need to handle multiple categorical features
      self.emb_layers = ModuleList(
          [
              torch.nn.Embedding(
                  num_embeddings=x, embedding_dim=y, max_norm=1.0
              )
              for x, y in self.emb_dims
          ]
      )
      self.task_covar_module = RBFKernel(
          ard_num_dims=n_embs,
          lengthscale_constraint=Interval(
              0.0, 2.0, transform=None, initial_value=1.0
          ),
      )
      self.to(train_X)
  
imports: import warnings
  from typing import List, Optional
  import torch
  from botorch.models.multitask import MultiTaskGP
  from botorch.models.transforms.input import InputTransform
  from botorch.models.transforms.outcome import OutcomeTransform
  from gpytorch.constraints import Interval
  from gpytorch.distributions.multivariate_normal import MultivariateNormal
  from gpytorch.kernels.rbf_kernel import RBFKernel
  from linear_operator.operators import InterpolatedLinearOperator, LinearOperator
  from torch import Tensor
  from torch.nn import ModuleList
label_desc: Use image node_id 1 to create a new LCEMGP object from inherited base classes: MultiTaskGP with example: obj = LCEMGP(train_X, train_Y, task_feature, train_Yvar, context_cat_feature, context_emb_feature, embs_dim_list, output_tasks, input_transform, outcome_transform)