code: def to_simple(
      g,
      return_counts="count",
      writeback_mapping=False,
      copy_ndata=True,
      copy_edata=False,
      aggregator="arbitrary",
  ):
      r"""Convert a graph to a simple graph without parallel edges and return.
  
      For a heterogeneous graph with multiple edge types, DGL treats edges with the same
      edge type and endpoints as parallel edges and removes them.
      Optionally, one can get the the number of parallel edges by specifying the
      :attr:`return_counts` argument. To get the a mapping from the edge IDs in the
      input graph to the edge IDs in the resulting graph, set :attr:`writeback_mapping`
      to true.
  
      Parameters
      ----------
      g : DGLGraph
          The input graph.  Must be on CPU.
      return_counts : str, optional
          If given, the count of each edge in the original graph
          will be stored as edge features under the name
          ``return_counts``.  The old features with the same name will be replaced.
  
          (Default: "count")
      writeback_mapping: bool, optional
          If True, return an extra write-back mapping for each edge
          type. The write-back mapping is a tensor recording
          the mapping from the edge IDs in the input graph to
          the edge IDs in the result graph. If the graph is
          heterogeneous, DGL returns a dictionary of edge types and such
          tensors.
  
          If False, only the simple graph is returned.
  
          (Default: False)
      copy_ndata: bool, optional
          If True, the node features of the simple graph are copied
          from the original graph.
  
          If False, the simple graph will not have any node features.
  
          (Default: True)
      copy_edata: bool, optional
          If True, the edge features of the simple graph are copied
          from the original graph. If there exists duplicate edges between
          two nodes (u, v), the feature of the edge is the aggregation
          of edge feature of duplicate edges.
  
          If False, the simple graph will not have any edge features.
  
          (Default: False)
      aggregator: str, optional
          Indicate how to coalesce edge feature of duplicate edges.
          If ``arbitrary``, select one of the duplicate edges' feature.
          If ``sum``, compute the summation of duplicate edges' feature.
          If ``mean``, compute the average of duplicate edges' feature.
  
          (Default: ``arbitrary``)
  
      Returns
      -------
      DGLGraph
          The graph.
      tensor or dict of tensor
          The writeback mapping. Only when ``writeback_mapping`` is True.
  
      Notes
      -----
      If :attr:`copy_ndata` is True, the resulting graph will share the node feature
      tensors with the input graph. Hence, users should try to avoid in-place operations
      which will be visible to both graphs.
  
      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
      --------
      **Homogeneous Graphs**
  
      Create a graph for demonstrating to_simple API.
      In the original graph, there are multiple edges between 1 and 2.
  
      >>> import dgl
      >>> import torch as th
      >>> g = dgl.graph((th.tensor([0, 1, 2, 1]), th.tensor([1, 2, 0, 2])))
      >>> g.ndata['h'] = th.tensor([[0.], [1.], [2.]])
      >>> g.edata['h'] = th.tensor([[3.], [4.], [5.], [6.]])
  
      Convert the graph to a simple graph. The return counts is
      stored in the edge feature 'cnt' and the writeback mapping
      is returned in a tensor.
  
      >>> sg, wm = dgl.to_simple(g, return_counts='cnt', writeback_mapping=True)
      >>> sg.ndata['h']
      tensor([[0.],
              [1.],
              [2.]])
      >>> u, v, eid = sg.edges(form='all')
      >>> u
      tensor([0, 1, 2])
      >>> v
      tensor([1, 2, 0])
      >>> eid
      tensor([0, 1, 2])
      >>> sg.edata['cnt']
      tensor([1, 2, 1])
      >>> wm
      tensor([0, 1, 2, 1])
      >>> 'h' in g.edata
      False
  
      **Heterogeneous Graphs**
  
      >>> g = dgl.heterograph({
      ...     ('user', 'wins', 'user'): (th.tensor([0, 2, 0, 2, 2]), th.tensor([1, 1, 2, 1, 0])),
      ...     ('user', 'plays', 'game'): (th.tensor([1, 2, 1]), th.tensor([2, 1, 1]))
      ... })
      >>> g.nodes['game'].data['hv'] = th.ones(3, 1)
      >>> g.edges['plays'].data['he'] = th.zeros(3, 1)
  
      The return counts is stored in the default edge feature 'count' for each edge type.
  
      >>> sg, wm = dgl.to_simple(g, copy_ndata=False, writeback_mapping=True)
      >>> sg
      Graph(num_nodes={'game': 3, 'user': 3},
            num_edges={('user', 'wins', 'user'): 4, ('game', 'plays', 'user'): 3},
            metagraph=[('user', 'user'), ('game', 'user')])
      >>> sg.edges(etype='wins')
      (tensor([0, 2, 0, 2]), tensor([1, 1, 2, 0]))
      >>> wm[('user', 'wins', 'user')]
      tensor([0, 1, 2, 1, 3])
      >>> sg.edges(etype='plays')
      (tensor([2, 1, 1]), tensor([1, 2, 1]))
      >>> wm[('user', 'plays', 'game')]
      tensor([0, 1, 2])
      >>> 'hv' in sg.nodes['game'].data
      False
      >>> 'he' in sg.edges['plays'].data
      False
      >>> sg.edata['count']
      {('user', 'wins', 'user'): tensor([1, 2, 1, 1])
       ('user', 'plays', 'game'): tensor([1, 1, 1])}
      """
      assert g.device == F.cpu(), "the graph must be on CPU"
      if g.is_block:
          raise DGLError(
              "Cannot convert a block graph to a simple graph."
          )
      simple_graph_index, counts, edge_maps = _CAPI_DGLToSimpleHetero(
          g._graph
      )
      simple_graph = DGLGraph(simple_graph_index, g.ntypes, g.etypes)
      counts = [F.from_dgl_nd(count) for count in counts]
      edge_maps = [F.from_dgl_nd(edge_map) for edge_map in edge_maps]
  
      if copy_ndata:
          node_frames = utils.extract_node_subframes(g, None)
          utils.set_new_frames(simple_graph, node_frames=node_frames)
      if copy_edata:
          new_edge_frames = _coalesce_edge_frame(
              g, edge_maps, counts, aggregator
          )
          utils.set_new_frames(
              simple_graph, edge_frames=new_edge_frames
          )
  
      if return_counts is not None:
          for count, canonical_etype in zip(counts, g.canonical_etypes):
              simple_graph.edges[canonical_etype].data[
                  return_counts
              ] = count
  
      if writeback_mapping:
          # single edge type
          if len(edge_maps) == 1:
              return simple_graph, edge_maps[0]
          # multiple edge type
          else:
              wb_map = {}
              for edge_map, canonical_etype in zip(
                  edge_maps, g.canonical_etypes
              ):
                  wb_map[canonical_etype] = edge_map
              return simple_graph, wb_map
  
      return simple_graph
  
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 25 for calling a global function with example usage: to_simple(g, return_counts, writeback_mapping, copy_ndata, copy_edata, aggregator) and returns: simple_graph, simple_graph,  edge_maps, simple_graph,  wb_map