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()
code: def setup(self, params):
      # We need to register a state sum for each embedding in the kvstore.
      for emb in params:
          assert isinstance(
              emb, NodeEmbedding
          ), "SparseAdagrad only supports dgl.nn.NodeEmbedding"
  
          emb_name = emb.name
          if th.device(emb.weight.device) == th.device("cpu"):
              # if our embedding is on the CPU, our state also has to be
              if self._rank < 0:
                  state = th.empty(
                      emb.weight.shape,
                      dtype=th.float32,
                      device=th.device("cpu"),
                  ).zero_()
              elif self._rank == 0:
                  state = create_shared_mem_array(
                      emb_name + "_state", emb.weight.shape, th.float32
                  ).zero_()
  
                  if self._world_size > 1:
                      emb.store.set(emb_name + "_opt", emb_name)
              elif self._rank > 0:
                  # receive
                  emb.store.wait([emb_name + "_opt"])
                  state = get_shared_mem_array(
                      emb_name + "_state", emb.weight.shape, th.float32
                  )
          else:
              # distributed state on on gpu
              state = th.empty(
                  emb.weight.shape,
                  dtype=th.float32,
                  device=emb.weight.device,
              ).zero_()
          emb.set_optm_state((state,))
  
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 2 for calling the SparseAdagrad obj's underlying member method code with example usage: obj.setup(params) without return types