class_docstr: Node embedding optimizer using the Adagrad algorithm.
  
  This optimizer implements a sparse version of Adagrad algorithm for
  optimizing :class:`dgl.nn.NodeEmbedding`. Being sparse means it only updates
  the embeddings whose gradients have updates, which are usually a very
  small portion of the total embeddings.
  
  Adagrad maintains a :math:`G_{t,i,j}` for every parameter in the embeddings, where
  :math:`G_{t,i,j}=G_{t-1,i,j} + g_{t,i,j}^2` and :math:`g_{t,i,j}` is the gradient of
  the dimension :math:`j` of embedding :math:`i` at step :math:`t`.
  
  NOTE: The support of sparse Adagrad optimizer is experimental.
  
  Parameters
  ----------
  params : list[dgl.nn.NodeEmbedding]
      The list of dgl.nn.NodeEmbedding.
  lr : float
      The learning rate.
  eps : float, Optional
      The term added to the denominator to improve numerical stability
      Default: 1e-10
  
  Examples
  --------
  >>> def initializer(emb):
          th.nn.init.xavier_uniform_(emb)
          return emb
  >>> emb = dgl.nn.NodeEmbedding(g.num_nodes(), 10, 'emb', init_func=initializer)
  >>> optimizer = dgl.optim.SparseAdagrad([emb], lr=0.001)
  >>> for blocks in dataloader:
  ...     ...
  ...     feats = emb(nids, gpu_0)
  ...     loss = F.sum(feats + 1, 0)
  ...     loss.backward()
  ...     optimizer.step()
docstr: Update embeddings in a sparse manner
  Sparse embeddings are updated in mini batches. We maintain gradient states for
  each embedding so they can be updated separately.
  
  Parameters
  ----------
  idx : tensor
      Index of the embeddings to be updated.
  grad : tensor
      Gradient of each embedding.
  emb : dgl.nn.NodeEmbedding
      Sparse embedding to update.
docstr_tok: ["Update","embeddings","in","a","sparse","manner","Sparse","embeddings","are","updated","in","mini","batches",".","We","maintain","gradient","states","for","each","embedding","so","they","can","be","updated","separately",".","Parameters","--","--","--","--","--","idx",":","tensor","Index","of","the","embeddings","to","be","updated",".","grad",":","tensor","Gradient","of","each","embedding",".","emb",":","dgl.nn.NodeEmbedding","Sparse","embedding","to","update","."]
code: def update(self, idx, grad, emb):
      """Update embeddings in a sparse manner
      Sparse embeddings are updated in mini batches. We maintain gradient states for
      each embedding so they can be updated separately.
  
      Parameters
      ----------
      idx : tensor
          Index of the embeddings to be updated.
      grad : tensor
          Gradient of each embedding.
      emb : dgl.nn.NodeEmbedding
          Sparse embedding to update.
      """
      eps = self._eps
      clr = self._lr
  
      # the update is non-linear so indices must be unique
      grad_indices, inverse, cnt = th.unique(
          idx, return_inverse=True, return_counts=True
      )
      grad_values = th.zeros(
          (grad_indices.shape[0], grad.shape[1]), device=grad.device
      )
      grad_values.index_add_(0, inverse, grad)
      grad_values = grad_values / cnt.unsqueeze(1)
  
      grad_sum = grad_values * grad_values
      (state,) = emb.optm_state
      state_dev = state.device
      state_idx = grad_indices.to(state_dev)
      grad_state = state[state_idx].to(grad.device)
      grad_state += grad_sum
      state[state_idx] = grad_state.to(state_dev)
  
      std_values = grad_state.add_(eps).sqrt_()
      tmp = clr * grad_values / std_values
      emb.weight[state_idx] -= tmp.to(state_dev)
  
imports: import abc
  from abc import abstractmethod
  import torch
  from ...cuda import nccl
  from ...nn.pytorch import NodeEmbedding
  from ...partition import NDArrayPartition
  from ...utils import create_shared_mem_array, gather_pinned_tensor_rows, get_shared_mem_array, pin_memory_inplace, scatter_pinned_tensor_rows
label_desc: Use image node_id 3 for calling the SparseAdagrad obj's underlying member method code with example usage: obj.update(idx, grad, emb) without return types