code: def scatter_plot_with_pareto_frontier_plotly(
      Y: np.ndarray,
      Y_pareto: Optional[np.ndarray],
      metric_x: Optional[str],
      metric_y: Optional[str],
      reference_point: Optional[Tuple[float, float]],
      minimize: Optional[Union[bool, Tuple[bool, bool]]] = True,
      hovertext: Optional[Iterable[str]] = None,
  ) -> go.Figure:
      """Plots a scatter of all points in ``Y`` for ``metric_x`` and ``metric_y``
      with a reference point and Pareto frontier from ``Y_pareto``.
  
      Points in the scatter are colored in a gradient representing their trial index,
      with metric_x on x-axis and metric_y on y-axis. Reference point is represented
      as a star and Pareto frontier –– as a line. The frontier connects to the reference
      point via projection lines.
  
      NOTE: Both metrics should have the same minimization setting, passed as `minimize`.
  
      Args:
          Y: Array of outcomes, of which the first two will be plotted.
          Y_pareto: Array of Pareto-optimal points, first two outcomes in which will be
              plotted.
          metric_x: Name of first outcome in ``Y``.
          metric_Y: Name of second outcome in ``Y``.
          reference_point: Reference point for ``metric_x`` and ``metric_y``.
          minimize: Whether the two metrics in the plot are being minimized or maximized.
      """
      title = "Observed metric values"
      if isinstance(minimize, bool):
          minimize = (minimize, minimize)
      Xs = Y[:, 0]
      Ys = Y[:, 1]
  
      experimental_points_scatter = [
          go.Scatter(
              x=Xs,
              y=Ys,
              mode="markers",
              marker={
                  "color": np.linspace(0, 100, int(len(Xs) * 1.05)),
                  "colorscale": "magma",
                  "colorbar": {
                      "tickvals": [0, 50, 100],
                      "ticktext": [
                          1,
                          "iteration",
                          len(Xs),
                      ],
                  },
              },
              name="Experimental points",
              hovertemplate="%{text}",
              text=hovertext,
          )
      ]
      # No Pareto frontier is drawn if none is provided, or if the frontier consists of
      # a single point and no reference points are provided.
      if (
          Y_pareto is None
          or len(Y_pareto) == 0
          or (len(Y_pareto) == 1 and reference_point is None)
      ):
          # `Y_pareto` input was not specified
          range_x = extend_range(lower=min(Y[:, 0]), upper=max(Y[:, 0]))
          range_y = extend_range(lower=min(Y[:, 1]), upper=max(Y[:, 1]))
          pareto_step = (
              reference_point_lines
          ) = reference_point_star = []
      else:
          title += " with Pareto frontier"
          if reference_point:
              if minimize is None:
                  minimize = tuple(
                      reference_point[i] >= max(Y_pareto[:, i])
                      for i in range(2)
                  )
              reference_point_star = [
                  go.Scatter(
                      x=[reference_point[0]],
                      y=[reference_point[1]],
                      mode="markers",
                      marker={
                          "color": rgba(COLORS.STEELBLUE.value),
                          "size": 25,
                          "symbol": "star",
                      },
                  )
              ]
              extra_point_x = (
                  min(Y_pareto[:, 0])
                  if minimize[0]
                  else max(Y_pareto[:, 0])
              )
              reference_point_line_1 = go.Scatter(
                  x=[extra_point_x, reference_point[0]],
                  y=[reference_point[1], reference_point[1]],
                  mode="lines",
                  marker={"color": rgba(COLORS.STEELBLUE.value)},
              )
              extra_point_y = (
                  min(Y_pareto[:, 1])
                  if minimize[1]
                  else max(Y_pareto[:, 1])
              )
              reference_point_line_2 = go.Scatter(
                  x=[reference_point[0], reference_point[0]],
                  y=[extra_point_y, reference_point[1]],
                  mode="lines",
                  marker={"color": rgba(COLORS.STEELBLUE.value)},
              )
              reference_point_lines = [
                  reference_point_line_1,
                  reference_point_line_2,
              ]
              Y_pareto_with_extra = np.concatenate(
                  (
                      [[extra_point_x, reference_point[1]]],
                      Y_pareto,
                      [[reference_point[0], extra_point_y]],
                  ),
                  axis=0,
              )
              pareto_step = [
                  go.Scatter(
                      x=Y_pareto_with_extra[:, 0],
                      y=Y_pareto_with_extra[:, 1],
                      mode="lines",
                      line_shape="hv",
                      marker={"color": rgba(COLORS.STEELBLUE.value)},
                  )
              ]
  
              range_x = (
                  extend_range(
                      lower=min(Y_pareto[:, 0]),
                      upper=reference_point[0],
                  )
                  if minimize[0]
                  else extend_range(
                      lower=reference_point[0],
                      upper=max(Y_pareto[:, 0]),
                  )
              )
              range_y = (
                  extend_range(
                      lower=min(Y_pareto[:, 1]),
                      upper=reference_point[1],
                  )
                  if minimize[1]
                  else extend_range(
                      lower=reference_point[1],
                      upper=max(Y_pareto[:, 1]),
                  )
              )
          else:  # Reference point was not specified
              pareto_step = [
                  go.Scatter(
                      x=Y_pareto[:, 0],
                      y=Y_pareto[:, 1],
                      mode="lines",
                      line_shape="hv",
                      marker={"color": rgba(COLORS.STEELBLUE.value)},
                  )
              ]
              reference_point_lines = reference_point_star = []
  
              range_x = extend_range(
                  lower=min(Y_pareto[:, 0]), upper=max(Y_pareto[:, 0])
              )
              range_y = extend_range(
                  lower=min(Y_pareto[:, 1]), upper=max(Y_pareto[:, 1])
              )
      layout = go.Layout(
          title=title,
          showlegend=False,
          xaxis={"title": metric_x or "", "range": range_x},
          yaxis={"title": metric_y or "", "range": range_y},
      )
      return go.Figure(
          layout=layout,
          data=pareto_step
          + reference_point_lines
          + experimental_points_scatter
          + reference_point_star,
      )
  
imports: import warnings
  from typing import Dict, Iterable, List, Optional, Tuple, Union
  import numpy
  import pandas
  import plotly.graph_objs
  from ax.core.experiment import Experiment
  from ax.core.objective import MultiObjective
  from ax.core.optimization_config import MultiObjectiveOptimizationConfig, OptimizationConfig
  from ax.core.outcome_constraint import ObjectiveThreshold
  from ax.exceptions.core import UserInputError
  from ax.plot.base import AxPlotConfig, AxPlotTypes, CI_OPACITY, DECIMALS
  from ax.plot.color import COLORS, DISCRETE_COLOR_SCALE, rgba
  from ax.plot.helper import _format_CI, _format_dict, extend_range
  from ax.plot.pareto_utils import ParetoFrontierResults
  from ax.service.utils.best_point_mixin import BestPointMixin
  from ax.utils.common.typeutils import checked_cast, not_none
  from plotly import express
  from scipy.stats import norm
label_desc: Use image node_id 4 for calling a global function with example usage: scatter_plot_with_pareto_frontier_plotly(Y, Y_pareto, metric_x, metric_y, reference_point, minimize, hovertext) and returns: go