docstr: Applies the given gradients to the network weights
  :param gradients: The gradients to use for the update
  :param scaler: A scaling factor that allows rescaling the gradients before applying them.
                 The gradients will be MULTIPLIED by this factor
  :param additional_inputs: optional additional inputs required for when applying the gradients (e.g. batchnorm's
                            update ops also requires the inputs)
code: def apply_gradients(
      self, gradients, scaler=1.0, additional_inputs=None
  ):
      """
      Applies the given gradients to the network weights
      :param gradients: The gradients to use for the update
      :param scaler: A scaling factor that allows rescaling the gradients before applying them.
                     The gradients will be MULTIPLIED by this factor
      :param additional_inputs: optional additional inputs required for when applying the gradients (e.g. batchnorm's
                                update ops also requires the inputs)
      """
  
      if self.network_parameters.async_training or not isinstance(
          self.ap.task_parameters, DistributedTaskParameters
      ):
          if hasattr(self, "global_step") and not self.network_is_local:
              self.sess.run(self.inc_step)
  
      if self.optimizer_type != "LBFGS":
          if (
              self.distributed_training
              and not self.network_parameters.async_training
          ):
              # rescale the gradients so that they average out with the gradients from the other workers
              if (
                  self.network_parameters.scale_down_gradients_by_number_of_workers_for_sync_training
              ):
                  scaler /= float(
                      self.ap.task_parameters.num_training_tasks
                  )
  
          # rescale the gradients
          if scaler != 1.0:
              for gradient in gradients:
                  gradient *= scaler
  
          # apply the gradients
          feed_dict = dict(zip(self.weights_placeholders, gradients))
          if (
              self.distributed_training
              and self.network_parameters.shared_optimizer
              and not self.network_parameters.async_training
          ):
              # synchronous distributed training with shared optimizer:
              # - each worker adds its gradients to the shared gradients accumulators
              # - we wait for all the workers to add their gradients
              # - the chief worker (worker with task index = 0) applies the gradients once and resets the accumulators
  
              self.sess.run(
                  self.accumulate_shared_gradients, feed_dict=feed_dict
              )
  
              self.wait_for_all_workers_barrier(
                  include_only_training_workers=True
              )
  
              if self.is_chief:
                  self.sess.run(
                      self.update_weights_from_shared_gradients
                  )
                  self.sess.run(self.init_shared_accumulated_gradients)
          else:
              # async distributed training / distributed training with independent optimizer
              #  / non-distributed training - just apply the gradients
              feed_dict = dict(
                  zip(self.weights_placeholders, gradients)
              )
              if additional_inputs is not None:
                  feed_dict = {
                      **feed_dict,
                      **self.create_feed_dict(additional_inputs),
                  }
              self.sess.run(
                  self.update_weights_from_batch_gradients,
                  feed_dict=feed_dict,
              )
  
          # release barrier
          if (
              self.distributed_training
              and not self.network_parameters.async_training
          ):
              self.wait_for_all_workers_barrier(
                  include_only_training_workers=True
              )
  
imports: import os
  import time
  from typing import Any, List, Tuple, Dict
  import numpy
  import tensorflow
  from rl_coach.architectures.architecture import Architecture
  from rl_coach.architectures.tensorflow_components.savers import GlobalVariableSaver
  from rl_coach.base_parameters import AgentParameters, DistributedTaskParameters
  from rl_coach.core_types import GradientClippingMethod
  from rl_coach.saver import SaverCollection
  from rl_coach.spaces import SpacesDefinition
  from rl_coach.utils import force_list, squeeze_list, start_shell_command_and_wait
label_desc: Use image node_id 16 for calling the TensorFlowArchitecture obj's underlying member method code with example usage: obj.apply_gradients(gradients, scaler, additional_inputs) without return types