code: def extract_Y_from_data(
      experiment: Experiment,
      metric_names: List[str],
      data: Optional[Data] = None,
  ) -> Tuple[Tensor, Tensor]:
      r"""Converts the experiment observation data into a tensor.
  
      NOTE: This requires block design for observations. It will
      error out if any trial is missing data for any of the given
      metrics or if the data is missing the `trial_index`.
  
      Args:
          experiment: The experiment to extract the data from.
          metric_names: List of metric names to extract data for.
          data: An optional `Data` object to use instead of the
              experiment data. Note that the experiment must have
              a corresponding COMPLETED or EARLY_STOPPED trial for
              each `trial_index` in the `data`.
  
      Returns:
          A two-element Tuple containing a tensor of observed metrics and a
          tensor of trial_indices.
      """
      df = data.df if data is not None else experiment.lookup_data().df
      if len(df) == 0:
          y = torch.empty(0, len(metric_names), dtype=torch.double)
          indices = torch.empty(0, dtype=torch.long)
          return y, indices
  
      trials_to_use = []
      data_to_use = df[df["metric_name"].isin(metric_names)]
  
      for trial_idx, trial_data in data_to_use.groupby("trial_index"):
          trial = experiment.trials[trial_idx]
          if trial.status not in [
              TrialStatus.COMPLETED,
              TrialStatus.EARLY_STOPPED,
          ]:
              # Skip trials that are not completed or early stopped.
              continue
          trials_to_use.append(trial_idx)
          if trial_data[["metric_name", "arm_name"]].duplicated().any():
              raise UserInputError(
                  "Trial data has more than one row per arm, metric pair. "
                  f"Got\n\n{trial_data}\n\nfor trial {trial_idx}."
              )
          # We have already ensured that `trial_data` has no metrics not in
          # `metric_names` and that there are no duplicate metrics, so if
          # len(trial_data) < len(metric_names), the only possibility is that
          if len(trial_data) < len(metric_names):
              raise UserInputError(
                  f"Trial {trial_idx} is missing data on metrics "
                  f"{set(metric_names) - set(trial_data['metric_name'])}."
              )
  
      keeps = df["trial_index"].isin(trials_to_use)
  
      if not keeps.any():
          return torch.empty(
              0, len(metric_names), dtype=torch.double
          ), torch.empty(0, dtype=torch.long)
  
      data_as_wide = df[keeps].pivot(
          columns="metric_name",
          index=["trial_index", "arm_name"],
          values="mean",
      )[metric_names]
      means = torch.tensor(data_as_wide.to_numpy()).to(torch.double)
      trial_indices = torch.tensor(
          data_as_wide.reset_index()["trial_index"].to_numpy(),
          dtype=torch.long,
      )
      return means, trial_indices
  
imports: from collections import OrderedDict
  from functools import reduce
  from logging import Logger
  from typing import Dict, Iterable, List, Optional, Tuple, Type
  import pandas
  import torch
  from ax.core.base_trial import TrialStatus
  from ax.core.batch_trial import BatchTrial
  from ax.core.data import Data
  from ax.core.experiment import Experiment
  from ax.core.generator_run import GeneratorRun
  from ax.core.objective import MultiObjective, Objective, ScalarizedObjective
  from ax.core.observation import Observation
  from ax.core.optimization_config import MultiObjectiveOptimizationConfig, OptimizationConfig
  from ax.core.outcome_constraint import ObjectiveThreshold, OutcomeConstraint
  from ax.core.trial import Trial
  from ax.core.types import ComparisonOp, TModelPredictArm, TParameterization
  from ax.exceptions.core import UnsupportedError, UserInputError
  from ax.modelbridge.base import ModelBridge
  from ax.modelbridge.cross_validation import assess_model_fit, compute_diagnostics, cross_validate
  from ax.modelbridge.generation_strategy import GenerationStrategy
  from ax.modelbridge.modelbridge_utils import observed_pareto_frontier, predicted_pareto_frontier
  from ax.modelbridge.registry import get_model_from_generator_run, ModelRegistryBase, Models
  from ax.modelbridge.torch import TorchModelBridge
  from ax.modelbridge.transforms.utils import derelativize_optimization_config_with_raw_status_quo
  from ax.plot.pareto_utils import get_tensor_converter_model
  from ax.utils.common.logger import get_logger
  from ax.utils.common.typeutils import checked_cast, not_none
  from numpy import NaN
  from torch import Tensor
label_desc: Use image node_id 18 for calling a global function with example usage: extract_Y_from_data(experiment, metric_names, data) and returns: means,  trial_indices, y,  indices, torch,  torch