code: def structured_negative_sampling(
      edge_index: Tensor,
      num_nodes: Optional[int] = None,
      contains_neg_self_loops: bool = True,
  ) -> Tuple[Tensor, Tensor, Tensor]:
      r"""Samples a negative edge :obj:`(i,k)` for every positive edge
      :obj:`(i,j)` in the graph given by :attr:`edge_index`, and returns it as a
      tuple of the form :obj:`(i,j,k)`.
  
      Args:
          edge_index (LongTensor): The edge indices.
          num_nodes (int, optional): The number of nodes, *i.e.*
              :obj:`max_val + 1` of :attr:`edge_index`. (default: :obj:`None`)
          contains_neg_self_loops (bool, optional): If set to
              :obj:`False`, sampled negative edges will not contain self loops.
              (default: :obj:`True`)
  
      :rtype: (LongTensor, LongTensor, LongTensor)
  
      Example:
          >>> edge_index = torch.as_tensor([[0, 0, 1, 2],
          ...                               [0, 1, 2, 3]])
          >>> structured_negative_sampling(edge_index)
          (tensor([0, 0, 1, 2]), tensor([0, 1, 2, 3]), tensor([2, 3, 0, 2]))
  
      """
      num_nodes = maybe_num_nodes(edge_index, num_nodes)
  
      row, col = edge_index.cpu()
      pos_idx = row * num_nodes + col
      if not contains_neg_self_loops:
          loop_idx = torch.arange(num_nodes) * (num_nodes + 1)
          pos_idx = torch.cat([pos_idx, loop_idx], dim=0)
  
      rand = torch.randint(num_nodes, (row.size(0),), dtype=torch.long)
      neg_idx = row * num_nodes + rand
  
      mask = torch.from_numpy(np.isin(neg_idx, pos_idx)).to(torch.bool)
      rest = mask.nonzero(as_tuple=False).view(-1)
      while rest.numel() > 0:  # pragma: no cover
          tmp = torch.randint(
              num_nodes, (rest.size(0),), dtype=torch.long
          )
          rand[rest] = tmp
          neg_idx = row[rest] * num_nodes + tmp
  
          mask = torch.from_numpy(np.isin(neg_idx, pos_idx)).to(
              torch.bool
          )
          rest = rest[mask]
  
      return edge_index[0], edge_index[1], rand.to(edge_index.device)
  
file_path: pytorch_geometric/torch_geometric/utils/_negative_sampling.py
imports: import random
  from typing import Optional, Tuple, Union
  import numpy
  import torch
  from torch import Tensor
  from torch_geometric.utils import coalesce, cumsum, degree, remove_self_loops
  from torch_geometric.utils.num_nodes import maybe_num_nodes
label_desc: Use image node_id 3 for calling a global function with example usage: structured_negative_sampling(edge_index, num_nodes, contains_neg__loops) and returns: edge_index,  edge_index,  rand