class_docstr: Trainer class for OpenAI Evolution Strategies.
  
  :param algorithm_config: Algorithm parameters.
  :param torch_policy: Multi-step policy encapsulating the policy networks
  :param shared_noise: The noise table, with the same content for every worker and the master.
  :param normalization_stats: Normalization statistics as calculated by the NormalizeObservationWrapper.
class_docstr_tok: ["Trainer","class","for","OpenAI","Evolution","Strategies",".",":","param","algorithm_config",":","Algorithm","parameters",".",":","param","torch_policy",":","Multi-step","policy","encapsulating","the","policy","networks",":","param","shared_noise",":","The","noise","table",",","with","the","same","content","for","every","worker","and","the","master",".",":","param","normalization_stats",":","Normalization","statistics","as","calculated","by","the","NormalizeObservationWrapper","."]
docstr: Run the ES training loop.
  :param distributed_rollouts: The distribution interface for experience collection.
  :param n_epochs: Number of epochs to train.
  :param model_selection: Optional model selection class, receives model evaluation results.
docstr_tok: ["Run","the","ES","training","loop",".",":","param","distributed_rollouts",":","The","distribution","interface","for","experience","collection",".",":","param","n_epochs",":","Number","of","epochs","to","train",".",":","param","model_selection",":","Optional","model","selection","class",",","receives","model","evaluation","results","."]
code: def train(
      self,
      distributed_rollouts: ESDistributedRollouts,
      n_epochs: Optional[int] = None,
      model_selection: Optional[ModelSelectionBase] = None,
  ) -> None:
      """
      Run the ES training loop.
      :param distributed_rollouts: The distribution interface for experience collection.
      :param n_epochs: Number of epochs to train.
      :param model_selection: Optional model selection class, receives model evaluation results.
      """
  
      n_epochs = (
          self.algorithm_config.n_epochs
          if n_epochs is None
          else n_epochs
      )
      self.model_selection = model_selection
  
      for epoch in itertools.count():
          # check if we reached the max number of epochs
          if n_epochs and epoch == n_epochs:
              break
  
          print("********** Iteration {} **********".format(epoch))
  
          step_start_time = time.time()
  
          # do the actual update step (disable autograd, as we calculate the gradient from the rollout returns)
          with torch.no_grad():
              self._update(distributed_rollouts)
  
          step_end_time = time.time()
  
          # log the step duration
          self.es_events.real_time(step_end_time - step_start_time)
  
          # update the epoch count
          increment_log_step()
  
imports: import itertools
  import logging
  import time
  from typing import Optional, Iterable, Generator, Tuple, Union, Dict
  import numpy
  import torch
  from typing.io import BinaryIO
  from maze.core.agent.policy import Policy
  from maze.core.agent.torch_model import TorchModel
  from maze.core.agent.torch_policy import TorchPolicy
  from maze.core.annotations import override
  from maze.core.env.base_env_events import BaseEnvEvents
  from maze.core.log_stats.log_stats import LogStatsAggregator, LogStatsLevel, get_stats_logger, increment_log_step
  from maze.core.utils.factory import Factory
  from maze.train.trainers.common.model_selection.model_selection_base import ModelSelectionBase
  from maze.train.trainers.common.trainer import Trainer
  from maze.train.trainers.es.distributed.es_distributed_rollouts import ESDistributedRollouts, ESRolloutResult
  from maze.train.trainers.es.es_algorithm_config import ESAlgorithmConfig
  from maze.train.trainers.es.es_events import ESEvents
  from maze.train.trainers.es.es_shared_noise_table import SharedNoiseTable
  from maze.train.trainers.es.es_utils import get_flat_parameters
  from maze.train.trainers.es.optimizers.base_optimizer import Optimizer
label_desc: Use image node_id 2 for calling the ESTrainer obj's underlying member method code with example usage: obj.train(distributed_rollouts, n_epochs, model_selection) without return types