code: def add_self_loop(g, edge_feat_names=None, fill_data=1.0, etype=None):
      r"""Add self-loops for each node in the graph and return a new graph.
  
      Parameters
      ----------
      g : DGLGraph
          The graph.
      edge_feat_names : list[str], optional
          The names of the self-loop features to apply `fill_data`. If None, it will apply `fill_data`
          to all self-loop features. Default: None.
      fill_data : int, float or str, optional
          The value to fill the self-loop features. Default: 1.
  
          * If ``fill_data`` is ``int`` or ``float``, self-loop features will be directly given by
            ``fill_data``.
          * if ``fill_data`` is ``str``, self-loop features will be generated by aggregating the
            features of the incoming edges of the corresponding nodes. The supported aggregation are:
            ``'mean'``, ``'sum'``, ``'max'``, ``'min'``.
      etype : str or (str, str, str), optional
          The type names of the edges. The allowed type name formats are:
  
          * ``(str, str, str)`` for source node type, edge type and destination node type.
          * or one ``str`` edge type name if the name can uniquely identify a
            triplet format in the graph.
  
          Can be omitted if the graph has only one type of edges.
  
      Return
      ------
      DGLGraph
          The graph with self-loops.
  
      Notes
      -----
      * The function only supports homogeneous graphs or heterogeneous graphs but
        the relation graph specified by the :attr:`etype` argument is homogeneous.
      * The function adds self-loops regardless of whether they already exist or not.
        If one wishes to have exactly one self-loop for every node,
        call :func:`remove_self_loop` before invoking :func:`add_self_loop`.
      * This function discards the batch information. Please use
        :func:`dgl.DGLGraph.set_batch_num_nodes`
        and :func:`dgl.DGLGraph.set_batch_num_edges` on the transformed graph
        to maintain the information.
  
      Examples
      --------
      >>> import dgl
      >>> import torch
  
      **Homogeneous Graphs**
  
      >>> g = dgl.graph((torch.tensor([0, 0, 2]), torch.tensor([2, 1, 0])))
      >>> g.ndata['hv'] = torch.arange(3).float().reshape(-1, 1)
      >>> g.edata['he'] = torch.arange(3).float().reshape(-1, 1)
      >>> g = dgl.add_self_loop(g, fill_data='sum')
      >>> g
      Graph(num_nodes=3, num_edges=6,
          ndata_schemes={'hv': Scheme(shape=(1,), dtype=torch.float32)}
          edata_schemes={'he': Scheme(shape=(1,), dtype=torch.float32)})
      >>> g.edata['he']
      tensor([[0.],
              [1.],
              [2.],
              [2.],
              [1.],
              [0.]])
  
      **Heterogeneous Graphs**
  
      >>> g = dgl.heterograph({
      ...     ('user', 'follows', 'user'): (torch.tensor([1, 2]),
      ...                                   torch.tensor([0, 1])),
      ...     ('user', 'plays', 'game'): (torch.tensor([0, 1]),
      ...                                 torch.tensor([0, 1]))})
      >>> g = dgl.add_self_loop(g, etype='follows')
      >>> g
      Graph(num_nodes={'user': 3, 'game': 2},
            num_edges={('user', 'plays', 'game'): 2, ('user', 'follows', 'user'): 5},
            metagraph=[('user', 'user'), ('user', 'game')])
      """
      etype = g.to_canonical_etype(etype)
      data = {}
      reduce_funcs = {
          "sum": function.sum,
          "mean": function.mean,
          "max": function.max,
          "min": function.min,
      }
  
      if edge_feat_names is None:
          edge_feat_names = g.edges[etype].data.keys()
  
      if etype[0] != etype[2]:
          raise DGLError(
              "add_self_loop does not support unidirectional bipartite graphs: {}."
              "Please make sure the types of head node and tail node are identical."
              "".format(etype)
          )
  
      for feat_name in edge_feat_names:
          if isinstance(fill_data, (int, float)):
              dtype = g.edges[etype].data[feat_name].dtype
              dshape = g.edges[etype].data[feat_name].shape
              tmp_fill_data = F.copy_to(
                  F.astype(F.tensor([fill_data]), dtype), g.device
              )
              if len(dshape) > 1:
                  data[feat_name] = (
                      F.zeros(
                          (g.num_nodes(etype[0]), *dshape[1:]),
                          dtype,
                          g.device,
                      )
                      + tmp_fill_data
                  )
              else:
                  data[feat_name] = (
                      F.zeros((g.num_nodes(etype[0]),), dtype, g.device)
                      + tmp_fill_data
                  )
  
          elif isinstance(fill_data, str):
              if fill_data not in reduce_funcs.keys():
                  raise DGLError(
                      "Unsupported aggregation: {}".format(fill_data)
                  )
              reducer = reduce_funcs[fill_data]
              with g.local_scope():
                  g.update_all(
                      function.copy_e(feat_name, "h"),
                      reducer("h", "h"),
                      etype=etype,
                  )
                  data[feat_name] = g.nodes[etype[0]].data["h"]
  
      nodes = g.nodes(etype[0])
      if len(data):
          new_g = add_edges(g, nodes, nodes, data=data, etype=etype)
      else:
          new_g = add_edges(g, nodes, nodes, etype=etype)
      return new_g
  
imports: import copy
  from collections.abc import Iterable, Mapping
  import numpy
  import scipy.sparse
  import scipy.sparse.linalg
  from ..utils import version
  from ..None import backend, batch, convert, function, ndarray, subgraph, utils
  from .._ffi.function import _init_api
  from ..base import dgl_warning, DGLError, EID, NID
  from ..frame import Frame
  from ..heterograph import DGLGraph
  from ..heterograph_index import create_heterograph_from_relations, create_metagraph_index
  from ..partition import metis_partition, metis_partition_assignment, partition_graph_with_halo
  from ..sampling.neighbor import sample_neighbors
label_desc: Use image node_id 21 for calling a global function with example usage: add_self_loop(g, edge_feat_names, fill_data, etype) and returns: new_g