class_docstr: Handles spawning new individuals from a parent, computing an estimated gradient,
  and applying that gradient to mutate the parent.
code: def apply_global_reward(
      self, rewards: torch.Tensor, next_iteration: int
  ):
      std_dev = torch.std(rewards)
      if torch.abs(std_dev) > 1e-6:
          normalized_rewards = (rewards - torch.mean(rewards)) / std_dev
          for parent_tensor in self.parent_tensors.values():
              # pyre-fixme[16]: Optional type has no attribute `zero_`.
              parent_tensor.grad.zero_()
          for i, individual in enumerate(self.population_tensors):
              for (
                  tensor_name,
                  parent_tensor,
              ) in self.parent_tensors.items():
                  individual_tensor = individual[tensor_name]
  
                  # Subtract the parent to get the gradient estimate
                  individual_tensor.sub_(parent_tensor)
  
                  # Amplify the gradient by the reward
                  individual_tensor.mul_(normalized_rewards[i])
  
                  # Divide by a normalizing constant
                  individual_tensor.div_(
                      self.es_params.population_size
                      * self.es_params.mutation_power
                      * -1
                  )
  
                  # pyre-fixme[16]: Optional type has no attribute `__iadd__`.
                  parent_tensor.grad += individual_tensor
          self.optimizer.step()
  
      self.populate_children(next_iteration)
  
file_path: ReAgent/reagent/training/gradient_free/evolution_pool.py
imports: import logging
  from typing import Dict, List
  import torch
  import torch.fb.rendezvous.zeus
  import torch.nn
  import torch.optim
  from reagent.core.parameters import EvolutionParameters
label_desc: Use image node_id 3 for calling the EvolutionPool obj's underlying member method code with example usage: obj.apply_global_reward(rewards, next_iteration) without return types