code: def run_experiment(
      experiment: config.ExperimentConfig,
      eval_every: int = 100,
      num_eval_episodes: int = 1,
  ):
      """Runs a simple, single-threaded training loop using the default evaluators.
  
      It targets simplicity of the code and so only the basic features of the
      ExperimentConfig are supported.
  
      Arguments:
        experiment: Definition and configuration of the agent to run.
        eval_every: After how many actor steps to perform evaluation.
        num_eval_episodes: How many evaluation episodes to execute at each
          evaluation step.
      """
  
      key = jax.random.PRNGKey(experiment.seed)
  
      # Create the environment and get its spec.
      environment = experiment.environment_factory(experiment.seed)
      environment_spec = (
          experiment.environment_spec
          or specs.make_environment_spec(environment)
      )
  
      # Create the networks and policy.
      networks = experiment.network_factory(environment_spec)
      policy = config.make_policy(
          experiment=experiment,
          networks=networks,
          environment_spec=environment_spec,
          evaluation=False,
      )
  
      # Create the replay server and grab its address.
      replay_tables = experiment.builder.make_replay_tables(
          environment_spec, policy
      )
  
      # Disable blocking of inserts by tables' rate limiters, as this function
      # executes learning (sampling from the table) and data generation
      # (inserting into the table) sequentially from the same thread
      # which could result in blocked insert making the algorithm hang.
      replay_tables, rate_limiters_max_diff = _disable_insert_blocking(
          replay_tables
      )
  
      replay_server = reverb.Server(replay_tables, port=None)
      replay_client = reverb.Client(f"localhost:{replay_server.port}")
  
      # Parent counter allows to share step counts between train and eval loops and
      # the learner, so that it is possible to plot for example evaluator's return
      # value as a function of the number of training episodes.
      parent_counter = counting.Counter(time_delta=0.0)
  
      dataset = experiment.builder.make_dataset_iterator(replay_client)
      # We always use prefetch as it provides an iterator with an additional
      # 'ready' method.
      dataset = utils.prefetch(dataset, buffer_size=1)
  
      # Create actor, adder, and learner for generating, storing, and consuming
      # data respectively.
      # NOTE: These are created in reverse order as the actor needs to be given the
      # adder and the learner (as a source of variables).
      learner_key, key = jax.random.split(key)
      learner = experiment.builder.make_learner(
          random_key=learner_key,
          networks=networks,
          dataset=dataset,
          logger_fn=experiment.logger_factory,
          environment_spec=environment_spec,
          replay_client=replay_client,
          counter=counting.Counter(
              parent_counter, prefix="learner", time_delta=0.0
          ),
      )
  
      adder = experiment.builder.make_adder(
          replay_client, environment_spec, policy
      )
  
      actor_key, key = jax.random.split(key)
      actor = experiment.builder.make_actor(
          actor_key,
          policy,
          environment_spec,
          variable_source=learner,
          adder=adder,
      )
  
      # Create the environment loop used for training.
      train_counter = counting.Counter(
          parent_counter, prefix="actor", time_delta=0.0
      )
      train_logger = experiment.logger_factory(
          "actor", train_counter.get_steps_key(), 0
      )
  
      checkpointer = None
      if experiment.checkpointing is not None:
          checkpointing = experiment.checkpointing
          checkpointer = savers.Checkpointer(
              objects_to_save={
                  "learner": learner,
                  "counter": parent_counter,
              },
              time_delta_minutes=checkpointing.time_delta_minutes,
              directory=checkpointing.directory,
              add_uid=checkpointing.add_uid,
              max_to_keep=checkpointing.max_to_keep,
              keep_checkpoint_every_n_hours=checkpointing.keep_checkpoint_every_n_hours,
              checkpoint_ttl_seconds=checkpointing.checkpoint_ttl_seconds,
          )
  
      # Replace the actor with a LearningActor. This makes sure that every time
      # that `update` is called on the actor it checks to see whether there is
      # any new data to learn from and if so it runs a learner step. The rate
      # at which new data is released is controlled by the replay table's
      # rate_limiter which is created by the builder.make_replay_tables call above.
      actor = _LearningActor(
          actor,
          learner,
          dataset,
          replay_tables,
          rate_limiters_max_diff,
          checkpointer,
      )
  
      train_loop = acme.EnvironmentLoop(
          environment,
          actor,
          counter=train_counter,
          logger=train_logger,
          observers=experiment.observers,
      )
  
      max_num_actor_steps = (
          experiment.max_num_actor_steps
          - parent_counter.get_counts().get(
              train_counter.get_steps_key(), 0
          )
      )
  
      if num_eval_episodes == 0:
          # No evaluation. Just run the training loop.
          train_loop.run(num_steps=max_num_actor_steps)
          return
  
      # Create the evaluation actor and loop.
      eval_counter = counting.Counter(
          parent_counter, prefix="evaluator", time_delta=0.0
      )
      eval_logger = experiment.logger_factory(
          "evaluator", eval_counter.get_steps_key(), 0
      )
      eval_policy = config.make_policy(
          experiment=experiment,
          networks=networks,
          environment_spec=environment_spec,
          evaluation=True,
      )
      eval_actor = experiment.builder.make_actor(
          random_key=jax.random.PRNGKey(experiment.seed),
          policy=eval_policy,
          environment_spec=environment_spec,
          variable_source=learner,
      )
      eval_loop = acme.EnvironmentLoop(
          environment,
          eval_actor,
          counter=eval_counter,
          logger=eval_logger,
          observers=experiment.observers,
      )
  
      steps = 0
      while steps < max_num_actor_steps:
          eval_loop.run(num_episodes=num_eval_episodes)
          num_steps = min(eval_every, max_num_actor_steps - steps)
          steps += train_loop.run(num_steps=num_steps)
      eval_loop.run(num_episodes=num_eval_episodes)
  
      environment.close()
  
imports: import sys
  import time
  from typing import Optional, Sequence, Tuple
  import acme
  from acme import core
  from acme import specs
  from acme import types
  from acme.jax import utils
  from acme.jax.experiments import config
  from acme.tf import savers
  from acme.utils import counting
  import dm_env
  import jax
  import reverb
label_desc: Use image node_id 1 for calling a global function with example usage: run_experiment(experiment, eval_every, num_eval_episodes) without return types