code: def add_random_edge(
      edge_index: Tensor,
      p: float = 0.5,
      force_undirected: bool = False,
      num_nodes: Optional[Union[int, Tuple[int, int]]] = None,
      training: bool = True,
  ) -> Tuple[Tensor, Tensor]:
      r"""Randomly adds edges to :obj:`edge_index`.
  
      The method returns (1) the retained :obj:`edge_index`, (2) the added
      edge indices.
  
      Args:
          edge_index (LongTensor): The edge indices.
          p (float): Ratio of added edges to the existing edges.
              (default: :obj:`0.5`)
          force_undirected (bool, optional): If set to :obj:`True`,
              added edges will be undirected.
              (default: :obj:`False`)
          num_nodes (int, Tuple[int], optional): The overall number of nodes,
              *i.e.* :obj:`max_val + 1`, or the number of source and
              destination nodes, *i.e.* :obj:`(max_src_val + 1, max_dst_val + 1)`
              of :attr:`edge_index`. (default: :obj:`None`)
          training (bool, optional): If set to :obj:`False`, this operation is a
              no-op. (default: :obj:`True`)
  
      :rtype: (:class:`LongTensor`, :class:`LongTensor`)
  
      Examples:
          >>> # Standard case
          >>> edge_index = torch.tensor([[0, 1, 1, 2, 2, 3],
          ...                            [1, 0, 2, 1, 3, 2]])
          >>> edge_index, added_edges = add_random_edge(edge_index, p=0.5)
          >>> edge_index
          tensor([[0, 1, 1, 2, 2, 3, 2, 1, 3],
                  [1, 0, 2, 1, 3, 2, 0, 2, 1]])
          >>> added_edges
          tensor([[2, 1, 3],
                  [0, 2, 1]])
  
          >>> # The returned graph is kept undirected
          >>> edge_index, added_edges = add_random_edge(edge_index, p=0.5,
          ...                                           force_undirected=True)
          >>> edge_index
          tensor([[0, 1, 1, 2, 2, 3, 2, 1, 3, 0, 2, 1],
                  [1, 0, 2, 1, 3, 2, 0, 2, 1, 2, 1, 3]])
          >>> added_edges
          tensor([[2, 1, 3, 0, 2, 1],
                  [0, 2, 1, 2, 1, 3]])
  
          >>> # For bipartite graphs
          >>> edge_index = torch.tensor([[0, 1, 2, 3, 4, 5],
          ...                            [2, 3, 1, 4, 2, 1]])
          >>> edge_index, added_edges = add_random_edge(edge_index, p=0.5,
          ...                                           num_nodes=(6, 5))
          >>> edge_index
          tensor([[0, 1, 2, 3, 4, 5, 3, 4, 1],
                  [2, 3, 1, 4, 2, 1, 1, 3, 2]])
          >>> added_edges
          tensor([[3, 4, 1],
                  [1, 3, 2]])
      """
      if p < 0.0 or p > 1.0:
          raise ValueError(
              f"Ratio of added edges has to be between 0 and 1 "
              f"(got '{p}')"
          )
      if force_undirected and isinstance(num_nodes, (tuple, list)):
          raise RuntimeError(
              "'force_undirected' is not supported for "
              "bipartite graphs"
          )
  
      device = edge_index.device
      if not training or p == 0.0:
          edge_index_to_add = torch.tensor([[], []], device=device)
          return edge_index, edge_index_to_add
  
      edge_index_to_add = negative_sampling(
          edge_index=edge_index,
          num_nodes=num_nodes,
          num_neg_samples=round(edge_index.size(1) * p),
          force_undirected=force_undirected,
      )
  
      edge_index = torch.cat([edge_index, edge_index_to_add], dim=1)
  
      return edge_index, edge_index_to_add
  
imports: from typing import Optional, Tuple, Union
  import torch
  from torch import Tensor
  from torch_geometric.utils import cumsum, negative_sampling, scatter
label_desc: Use image node_id 3 for calling a global function with example usage: add_random_edge(edge_index, p, force_undirected, num_nodes, training) and returns: edge_index,  edge_index_to_add, edge_index,  edge_index_to_add