code: def train(
      args,
      device,
      g,
      reverse_eids,
      seed_edges,
      model,
      use_uva,
      fused_sampling,
  ):
      #####################################################################
      # (HIGHLIGHT) Instantiate a NeighborSampler object for efficient
      # training of Graph Neural Networks (GNNs) on large-scale graphs.
      #
      # The argument [15, 10, 5] sets the number of neighbors (fanout)
      # to be sampled at each layer. Here, we have three layers, and
      # 15/10/5 neighbors will be randomly selected for each node at each
      # layer.
      #
      # The 'prefetch_node_feats' parameter specify the node features that
      # needs to be pre-fetched during sampling. More details about
      # `prefetch` can be found in the `SAGE.inference` function.
      #
      # (HIGHLIGHT) Modify the NeighborSampler for Edge Prediction
      #
      # This `as_edge_prediction_sampler` augments the original NeighborSampler
      # to specifically handle edge prediction tasks, where not only the
      # structure but also the relationships between nodes (edges) are of
      # importance.
      #
      # - `exclude="reverse_id"` ensures that the edges corresponding to the
      #   reverse of the original edges are excluded during sampling, given that
      #   reverse edges can introduce unnecessary redundancy in edge prediction.
      #
      # - `reverse_eids=reverse_eids` specifies the IDs of the reverse edges.
      #   This information is vital so the sampler knows which edges to avoid.
      #
      # - The negative sampling strategy is specified using the
      #   `negative_sampler`. Here, a uniform negative sampling method is
      #   employed, where a negative sample (an edge that doesn't exist in the
      #   original graph) is uniformly drawn from the set of all possible edges.
      #
      # The modified sampler is tailor-made for scenarios where the goal is
      # not just to learn node representations, but also to predict the
      # likelihood of an edge existing between two nodes (link prediction).
      #####################################################################
      sampler = NeighborSampler(
          [15, 10, 5],
          prefetch_node_feats=["feat"],
          fused=fused_sampling,
      )
      sampler = as_edge_prediction_sampler(
          sampler,
          exclude="reverse_id" if args.exclude_edges else None,
          reverse_eids=reverse_eids if args.exclude_edges else None,
          negative_sampler=negative_sampler.Uniform(1),
      )
  
      dataloader = DataLoader(
          g,
          seed_edges,
          sampler,
          device=device,
          batch_size=args.train_batch_size,
          shuffle=True,
          drop_last=False,
          # If `g` is on gpu or `use_uva` is True, `num_workers` must be zero,
          # otherwise it will cause error.
          num_workers=0,
          use_uva=use_uva,
      )
      opt = torch.optim.Adam(model.parameters(), lr=args.lr)
      for epoch in range(args.epochs):
          model.train()
          total_loss = 0
          start_epoch_time = time.time()
          # A block is a graph consisting of two sets of nodes: the
          # source nodes and destination nodes. The source and destination
          # nodes can have multiple node types. All the edges connect from
          # source nodes to destination nodes.
          # For more details: https://discuss.dgl.ai/t/what-is-the-block/2932.
          for it, (
              input_nodes,
              pair_graph,
              neg_pair_graph,
              blocks,
          ) in enumerate(dataloader):
              # The input features from the source nodes in the first layer's
              # computation graph.
              x = blocks[0].srcdata["feat"]
              pos_score, neg_score = model(
                  pair_graph, neg_pair_graph, blocks, x
              )
              score = torch.cat([pos_score, neg_score])
  
              # Create true labels for positive and negative samples.
              pos_label = torch.ones_like(pos_score)
              neg_label = torch.zeros_like(neg_score)
              labels = torch.cat([pos_label, neg_label])
  
              # Compute the binary cross-entropy loss.
              loss = F.binary_cross_entropy_with_logits(score, labels)
              opt.zero_grad()
              loss.backward()
              opt.step()
              total_loss += loss.item()
              if (it + 1) == args.early_stop:
                  break
          end_epoch_time = time.time()
          print(
              f"Epoch {epoch:05d} | "
              f"Loss {total_loss / (it + 1):.4f} | "
              f"Time {(end_epoch_time - start_epoch_time):.4f} s"
          )
  
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 4 for calling a global function with example usage: train(args, device, g, reverse_eids, seed_edges, model, use_uva, fused_sampling) without return types