class_docstr: The median stopping rule stops a pending trial X at step S
  if the trial’s best objective value by step S is strictly worse than the median value
  of the running averages of all completed trials’ objectives reported up to step S
  
  Paper: `Google Vizer: A Service for Black-Box Optimization
  <https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/46180.pdf>`__
  
  Examples
  --------
  
  .. code-block::
  
      config.assessor.name = 'Medianstop'
      config.assessor.class_args = {
          'optimize_mode': 'maximize',
          'start_step': 5
      }
  
  Parameters
  ----------
  optimize_mode
      Whether optimize to minimize or maximize trial result.
  start_step
      A trial is determined to be stopped or not
      only after receiving start_step number of reported intermediate results.
code: def assess_trial(self, trial_job_id, trial_history):
      curr_step = len(trial_history)
      if curr_step < self._start_step:
          return AssessResult.Good
  
      scalar_trial_history = extract_scalar_history(trial_history)
      self._update_data(trial_job_id, scalar_trial_history)
      if self._high_better:
          best_history = max(scalar_trial_history)
      else:
          best_history = min(scalar_trial_history)
  
      avg_array = []
      for id_ in self._completed_avg_history:
          if len(self._completed_avg_history[id_]) >= curr_step:
              avg_array.append(
                  self._completed_avg_history[id_][curr_step - 1]
              )
      if avg_array:
          avg_array.sort()
          if self._high_better:
              median = avg_array[(len(avg_array) - 1) // 2]
              return (
                  AssessResult.Bad
                  if best_history < median
                  else AssessResult.Good
              )
          else:
              median = avg_array[len(avg_array) // 2]
              return (
                  AssessResult.Bad
                  if best_history > median
                  else AssessResult.Good
              )
      else:
          return AssessResult.Good
  
file_path: nni/nni/algorithms/hpo/medianstop_assessor.py
imports: from __future__ import annotations
  import logging
  from schema import Schema, Optional
  from typing_extensions import Literal
  from nni import ClassArgsValidator
  from nni.assessor import Assessor, AssessResult
  from nni.utils import extract_scalar_history
label_desc: Use image node_id 4 for calling the MedianstopAssessor obj's underlying member method code with example usage: obj.assess_trial(trial_job_id, trial_history) and returns: AssessResult, AssessResult, unknown, unknown