class_docstr: GenerationStrategy describes which model should be used to generate new
  points for which trials, enabling and automating use of different models
  throughout the optimization process. For instance, it allows to use one
  model for the initialization trials, and another one for all subsequent
  trials. In the general case, this allows to automate use of an arbitrary
  number of models to generate an arbitrary numbers of trials
  described in the `trials_per_model` argument.
  
  Args:
      nodes: A list of `GenerationNode`. Each `GenerationNode` in the list
          represents a single node in a `GenerationStrategy` which, when
          composed of `GenerationNodes`, can be conceptualized as a graph instead
          of a linear list. `TransitionCriterion` defined in each `GenerationNode`
          represent the edges in the `GenerationStrategy` graph. `GenerationNodes`
          are more flexible than `GenerationSteps` and new `GenerationStrategies`
          should use nodes. Notably, either, but not both, of `nodes` and `steps`
          must be provided.
      steps: A list of `GenerationStep` describing steps of this strategy.
      name: An optional name for this generaiton strategy. If not specified,
          strategy's name will be names of its nodes' models joined with '+'.
docstr: Produce multiple generator runs at once, to be made into multiple
  trials on the experiment.
  
  NOTE: This is used to ensure that maximum paralellism and number
  of trials per node are not violated when producing many generator
  runs from this generation strategy in a row. Without this function,
  if one generates multiple generator runs without first making any
  of them into running trials, generation strategy cannot enforce that it only
  produces as many generator runs as are allowed by the paralellism
  limit and the limit on number of trials in current node.
  
  Args:
      experiment: Experiment, for which the generation strategy is producing
          a new generator run in the course of `gen`, and to which that
          generator run will be added as trial(s). Information stored on the
          experiment (e.g., trial statuses) is used to determine which model
          will be used to produce the generator run returned from this method.
      data: Optional data to be passed to the underlying model's `gen`, which
          is called within this method and actually produces the resulting
          generator run. By default, data is all data on the `experiment`.
      n: Integer representing how many arms should be in the generator run
          produced by this method. NOTE: Some underlying models may ignore
          the ``n`` 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``.
      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.
      model_gen_kwargs: Keyword arguments that are passed through to
          ``GenerationNode.gen``, which will pass them through to
          ``ModelSpec.gen``, which will pass them to ``ModelBridge.gen``.
docstr_tok: ["Produce","multiple","generator","runs","at","once",",","to","be","made","into","multiple","trials","on","the","experiment",".","NOTE",":","This","is","used","to","ensure","that","maximum","paralellism","and","number","of","trials","per","node","are","not","violated","when","producing","many","generator","runs","from","this","generation","strategy","in","a","row",".","Without","this","function",",","if","one","generates","multiple","generator","runs","without","first","making","any","of","them","into","running","trials",",","generation","strategy","can","not","enforce","that","it","only","produces","as","many","generator","runs","as","are","allowed","by","the","paralellism","limit","and","the","limit","on","number","of","trials","in","current","node",".","Args",":","experiment",":","Experiment",",","for","which","the","generation","strategy","is","producing","a","new","generator","run","in","the","course","of","`","gen","`",",","and","to","which","that","generator","run","will","be","added","as","trial","(","s",")",".","Information","stored","on","the","experiment","(","e.g.",",","trial","statuses",")","is","used","to","determine","which","model","will","be","used","to","produce","the","generator","run","returned","from","this","method",".","data",":","Optional","data","to","be","passed","to","the","underlying","model","'s","`","gen","`",",","which","is","called","within","this","method","and","actually","produces","the","resulting","generator","run",".","By","default",",","data","is","all","data","on","the","`","experiment","`",".","n",":","Integer","representing","how","many","arms","should","be","in","the","generator","run","produced","by","this","method",".","NOTE",":","Some","underlying","models","may","ignore","the","``","n","``","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","``",".","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",".","model_gen_kwargs",":","Keyword","arguments","that","are","passed","through","to","``","GenerationNode.gen","``",",","which","will","pass","them","through","to","``","ModelSpec.gen","``",",","which","will","pass","them","to","``","ModelBridge.gen","``","."]
code: def _gen_multiple(
      self,
      experiment: Experiment,
      num_generator_runs: int,
      data: Optional[Data] = None,
      n: int = 1,
      pending_observations: Optional[
          Dict[str, List[ObservationFeatures]]
      ] = None,
      **model_gen_kwargs: Any,
  ) -> List[GeneratorRun]:
      """Produce multiple generator runs at once, to be made into multiple
      trials on the experiment.
  
      NOTE: This is used to ensure that maximum paralellism and number
      of trials per node are not violated when producing many generator
      runs from this generation strategy in a row. Without this function,
      if one generates multiple generator runs without first making any
      of them into running trials, generation strategy cannot enforce that it only
      produces as many generator runs as are allowed by the paralellism
      limit and the limit on number of trials in current node.
  
      Args:
          experiment: Experiment, for which the generation strategy is producing
              a new generator run in the course of `gen`, and to which that
              generator run will be added as trial(s). Information stored on the
              experiment (e.g., trial statuses) is used to determine which model
              will be used to produce the generator run returned from this method.
          data: Optional data to be passed to the underlying model's `gen`, which
              is called within this method and actually produces the resulting
              generator run. By default, data is all data on the `experiment`.
          n: Integer representing how many arms should be in the generator run
              produced by this method. NOTE: Some underlying models may ignore
              the ``n`` 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``.
          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.
          model_gen_kwargs: Keyword arguments that are passed through to
              ``GenerationNode.gen``, which will pass them through to
              ``ModelSpec.gen``, which will pass them to ``ModelBridge.gen``.
      """
      self.experiment = experiment
      self._maybe_move_to_next_step()
      self._fit_current_model(data=data)
  
      # Get GeneratorRun limit that respects the node's transition criterion that
      # affect the number of generator runs that can be produced.
      gr_limit = self._curr.generator_run_limit(
          supress_generation_errors=False
      )
      if gr_limit == -1:
          num_generator_runs = max(num_generator_runs, 1)
      else:
          num_generator_runs = max(min(num_generator_runs, gr_limit), 1)
      generator_runs = []
      pending_observations = deepcopy(pending_observations) or {}
      for _ in range(num_generator_runs):
          try:
              generator_run = self._curr.gen(
                  n=n,
                  pending_observations=pending_observations,
                  arms_by_signature_for_deduplication=experiment.arms_by_signature,
                  **model_gen_kwargs,
              )
  
          except DataRequiredError as err:
              # Model needs more data, so we log the error and return
              # as many generator runs as we were able to produce, unless
              # no trials were produced at all (in which case its safe to raise).
              if len(generator_runs) == 0:
                  raise
              logger.debug(f"Model required more data: {err}.")
              break
  
          self._generator_runs.append(generator_run)
          generator_runs.append(generator_run)
  
          # Extend the `pending_observation` with newly generated point(s)
          # to avoid repeating them.
          extend_pending_observations(
              experiment=experiment,
              pending_observations=pending_observations,
              generator_run=generator_run,
          )
      return generator_runs
  
imports: from __future__ import annotations
  from copy import deepcopy
  from functools import wraps
  from logging import Logger
  from typing import Any, Callable, Dict, List, Optional, Tuple, TypeVar
  import pandas
  from ax.core.data import Data
  from ax.core.experiment import Experiment
  from ax.core.generation_strategy_interface import GenerationStrategyInterface
  from ax.core.generator_run import GeneratorRun
  from ax.core.observation import ObservationFeatures
  from ax.core.utils import extend_pending_observations, get_pending_observation_features_based_on_trial_status
  from ax.exceptions.core import DataRequiredError, UnsupportedError, UserInputError
  from ax.exceptions.generation_strategy import GenerationStrategyCompleted, GenerationStrategyMisconfiguredException
  from ax.modelbridge.base import ModelBridge
  from ax.modelbridge.generation_node import GenerationNode, GenerationStep
  from ax.modelbridge.model_spec import FactoryFunctionModelSpec
  from ax.modelbridge.registry import _extract_model_state_after_gen, ModelRegistryBase
  from ax.modelbridge.transition_criterion import TrialBasedCriterion
  from ax.utils.common.logger import _round_floats_for_logging, get_logger
  from ax.utils.common.typeutils import checked_cast, not_none
label_desc: Use image node_id 26 for calling the GenerationStrategy obj's underlying member method code with example usage: obj._gen_multiple(experiment, num_generator_runs, data, n, pending_observations) and returns: generator_runs