code: def _multi_tensor_adagrad(
      params: List[Tensor],
      grads: List[Tensor],
      state_sums: List[Tensor],
      state_steps: List[Tensor],
      *,
      lr: float,
      weight_decay: float,
      lr_decay: float,
      eps: float,
      has_sparse_grad: bool,
      maximize: bool,
      differentiable: bool,
      has_complex: bool,
  ):
      assert not differentiable, "_foreach ops don't support autograd"
  
      # Foreach functions will throw errors if given empty lists
      if len(params) == 0:
          return
  
      grouped_tensorlists = (
          Optimizer._group_tensors_by_device_and_dtype(
              [params, grads, state_sums, state_steps]
          )
      )
      for (
          device_params,
          device_grads,
          device_state_sums,
          device_state_steps,
      ), _ in grouped_tensorlists.values():
          device_has_sparse_grad = has_sparse_grad and any(
              grad.is_sparse for grad in device_grads
          )
  
          if device_has_sparse_grad:
              _single_tensor_adagrad(
                  device_params,
                  device_grads,
                  device_state_sums,
                  device_state_steps,
                  lr=lr,
                  weight_decay=weight_decay,
                  lr_decay=lr_decay,
                  eps=eps,
                  has_sparse_grad=True,
                  maximize=False,
                  differentiable=differentiable,
                  has_complex=has_complex,
              )
              continue
  
          if maximize:
              device_grads = torch._foreach_neg(device_grads)
  
          # Handle complex parameters
          if has_complex:
              _view_as_real(
                  device_params, device_grads, device_state_sums
              )
  
          # Update steps
          # If steps are on CPU, foreach will fall back to the slow path, which is a for-loop calling t.add(1) over
          # and over. 1 will then be wrapped into a Tensor over and over again, which is slower than if we just
          # wrapped it once now. The alpha is required to assure we go to the right overload.
          if device_state_steps[0].is_cpu:
              torch._foreach_add_(
                  device_state_steps,
                  torch.tensor(1.0, device="cpu"),
                  alpha=1.0,
              )
          else:
              torch._foreach_add_(device_state_steps, 1)
  
          if weight_decay != 0:
              # Re-use the intermediate memory (device_grads) already allocated for maximize
              if maximize:
                  torch._foreach_add_(
                      device_grads, device_params, alpha=weight_decay
                  )
              else:
                  device_grads = torch._foreach_add(
                      device_grads, device_params, alpha=weight_decay
                  )
  
          minus_clr = [
              -lr / (1 + (_get_value(step) - 1) * lr_decay)
              for step in device_state_steps
          ]
  
          torch._foreach_addcmul_(
              device_state_sums, device_grads, device_grads, value=1
          )
  
          std = torch._foreach_sqrt(device_state_sums)
          torch._foreach_add_(std, eps)
  
          if weight_decay != 0 or maximize:
              # Again, re-use the intermediate memory (device_grads) already allocated
              torch._foreach_mul_(device_grads, minus_clr)
              numerator = device_grads
          else:
              numerator = torch._foreach_mul(device_grads, minus_clr)
  
          torch._foreach_addcdiv_(device_params, numerator, std)
  
file_path: pytorch/torch/optim/adagrad.py
imports: import torch
  from torch import Tensor
  from .optimizer import Optimizer, _use_grad_for_differentiable, _get_value, _view_as_real, _default_to_fused_or_foreach, _get_scalar_dtype, _differentiable_doc, _foreach_doc, _maximize_doc
  from typing import List, Optional
label_desc: Use image node_id 4 for calling a global function with example usage: _multi_tensor_adagrad(params, grads, state_sums, state_steps) without return types