class_docstr: Base class for GenerationNode, capable of fitting one or more model specs under
  the hood and generating candidates from them.
  
  Args:
      model_specs: A list of ModelSpecs to be selected from for generation in this
          GenerationNode
      should_deduplicate: Whether to deduplicate the parameters of proposed arms
          against those of previous arms via rejection sampling. If this is True,
          the GenerationStrategy will discard generator runs produced from the
          GenerationNode that has `should_deduplicate=True` if they contain arms
          already present on the experiment and replace them with new generator runs.
          If no generator run with entirely unique arms could be produced in 5
          attempts, a `GenerationStrategyRepeatedPoints` error will be raised, as we
          assume that the optimization converged when the model can no longer suggest
          unique arms.
      node_name: A unique name for the GenerationNode. Used for storage purposes.
      transition_criteria: List of TransitionCriterion, each of which describes a
          condition that must be met before completing a GenerationNode. All `is_met`
          must evaluateTrue for the GenerationStrategy to move on to the next
          GenerationNode.
      gen_unlimited_trials: If True the number of trials that can be generated from
          this GenerationNode is unlimited.
  
  Note for developers: by "model" here we really mean an Ax ModelBridge object, which
  contains an Ax Model under the hood. We call it "model" here to simplify and focus
  on explaining the logic of GenerationStep and GenerationStrategy.
class_docstr_tok: ["Base","class","for","GenerationNode",",","capable","of","fitting","one","or","more","model","specs","under","the","hood","and","generating","candidates","from","them",".","Args",":","model_specs",":","A","list","of","ModelSpecs","to","be","selected","from","for","generation","in","this","GenerationNode","should_deduplicate",":","Whether","to","deduplicate","the","parameters","of","proposed","arms","against","those","of","previous","arms","via","rejection","sampling",".","If","this","is","True",",","the","GenerationStrategy","will","discard","generator","runs","produced","from","the","GenerationNode","that","has","`","should_deduplicate=True","`","if","they","contain","arms","already","present","on","the","experiment","and","replace","them","with","new","generator","runs",".","If","no","generator","run","with","entirely","unique","arms","could","be","produced","in","5","attempts",",","a","`","GenerationStrategyRepeatedPoints","`","error","will","be","raised",",","as","we","assume","that","the","optimization","converged","when","the","model","can","no","longer","suggest","unique","arms",".","node_name",":","A","unique","name","for","the","GenerationNode",".","Used","for","storage","purposes",".","transition_criteria",":","List","of","TransitionCriterion",",","each","of","which","describes","a","condition","that","must","be","met","before","completing","a","GenerationNode",".","All","`","is_met","`","must","evaluateTrue","for","the","GenerationStrategy","to","move","on","to","the","next","GenerationNode",".","gen_unlimited_trials",":","If","True","the","number","of","trials","that","can","be","generated","from","this","GenerationNode","is","unlimited",".","Note","for","developers",":","by","``","model","''","here","we","really","mean","an","Ax","ModelBridge","object",",","which","contains","an","Ax","Model","under","the","hood",".","We","call","it","``","model","''","here","to","simplify","and","focus","on","explaining","the","logic","of","GenerationStep","and","GenerationStrategy","."]
docstr: Picks a fitted model, from which to generate candidates (via
  ``self._pick_fitted_model_to_gen_from``) and generates candidates
  from it. Uses the ``model_gen_kwargs`` set on the selected ``ModelSpec``
  alongside any kwargs passed in to this function (with local kwargs)
  taking precedent.
  
  Args:
      n: Optional nteger representing how many arms should be in the generator
          run produced by this method. When this is ``None``, ``n`` will be
          determined by the ``ModelSpec`` that we are generating from.
      pending_observations: A map from metric name to pending
          observations for that metric, used by some models to avoid
          resuggesting points that are currently being evaluated.
      max_gen_draws_for_deduplication: TODO
      model_gen_kwargs: Keyword arguments, passed through to ``ModelSpec.gen``;
          these override any pre-specified in ``ModelSpec.model_gen_kwargs``.
  
  NOTE: Models must have been fit prior to calling ``gen``.
  NOTE: Some underlying models may ignore the ``n`` argument and produce a
      model-determined number of arms. In that case this method will also output
      a generator run with number of arms (that can differ from ``n``).
code: def gen(
      self,
      n: Optional[int] = None,
      pending_observations: Optional[
          Dict[str, List[ObservationFeatures]]
      ] = None,
      max_gen_draws_for_deduplication: int = MAX_GEN_DRAWS,
      arms_by_signature_for_deduplication: Optional[
          Dict[str, Arm]
      ] = None,
      **model_gen_kwargs: Any,
  ) -> GeneratorRun:
      """Picks a fitted model, from which to generate candidates (via
      ``self._pick_fitted_model_to_gen_from``) and generates candidates
      from it. Uses the ``model_gen_kwargs`` set on the selected ``ModelSpec``
      alongside any kwargs passed in to this function (with local kwargs)
      taking precedent.
  
      Args:
          n: Optional nteger representing how many arms should be in the generator
              run produced by this method. When this is ``None``, ``n`` will be
              determined by the ``ModelSpec`` that we are generating from.
          pending_observations: A map from metric name to pending
              observations for that metric, used by some models to avoid
              resuggesting points that are currently being evaluated.
          max_gen_draws_for_deduplication: TODO
          model_gen_kwargs: Keyword arguments, passed through to ``ModelSpec.gen``;
              these override any pre-specified in ``ModelSpec.model_gen_kwargs``.
  
      NOTE: Models must have been fit prior to calling ``gen``.
      NOTE: Some underlying models may ignore the ``n`` argument and produce a
          model-determined number of arms. In that case this method will also output
          a generator run with number of arms (that can differ from ``n``).
      """
      model_spec = self.model_spec_to_gen_from
      should_generate_run = True
      generator_run = None
      n_gen_draws = 0
      # Keep generating until each of `generator_run.arms` is not a duplicate
      # of a previous arm, if `should_deduplicate is True`
      while should_generate_run:
          generator_run = model_spec.gen(
              # If `n` is not specified, ensure that the `None` value does not
              # override the one set in `model_spec.model_gen_kwargs`.
              n=model_spec.model_gen_kwargs.get("n")
              if n is None and model_spec.model_gen_kwargs
              else n,
              # For `pending_observations`, prefer the input to this function, as
              # `pending_observations` are dynamic throughout the experiment and thus
              # unlikely to be specified in `model_spec.model_gen_kwargs`.
              pending_observations=pending_observations,
              **model_gen_kwargs,
          )
  
          should_generate_run = (
              self.should_deduplicate
              and arms_by_signature_for_deduplication
              and any(
                  arm.signature in arms_by_signature_for_deduplication
                  for arm in generator_run.arms
              )
          )
          n_gen_draws += 1
          if should_generate_run:
              if n_gen_draws > max_gen_draws_for_deduplication:
                  raise GenerationStrategyRepeatedPoints(
                      MAX_GEN_DRAWS_EXCEEDED_MESSAGE
                  )
              else:
                  logger.info(
                      "The generator run produced duplicate arms. Re-running the "
                      "generation step in an attempt to deduplicate. Candidates "
                      f"produced in the last generator run: {generator_run.arms}."
                  )
      assert generator_run is not None, (
          "The GeneratorRun is None which is an unexpected state of this"
          " GenerationStrategy. This occured on GenerationNode: {self.node_name}."
      )
      generator_run._generation_node_name = self.node_name
      return generator_run
  
imports: from __future__ import annotations
  from dataclasses import dataclass, field
  from logging import Logger
  from typing import Any, Callable, Dict, List, Optional, Sequence, Set, Tuple, Union
  from ax import modelbridge
  from ax.core.arm import Arm
  from ax.core.base_trial import TrialStatus
  from ax.core.data import Data
  from ax.core.experiment import Experiment
  from ax.core.generator_run import GeneratorRun
  from ax.core.observation import ObservationFeatures
  from ax.core.optimization_config import OptimizationConfig
  from ax.core.search_space import SearchSpace
  from ax.exceptions.core import UserInputError
  from ax.exceptions.generation_strategy import GenerationStrategyRepeatedPoints
  from ax.modelbridge.base import ModelBridge
  from ax.modelbridge.cross_validation import BestModelSelector, CVDiagnostics, CVResult
  from ax.modelbridge.model_spec import FactoryFunctionModelSpec, ModelSpec
  from ax.modelbridge.registry import ModelRegistryBase
  from ax.modelbridge.transition_criterion import MaxGenerationParallelism, MaxTrials, MinTrials, TransitionCriterion, TrialBasedCriterion
  from ax.utils.common.base import Base, SortableBase
  from ax.utils.common.logger import get_logger
  from ax.utils.common.serialization import SerializationMixin
  from ax.utils.common.typeutils import not_none
label_desc: Use image node_id 21 for calling the GenerationNode obj's underlying member method code with example usage: obj.gen(n, pending_observations, max_gen_draws_for_deduplication, arms_by_signature_for_deduplication) and returns: generator_run