class_docstr: LocalGradientAggregationHelper aggregates gradient updates locally,
  and communicates the updates across machines only once every
  backward_passes_per_step. Only supports graph mode execution.
class_docstr_tok: ["LocalGradientAggregationHelper","aggregates","gradient","updates","locally",",","and","communicates","the","updates","across","machines","only","once","every","backward_passes_per_step",".","Only","supports","graph","mode","execution","."]
docstr: Apply updates every backward_passes_per_step, which lines up with
  the batches on which we communicated the locally aggregated gradients.
docstr_tok: ["Apply","updates","every","backward_passes_per_step",",","which","lines","up","with","the","batches","on","which","we","communicated","the","locally","aggregated","gradients","."]
code: def apply_gradients(
      self, apply_grads_closure, optimizer, *args, **kwargs
  ):
      """
      Apply updates every backward_passes_per_step, which lines up with
      the batches on which we communicated the locally aggregated gradients.
      """
      flattended_args0 = [item for tup in args[0] for item in tup]
  
      # If optimizer tracks iterations, we increment it on steps where we
      # are not going to call `apply_gradients()`.
      def increment_optimizer_iteration():
          # (kvignesh1420): Since all `tf.OptimizerV2` instances have the `iterations`
          # property for modifying the underlying `optimizer._iterations`, it is safe to use
          # the property instead of the private variable. For instance, the keras
          # `LossScaleOptimizer` inherits `tf.Optimizer` and exposes the cleaner `iterations`
          # property instead of the unsafe `_iterations`.
  
          if (
              hasattr(optimizer, "iterations")
              and optimizer.iterations is not None
          ):
              return optimizer.iterations.assign_add(1).op
          return tf.no_op()
  
      with tf.control_dependencies(
          [tf.group(*get_not_none_from_list(flattended_args0))]
      ):
          train_op = tf.cond(
              pred=tf.equal(self.counter, 0),
              true_fn=apply_grads_closure,
              false_fn=increment_optimizer_iteration,
          )
  
      # Since we skip applying updates when the counter is not at zero we
      # still want to increment the global step if it is being tracked
      # (e.g., Tensorflow Estimators).
      def increment_global_step_counter():
          global_step_counter = kwargs.get("global_step")
          if global_step_counter is None:
              return tf.no_op()
          return global_step_counter.assign_add(
              tf.constant(1, dtype=tf.int64),
              use_locking=True,
              read_value=False,
          )
  
      with tf.control_dependencies([train_op]):
          # Increment global step on iterations where we don't call `apply_gradients()`.
          return tf.cond(
              pred=tf.equal(self.counter, 0),
              true_fn=tf.no_op,
              false_fn=increment_global_step_counter,
          )
  
file_path: horovod/horovod/tensorflow/gradient_aggregation.py
imports: import os
  import tensorflow
  from packaging import version
  from horovod.tensorflow.mpi_ops import size_op
  from horovod.tensorflow.mpi_ops import global_process_set
label_desc: Use image node_id 9 for calling the LocalGradientAggregationHelper obj's underlying member method code with example usage: obj.apply_gradients(apply_grads_closure, optimizer) and returns: tf, global_step_counter, tf, optimizer, tf