class_docstr: A Global Stopping Strategy which recommends stopping optimization if there
  is no significant improvement over recent iterations.
  
  This stopping strategy recommends stopping if there is no significant improvement
  over the past `window_size` trials, among those that are feasible
  (satisfying constraints). The meaning of a "significant"
  improvement differs between single-objective and multi-objective optimizations.
  For single-objective optimizations, improvement is as a fraction of the
  interquartile range (IQR) of the objective values seen so far. For
  multi-objective optimizations (MOO), improvement is as a fraction of the hypervolume
  obtained `window_size` iterations ago.
docstr: This is the "should_stop_optimization" method of this class, specialized
  to MOO experiments.
  
  It computes the (feasible) hypervolume of the Pareto front at
  `trial_to_check` trial and `window_size` trials before, and suggest to stop the
  optimization if the improvment in hypervolume over the past
  `window_size` trials, as a fraction of the hypervolume at the start of
  the window, is less than `self.improvement_bar`. When the hypervolume is
  zero at the beginning of the window, stopping is never recommended.
  
  Becaues hypervolume computations are expensive, these are stored to
  increase the speed of future checks.
  
  Args:
      experiment: The experiment to apply the strategy on.
      trial_to_check: The trial in the experiment at which we want to check
          for stopping. If None, we check at the latest trial.
      objective_thresholds: Custom objective thresholds to use as reference pooint
          when computing hv of the pareto front against. This is used only in the
          MOO setting. If not specified, the objective thresholds on the
          experiment's optimization config will be used for the purpose.
  
  Returns:
      A Tuple with a boolean determining whether the optimization should stop,
          and a str declaring the reason for stopping.
docstr_tok: ["This","is","the","``","should_stop_optimization","''","method","of","this","class",",","specialized","to","MOO","experiments",".","It","computes","the","(","feasible",")","hypervolume","of","the","Pareto","front","at","`","trial_to_check","`","trial","and","`","window_size","`","trials","before",",","and","suggest","to","stop","the","optimization","if","the","improvment","in","hypervolume","over","the","past","`","window_size","`","trials",",","as","a","fraction","of","the","hypervolume","at","the","start","of","the","window",",","is","less","than","`","self.improvement_bar","`",".","When","the","hypervolume","is","zero","at","the","beginning","of","the","window",",","stopping","is","never","recommended",".","Becaues","hypervolume","computations","are","expensive",",","these","are","stored","to","increase","the","speed","of","future","checks",".","Args",":","experiment",":","The","experiment","to","apply","the","strategy","on",".","trial_to_check",":","The","trial","in","the","experiment","at","which","we","want","to","check","for","stopping",".","If","None",",","we","check","at","the","latest","trial",".","objective_thresholds",":","Custom","objective","thresholds","to","use","as","reference","pooint","when","computing","hv","of","the","pareto","front","against",".","This","is","used","only","in","the","MOO","setting",".","If","not","specified",",","the","objective","thresholds","on","the","experiment","'s","optimization","config","will","be","used","for","the","purpose",".","Returns",":","A","Tuple","with","a","boolean","determining","whether","the","optimization","should","stop",",","and","a","str","declaring","the","reason","for","stopping","."]
code: def _should_stop_moo(
      self,
      experiment: Experiment,
      trial_to_check: int,
      objective_thresholds: Optional[List[ObjectiveThreshold]] = None,
  ) -> Tuple[bool, str]:
      """
      This is the "should_stop_optimization" method of this class, specialized
      to MOO experiments.
  
      It computes the (feasible) hypervolume of the Pareto front at
      `trial_to_check` trial and `window_size` trials before, and suggest to stop the
      optimization if the improvment in hypervolume over the past
      `window_size` trials, as a fraction of the hypervolume at the start of
      the window, is less than `self.improvement_bar`. When the hypervolume is
      zero at the beginning of the window, stopping is never recommended.
  
      Becaues hypervolume computations are expensive, these are stored to
      increase the speed of future checks.
  
      Args:
          experiment: The experiment to apply the strategy on.
          trial_to_check: The trial in the experiment at which we want to check
              for stopping. If None, we check at the latest trial.
          objective_thresholds: Custom objective thresholds to use as reference pooint
              when computing hv of the pareto front against. This is used only in the
              MOO setting. If not specified, the objective thresholds on the
              experiment's optimization config will be used for the purpose.
  
      Returns:
          A Tuple with a boolean determining whether the optimization should stop,
              and a str declaring the reason for stopping.
      """
      reference_trial_index = trial_to_check - self.window_size + 1
      data_df = experiment.fetch_data().df
      data_df_reference = data_df[
          data_df["trial_index"] <= reference_trial_index
      ]
      data_df = data_df[data_df["trial_index"] <= trial_to_check]
  
      optimization_config = checked_cast(
          MultiObjectiveOptimizationConfig,
          experiment.optimization_config,
      ).clone_with_args(objective_thresholds=objective_thresholds)
      objective_thresholds = fill_missing_thresholds_from_nadir(
          experiment=experiment, optimization_config=optimization_config
      )
  
      # Computing or retrieving HV at "window_size" iteration before
      if reference_trial_index in self.hv_by_trial:
          hv_reference = self.hv_by_trial[reference_trial_index]
      else:
          mb_reference = get_tensor_converter_model(
              experiment=experiment, data=Data(data_df_reference)
          )
          hv_reference = observed_hypervolume(
              modelbridge=mb_reference,
              objective_thresholds=objective_thresholds,
          )
          self.hv_by_trial[reference_trial_index] = hv_reference
  
      if hv_reference == 0:
          message = "The reference hypervolume is 0. Continue the optimization."
          return False, message
  
      # Computing HV at current trial
      mb = get_tensor_converter_model(
          experiment=experiment, data=Data(data_df)
      )
      hv = observed_hypervolume(
          mb, objective_thresholds=objective_thresholds
      )
      self.hv_by_trial[trial_to_check] = hv
  
      hv_improvement = (hv - hv_reference) / hv_reference
      stop = hv_improvement < self.improvement_bar
  
      if stop:
          message = (
              f"The improvement in hypervolume in the past {self.window_size} "
              f"trials (={hv_improvement:.3f}) is less than improvement_bar "
              f"(={self.improvement_bar}) times the hypervolume at the start "
              f"of the window (={hv_reference:.3f})."
          )
      else:
          message = ""
      return stop, message
  
imports: from logging import Logger
  from typing import Any, Dict, List, Optional, Tuple
  import numpy
  from ax.core.base_trial import BaseTrial, TrialStatus
  from ax.core.data import Data
  from ax.core.experiment import Experiment
  from ax.core.optimization_config import MultiObjectiveOptimizationConfig, OptimizationConfig
  from ax.core.outcome_constraint import ObjectiveThreshold
  from ax.core.trial import Trial
  from ax.core.types import ComparisonOp
  from ax.global_stopping.strategies.base import BaseGlobalStoppingStrategy
  from ax.modelbridge.modelbridge_utils import observed_hypervolume
  from ax.plot.pareto_utils import get_tensor_converter_model
  from ax.service.utils.best_point import fill_missing_thresholds_from_nadir
  from ax.utils.common.logger import get_logger
  from ax.utils.common.typeutils import checked_cast, not_none
label_desc: Use image node_id 3 for calling the ImprovementGlobalStoppingStrategy obj's underlying member method code with example usage: obj._should_stop_moo(experiment, trial_to_check, objective_thresholds) and returns: stop,  message, False,  message