docstr: Compute the optimization trace at each iteration.
  
  Given an experiment and an optimization config, compute the performance
  at each iteration. For multi-objective, the performance is compute as the
  hypervolume. For single objective, the performance is compute as the best
  observed objective value.
  
  An iteration here refers to a completed or early-stopped (batch) trial.
  There will be one performance metric in the trace for each iteration.
  
  Args:
      experiment: The experiment to get the trace for.
      optimization_config: Optimization config to use in place of the one
          stored on the experiment.
  
  Returns:
      A list of performance values at each iteration.
docstr_tok: ["Compute","the","optimization","trace","at","each","iteration",".","Given","an","experiment","and","an","optimization","config",",","compute","the","performance","at","each","iteration",".","For","multi-objective",",","the","performance","is","compute","as","the","hypervolume",".","For","single","objective",",","the","performance","is","compute","as","the","best","observed","objective","value",".","An","iteration","here","refers","to","a","completed","or","early-stopped","(","batch",")","trial",".","There","will","be","one","performance","metric","in","the","trace","for","each","iteration",".","Args",":","experiment",":","The","experiment","to","get","the","trace","for",".","optimization_config",":","Optimization","config","to","use","in","place","of","the","one","stored","on","the","experiment",".","Returns",":","A","list","of","performance","values","at","each","iteration","."]
code: def _get_trace(
      experiment: Experiment,
      optimization_config: Optional[OptimizationConfig] = None,
  ) -> List[float]:
      """Compute the optimization trace at each iteration.
  
      Given an experiment and an optimization config, compute the performance
      at each iteration. For multi-objective, the performance is compute as the
      hypervolume. For single objective, the performance is compute as the best
      observed objective value.
  
      An iteration here refers to a completed or early-stopped (batch) trial.
      There will be one performance metric in the trace for each iteration.
  
      Args:
          experiment: The experiment to get the trace for.
          optimization_config: Optimization config to use in place of the one
              stored on the experiment.
  
      Returns:
          A list of performance values at each iteration.
      """
      optimization_config = optimization_config or not_none(
          experiment.optimization_config
      )
      # Get the names of the metrics in optimization config.
      metric_names = set(optimization_config.objective.metric_names)
      for cons in optimization_config.outcome_constraints:
          metric_names.update({cons.metric.name})
      metric_names = list(metric_names)
      # Convert data into a tensor.
      Y, trial_indices = extract_Y_from_data(
          experiment=experiment, metric_names=metric_names
      )
      if Y.numel() == 0:
          return []
  
      # Derelativize the optimization config.
      tf = Derelativize(
          search_space=None,
          observations=None,
          config={"use_raw_status_quo": True},
      )
      optimization_config = tf.transform_optimization_config(
          optimization_config=optimization_config.clone(),
          modelbridge=get_tensor_converter_model(
              experiment=experiment, data=experiment.lookup_data()
          ),
          fixed_features=None,
      )
  
      # Extract weights, constraints, and objective_thresholds.
      objective_weights = extract_objective_weights(
          objective=optimization_config.objective, outcomes=metric_names
      )
      outcome_constraints = extract_outcome_constraints(
          outcome_constraints=optimization_config.outcome_constraints,
          outcomes=metric_names,
      )
      to_tensor = partial(
          torch.as_tensor,
          dtype=torch.double,
          device=torch.device("cpu"),
      )
      if optimization_config.is_moo_problem:
          objective_thresholds = extract_objective_thresholds(
              objective_thresholds=fill_missing_thresholds_from_nadir(
                  experiment=experiment,
                  optimization_config=optimization_config,
              ),
              objective=optimization_config.objective,
              outcomes=metric_names,
          )
          objective_thresholds = to_tensor(
              not_none(objective_thresholds)
          )
      else:
          objective_thresholds = None
      (
          objective_weights,
          outcome_constraints,
          _,
          _,
          _,
      ) = validate_and_apply_final_transform(
          objective_weights=objective_weights,
          outcome_constraints=outcome_constraints,
          linear_constraints=None,
          pending_observations=None,
          final_transform=to_tensor,
      )
      # Get weighted tensor objectives.
      if optimization_config.is_moo_problem:
          (
              obj,
              weighted_objective_thresholds,
          ) = get_weighted_mc_objective_and_objective_thresholds(
              objective_weights=objective_weights,
              objective_thresholds=not_none(objective_thresholds),
          )
          Y_obj = obj(Y)
          infeas_value = weighted_objective_thresholds
      else:
          Y_obj = Y @ objective_weights
          infeas_value = Y_obj.min()
      # Account for feasibility.
      if outcome_constraints is not None:
          cons_tfs = not_none(
              get_outcome_constraint_transforms(outcome_constraints)
          )
          feas = torch.all(
              torch.stack([c(Y) <= 0 for c in cons_tfs], dim=-1), dim=-1
          )
          # Set the infeasible points to reference point or the worst observed value.
          Y_obj[~feas] = infeas_value
      # Get unique trial indices. Note: only completed/early-stopped
      # trials are present.
      unique_trial_indices = (
          trial_indices.unique().sort().values.tolist()
      )
      # compute the performance at each iteration (completed/early-stopped
      # trial).
      # For `BatchTrial`s, there is one performance value per iteration, even
      # if the iteration (`BatchTrial`) has multiple arms.
      if optimization_config.is_moo_problem:
          # Compute the hypervolume trace.
          partitioning = DominatedPartitioning(
              ref_point=weighted_objective_thresholds.double()
          )
          # compute hv for each iteration (trial_index)
          hvs = []
          for trial_index in unique_trial_indices:
              new_Y = Y_obj[trial_indices == trial_index]
              # update with new point
              partitioning.update(Y=new_Y)
              hv = partitioning.compute_hypervolume().item()
              hvs.append(hv)
          return hvs
      running_max = float("-inf")
      raw_maximum = np.zeros(len(unique_trial_indices))
      # Find the best observed value for each iterations.
      # Enumerate the unique trial indices because only indices
      # of completed/early-stopped trials are present.
      for i, trial_index in enumerate(unique_trial_indices):
          new_Y = Y_obj[trial_indices == trial_index]
          running_max = max(running_max, new_Y.max().item())
          raw_maximum[i] = running_max
      if optimization_config.objective.minimize:
          # Negate the result if it is a minimization problem.
          raw_maximum = -raw_maximum
      return raw_maximum.tolist()
  
imports: from abc import ABCMeta, abstractmethod
  from functools import partial
  from logging import Logger
  from typing import Dict, Iterable, List, Optional, Tuple
  import numpy
  import torch
  from ax.core.experiment import Experiment
  from ax.core.map_data import MapData
  from ax.core.objective import ScalarizedObjective
  from ax.core.optimization_config import MultiObjectiveOptimizationConfig, OptimizationConfig
  from ax.core.types import TModelPredictArm, TParameterization
  from ax.modelbridge.generation_strategy import GenerationStrategy
  from ax.modelbridge.modelbridge_utils import extract_objective_thresholds, extract_objective_weights, extract_outcome_constraints, observed_hypervolume, predicted_hypervolume, validate_and_apply_final_transform
  from ax.modelbridge.registry import get_model_from_generator_run, ModelRegistryBase
  from ax.modelbridge.torch import TorchModelBridge
  from ax.modelbridge.transforms.derelativize import Derelativize
  from ax.models.torch.botorch_moo_defaults import get_outcome_constraint_transforms, get_weighted_mc_objective_and_objective_thresholds
  from ax.plot.pareto_utils import get_tensor_converter_model
  from ax.service.utils import best_point
  from ax.service.utils.best_point import extract_Y_from_data, fill_missing_thresholds_from_nadir
  from ax.utils.common.logger import get_logger
  from ax.utils.common.typeutils import checked_cast, not_none
  from botorch.utils.multi_objective.box_decompositions import DominatedPartitioning
label_desc: Use image node_id 11 for calling the BestPointMixin obj's underlying member method code with example usage: obj._get_trace(experiment, optimization_config) and returns: raw_maximum, list, hvs