code: def lattice_multiple_metrics(
      model: ModelBridge,
      generator_runs_dict: TNullableGeneratorRunsDict = None,
      rel: bool = True,
      show_arm_details_on_hover: bool = False,
      data_selector: Optional[Callable[[Observation], bool]] = None,
  ) -> AxPlotConfig:
      """Plot raw values or predictions of combinations of two metrics for arms.
  
      Args:
          model: model to draw predictions from.
          generator_runs_dict: a mapping from
              generator run name to generator run.
          rel: if True, use relative effects. Default is True.
          show_arm_details_on_hover: if True, display
              parameterizations of arms on hover. Default is False.
          data_selector: Function for selecting observations for plotting.
      """
      metrics = model.metric_names
      fig = subplots.make_subplots(
          rows=len(metrics),
          cols=len(metrics),
          print_grid=False,
          shared_xaxes=False,
          shared_yaxes=False,
      )
  
      plot_data, _, _ = get_plot_data(
          model,
          generator_runs_dict
          if generator_runs_dict is not None
          else {},
          metrics,
          data_selector=data_selector,
      )
      status_quo_arm = (
          None
          if plot_data.status_quo_name is None
          else plot_data.in_sample.get(plot_data.status_quo_name)
      )
  
      # iterate over all combinations of metrics and generate scatter traces
      for i, o1 in enumerate(metrics, start=1):
          for j, o2 in enumerate(metrics, start=1):
              if o1 != o2:
                  # in-sample observed and predicted
                  obs_insample_trace = _error_scatter_trace(
                      list(plot_data.in_sample.values()),
                      x_axis_var=PlotMetric(o1, pred=False, rel=rel),
                      y_axis_var=PlotMetric(o2, pred=False, rel=rel),
                      status_quo_arm=status_quo_arm,
                      showlegend=(i == 1 and j == 2),
                      legendgroup="In-sample",
                      visible=False,
                      show_arm_details_on_hover=show_arm_details_on_hover,
                  )
                  predicted_insample_trace = _error_scatter_trace(
                      list(plot_data.in_sample.values()),
                      x_axis_var=PlotMetric(o1, pred=True, rel=rel),
                      y_axis_var=PlotMetric(o2, pred=True, rel=rel),
                      status_quo_arm=status_quo_arm,
                      legendgroup="In-sample",
                      showlegend=(i == 1 and j == 2),
                      visible=True,
                      show_arm_details_on_hover=show_arm_details_on_hover,
                  )
                  fig.append_trace(
                      obs_insample_trace, j, i
                  )  # pyre-ignore[16]
                  fig.append_trace(predicted_insample_trace, j, i)
  
                  # iterate over models here
                  for k, (generator_run_name, cand_arms) in enumerate(
                      (plot_data.out_of_sample or {}).items(), start=1
                  ):
                      fig.append_trace(
                          _error_scatter_trace(
                              list(cand_arms.values()),
                              x_axis_var=PlotMetric(
                                  o1, pred=True, rel=rel
                              ),
                              y_axis_var=PlotMetric(
                                  o2, pred=True, rel=rel
                              ),
                              status_quo_arm=status_quo_arm,
                              name=generator_run_name,
                              color=DISCRETE_COLOR_SCALE[k],
                              showlegend=(i == 1 and j == 2),
                              legendgroup=generator_run_name,
                              show_arm_details_on_hover=show_arm_details_on_hover,
                          ),
                          j,
                          i,
                      )
              else:
                  # if diagonal is set to True, add box plots
                  fig.append_trace(
                      go.Box(
                          y=[
                              arm.y[o1]
                              for arm in plot_data.in_sample.values()
                          ],
                          name=None,
                          marker={
                              "color": rgba(COLORS.STEELBLUE.value)
                          },
                          showlegend=False,
                          legendgroup="In-sample",
                          visible=False,
                          hoverinfo="none",
                      ),
                      j,
                      i,
                  )
                  fig.append_trace(
                      go.Box(
                          y=[
                              arm.y_hat[o1]
                              for arm in plot_data.in_sample.values()
                          ],
                          name=None,
                          marker={
                              "color": rgba(COLORS.STEELBLUE.value)
                          },
                          showlegend=False,
                          legendgroup="In-sample",
                          hoverinfo="none",
                      ),
                      j,
                      i,
                  )
  
                  for k, (generator_run_name, cand_arms) in enumerate(
                      (plot_data.out_of_sample or {}).items(), start=1
                  ):
                      fig.append_trace(
                          go.Box(
                              y=[
                                  arm.y_hat[o1]
                                  for arm in cand_arms.values()
                              ],
                              name=None,
                              marker={
                                  "color": rgba(DISCRETE_COLOR_SCALE[k])
                              },
                              showlegend=False,
                              legendgroup=generator_run_name,
                              hoverinfo="none",
                          ),
                          j,
                          i,
                      )
  
      fig["layout"].update(
          height=800,
          width=960,
          font={"size": 10},
          hovermode="closest",
          legend={
              "orientation": "h",
              "x": 0,
              "y": 1.05,
              "xanchor": "left",
              "yanchor": "middle",
          },
          updatemenus=[
              {
                  "x": 0.35,
                  "y": 1.08,
                  "xanchor": "left",
                  "yanchor": "middle",
                  "buttons": [
                      {
                          "args": [
                              {
                                  "error_x.width": 0,
                                  "error_x.thickness": 0,
                                  "error_y.width": 0,
                                  "error_y.thickness": 0,
                              }
                          ],
                          "label": "No",
                          "method": "restyle",
                      },
                      {
                          "args": [
                              {
                                  "error_x.width": 4,
                                  "error_x.thickness": 2,
                                  "error_y.width": 4,
                                  "error_y.thickness": 2,
                              }
                          ],
                          "label": "Yes",
                          "method": "restyle",
                      },
                  ],
              },
              {
                  "x": 0.1,
                  "y": 1.08,
                  "xanchor": "left",
                  "yanchor": "middle",
                  "buttons": [
                      {
                          "args": [
                              {
                                  "visible": (
                                      (
                                          [False, True]
                                          + [True]
                                          * len(
                                              plot_data.out_of_sample
                                              or {}
                                          )
                                      )
                                      * (len(metrics) ** 2)
                                  )
                              }
                          ],
                          "label": "Modeled",
                          "method": "restyle",
                      },
                      {
                          "args": [
                              {
                                  "visible": (
                                      (
                                          [True, False]
                                          + [False]
                                          * len(
                                              plot_data.out_of_sample
                                              or {}
                                          )
                                      )
                                      * (len(metrics) ** 2)
                                  )
                              }
                          ],
                          "label": "In-sample",
                          "method": "restyle",
                      },
                  ],
              },
          ],
          annotations=[
              {
                  "x": 0.02,
                  "y": 1.1,
                  "xref": "paper",
                  "yref": "paper",
                  "text": "Type",
                  "showarrow": False,
                  "yanchor": "middle",
                  "xanchor": "left",
              },
              {
                  "x": 0.30,
                  "y": 1.1,
                  "xref": "paper",
                  "yref": "paper",
                  "text": "Show CI",
                  "showarrow": False,
                  "yanchor": "middle",
                  "xanchor": "left",
              },
          ],
      )
  
      # add metric names to axes - add to each subplot if boxplots on the
      # diagonal and axes are not shared; else, add to the leftmost y-axes
      # and bottom x-axes.
      for i, o in enumerate(metrics):
          pos_x = len(metrics) * len(metrics) - len(metrics) + i + 1
          pos_y = 1 + (len(metrics) * i)
          fig["layout"]["xaxis{}".format(pos_x)].update(
              title=_wrap_metric(o), titlefont={"size": 10}
          )
          fig["layout"]["yaxis{}".format(pos_y)].update(
              title=_wrap_metric(o), titlefont={"size": 10}
          )
  
      # do not put x-axis ticks for boxplots
      boxplot_xaxes = []
      for trace in fig["data"]:
          if trace["type"] == "box":
              # stores the xaxes which correspond to boxplot subplots
              # since we use xaxis1, xaxis2, etc, in plotly.py
              boxplot_xaxes.append("xaxis{}".format(trace["xaxis"][1:]))
          else:
              # clear all error bars since default is no CI
              trace["error_x"].update(width=0, thickness=0)
              trace["error_y"].update(width=0, thickness=0)
      for xaxis in boxplot_xaxes:
          fig["layout"][xaxis]["showticklabels"] = False
  
      return AxPlotConfig(data=fig, plot_type=AxPlotTypes.GENERIC)
  
imports: import numbers
  import warnings
  from collections import OrderedDict
  from logging import Logger
  from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Tuple, Union
  import numpy
  import plotly.graph_objs
  from ax.core.data import Data
  from ax.core.experiment import Experiment
  from ax.core.observation import Observation, ObservationFeatures
  from ax.modelbridge.base import ModelBridge
  from ax.modelbridge.registry import Models
  from ax.plot.base import AxPlotConfig, AxPlotTypes, CI_OPACITY, DECIMALS, PlotInSampleArm, PlotMetric, PlotOutOfSampleArm, Z
  from ax.plot.color import BLUE_SCALE, COLORS, DISCRETE_COLOR_SCALE, rgba
  from ax.plot.helper import _format_CI, _format_dict, _wrap_metric, arm_name_to_sort_key, arm_name_to_tuple, get_plot_data, infer_is_relative, resize_subtitles, TNullableGeneratorRunsDict
  from ax.utils.common.logger import get_logger
  from ax.utils.common.typeutils import checked_cast_optional
  from ax.utils.stats.statstools import relativize
  from plotly import subplots
label_desc: Use image node_id 8 for calling a global function with example usage: lattice_multiple_metrics(model, generator_runs_dict, rel, show_arm_details_on_hover, data_selector) and returns: AxPlotConfig