code: def to_bidirected_with_reverse_mapping(g):
      """Convert the graph to bidirectional and return the reverse mapping.
  
      This function transforms the input graph into its bidirectional form. It
      then returns the newly formed bidirectional graph and the mapping that
      represents the reverse edges. The function does not work with graphs that
      have self-loops.
  
      Parameters:
      ----------
      g : DGLGraph
          Input graph.
  
      Returns:
      -------
      DGLGraph :
          Bidirectional graph.
      Tensor :
          Mapping to reverse edges.
      """
      # First, add reverse edges to the graph, effectively making it
      # bidirectional. Then, simplify the resulting graph by merging any duplicate
      # edges. The resulting simplified graph is stored in `g_simple`, and
      # `mapping` provides information on how edges in `g_simple` correspond to
      # edges in the original graph.
      g_simple, mapping = dgl.to_simple(
          dgl.add_reverse_edges(g),
          return_counts="count",
          writeback_mapping=True,
      )
  
      # The `return_counts` option in `dgl.to_simple` returns the count of how
      # many times each edge in the simplified graph corresponds to an edge in the
      # original graph. This count is saved in the edge data of the returned
      # graph with the key "count".
      c = g_simple.edata["count"]
      num_edges = g.num_edges()
  
      # `mapping_offset` is an auxiliary tensor used to understand how edges in
      # the simplified bidirectional graph (g_simple) relate to the edges in the
      # original graph.
      mapping_offset = torch.zeros(
          g_simple.num_edges() + 1, dtype=g_simple.idtype
      )
  
      # Calculate the cumulative sum of counts to determine boundaries for each
      # unique edge.
      mapping_offset[1:] = c.cumsum(0)
  
      # Sort the mapping tensor to group the same edge indices.
      idx = mapping.argsort()
  
      # Using the previously computed `mapping_offset`, it extracts the first
      # index of each group, which represents the unique edge indices from the
      # sorted mapping.
      idx_uniq = idx[mapping_offset[:-1]]
  
      # If an edge index is greater than or equal to the number of edges in the
      # original graph, it indicates that this edge is a reversed edge, and the
      # original edge index for it is (idx_uniq - num_edges). Otherwise, its
      # reverse edge index is (idx_uniq + num_edges).
      reverse_idx = torch.where(
          idx_uniq >= num_edges,
          idx_uniq - num_edges,
          idx_uniq + num_edges,
      )
      reverse_mapping = mapping[reverse_idx]
  
      # Sanity check to ensure valid mapping.
      src1, dst1 = g_simple.edges()
      src2, dst2 = g_simple.find_edges(reverse_mapping)
      assert torch.equal(src1, dst2)
      assert torch.equal(src2, dst1)
      return g_simple, reverse_mapping
  
imports: import argparse
  import time
  import dgl
  import dgl.nn
  import torch
  import torch.nn
  import torch.nn.functional
  import tqdm
  from dgl.dataloading import as_edge_prediction_sampler, DataLoader, MultiLayerFullNeighborSampler, negative_sampler, NeighborSampler
  from ogb.linkproppred import DglLinkPropPredDataset, Evaluator
label_desc: Use image node_id 1 for calling a global function with example usage: to_bidirected_with_reverse_mapping(g) and returns: g_simple,  reverse_mapping